为了让项目更接近于“高内聚,低耦合”的状况,我提出了分步行动机制(Substep-Action,又称分步动作机制)。
以往的stamon都是一站式完成工作的,例如只需给定源码文件和目标文件,就能利用build指令直接编译(这在方便的同时带来了局限性)。
但是假设以下背景:
有开发者将stamon的词法加以改装(例如支持中文标识符、改名了关键字和运算符),并想要用stamon运行改装后的语言。
理想的解决方案是:由stamon制定一套词法单元文件的二进制编码格式,开发者只需开发一个词法分析器,将代码按照编码格式生成二进制词法单元文件,这样stamon就能直接通过这个文件进行语法分析(乃至编译运行)。
再考虑以下场景:
有开发者希望不生成目标文件,直接把编译的字节码存入内存中,然后交给虚拟机运行,以此达到“解释器”的效果。
理想的解决方案是:由stamon开发一套插件(STVCBufferWriter
和STVCBufferReader
),在用户使用时直接指定使用STVCBufferWriter
来把字节码写入内存,接着用STVCBufferReader
读取,直接交给虚拟机运行。
一句话:Stamon想要变得更加灵活,就必须支持自定义更多细节。
我在参考了函数式编程的思想后,提出了这个机制。
定义行动工具和行动物品两个概念。
分步行动机制有以下要点:
我们以编译过程为例,传统的编译过程可以被分解为以下行动工具:
文本文件读取器(用户给定文件名作为行动物品,生成一个行读取器)->词法分析器(接受到文本文件读取器传来的行阅读器,生成一个词法单元读取器)->语法分析器(接受到词法分析器传来的词法单元读取器,生成语法树)->ASTIR生成器(接收到语法分析器传来的语法树,生成Ast-IR)->AstIrWriter(接收到ASTIR生成器的Ast-IR,同时接受到用户指定的行动物品——目标文件名,将其以二进制形式写入用户给定的目标文件名)
行动工具生成的行动物品交给下一个行动工具处理,环环相扣。但是你不能让以下行动工具按顺序执行:
文本文件读取器(用户给定文件名作为行动物品,生成一个行阅读器)->语法分析器(接收到文本文件阅读器传来的行阅读器,语法分析所需要的行动物品应该是词法单元读取器,而非行阅读器,发生错误)
编译过程被分解之后,我们可以自由的执行行动流程:例如将文本文件阅读器换为终端输入器,这样用户可以直接在终端输入代码,输入特定的结束字符作为EOF
后就能被词法分析器当作正常的文件,进行后续的编译操作。这极大的扩展了stamon的灵活性。
回到前言,我们来看看分布行动机制能不能解决前言提出的问题:
对于第一个场景,我们可以指定以下行动工具:
词法单元文件读取器(用户给定文件名作为行动物品,生成一个词法单元读取器)->语法分析器(接收到词法单元文件读取器传来的词法单元阅读器,生成语法树)->……
由stamon制定词法单元文件编码格式,并开发相应的读取器。如此一来,用户只需把词法分析的行动工具替换掉即可正常编译。
对于第二个场景,我们可以指定以下行动工具:
文本文件阅读器(略)->……->ASTIR生成器(略)->STVCBufferWriter(接收到ASTIR生成器的Ast-IR,将其以二进制编码格式写入到内存中)->STVCBufferReader(接收到STVCBufferWriter所写入的内存的地址,生成Ast-IR)->ASTIR读取器(接收到Ast-IR,复原并生成Running-AST等运行信息)->AstRunner(接收到Running-AST等运行信息,开始运行)
由stamon开发STVCBufferWriter和STVCBufferReader。如此一来,用户只要把文件生成和文件处理这部分替换成STVCBufferWriter和STVCBufferReader即可实现“解释器”的效果。
目前,由用户传入的行动物品会在行动工具初始化的时候以参数的形式传给行动工具。
行动物品的基类为ActionItem
,其子类有:
行动工具的基类为ActionTool
,其子类有:
行动工具名 | 作用 | 接受的行动物品类型 | 生成的行动物品类型 |
---|---|---|---|
ParaSetterTool | 参数设置工具 | 只接受用户提供的物品,通常放在行动工具链的开头 | NoneItem |
LineReaderTool | 打开文件 | 只接受用户提供的物品(文件名) | LineReaderItem |
TokenReaderTool | 打开词法单元文件 | 只接受用户提供的物品(文件名) | TokenReaderItem |
TokenWriterTool | 写入词法单元文件 | 接受TokenReaderItem和用户提供的物品(文件名) | NoneItem |
LexerTool | 从文件开始词法分析 | 接受LineReaderItem以及用户加入的物品 | TokenReaderItem |
AstReaderTool | 打开语法树文件 | 只接受用户提供的物品(文件名) | AstItem |
AstWriterTool | 写入语法树文件 | 接受AstItem和用户提供的物品(文件名) | NoneItem |
ParserTool | 语法分析 | 接受TokenReaderItem以及用户加入的物品 | AstItem |
AstIrTool | 将Ast转为Ast-IR | 接受AstItem以及用户加入的物品 | AstIrItem |
AstIrWriterTool | 将Ast-IR写入二进制文件 | 接受AstIrItem和用户加入的物品(文件名) | NoneItem |
STVCBufferWriterTool | 将Ast-IR以二进制格式写入内存 | 接受AstIrItem以及用户加入的物品 | BufferItem |
AstIrReaderTool | 打开Ast-IR的STVC文件 | 只接受用户提供的物品(文件名) | AstRunnerItem |
STVCBufferReaderTool | 将指定的内存按照Ast-IR的二进制格式读取 | 接受BufferItem以及用户加入的物品 | AstRunnerItem |
AstRunnerTool | 运行Running-Ast | 接受AstRunnerItem以及用户加入的物品 | NoneItem |
优点:
缺点: