jvm与非jvm语言优劣_都灵JVM编程语言:使用ANTLR构建高级词法分析器
jvm與非jvm語言優(yōu)劣
正如我在上一篇文章中所寫的那樣,我最近開始研究一種名為Turin的新編程語言。 可以在GitHub上找到適用于languag初始版本的編譯器。 我目前正在改善語言,并正在開發(fā)Maven和IntelliJ插件。 在這里和下一篇文章中,我將介紹編譯器和相關(guān)工具的不同組件。
編譯器的結(jié)構(gòu)
編譯器需要做幾件事:
第一步需要構(gòu)建兩個組件:詞法分析器和解析器。 詞法分析器對文本進(jìn)行操作并生成標(biāo)記序列,而解析器將標(biāo)記組合到創(chuàng)建AST的構(gòu)造(類型聲明,語句,表達(dá)式等)中。 為了編寫詞法分析器和解析器,我使用了ANTLR。
在本文的其余部分中,我們將研究詞法分析器。 解析器和編譯器的其他組件將在以后的文章中討論。
為什么要使用ANTLR?
ANTLR是用于編寫詞法分析器和解析器的非常成熟的工具。 它可以生成多種語言的代碼,并具有不錯的性能。 它維護(hù)良好,我確信它具有處理可能遇到的所有極端情況所需的所有功能。 除此之外,ANTLR 4可以編寫簡單的語法,因為它可以為您解決左遞歸定義。 因此,您不必編寫許多中間節(jié)點類型即可為表達(dá)式指定優(yōu)先級規(guī)則。 我們將在分析器中對此進(jìn)行更多介紹。
Xtext(我已經(jīng)使用過很多)使用ANTLR,并且在為.NET平臺 (一種用于.NET的EMF)建立模型驅(qū)動的開發(fā)框架時 ,我使用了ANTLR。 因此,我知道并信任ANTLR,因此沒有理由尋找其他選擇。
當(dāng)前的詞法分析器語法
這是詞法分析器語法的當(dāng)前版本。
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;// Let's 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)!='{' }?;我已經(jīng)做了一些選擇:
- 有兩種不同類型的ID: VALUE_ID和TYPE_ID。 由于可以容易地區(qū)分值和類型,因此語法上的歧義性較小。 在Java中,當(dāng)遇到(foo)時,我們不知道它是表達(dá)式(對括號之間foo表示的值的引用)還是強(qiáng)制轉(zhuǎn)換為foo類型。 我們需要看下面的內(nèi)容才能理解它。 我認(rèn)為這很愚蠢,因為實際上每個人都只對類型使用大寫的標(biāo)識符,但是由于這不是由語言強(qiáng)制執(zhí)行的,因此編譯器無法利用它
- 換行符與都靈相關(guān),因此我們有針對它們的令牌,我們基本上希望語句以換行符終止,但我們在逗號后接受可選的換行符
- 空格(但換行符)和注釋是在它們自己的通道中捕獲的,因此我們可以在解析器語法中忽略它們,但可以在需要時檢索它們。 例如,我們需要它們來突出顯示語法,并且通常需要IntelliJ插件,因為它需要為源文件中的每個單個字符定義標(biāo)記,而沒有間隙
- 最棘手的部分是在Ruby中解析字符串插值,例如“我的名字是#{user.name}”。 我們使用模式:遇到字符串開始(“)時,我們切換到詞法分析器模式IN_STRING。 在IN_STRING模式下,如果遇到插值(#{)的開始,我們將移至詞法分析器模式IN_INTERPOLATION。 在IN_INTERPOLATION模式下,我們需要接受表達(dá)式中使用的大多數(shù)標(biāo)記(這在詞法分析器語法中意味著很多重復(fù))。
- 我不得不將關(guān)系運算符折疊為一個單一的令牌類型,以使生成的詞法分析器的狀態(tài)數(shù)不會太大。 這意味著我將不得不查看RELOP令牌的文本,以確定需要執(zhí)行哪個操作。 沒什么可怕的,但是您必須知道如何解決此類問題。
測試詞法分析器
我寫了許多針對詞法分析器的測試。 特別是,我測試了最復(fù)雜的部分:有關(guān)字符串插值的部分。
一些測試的示例:
@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);}如您所見,我只是在字符串上測試令牌并驗證它是否生成了正確的令牌列表。 簡單直接。
結(jié)論
我在ANTLR上使用該語言的經(jīng)驗并不完美:存在問題和局限性。 必須在單個令牌類型中折疊多個運算符并不好。 必須為不同的詞法分析器模式重復(fù)幾個標(biāo)記定義是不好的。 但是,ANTLR被證明是在實踐中可用的工具:它可以完成它需要做的所有事情,并且對于每個問題都有一個可接受的解決方案。 解決方案可能不是理想的,可能不是理想的解決方案,但是有一個。 因此,我可以使用它并繼續(xù)進(jìn)行編譯器中更有趣的部分。
翻譯自: https://www.javacodegeeks.com/2015/09/turin-programming-language-for-the-jvm-building-advanced-lexers-with-antlr.html
jvm與非jvm語言優(yōu)劣
總結(jié)
以上是生活随笔為你收集整理的jvm与非jvm语言优劣_都灵JVM编程语言:使用ANTLR构建高级词法分析器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 车辆通行备案(通道汽车备案)
- 下一篇: linux被入侵如何溯源(linux 被