在上一章(第三章)中我们用纯手工的方式构造了一个Pascal的扫描器(也称词法分析器)。细心的读者会想到,大部分语言的词法构造过程都差不多,都有变量ID,字符串,整数,浮点数,关键字,特殊符号等(如比较符,赋值,索引括号)等等。事实上在编译技术发展到今天,手写词法分析器基本很少了,因为编程语言的词不同于自然语言,很容易通过机械的手段实现。有很多工具可以生成词法分析器,比如Flex,JavaCC等。不过在本书中,不管词法还是语法还是代码生成,我将使用Antlr(http://www.antlr.org)这个大名鼎鼎的工具来完成后续的代码自动化工作。关于Antlr的基础我就不介绍了,网上有很多的教程。这儿我推荐两个:《使用 Antlr 开发领域语言 - 开发一个完整的应用》,《The Definitive ANTLR Reference》

Antlr的Lexer和Parser还有AST属于自动化产物,自成体系。编译器/解释器的非自动化部分都是通过"hook"手段灌进Antlr语法中。如果使用Antlr,将会抛弃原有框架但是复用原有的代码和逻辑,再者项目也从纯粹的Java项目变成了Maven项目。

1 创建一个Antlr文件Pascal.g,首先是抬头,我们使用了不同于PascalToken的另外一个Token类型PascalAntlrToken,因为对于Antlr来说,只能继承Antlr的CommonToken,而PascalToken继承自Token。

1: grammar Pascal;2: options{3: TokenLabelType=PascalAntlrToken;4: output=AST;5: }6: tokens{7: NUMBER_REAL;8: }

这里为简单使用了混合语法,即Lexer和Parser的语法放在一起。因为Antlr认为词法的前向过程(LL)和语法的前向过程基本一样,这两个只是单位不一样,一个是character,一个是token。输出为AST,现在暂且不用管它。调用maven命令之后,此语法文件会生成两个Java文件:PascalLexer和PascalParser。

2 因为Pascal大小写不敏感,必须让Antlr支持忽略大小写:

1: fragment A:('a'|'A');2: fragment B:('b'|'B');3: fragment C:('c'|'C');4: ...5: ...6: fragment X:('x'|'X');7: fragment Y:('y'|'Y');8: fragment Z:('z'|'Z');

3 建立Pascal关键字列表:

1: AND : A N D ;2: ARRAY : A R R A Y ;3: BEGIN : B E G I N ;4: CASE : C A S E ;5: CHAR : C H A R ;6: CHR : C H R ;7: CONST : C O N S T ;8: DIV : D I V ;9: DO : D O ;10: DOWNTO : D O W N T O ;11: ELSE : E L S E ;12: END : E N D ;13: FILE : F I L E ;14: FOR : F O R ;15: FUNCTION : F U N C T I O N;16: GOTO : G O T O ;17: IF : I F ;18: IN : I N ;19: INTEGER : I N T E G E R;20: LABEL : L A B E L ;21: MOD : M O D ;22: NIL : N I L ;23: NOT : N O T ;24: OF : O F ;25: OR : O R ;26: PACKED : P A C K E D ;27: PROCEDURE : P R O C E D U R E;28: PROGRAM : P R O G R A M;29: REAL : R E A L ;30: RECORD : R E C O R D ;31: REPEAT : R E P E A T ;32: SET : S E T ;33: THEN : T H E N ;34: TO : T O ;35: TYPE : T Y P E ;36: UNTIL : U N T I L ;37: VAR : V A R ;38: WHILE : W H I L E ;39: WITH : W I T H ;4 建立特殊符号列表:

1: PLUS : '+' ;2: MINUS : '-' ;3: STAR : '*' ;4: SLASH : '/' ;5: ASSIGN : ':=' ;6: COMMA : ',' ;7: SEMI : ';' ;8: COLON : ':' ;9: EQUAL : '=' ;10: NOT_EQUAL : '<>' ;11: LT : '=' ;14: GT : '>' ;15: LPAREN : '(' ;16: RPAREN : ')' ;17: LBRACK : '[' ;18: LBRACK2 : '(.' ;19: RBRACK : ']' ;20: RBRACK2 : '.)' ;21: POINTER : '^' ;22: AT : '@' ;23: DOT : '.' ;24: DOTDOT25: : '..' ;26: LCURLY : '{' ;27: RCURLY : '}' ;

5 上一章讲述了三种Token,单词Token,数字Token和字符串Token。对于单词Token来说,关键字已经在第三条列出,剩下的只是标识符即ID了。

标识符 Token:

ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*

表示由一个字母开头,后续是组成ID的字母和数字,还有下划线。

字符串Token:

STRING

: '\'' ('\'\'' | ~('\''))* '\'' ;

这里考虑到了两个连续单引号的问题。

数字Token:

1: NUMBER2: : ('0'..'9')+3: ( ( {(input.LA(2)!='.')&&(input.LA(2)!=')')}?4: '.' {$type = NUMBER_REAL;}5: ('0'..'9')+ (EXPONENT)?6: )?7: | EXPONENT {$type= NUMBER_REAL;}8: )9: ;10: fragment11: EXPONENT12: : ('e') ('+'|'-')? ('0'..'9')+13: ;

这个数字Token词法稍微复杂了点,但与PascalNumberToken里的extract方法基本一样,同样有整数部分,小数点,小数部分和指数部分。在第4行和第7行红色部分我们修改了Token的Type为预定义的NUMBER_REAL。NUMBER默认表示整数(Integer)。碰到小数点和(或)指数就表示实数(Real)了

6 空白和注释的处理

空白和注释在词法分析里面是要被“处理”掉的,也就是这个只有阅读上的意义,基本对语法和语义没影响。

空白:

1: WS : ( ' '2: | '\t'3: | '\f'4: | ( '\r\n'5: | '\r'6: | '\n'7: )8: {9: }10: )11: { $channel=HIDDEN; }12: ;

注意11行有一个$channel=HIDDEN的Antlr Action,这就是要被Antlr忽略掉的意思。

注释:

1: COMMENT2: : '{'3: (4: : '\r' '\n'5: | '\r'6: | '\n'7: | ~('}' | '\n' | '\r')8: )*9: '}'10: {$channel=HIDDEN;}11: ;

在2行有一个标明注释的大括号,第9行有一个结束的反大括号。注释里面包含了各种操作系统的换行符,比如 \r(苹果MAC),\n(类Unix),和\r\n(Win系列),所以这种注释是可以跨行的。

上一章中的Token常量token比如字符串和数字,我个人认为token只表示字面上的意思,即这样写对不对,至于逻辑(比如数字大小)是否正确,那应该是运行时的行为,还有好多编译优化都会讲常量放入常量池中,而不是每个都计算。这里虽然用了带value域的PascalAntlrToken,但我们不准备在词法阶段就将常量词的值计算出来,使用了一个占位方法ValueComputer.computerToken()来与前章意义统一。

7 错误恢复目前暂且不适用,到时候联合语法错误恢复一起。

8 运行源代码中的ShowToken类看输出效果,下面是从hello.pas文件中抽取的token。

--hello.pas----------------

1行:PROGRAM[0] ID[8] LPAREN[14] ID[15] RPAREN[21] SEMI[22]

5行:VAR[0]

6行:ID[4] COLON[6] INTEGER[8] SEMI[15]

8行:BEGIN[0]

9行:FOR[4] ID[8] ASSIGN[10] NUMBER[13] TO[15] NUMBER[18] DO[21] BEGIN[24]

10行:ID[8] LPAREN[15] STRING[16] RPAREN[31] SEMI[32]

11行:END[4] SEMI[7]

12行:END[0] DOT[11]

每个token标识了它的类型,括号里面的数字表示它在当前行中的其实位置。

9 在Pascal.g文件中有@lex::members的类容,它表示覆盖lexer中token的输出。从默认的CommonToken改成继承自CommonTOken的PascalAntlrToken。

编译器构造java语言描述pdf_(基于Java)编写编译器和解释器-第3A章:基于Antlr构造词法分析器(连载)...相关推荐

  1. 基于栈的后缀表达式求值(洛谷P1449题题解,Java语言描述)

    题目要求 P1449题目链接 分析 给出后缀表达式求值,就相当于没了括号,而且很直接. 我们需要使用栈结构来完成任务. Java原装的栈使用了很多synchronized,导致性能不佳,但我们也就将就 ...

  2. 用Map构造选择题题库(洛谷P5601题题解,Java语言描述)

    题目要求 P5601题目链接 分析 用Map把题和答案一一对应,先做出来题库. 然后读题目,每读一行就用split()切成字符串数组,然后从Map里取答案作比较,得到选择题答案. AC代码(Java语 ...

  3. [SpecialJudge]构造“神秘“字符串(洛谷P3742题题解,Java语言描述)

    题目要求 P3742题目链接 分析 这题挺有意思,是SpecialJudge,因为可能有多个答案,判题的时候出现一个就OK--测试用例貌似是足足有50个之多呢-- 要求是,找一个y使f(x,y)=z, ...

  4. 涂国旗(洛谷P3392题题解,Java语言描述)

    题目要求 题目链接 分析 我做的就比较暴力啦,由于三种颜色都至少有一行,所以: W:[0,N−2)W:[0, N-2)W:[0,N−2) B:[1,N−1)B: [1, N-1)B:[1,N−1) R ...

  5. 去重的Set解不出“斯诺登的密码”(洛谷P1603题题解,Java语言描述)

    题目要求 P1603题目链接 分析 我们不如直接获取其平方数,这个就要基于HashMap啦,提前指定一下初始长度! 像10啊,20啊这种模完只剩00的,就不要了吧(他说是要最小的结果,开头0要去掉,那 ...

  6. 复试分数线该怎么划定呢(洛谷P1068题题解,Java语言描述)

    题目要求 P1068题目链接 分析 本题类似于此题,可以包装成一个类,然后利用自己定义的排序规则基于TreeSet去排序然后输出. 在实现的时候注意两个细节: *1.5以后要向下取整,这个是必须做的. ...

  7. 巧用HashSet装载非重数据(洛谷P2250题题解,Java语言描述)

    题目要求 P2550题目链接 分析 其实既然是Java来写,不用集合框架就是浪费啊!! 比较简单的思路是把中奖号码放进HashSet里,利用Hash来查找. contains()就避免了又双叒叕疯狂遍 ...

  8. 到天宫做客(洛谷P1178题题解,Java语言描述)

    题目要求 题目链接 分析 早期代码大概是这么写的,先是用了Date,结果连样例都过不去,就换了日期数组自己做,还是不对. import java.util.*;public class Main {p ...

  9. 高手追小萝莉的故事(洛谷P1184题题解,Java语言描述)

    题目要求 题目链接 分析 吐槽: 无时无刻都要跟着?这还追个锤子? 数据点1是:第一行1 1:第2行WC:第三行WC:追到女厕所-- 数据换行问题和空格问题真的恶心!!! 好了这个题,其实就用一个便于 ...

最新文章

  1. 一个复杂系统的拆分改造,压力真大!
  2. 移植uboot第八步:裁剪、修改默认参数、分区
  3. 原生JS实现图片滚动
  4. python怎样使用各个日期赤纬_python--日期操作
  5. 安装NTOP网络监控工具(wheezy)
  6. Netflix正在搞的混沌工程到底是什么?终于有人讲明白了
  7. 西门子opc服务器注册,《西门子opc服务器的说明》.pdf
  8. [Tip]ActiveScaffold本地化
  9. 第八届蓝桥杯省赛真题--最大公共子串
  10. 合并两个有序数组(C语言)
  11. 题解 P1774 【最接近神的人_NOI导刊2010提高(02)】
  12. 批处理检测局域网电脑是否开机(转载)
  13. js验证开始日期不能大于结束日期_js如何判断开始时间不能大于结束时间
  14. 10种常见的HTML标签错误写法
  15. docker的容器下使用apt-get update卡在[0%] Working问题解决办法
  16. 深度学习框架tensorflow学习与应用6(防止过拟合dropout,keep_prob =tf.placeholder(tf.float32))
  17. Cortex-M系列处理器对比
  18. 网页版JS游戏五子棋单机版也可以改版成网络版
  19. 阴阳师系统转移开放服务器,阴阳师:运营商倒闭后?原来只是子账号数据转移,可迁移到官服...
  20. 【微服务】SpringCloud微服务环境搭建

热门文章

  1. 国二c语言大纲,2014年全国计算机等级考试二级C语言考试大纲
  2. 猿创征文 | 国产数据实战之docker部署MyWebSQL数据库管理工具
  3. springboot easypoi 报错The bean 'beanNameViewResolver', defined in class path resource [cn/afterturn/e
  4. MySql 查询某某老师教授的课名和教室
  5. 区块链+游戏新玩法:BitRichman--游戏与现实的联动
  6. 安装sql serve2016+JDK配置环境变量+解决KB2919355报错最全教程
  7. lexaiduer psd_简约自然风清新零食早餐麦片食品燕麦详情_psd素材免费下载_ 790*6368像素(编号:24565017)...
  8. 您没有足够的权限为该计算机所有用户,没有足够的权限卸载请与系统管理员联系怎么解决...
  9. mint linux qq,linux mint 1.9 qq 安装
  10. 如何提取Redis中的大KEY