网站 稳定性,做药的常用网站有哪些,百姓网站外推广怎么做,网站维护服务器正如我在上一篇文章中所写的那样#xff0c;我最近开始研究一种名为Turin的新编程语言。 可以在GitHub上找到适用于languag初始版本的编译器。 我目前正在改进语言#xff0c;并正在开发Maven和IntelliJ插件。 在这里和下一篇文章中#xff0c;我将介绍编译器和相关工具的不… 正如我在上一篇文章中所写的那样我最近开始研究一种名为Turin的新编程语言。 可以在GitHub上找到适用于languag初始版本的编译器。 我目前正在改进语言并正在开发Maven和IntelliJ插件。 在这里和下一篇文章中我将介绍编译器和相关工具的不同组件。 编译器的结构 编译器需要做几件事 获取源代码并生成抽象语法树AST 通过不同阶段转换AST以简化处理。 我们基本上希望从非常接近语法的表示形式过渡到更易于处理的表示形式。 例如我们可以对语言进行“去糖化”将几种显然不同的结构表示为同一结构的变体。 一个例子 Java编译器将字符串连接转换为对StringBuffer.append的调用 执行语义检查。 例如我们要检查所有表达式是否都使用可接受的类型我们不想对字符求和对吗 产生字节码 第一步需要构建两个组件词法分析器和解析器。 词法分析器对文本进行操作并生成标记序列而解析器将标记组合到用于创建AST的构造类型声明语句表达式等中。 为了编写词法分析器和解析器我使用了ANTLR。 在本文的其余部分我们将研究词法分析器。 解析器和编译器的其他组件将在以后的文章中讨论。 为什么要使用ANTLR ANTLR是用于编写词法分析器和解析器的非常成熟的工具。 它可以生成多种语言的代码并具有良好的性能。 它维护良好我确信它具有处理可能遇到的所有极端情况所需的所有功能。 除此之外ANTLR 4可以编写简单的语法因为它可以为您解决左递归定义。 因此您不必编写许多中间节点类型即可为表达式指定优先级规则。 我们将在分析器中对此进行更多介绍。 Xtext使用了ANTLR我已经使用了很多并且在为.NET平台 一种用于.NET的EMF构建模型驱动的开发框架时 我使用了ANTLR。 因此我知道并信任ANTLR因此没有理由寻找其他选择。 当前的词法分析器语法 这是词法分析器语法的当前版本。 lexer grammar TurinLexer;header {}lexer::members {public static final int WHITESPACE 1;public static final int COMMENTS 2;
}// It is suggested to define the token types reused in different mode.
// See mode in-interpolation below
tokens { VALUE_ID, TYPE_ID, INT, LPAREN, RPAREN, COMMA, RELOP, AND_KW, OR_KW, NOT_KW }// Of course keywords has to be defined before the rules for identifiers
NAMESPACE_KW : namespace;
PROGRAM_KW : program;
PROPERTY_KW : property;
TYPE_KW : type;
VAL_KW : val;
HAS_KW : has;
ABSTRACT_KW : abstract;
SHARED_KW : shared;
IMPORT_KW : import;
AS_KW : as;
VOID_KW : Void;
RETURN_KW : return;
FALSE_KW : false;
TRUE_KW : true;
IF_KW : if;
ELIF_KW : elif;
ELSE_KW : else;// For definitions reused in mode in-interpolation we define and refer to fragments
AND_KW : F_AND;
OR_KW : F_OR;
NOT_KW : F_NOT;LPAREN : (;
RPAREN : );
LBRACKET : {;
RBRACKET : };
LSQUARE : [;
RSQUARE : ];
COMMA : ,;
POINT : .;
COLON : :;
// We use just one token type to reduce the number of states (and not crash Antlr...)
// https://github.com/antlr/antlr4/issues/840
EQUAL : - type(RELOP);
DIFFERENT : ! - type(RELOP);
LESSEQ : - type(RELOP);
LESS : - type(RELOP);
MOREEQ : - type(RELOP);
MORE : - type(RELOP);
// ASSIGNMENT has to comes after EQUAL
ASSIGNMENT : ;
// Mathematical operators cannot be merged in one token type because
// they have different precedences
ASTERISK : *;
SLASH : /;
PLUS : ;
MINUS : -;PRIMITIVE_TYPE : F_PRIMITIVE_TYPE;
BASIC_TYPE : F_BASIC_TYPE;VALUE_ID : F_VALUE_ID;
// Only for types
TYPE_ID : F_TYPE_ID;
INT : F_INT;// Lets switch to another mode here
STRING_START : - pushMode(IN_STRING);WS : ( | \t) - channel(WHITESPACE);
NL : \r? \n;COMMENT : /* .*? */ - channel(COMMENTS);LINE_COMMENT : // ~[\r\n]* - channel(COMMENTS);mode IN_STRING;STRING_STOP : - popMode;
STRING_CONTENT : (~[\\#]|ESCAPE_SEQUENCE|SHARP);
INTERPOLATION_START : #{ - pushMode(IN_INTERPOLATION);mode IN_INTERPOLATION;INTERPOLATION_END : } - popMode;
I_PRIMITIVE_TYPE : F_PRIMITIVE_TYPE - type(PRIMITIVE_TYPE);
I_BASIC_TYPE : F_BASIC_TYPE - type(BASIC_TYPE);
I_FALSE_KW : false - type(FALSE_KW);
I_TRUE_KW : true - type(TRUE_KW);
I_AND_KW : F_AND - type(AND_KW);
I_OR_KW : F_OR - type(OR_KW);
I_NOT_KW : F_NOT - type(NOT_KW);
I_IF_KW : if - type(IF_KW);
I_ELSE_KW : else - type(ELSE_KW);
I_VALUE_ID : F_VALUE_ID - type(VALUE_ID);
I_TYPE_ID : F_TYPE_ID - type(TYPE_ID);
I_INT : F_INT - type(INT);
I_COMMA : , - type(COMMA);
I_LPAREN : ( - type(LPAREN);
I_RPAREN : ) - type(RPAREN);
I_LSQUARE : [ - type(LSQUARE);
I_RSQUARE : ] - type(RSQUARE);I_ASTERISK : * - type(ASTERISK);
I_SLASH : / - type(SLASH);
I_PLUS : - type(PLUS);
I_MINUS : - - type(MINUS);I_POINT : . - type(POINT);
I_EQUAL : - type(RELOP);
I_DIFFERENT : ! - type(RELOP);
I_LESSEQ : - type(RELOP);
I_LESS : - type(RELOP);
I_MOREEQ : - type(RELOP);
I_MORE : - type(RELOP);
I_STRING_START : - type(STRING_START), pushMode(IN_STRING);
I_WS : ( | \t) - type(WS), channel(WHITESPACE);fragment F_AND : and;
fragment F_OR : or;
fragment F_NOT : not;
fragment F_VALUE_ID : (_)*a..z (A..Z | a..z | 0..9 | _)*;
// Only for types
fragment F_TYPE_ID : (_)*A..Z (A..Z | a..z | 0..9 | _)*;
fragment F_INT : 0|((1..9)(0..9)*);
fragment F_PRIMITIVE_TYPE : Byte|Int|Long|Boolean|Char|Float|Double|Short;
fragment F_BASIC_TYPE : UInt;fragment ESCAPE_SEQUENCE : \\r|\\n|\\t|\\|\\\\;
fragment SHARP : #{ _input.LA(1)!{ }?; 我已经做了一些选择 有两种不同类型的ID VALUE_ID和TYPE_ID。 由于可以轻松地区分值和类型因此语法上的歧义性较小。 在Java中当遇到foo时我们不知道它是表达式对括号之间foo表示的值的引用还是类型foo的强制转换。 我们需要看下面的内容才能理解它。 我认为这很愚蠢因为实际上每个人都只对类型使用大写的标识符但是由于这不是由语言强制执行的因此编译器无法从中受益 换行符与都灵相关因此我们有针对它们的标记我们基本上希望语句以换行符终止但我们在逗号后接受可选的换行符 空格但换行符和注释是在它们自己的通道中捕获的因此我们可以在解析器语法中忽略它们但可以在需要时检索它们。 例如我们需要它们来突出显示语法并且通常需要IntelliJ插件因为它需要为源文件中的每个单个字符定义标记而没有空格 最棘手的部分是在Ruby中解析字符串插值例如“我的名字是{user.name}”。 我们使用模式遇到字符串开始“时我们切换到词法分析器模式IN_STRING。 在IN_STRING模式下如果遇到插值{的开始我们将移至词法分析器模式IN_INTERPOLATION。 在IN_INTERPOLATION模式下我们需要接受表达式中使用的大多数标记可悲的是这意味着我们的词法分析器语法有很多重复。 我不得不将关系运算符折叠为一种令牌类型以使生成的词法分析器的状态数不会太大。 这意味着我将不得不查看RELOP令牌的文本以确定需要执行哪个操作。 没什么可怕的但是您必须知道如何解决此类问题。 测试词法分析器 我写了很多针对词法分析器的测试。 特别是我测试了最复杂的部分有关字符串插值的部分。 一些测试的示例 Testpublic void parseStringWithEmptyInterpolation() throws IOException {String code \Hel#{}lo!\;verify(code, TurinLexer.STRING_START, TurinLexer.STRING_CONTENT, TurinLexer.INTERPOLATION_START, TurinLexer.INTERPOLATION_END, TurinLexer.STRING_CONTENT, TurinLexer.STRING_STOP);}Testpublic void parseStringWithInterpolationContainingID() throws IOException {String code \Hel#{foo}lo!\;verify(code, TurinLexer.STRING_START, TurinLexer.STRING_CONTENT, TurinLexer.INTERPOLATION_START,TurinLexer.VALUE_ID,TurinLexer.INTERPOLATION_END, TurinLexer.STRING_CONTENT, TurinLexer.STRING_STOP);}Testpublic void parseStringWithSharpSymbol() throws IOException {String code \Hel#lo!\;verify(code, TurinLexer.STRING_START, TurinLexer.STRING_CONTENT, TurinLexer.STRING_STOP);}Testpublic void parseMethodDefinitionWithExpressionBody() throws IOException {String code Void toString() \foo\;verify(code, TurinLexer.VOID_KW, TurinLexer.VALUE_ID, TurinLexer.LPAREN, TurinLexer.RPAREN, TurinLexer.ASSIGNMENT, TurinLexer.STRING_START, TurinLexer.STRING_CONTENT, TurinLexer.STRING_STOP);} 如您所见我只是在字符串上测试令牌并验证它是否生成了正确的令牌列表。 简单而直接。 结论 我在ANTLR上使用该语言的经验并不完美存在问题和局限性。 必须在单个令牌类型中折叠多个运算符并不好。 必须为不同的词法分析器模式重复几个标记定义是不好的。 但是ANTLR被证明是在实践中可用的工具它可以完成它需要做的所有事情并且对于每个问题都有一个可接受的解决方案。 解决方案可能不是理想的也许不是理想的解决方案但是有一个解决方案。 因此我可以使用它并继续进行编译器中更有趣的部分。 翻译自: https://www.javacodegeeks.com/2015/09/turin-programming-language-for-the-jvm-building-advanced-lexers-with-antlr.html