【整理】什么是lexer,什么是parser,lexer和parser之间的关系

【背景】

折腾:

【未解决】antlr解析字符串STRING出错:no viable alternative at input,对应的错误是NoViableAltException(0@[null])

的过程中,需要搞懂antlr中的lexer和parser。到底是什么。以及两者之间的关系。

【解决过程】

1.参考:

Lexical analysis

了解到。

  • 输入:一堆字符,即我们写的语法代码,一个文件,也就是一堆字符;
  • 经过lexer处理;
  • 输出:一系列的token,相当于一堆的变量或者说符号;

 

lexer也被叫做:

  • lexical analyzer
  • scanner

 

2.后来参考:

【资料下载】ANTLR的最全的官方文档:The Definitive ANTLR Reference:v2,v3,v4版本都有下载

去找到了antlr v3的文档。

看其解释,更加深入了解到了一些内容:

(1)lexer和parser的语法是相同的,但是内部含义不同。

即,最好是自己真正搞懂内部,底层所涉及的含义如何。

即,写了个语法,要知道真正的含义。

 

(2)Lexer的rule,是以大写字母开头的。

比如:

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

所以,自己去antlrworks中,把之前的某个,小写字母开头的规则,从小写:

small letter begin is parser rule

改为大写后,antlrworks中,果然就识别出不同效果,从parser变成lexer了:

change first letter to capital so become to lexer

 

3. 为了区别rule所生成的方法,antlr会给生成的id前加m。

比如,上面的ID所生成的方法叫做mID()

 

4.parser有起始符号,start symbol(类似于入口函数);

lexer没有start symbol。

 

5.lexer的语法,其实,就是一堆的符号定义(token definition)

每个token,都是,针对于输入的内容,全局性的,任何时刻都试用,都去匹配的。

内部机制:

antlr生成一个nextToken()的方法,其中就是个大的switch结构,将输入的内容,匹配到对应的类型的token后,即路由到某个lexer的rule,就去调用对应的rule去处理。

 

6.不论对于lexer还是parser,其中的大的rule,都最好拆分为小的rule。

目的:使得程序可读性增加,重用性增加。

 

7.antlr默认所有的token都是有效(valid)的。

如果你打算某个规则,只是起到帮助作用,即helper rule,那么就应该加上对应的fragment前缀。

目的在于,告诉antlr,此rule,只是被其他(lexer)的rule调用,而不会被传递到parser中。(也不希望被parser中调用?)

举例:

UNICODE_CHAR
    : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;
fragment
HEX_DIGIT
    : '0'..'9' |'a'..'f' |'A'..'F'
    ;

中的HEX_DIGIT,就是,只在lexer中用,只被lexer中的,其他的rule,此处是UNICODE_CHAR调用。

而不会在生成的token中存在。

对应的 ,如果此处的UNICODE_CHAR,也只是希望被别的lexer中的rule调用的话,则也应该加上对应的fragment前缀。

 

8.由于antlr支持多个通道(channel),有了这种机制,antlr甚至是可以实现这种情况:

对于解析Java代码过程中:

对于白空格和普通的注释代码,将其输出到一个通道(channel)中;

而对于JavDoc类的注释代码,则将其输出到另外一个独立的通道中;

 

10.

 

注:

关于antrl的语法的解释,更多详细内容见:

【整理】antlr中的各种语法:集合元素(Element Sets),标签元素(Element Labels),构造树操作符(Tree construction operators)



发表评论

电子邮件地址不会被公开。 必填项已用*标注

无觅相关文章插件,快速提升流量