【问题标题】:Parsing Cool Language with antlr, cant print the desired out put用 antlr 解析酷语言,无法打印所需的输出
【发布时间】:2014-10-30 12:06:39
【问题描述】:

我正在为 COOL(面向对象的课堂语言)编写解析器/词法分析器。您可以在以下链接中查看语法:(手册的最后一页)

http://theory.stanford.edu/~aiken/software/cool/cool-manual.pdf

我正在使用 ANTLR 编写此程序,并通过以下输入,我希望得到以下输出:

输入:

class Main inherits IO {
  main(): Object {{
    x <- 2 + 3 *4;
  }};
};

输出:

1
2
3
11
6
16
27
18
27
27

但我得到的输出是:

1
2
3
11
6
27
16
27
18
27

这是我的解析器/词法分析器代码:

// parser
grammar CA2;

program : {System.out.println("1");} (classdef';')+ ;
classdef : {System.out.println("2");} CLASS ID (INHERITS ID)? '{' (feature';')* '}' ;
feature : {System.out.println("3");} ID OPENP (formal (','formal)*)? CLOSEP ':' ID '{' expr '}' 
        | {System.out.println("4");} ID ':' ID ( POINTTOLEFT expr )? ;
formal : {System.out.println("5");} ID ':' ID ;
expr : {System.out.println("6");} ID POINTTOLEFT expr exprprime
     | {System.out.println("8");} ID OPENP ( expr (','expr)* )? CLOSEP exprprime
     | {System.out.println("9");} IF expr THEN expr ELSE expr FI exprprime
     | {System.out.println("10");} WHILE expr LOOP expr POOL exprprime 
     | {System.out.println("11");} '{' (expr';')+ '}' exprprime 
     | {System.out.println("12");} LET ID ':' ID (POINTTOLEFT expr)? (','ID ':' ID (POINTTOLEFT expr)?)* IN expr exprprime
     | {System.out.println("13");} CASE expr OF (ID POINTTORIGHT expr ';')+ ESAC exprprime
     | {System.out.println("14");} NEW ID exprprime
     | {System.out.println("15");} ISVOID expr exprprime
     /*| {System.out.println("16");} expr ADD expr
     | {System.out.println("17");} expr SUB expr
     | {System.out.println("18");} expr MULTIPLY expr
     | {System.out.println("19");} expr DIV expr
     | {System.out.println("20");} TILDA expr
     | {System.out.println("21");} expr LARGERTHAN expr
     | {System.out.println("22");} expr LARGEREQ expr
     | {System.out.println("23");} expr EQUALS expr
     | {System.out.println("24");} NOT expr
     | {System.out.println("25");} OPENP expr CLOSEP
     | {System.out.println("26");} ID
     | {System.out.println("27");} INTEGER*/
     | {System.out.println("28");} STRING exprprime | mathex exprprime ;
     /*| {System.out.println("29");} TRUE
     | {System.out.println("30");} FALSE ;*/
exprprime : {System.out.println("7");} (('@'ID)?)'.'ID OPENP (expr (','expr)*)? CLOSEP exprprime | ;
mathex : b ;
b : {System.out.println("24");} NOT b | c ;
cprime : {System.out.println("21");} LARGERTHAN d cprime 
       | {System.out.println("22");} LARGEREQ d cprime 
       | {System.out.println("23");} EQUALS d cprime | ;
c : d cprime ;
dprime : {System.out.println("16");} ADD e dprime 
       | {System.out.println("17");} SUB e dprime | ;
d : e dprime ;
eprime : {System.out.println("18");} MULTIPLY f eprime 
       | {System.out.println("19");} DIV f eprime | ;
e : f eprime ;
f : {System.out.println("20");} TILDA f | g ;
g : {System.out.println("25");} OPENP mathex CLOSEP 
  | {System.out.println("26");} ID 
  | {System.out.println("27");} INTEGER 
  | {System.out.println("29");} TRUE 
  | {System.out.println("30");} FALSE ;

//lexer
TRUE : 'true' ;
FALSE : 'false' ;
INHERITS : 'inherits' ;
CLASS : 'class' ;
IF : 'if' ;
THEN : 'then' ;
ELSE : 'else' ;
FI : 'fi' ;
WHILE : 'while' ;
LOOP : 'loop' ;
POOL : 'pool' ;
LET : 'let' ;
IN : 'in' ;
CASE : 'case' ;
OF : 'of' ;
ESAC : 'esac' ;
NEW : 'new' ;
ISVOID : 'isvoid' ;
NOT : 'not' ;
TILDA : '~' ;
WHITESPACE : [ ' '|'\r'|'\n'|'\t']+ ->skip ;
INTEGER : [0-9]+ ;
ID : ['_'a-zA-Z][a-zA-Z0-9'_']* ;
ADD : '+' ;
MULTIPLY : '*' ;
SUB : '-' ;
DIV : '/' ;
OPENP : '(' ;
CLOSEP : ')' ;
EQUALS : '=' ;
LARGERTHAN : '<' ;
LARGEREQ : '<=' ;
POINTTOLEFT : '<-' ;
POINTTORIGHT : '=>' ;
STRING : '"'(~[\r\n])*'"' ;

这是 ANTLR 中 COOL 语法的代码版本。主代码中注释的部分已消除歧义(意味着消除了歧义!),并在第二部分(数学规则)中消除了左递归。

谁能指出这是哪里出了问题,为什么我没有得到想要的输出?

提前致谢!

【问题讨论】:

    标签: parsing compiler-construction antlr left-recursion ambiguous-grammar


    【解决方案1】:

    除了program 中的操作之外,您的每个println 调用都会出现在语法中的标记引用之前。很明显,这意味着它们将按照令牌在文件中出现的顺序执行。

    预期输出和实际输出之间的第一个不匹配是 1627 行的反转。只有当您输入中的 + 标记出现在您输入中的 2 标记之前,您的预期输出才会出现,但显然您可以看到情况并非如此。第二个不匹配发生的原因相同;具体来说,这是因为预期的输出假定* 标记在您的语法中出现的时间比3 标记早。


    我注意到您最初编写了左递归 expr 规则并在其中包含嵌入式操作。以下信息与解决您的具体问题无关,但如果您决定取消注释该代码并使用 expr 的左递归形式,了解这一点很重要。

    考虑以下左递归规则以允许简单添加标识符,并添加两个嵌入式操作。

    expr
      : {a();} ID
      | {b();} expr '+' ID
      ;
    

    您可能已经发现,这种语法不能用 ANTLR 编译。我们发现在我在这里展示它的位置评估表达式{b();} 会对生成的代码产生巨大的(负面)性能影响,因此我们选择不允许它。输出将是表达式的Polish prefix form,而解析器实际上正在尝试使用infix notation 对输入进行操作。解决方案是改为发出中缀符号:

    expr
      : {a();} ID
      | expr {b();} '+' ID
      ;
    

    通过收集对ab 的调用结果,您可以在编写结果之前将结果转换为您喜欢的任何符号。另一种选择是将嵌入的操作移动到解析完成后执行的访问者,在其中以您喜欢的任何顺序执行它们是简单的

    延伸阅读:Infix, Postfix, and Prefix

    【讨论】:

    • 我猜你的答案实际上并不是一个答案,而是一个改变我们想要的输出的技巧,对吗?而且您还暗示所需的输出不正确,而我的输出实际上还可以?
    • @AshkanKzme 我的回答的第一部分(在 hr 上方)准确解释了您获得所看到输出的内容/原因。第二部分只是为您提供的额外信息,因为我看到您注释掉了 expr 规则的左递归形式,并认为您可能想知道它为什么不起作用。
    • 所以你实际上并没有回答这个问题,只是添加了类似的信息。对不起,不能接受这个作为答案。
    • 你不会得到比 Sam 更好的答案;他是典型的 Antlr4 专家。你可以忽略它。
    猜你喜欢
    • 2022-07-12
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多