-
Notifications
You must be signed in to change notification settings - Fork 3
CodeGen最终可以获取一个中间表示,它的类型是YukimiScript.Parser.Intermediate
。
编译前将会使用System.IO.File.ReadAllLines
函数按行读取所有要编译的文件和--lib
参数中定义的库。
对加载的每个文件,执行YukimiScript.Parser.Parser.parseLines: string[] -> Result<Parsed list, (int * exn) list>
。
如果解析成功,将会返回Result.Ok (Parsed list)
,其中Parsed
是单行解析结果,包含了解析结果YukimiScript.Elements.Line
,和这一行的注释。
如果解析失败,将会返回Result.Error ((int * exn) list)
,这是一个异常列表,其中int
的部分为此异常发生的行号,exn
则是此行发生的异常,异常请参见后文“异常处理”。
对于加载的每个文件,如果上一步解析成功,则应该可以得到一个Parsed list
,之后需要将其组织为具有层级结构的DOM。
可以使用YukimiScript.Parser.Dom.analyze: fileName: string -> parsed: Parsed seq -> Result<Dom>
来生成当前文件的DOM。
其中fileName
参数用于为DOM附加调试信息,parsed
则是上一步生成的按行解析的解析结果,之后返回一个Result<Dom>
。
对于从--lib
加载的文件,需要对这些文件进行合并,以方便后续在其中查找外部定义和宏。
YukimiScript.Parser.Dom.merge: Dom -> Dom -> Dom
可用于合并两个Dom,而YukimiScript.Parser.Dom.empty: Dom
则是预定义的一个空Dom,可以使用fold
操作来将一组Dom合并为一个。
当合并结束后,应当对其进行合法性检查:
当以上合并完成后,再将其合并到当前待编译的DOM中。
YukimiScript.Parser.Dom.expandTextCommands: Dom -> Dom
函数实现了对整个Dom中所有的Scene执行文本命令的展开。
对于每个待编译文件的DOM,需要消除其文本语法,将其变换为命令调用语法,即将所有的YukimiScript.Parser.Elements.TextBlock
变换为YukimiScript.Parser.Elements.CommandCall
的序列,对于文字语法和命令的对应关系,需要参考“系统外部定义参考”中“文字相关”的部分。
YukimiScript.Parser.Text.toCommands: TextBlock -> CommandCall list
可将单个TextBlock
变换为一组CommandCall
序列,而YukimiScript.Parser.Text.expandTextBlock: TextBlock -> DebugInformation -> Block
函数用于在展开的同时为其添加合适的调试信息。
YukimiScript.Parser.Dom.expandUserMacros: src: Dom -> Dom
实现了对整个Dom进行宏展开,由于之前已经将lib合并入待编译的DOM,因此可以查找到lib中的宏。
对于DOM中Scenes
中的Block
部分,执行YukimiScript.Parser.Macro.expandBlock: (MacroDefination * Block) list -> Block -> Result<(Operation * DebugInformation) list>
会在给定的(MacroDefination * Block) list中搜索宏并对Block进行宏展开,它会调用YukimiScript.Parser.Macro.expandSingleOperation: (MacroDefination * Block) list -> Operation -> Result<Block>
对单个操作进行宏展开。
对于单个Operation,首先检查它是否为CommandCall,如果不是,则原样输出不做展开,如果是,则执行以下操作:
- 使用
YukimiScript.Parser.Macro.matchMacro: DebugInformation -> CommandCall -> (MacroDefination * 'a) list -> Result<MacroDefination * 'a * (string * Constant) list>
找到一个匹配到的宏,并返回此宏、宏的实现及以键值对构成的参数列表。其中参数匹配是由YukimiScript.Parser.Macro.matchArguments: DebugInformation -> Parameter list -> CommandCall -> Result<(string * Constant> list>
函数实现的。 - 将宏的实现取出,对其每个Operation,将形式参数替换为实际参数,此操作通过
YukimiScript.Parser.Macro.replaceParamToArgs: (string * Constant) list -> CommandCall -> CommandCall
函数实现。
之后需要进行合法性检查:
- 不应当存在重复定义的externs与macros
此操作由YukimiScript.Parser.Diagram.analyze: (string * Dom) list -> Result<Diagram>)
函数实现,其需要传入一组待分析的脚本及其文件名,返回一个Diagram
表示的图。
之后由YukimiScript.Parser.Diagram.exportDgml: Diagram -> string
函数产生DGML有向图文件,或者使用YukimiScript.Parser.Diagram.exportMermaid: Diagram -> string
生成Mermaid Flowchart图文件。
关于DGML有向图文件,可以参考微软的文档:https://docs.microsoft.com/zh-cn/visualstudio/modeling/directed-graph-markup-language-dgml-reference?view=vs-2019
生成的Mermaid Flowchart图,可以嵌入到Markdown文档中,关于Mermaid,请查看:https://github.com/mermaid-js/mermaid
此操作由YukimiScript.Parser.Dom.expandSystemMacros: Dom -> Dom
函数实现,它将调用YukimiScript.Parser.Macro.expandSystemMacros: Block -> Block
函数实现这个操作。
目前它仅仅用于消除__diagram_link_to
宏。
此操作由YukimiScript.Parser.Dom.linkToExternCommands
函数实现,它首先会载入系统外部定义,系统外部定义在YukimiScript.Parser.Dom.systemCommands
定义。
它将会消除所有由键值对构成的参数,并将其按顺序以匿名参数的方式传入,同时以默认值补全没有传入的参数。
以上DOM将会被剔除所有在CodeGen阶段不需要的信息,只保留场景定义信息和对外部定义的调用,最终产生一个YukimiScript.Parser.Intermediate
经过以上处理后,Intermediate便可提交给CodeGen生成目标代码,关于目标代码请参阅“目标代码”。
可以参考Lua的CodeGen:https://github.com/Strrationalism/YukimiScript/blob/main/YukimiScript.CodeGen.Lua/Lua.fs
对于大多数异常,都使用exn
类型进行处理,在YukimiScript.Parser
的Types.fs
文件中定义了YukimiScript.Parser.Result
,为F#中Result<'a, exn>
类型的缩写。
对于YukimiScript.Parser
中产生的大多数异常,可以在YukimiScript.Parser.ErrorStringing
模块中产生一个可读的字符串说明。