jvm与非jvm语言优劣

正如我在上一篇文章中所写的那样,我最近开始研究一种名为Turin的新编程语言。 可以在GitHub上找到适用于languag初始版本的编译器。 我目前正在改善语言,并正在开发Maven和IntelliJ插件。 在这里和下一篇文章中,我将介绍编译器和相关工具的不同组件。

编译器的结构

编译器需要做几件事:

  1. 获取源代码并生成抽象语法树(AST)
  2. 通过不同阶段转换AST以简化处理。 我们基本上希望从非常接近语法的表示形式过渡到更易于处理的表示形式。 例如,我们可以对语言进行“去糖化”,将几种(显然)不同的结构表示为同一结构的变体。 一个例子? Java编译器将字符串连接转换为对StringBuffer.append的调用
  3. 执行语义检查。 例如,我们要检查所有表达式是否都使用可接受的类型(我们不想对字符求和,对吗?)
  4. 产生字节码

第一步需要构建两个组件:词法分析器和解析器。 词法分析器对文本进行操作并生成标记序列,而解析器将标记组合到创建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;// 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)!='{' }?;

我已经做了一些选择:

  • 有两种不同类型的ID: VALUE_IDTYPE_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

jvm与非jvm语言优劣

jvm与非jvm语言优劣_都灵JVM编程语言:使用ANTLR构建高级词法分析器相关推荐

  1. 都灵JVM编程语言:使用ANTLR构建高级词法分析器

    正如我在上一篇文章中所写的那样,我最近开始研究一种名为Turin的新编程语言. 可以在GitHub上找到适用于languag初始版本的编译器. 我目前正在改进语言,并正在开发Maven和Intelli ...

  2. jvm与jit编译器的区别_了解jvm和jit编译器的第1部分

    jvm与jit编译器的区别 Hello people!! 大家好!! I can see that the Java community is growing a lot but there are ...

  3. jvm类加载机制和类加载器_在JVM之下–类加载器

    jvm类加载机制和类加载器 在许多开发人员中,类加载器是Java语言的底层,并且经常被忽略. 在ZeroTurnaround上 ,我们的开发人员必须生活,呼吸,饮食,喝酒,并且几乎与类加载器保持亲密关 ...

  4. python十大语言领域_十大编程语言榜单

    如果你是软件开发领域的新手,那么你会想到的第一个问题是"如何开始?"编程语言有数百种可供选择,但是你怎么发现哪个最适合你,你的兴趣和职业目标又在哪里呢?选择最佳编程语言以学习的最简 ...

  5. jvm与非jvm语言优劣_JVM语言的兴衰

    jvm与非jvm语言优劣 时不时有一篇文章预测Java语言的消亡. 有趣的是,他们都没有写日期. 但老实说,它们可能都是真实的. 这是每种语言的命运:消失于遗忘-或更确切地说,越来越少地用于新项目. ...

  6. jvm内存结构_聊聊JVM内存结构

    起因 我们经常会在面试的时候被问到JVM的内存结构,很多人会觉得这东西真的有用吗?也就是面试造火箭,入职拧螺丝.问这个就是纯粹来刁难人的吧. 但实际上,我们细想一下. •假设你不知道局部变量实际上属于 ...

  7. JVM上篇_01_JVM与Java体系结构_尚硅谷

    我们为什要学习JVM 面试的需要(BATJ.TMA等面试都爱问) 中高级程序员必备技能 项目管理.调优的需要 追求极客的京城 比如垃圾回收算法.JIT.底层原理 1-前言 大部分Java开发人员,除了 ...

  8. JVM内存管理------JAVA语言的内存管理概述

    转载自  JVM内存管理------JAVA语言的内存管理概述 引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上 ...

  9. jvm高并发_在JVM上对高并发HTTP服务器进行基准测试

    jvm高并发 在第一篇关于HTTP客户端的文章 (我将您重定向到JVM上的高效HTTP的介绍)之后,现在让我们来谈谈HTTP 服务器 . 有一些关于HTTP服务器的基准测试,但通常受到诸如以下缺点的阻 ...

最新文章

  1. Object​.assign()
  2. 【翻译】TCP backlog在Linux中的工作原理
  3. OpenCASCADE:Modeling Data之形状的属性
  4. 《剑指offer》按之字行顺序打印二叉树
  5. 蚂蚁金服支付平台代码配置
  6. mysql优化(九)
  7. Spring –持久层–编写实体并配置Hibernate
  8. android tv nugat,GitHub - GongXunYoung/Android-tv-widget: Android tv,盒子,投影仪 控件
  9. 步步为营-75-Cookie简介
  10. 用Aria2加速下载数据集等
  11. visual basic是不是计算机语言,辉煌不再!Microsoft计划结束Visual Basic编程语言
  12. 红黑树时间复杂度证明(O(lgn))
  13. 小米手机Android怎么截屏,小米手机怎么截屏 5种截屏方法分享
  14. IE 零日漏洞风险评估
  15. python cookbook 读书笔记2(字符串处理2)
  16. 永恒之蓝(Eternal Blue)复现
  17. 7-6 查询水果价格
  18. P3332 [ZJOI2013]K大数查询 - 整体二分-区间修改
  19. 亚马逊影响者红人,用关联视频给卖家带来哪些好处?
  20. FileZilla FTP服务器源代码分析

热门文章

  1. Loj#2035-[SDOI2016]征途【斜率优化】
  2. 牛客练习赛 57——manacher算法 树形dp?
  3. 【Trie】最长异或路径(ybtoj Trie-3/luogu 4551)
  4. 战争尾声(nowcoder 215073)
  5. 开关灯(jzoj 3926)
  6. 洛谷-P3396 哈希冲突 分块
  7. Hadoop生态Flume(三)拦截器(Interceptor)介绍与使用(1)
  8. 常用公有云接入——谷歌
  9. JavaFX UI控件教程(二十四)之Password Field
  10. 小菜:关于main方法的面试题