Parsing 为什么会发生此错误?-&引用;以下选项永远无法匹配”;

Parsing 为什么会发生此错误?-&引用;以下选项永远无法匹配”;,parsing,antlr,antlrworks,Parsing,Antlr,Antlrworks,我对制作编译器、解析器和解析器生成器感兴趣,但对它们了解不多 在阅读了的答案后,我尝试制作一个“非常”简单的LaTeX解析器 代码如下: grammar Latex; latex : ITEM*; ITEM : CMD|LAWTEXT; CMD : CHEAD ARGS; CHEAD : '\\' LETTER(LETTER|DIGIT)*; LETTER : 'A'..'Z'|'a'..'z'; DIGIT : '0'|'1'|'2'|'3'|'4

我对制作编译器、解析器和解析器生成器感兴趣,但对它们了解不多

在阅读了的答案后,我尝试制作一个“非常”简单的LaTeX解析器

代码如下:

grammar Latex;

latex   :   ITEM*;
ITEM    :   CMD|LAWTEXT;
CMD :   CHEAD ARGS;
CHEAD   :   '\\' LETTER(LETTER|DIGIT)*;
LETTER  :   'A'..'Z'|'a'..'z';
DIGIT   :   '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9';
ARGS    :   '{' ITEM* '}';
LAWTEXT :   (LETTER|DIGIT|WHITESPACE|PUNC)*;
WHITESPACE
    :   ' '|'\t'|'\n'|'\r';
PUNC    :   '!'|'^';
(出于测试目的,PUNC中只有两个字符)

这是错误消息:

[18:39:09] warning(200): C:\Users\***\Documents\Latex.g:9:12: Decision can match input such as "{'\t'..'\n', '\r', ' '..'!', '0'..'9', 'A'..'Z', '\\', '^', 'a'..'z', '}'}" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input
[18:39:09] error(201): C:\Users\***\Documents\Latex.g:9:12: The following alternatives can never be matched: 2

[18:39:09] error(211): C:\Users\***\Documents\Latex.g:1:8: [fatal] rule Tokens has non-LL(*) decision due to recursive rule invocations reachable from alts 1,2.  Resolve by left-factoring or using syntactic predicates or using backtrack=true option.
我发现发生这种错误是因为存在歧义,代码可以用两种以上的方式解释,但我不知道这种歧义是如何产生的

这是一张图表,有两种解释方式(可能)

。。。但是如何将
\
}
混为一谈呢

JiminP写道:

我对制作编译器、解析器和解析器生成器感兴趣,但对它们了解不多

ANTLR根据您编写的语法为您创建一个词法分析器和解析器。ANTLR本身就是解析器生成器,所以您不需要编写解析器生成器本身(幸运的是!)。编译器是一个应用程序,它接受解析器生成的树,并将输入转换为其他形式:这是您需要自己做的事情。因此,要强调的是:ANTLR只帮助您为您的语言创建解析器,其余的由您决定

现在,问题来了

你的语法几乎只包含词法规则。Lexer规则以大写字母开头,用于标记输入源。所以像这样的规则:

LETTER  :   'A'..'Z'|'a'..'z';
...
LAWTEXT :   (LETTER|DIGIT|WHITESPACE|PUNC)*;
可能导致lexer自己创建
字母
标记。如果您总是希望小写或大写ascii字母成为
LAWTEXT
标记,则需要将
字母
设置为如下片段规则:

fragment LETTER  :   'A'..'Z'|'a'..'z';
...
LAWTEXT :   (LETTER|DIGIT|WHITESPACE|PUNC)+;
正如您所看到的,我用
+
而不是
*
结束了
LAWTEXT
规则:您不想创建不包含任何内容的令牌(空字符串)

另外,
args
item
cmd
也不适合使用lexer规则:它们应该是解析器规则

下面是一个生成lexer和parser的语法,没有任何错误:

grammar Latex;

latex
  :  item* EOF
  ;

item 
  :  cmd
  |  LAWTEXT
  ;

cmd
  :  CHEAD args
  ;

args
  :  '{' item* '}'
  ;

CHEAD 
  :  '\\' LETTER (LETTER | DIGIT)*
  ;  

LAWTEXT
  :  (LETTER | DIGIT | WHITESPACE | PUNC)+
  ;

fragment  
WHITESPACE 
  :  ' ' | '\t' | '\n' | '\r'
  ;

fragment  
PUNC       
  : '!' | '^'
  ;

fragment
LETTER
  :  'A'..'Z' | 'a'..'z'
  ;

fragment
DIGIT
  :  '0'..'9'
  ;
编辑 正如我已经提到的:lexer规则以大写字母开始,而解析器规则以小写字母开始。lexer有时被称为标记器或扫描器,负责截断输入源。输入源一开始只是一个字符流。这些字符然后由lexer组合在一起。因此,考虑到以下lexer规则:

Identifier
  :  (Letter | '_') (Letter | '_' | Digit)*
  ;

Assign
  :  '='
  ;

Number
  :  Digit+ ('.' Digit+)?
  ;

fragment Digit
  :  '0'..'9'
  ;

fragment Letter
  :  'a'..'z' | 'A'..'Z'
  ;

Spaces
  :  (' ' | '\t' | '\r' | '\n') {skip();}
  ;
可以采用如下输入源:

foo = 12.34
lexer将其视为:

'f', 'o', 'o', ' ', '=', ' ', '1', '2', '.', '3', '4', EOF
并将创建以下令牌:

  • 标识符“foo”
  • 分配“=”
  • 编号“12.34”
(注意,没有从空白处创建令牌:我跳过了这些!)

lexer从您的输入源创建令牌后,解析器将被传递这些令牌。然后,分配解析器规则可能如下所示:

assignment
  :  Identifier Assign Number
  ;
重要的是要记住,输入源首先由lexer标记,只有在该过程之后,解析器规则才发挥作用

JiminP写道:

我对制作编译器、解析器和解析器生成器感兴趣,但对它们了解不多

ANTLR根据您编写的语法为您创建一个词法分析器和解析器。ANTLR本身就是解析器生成器,所以您不需要编写解析器生成器本身(幸运的是!)。编译器是一个应用程序,它接受解析器生成的树,并将输入转换为其他形式:这是您需要自己做的事情。因此,要强调的是:ANTLR只帮助您为您的语言创建解析器,其余的由您决定

现在,问题来了

你的语法几乎只包含词法规则。Lexer规则以大写字母开头,用于标记输入源。所以像这样的规则:

LETTER  :   'A'..'Z'|'a'..'z';
...
LAWTEXT :   (LETTER|DIGIT|WHITESPACE|PUNC)*;
可能导致lexer自己创建
字母
标记。如果您总是希望小写或大写ascii字母成为
LAWTEXT
标记,则需要将
字母
设置为如下片段规则:

fragment LETTER  :   'A'..'Z'|'a'..'z';
...
LAWTEXT :   (LETTER|DIGIT|WHITESPACE|PUNC)+;
正如您所看到的,我用
+
而不是
*
结束了
LAWTEXT
规则:您不想创建不包含任何内容的令牌(空字符串)

另外,
args
item
cmd
也不适合使用lexer规则:它们应该是解析器规则

下面是一个生成lexer和parser的语法,没有任何错误:

grammar Latex;

latex
  :  item* EOF
  ;

item 
  :  cmd
  |  LAWTEXT
  ;

cmd
  :  CHEAD args
  ;

args
  :  '{' item* '}'
  ;

CHEAD 
  :  '\\' LETTER (LETTER | DIGIT)*
  ;  

LAWTEXT
  :  (LETTER | DIGIT | WHITESPACE | PUNC)+
  ;

fragment  
WHITESPACE 
  :  ' ' | '\t' | '\n' | '\r'
  ;

fragment  
PUNC       
  : '!' | '^'
  ;

fragment
LETTER
  :  'A'..'Z' | 'a'..'z'
  ;

fragment
DIGIT
  :  '0'..'9'
  ;
编辑 正如我已经提到的:lexer规则以大写字母开始,而解析器规则以小写字母开始。lexer有时被称为标记器或扫描器,负责截断输入源。输入源一开始只是一个字符流。这些字符然后由lexer组合在一起。因此,考虑到以下lexer规则:

Identifier
  :  (Letter | '_') (Letter | '_' | Digit)*
  ;

Assign
  :  '='
  ;

Number
  :  Digit+ ('.' Digit+)?
  ;

fragment Digit
  :  '0'..'9'
  ;

fragment Letter
  :  'a'..'z' | 'A'..'Z'
  ;

Spaces
  :  (' ' | '\t' | '\r' | '\n') {skip();}
  ;
可以采用如下输入源:

foo = 12.34
lexer将其视为:

'f', 'o', 'o', ' ', '=', ' ', '1', '2', '.', '3', '4', EOF
并将创建以下令牌:

  • 标识符“foo”
  • 分配“=”
  • 编号“12.34”
(注意,没有从空白处创建令牌:我跳过了这些!)

lexer从您的输入源创建令牌后,解析器将被传递这些令牌。然后,分配解析器规则可能如下所示:

assignment
  :  Identifier Assign Number
  ;

请务必记住,输入源首先由lexer标记,只有在该过程之后,解析器规则才会发挥作用。

谢谢,但是您能解释lexer和解析器之间的区别吗?我不知道他们之间的区别…:(谢谢,但你能解释一下lexer和parser之间的区别吗?我不知道它们之间的区别…:(