Irony - 语言实现工具包/语法/终结符
终结符是扫描器识别的标记,并传递给解析器。Irony 提供了一些关键的终结符,这些终结符几乎存在于每一种编程语言中(注释、标识符、字符串文字等)。
这些终结符已在 Grammar 基类中定义
Empty
用于识别非终结符中的可选元素
term.Rule = term1 | Empty;
Eof
标识文件结束(在语法规则中使用 Eof 是可选的,解析器会自动将此符号作为前瞻添加到 Root 非终结符)
LineStartTerminal
用于错误标记
SyntaxError
用于错误标记
以下用于缩进敏感语言,如 Python。它们不是由扫描器生成的,而是由扫描后和解析前 CodeOutlineFilter 生成的
NewLine
Indent
表示缩进
Dedent
表示缩进的结束
Eos
语句结束终结符 - 用于缩进敏感语言,用于指示语句结束。它并不总是与 CRLF 字符同步,CodeOutlineFilter 会根据传入内容标记中的行/列信息仔细生成 Eos 标记(以及 Indent 和 Dedent)。
注释终结符允许您轻松地声明语言中注释的定义。大多数语言至少提供行注释,但许多语言允许使用块注释的概念。
要设置任何一种注释终结符,只需声明一个新的 CommentTerminal 类型并设置开始和结束字符。
行注释示例
CommentTerminal LINE_COMMENT = new CommentTerminal("LINE_COMMENT", "--", "\n", "\r\n");
块注释示例
CommentTerminal BLOCK_COMMENT = new CommentTerminal("BLOCK_COMMENT", "/*", "*/");
如果您希望扫描器基本上忽略您的注释终结符,以便它们不会出现在您的解析树中,那么将它们添加到非语法终结符列表中。
NonGrammarTerminals.Add(BLOCK_COMMENT);
NonGrammarTerminals.Add(LINE_COMMENT);
此终结符允许在输入语言中声明一组常量。
当常量符号看起来不像普通标识符时,应该使用它;例如,在 Scheme 中,#t、#f 是真/假常量,它们不符合 Scheme 标识符模式。
ConstantTerminal CONSTANT = new ConstantTerminal("CONSTANT");
CONSTANT.Add("#t", true);
CONSTANT.Add("#f", false);
DataLiteralBase DATETIME = new DataLiteralBase("DATETIME", TypeCode.DateTime);
标识符终结符将识别源代码中以正常标准方式表示变量的标记(即以下划线或字母开头,仅包含字母、数字和下划线),但可以配置为识别其他非标准的变量表达方式。
IdentifierTerminal IDENTIFIER = new IdentifierTerminal("IDENTIFIER");
内置数字文字终结符可以识别多种类型的数字,从简单的整数(例如 1)到小数(例如 1.0)到以科学记数法表示的数字(例如 1.1e2)。
NumberLiteral NUMBER = new NumberLiteral("NUMBER");
使用此终结符识别字符串文字;只需设置开始/结束字符。
StringLiteral STRING = new StringLiteral("STRING", "\"", StringOptions.IsTemplate);
StringLiteral 终结符的一个有用属性是它能够将字符串视为模板,并解析其中嵌入的表达式,如 Ruby 中那样。只需设置 IsTemplate 选项,然后为它提供一个设置类,告诉它如何查找这些表达式。您的表达式根(用于解析嵌入表达式的非终结符)也需要添加到 SnippetRoots 列表中。
在此示例中,创建了一个新的 StringTemplateSettings,其中任何用花括号({ 和 })包围的表达式都被视为表达式(“表达式”是非终结符,充当根表达式)
StringTemplateSettings stringTemplateSettings = new StringTemplateSettings();
stringTemplateSettings.StartTag = "{";
stringTemplateSettings.EndTag = "}";
stringTemplateSettings.ExpressionRoot = expression;
this.SnippetRoots.Add(expression);
STRING.AstNodeConfig = stringTemplateSettings;
关键字终结符可以以两种方式声明:在变量声明中显式使用 ToTerm 方法,或在生产规则中隐式声明。
在 SQL 中显式声明关键字 SELECT,然后在 SELECT 语句生产中使用它
KeyTerm SELECT = ToTerm("select");
selectStatement.Rule = SELECT + optionalSelectArgs + FROM + ... + SEMICOLON;
在 SQL 中的 SELECT 语句生产中隐式声明
selectStatement.Rule = ToTerm("select") + optionalSelectArgs + ToTerm("from") + ... + ToTerm(";");
您以与关键字相同的方式定义运算符作为终结符。您可以使用 Grammar 基类中的 RegisterOperators 方法定义这些运算符的结合性和优先级。
示例指示简单二元运算符的结合性和优先级
RegisterOperators(6, Associativity.Right, POW);
RegisterOperators(5, MULT, DIV);
RegisterOperators(4, PLUS, MINUS);
您可以使用 Grammar 基类中的 MarkPunctuation 方法告诉扫描器和解析器您的语言中哪些终结符用作标点符号。通常,这些是左括号和右括号字符或花括号字符等终结符。
示例指示哪些终结符充当标点符号(假设 LPAREN、RPAREN、LBRACE 和 RBRACE 是预先定义的 KeyTerm 对象)
MarkPunctuation(LPAREN, RPAREN, LBRACE, RBRACE);
如果您对内置的终结符不满意,可以创建自己的终结符。只需扩展 Irony.Parsing.Terminal,然后开始使用。如果您需要对内置终结符进行一些调整以适应您的语言,但不能通过简单地设置现有属性来做到这一点,您也可以扩展内置终结符。