在antlr4语法中消除一元和二元减号的歧义

时间:2015-08-23 12:39:29

标签: antlr4

我正在为一种中等简单的语言创建一个antlr4语法。我正在努力使语法区分一元和二元减。我已经在Stackoverflow上阅读了我在这个主题上可以找到的所有其他帖子,但是已经发现答案要么适用于antlr3,要么我无法弄清楚如何在antlr4中表达,或者我似乎不擅长翻译这些答案对我自己的情况提出了建议。我常常会遇到这样的问题:如果我使用其他替代方案,antlr无法明确地解决规则。

以下是整个antlr文件。这个版本的模糊性出现在制作周围:

binop_expr
        : SUMOP product
        | product ( SUMOP product )*
        ;

(我最初使用UNARY_ABELIAN_OP而不是第二个SUMOP,但这导致了一种不同的歧义 - 该工具显然无法识别它需要在两个不同的上下文中区分相同的令牌。我提到这一点是因为其中一个帖子建议为一元运算符使用不同的名称。)

grammar Kant;

program
        : type_declaration_list main
        ;

type_declaration_list
        : type_declaration
        | type_declaration_list type_declaration
        | /* null */
        ;

type_declaration
        : 'context' JAVA_ID '{' context_body '}'
        | 'class'   JAVA_ID '{' class_body   '}'
        | 'class'   JAVA_ID 'extends' JAVA_ID '{' class_body   '}'
        ;

context_body
        : context_body context_body_element
        | context_body_element
        | /* null */
        ;

context_body_element
        : method_decl
        | object_decl
        | role_decl
        | stageprop_decl
        ;

role_decl
        : 'role' JAVA_ID '{' role_body '}'
        | 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        | access_qualifier 'role' JAVA_ID '{' role_body '}'
        | access_qualifier 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        ;

role_body
        : method_decl
        | role_body method_decl
        | object_decl               // illegal
        | role_body object_decl     // illegal — for better error messages only
        ;

self_methods
        : self_methods ';' method_signature
        | method_signature
        | self_methods /* null */ ';'
        ;

stageprop_decl
        : 'stageprop' JAVA_ID '{' stageprop_body '}'
        | 'stageprop' JAVA_ID '{' stageprop_body '}' REQUIRES '{' self_methods '}'
        | access_qualifier 'stageprop' JAVA_ID '{' stageprop_body '}'
        | access_qualifier 'stageprop' JAVA_ID '{' stageprop_body '}' REQUIRES '{' self_methods '}'
        ;

stageprop_body
        : method_decl
        | stageprop_body method_decl
        ;

class_body
        : class_body class_body_element
        | class_body_element
        | /* null */
        ;

class_body_element
        : method_decl
        | object_decl
        ;

method_decl
        : method_decl_hook '{' expr_and_decl_list '}'
        ;

method_decl_hook
        : method_signature
        | method_signature CONST
        ;

method_signature
        : access_qualifier return_type method_name '(' param_list ')'
        | access_qualifier return_type method_name 
        | access_qualifier method_name '(' param_list ')'
        ;

expr_and_decl_list
        : object_decl
        | expr ';' object_decl
        | expr_and_decl_list object_decl
        | expr_and_decl_list expr
        | expr_and_decl_list /*null-expr */ ';'
        | /* null */
        ;

return_type
        : type_name
        | /* null */
        ;

method_name
        : JAVA_ID
        ;

access_qualifier
        : 'public' | 'private' | /* null */
        ;

object_decl
        : access_qualifier compound_type_name identifier_list ';'
        | access_qualifier compound_type_name identifier_list
        | compound_type_name identifier_list /* null expr */ ';'
        | compound_type_name identifier_list
        ;

compound_type_name
        : type_name '[' ']'
        | type_name
        ;

type_name
        : JAVA_ID
        | 'int'
        | 'double'
        | 'char'
        | 'String'
        ;

identifier_list
        : JAVA_ID
        | identifier_list ',' JAVA_ID
        | JAVA_ID ASSIGN expr
        | identifier_list ',' JAVA_ID ASSIGN expr
        ;

param_list
        : param_decl
        | param_list ',' param_decl
        | /* null */
        ;

param_decl
        : type_name JAVA_ID
        ;

main
        : expr
        ;

expr
        : block
        | expr '.' message
        | expr '.' CLONE
        | expr '.' JAVA_ID
        | ABELIAN_INCREMENT_OP expr '.' JAVA_ID
        | expr '.' JAVA_ID ABELIAN_INCREMENT_OP
        | /* this. */ message
        | JAVA_ID
        | constant
        | if_expr
        | for_expr
        | while_expr
        | do_while_expr
        | switch_expr
        | BREAK
        | CONTINUE
        | boolean_expr
        | binop_expr
        | '(' expr ')'
        | <assoc=right> expr ASSIGN expr
        | NEW message
        | NEW type_name '[' expr ']'
        | RETURN expr
        | RETURN
        ;

relop_expr
        : sexpr RELATIONAL_OPERATOR sexpr
        ;


// This is just a duplication of expr. We separate it out
// because a top-down antlr4 parser can't handle the
// left associative ambiguity. It is used only
// for abelian types.
sexpr
        : block 
        | sexpr '.' message
        | sexpr '.' CLONE
        | sexpr '.' JAVA_ID
        | ABELIAN_INCREMENT_OP sexpr '.' JAVA_ID
        | sexpr '.' JAVA_ID ABELIAN_INCREMENT_OP
        | /* this. */ message
        | JAVA_ID
        | constant
        | if_expr
        | for_expr
        | while_expr
        | do_while_expr
        | switch_expr
        | BREAK
        | CONTINUE
        | '(' sexpr ')'
        | <assoc=right> sexpr ASSIGN sexpr
        | NEW message
        | NEW type_name '[' expr ']'
        | RETURN expr
        | RETURN
        ;

block
        : '{' expr_and_decl_list '}'
        | '{' '}'
        ;

expr_or_null
        : expr
        | /* null */
        ;

if_expr
        : 'if' '(' boolean_expr ')' expr
        | 'if' '(' boolean_expr ')' expr 'else' expr
        ;

for_expr
        : 'for' '(' object_decl boolean_expr ';' expr ')' expr  // O.K. — expr can be a block
        | 'for' '(' JAVA_ID ':' expr ')' expr
        ;

while_expr
        : 'while' '(' boolean_expr ')' expr
        ;

do_while_expr
        : 'do' expr 'while' '(' boolean_expr ')'
        ;

switch_expr
        : SWITCH '(' expr ')' '{'  ( switch_body )* '}'
        ;

switch_body
        : ( CASE constant | DEFAULT ) ':' expr_and_decl_list
        ;

binop_expr
        : SUMOP product
        | product ( SUMOP product )*
        ;

product
        : atom  ( MULOP atom )*
        ;

atom
        : null_expr
        | JAVA_ID
        | JAVA_ID ABELIAN_INCREMENT_OP
        | ABELIAN_INCREMENT_OP JAVA_ID
        | constant
        | '(' expr ')'
        | array_expr '[' sexpr ']'
        | array_expr '[' sexpr ']' ABELIAN_INCREMENT_OP
        | ABELIAN_INCREMENT_OP array_expr '[' sexpr ']'
        ;

null_expr
        : NULL
        ;

array_expr
        : sexpr
        ;

boolean_expr
        : boolean_product ( BOOLEAN_SUMOP boolean_product )*
        ;

boolean_product
        : boolean_atom  ( BOOLEAN_MULOP boolean_atom )*
        ;

boolean_atom
        : BOOLEAN
        | JAVA_ID
        | '(' boolean_expr ')'
        | LOGICAL_NOT boolean_expr
        | relop_expr
        ;

constant
        : STRING
        | INTEGER
        | FLOAT
        | BOOLEAN
        ;

message
        : <assoc=right> JAVA_ID '(' argument_list ')'
        ;

argument_list
        : expr
        | argument_list ',' expr
        | /* null */
        ;

// Lexer rules

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

INTEGER : ('1' .. '9')+ ('0' .. '9')* | '0';

FLOAT : (('1' .. '9')* | '0') '.' ('0' .. '9')* ;

BOOLEAN : 'true' | 'false' ;

SWITCH : 'switch' ;

CASE : 'case' ;

DEFAULT : 'default' ;

BREAK : 'break' ;

CONTINUE : 'continue' ;

RETURN : 'return' ;

REQUIRES : 'requires' ;

NEW : 'new' ;

CLONE : 'clone' ;

NULL : 'null' ;

CONST : 'const' ;

RELATIONAL_OPERATOR : '!=' | '==' | '>' | '<' | '>=' | '<=';

LOGICAL_NOT : '!' ;

BOOLEAN_MULOP : '&&'  ;

BOOLEAN_SUMOP : '||' | '^' ;

SUMOP : '+' | '-' ;

MULOP : '*' | '/' ;

ABELIAN_INCREMENT_OP : '++' | '--' ;

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

INLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ;

C_COMMENT: '/*' .*? '*/' -> channel(HIDDEN) ;

WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ -> channel(HIDDEN) ;

ASSIGN : '=' ;

典型的问题是解析器无法识别此表达式中的一元减号(它根本不接受构造):

Base b1 = new Base(-threetwoone);

2 个答案:

答案 0 :(得分:1)

问题似乎是由于语法中有两种表达式需要更好地相互分组而不是混合在一起。总体意图是创建一种语言,其中一切都是表达式,包括例如循环和条件。 (你可以用你的想象来推断它们产生的价值;然而,这是一个语义而不是句法问题。)

这些表达式中的一组具有良好的阿贝尔属性(它们的行为类似于具有良好操作的普通算术类型)。该语言指定了较低级别的制作如何通过赋予它们背景的操作联系在一起:所以两个标识符,&#34; a&#34;和&#34; b&#34;,可由运营商关联&#34; +&#34; as&#34; a + b&#34;。这是一种高度语境化的语法。

这些表达式中的另一组包括具有低得多的句法衔接程度的作品。人们可以结合&#34;如果&#34;表达式,&#34; for&#34;表达式和任何顺序的声明。关联它们的唯一语言属性是连锁,正如我们在object_bodyclass_bodyexpr_and_decl_list的作品中找到的那样。

基本问题是这两个语法在antlr规范中交织在一起。结合运营商喜欢&#34; +&#34;是上下文敏感的,并且上下文可以来自catenation或更多上下文化的abelian替代,解析器留下了两种解析某些表达式的替代方法。我设法摆脱了大部分歧义,但它被推入了一元前缀运营商的领域。所以如果&#34; a&#34;是表达式(它是),&#34; b&#34;是一个表达式,然后是字符串&#34; a + b&#34;很暧昧。它可能意味着product ( SUMOP product )*,或者它可能意味着expr_and_decl_list expr expr_and_decl_list早先从expr&#34; a&#34;中减少了exprexpr_and_decl_list expr中的expr可以减少&#34; + b&#34;。

我重新编写了语法,受到我在网络上找到的其他例子的启发。 (其中一个更有影响力的是Bart Kiers指出的那个:If/else statements in ANTLR using listeners)你可以在下面找到新的语法。与阿贝尔表达式的语法一起使用但与NEW expr expr '.' JAVA_ID abelian_expr目标一致的东西现在已被分离出来以减少到{{1}目标。减少到abelian_expr的所有产品现在或多或少是直接语法中abelian_expr节点的后代;这有助于antlr聪明地处理否则会出现歧义。一元&#34; +&#34;表达式不再可以直接缩减为expr,但只能通过expr找到abelian_expr的方式,因此它永远不会被视为可以简单地作为一个很大程度上非语言化的表达式进行链接的生产。

我无法证明那是怎么回事,但有迹象表明这个方向。如果其他人有更正式或演绎的分析(特别是如果它指出另一个结论),我很乐意听你的推理。

这里的教训似乎是,语言设计中的一个高级别,根本性的缺陷被设法显示为只是一个孤立的,在某种意义上的小错误:无法消除一元和二元之间的歧义使用操作员。

grammar Kant;

program
        : type_declaration_list main
        | type_declaration_list     // missing main
        ;

main
        : expr
        ;

type_declaration_list
        : type_declaration
        | type_declaration_list type_declaration
        | /* null */
        ;

type_declaration
        : 'context' JAVA_ID '{' context_body '}'
        | 'class'   JAVA_ID '{' class_body   '}'
        | 'class'   JAVA_ID 'extends' JAVA_ID '{' class_body   '}'
        ;

context_body
        : context_body context_body_element
        | context_body_element
        | /* null */
        ;

context_body_element
        : method_decl
        | object_decl
        | role_decl
        | stageprop_decl
        ;

role_decl
        : 'role' JAVA_ID '{' role_body '}'
        | 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        | access_qualifier 'role' JAVA_ID '{' role_body '}'
        | access_qualifier 'role' JAVA_ID '{' role_body '}' REQUIRES '{' self_methods '}'
        ;

role_body
        : method_decl
        | role_body method_decl
        | object_decl               // illegal
        | role_body object_decl     // illegal — for better error messages only
        ;

self_methods
        : self_methods ';' method_signature
        | method_signature
        | self_methods /* null */ ';'
        ;

stageprop_decl
        : 'stageprop' JAVA_ID '{' stageprop_body '}'
        | 'stageprop' JAVA_ID '{' stageprop_body '}' REQUIRES '{' self_methods '}'
        | access_qualifier 'stageprop' JAVA_ID '{' stageprop_body '}'
        | access_qualifier 'stageprop' JAVA_ID '{' stageprop_body '}' REQUIRES '{' self_methods '}'
        ;

stageprop_body
        : method_decl
        | stageprop_body method_decl
        | object_decl               // illegal
        | stageprop_body object_decl        // illegal — for better error messages only
        ;

class_body
        : class_body class_body_element
        | class_body_element
        | /* null */
        ;

class_body_element
        : method_decl
        | object_decl
        ;

method_decl
        : method_decl_hook '{' expr_and_decl_list '}'
        ;

method_decl_hook
        : method_signature
        ;

method_signature
        : access_qualifier return_type method_name '(' param_list ')' CONST*
        | access_qualifier return_type method_name CONST*
        | access_qualifier method_name '(' param_list ')' CONST*
        ;

expr_and_decl_list
        : object_decl
        | expr ';' object_decl
        | expr_and_decl_list object_decl
        | expr_and_decl_list expr
        | expr_and_decl_list /*null-expr */ ';'
        | /* null */
        ;

return_type
        : type_name
        | /* null */
        ;

method_name
        : JAVA_ID
        ;

access_qualifier
        : 'public' | 'private' | /* null */
        ;

object_decl
        : access_qualifier compound_type_name identifier_list ';'
        | access_qualifier compound_type_name identifier_list
        | compound_type_name identifier_list /* null expr */ ';'
        | compound_type_name identifier_list
        ;

compound_type_name
        : type_name '[' ']'
        | type_name
        ;

type_name
        : JAVA_ID
        | 'int'
        | 'double'
        | 'char'
        | 'String'
        ;

identifier_list
        : JAVA_ID
        | identifier_list ',' JAVA_ID
        | JAVA_ID ASSIGN expr
        | identifier_list ',' JAVA_ID ASSIGN expr
        ;

param_list
        : param_decl
        | param_list ',' param_decl
        | /* null */
        ;

param_decl
        : type_name JAVA_ID
        ;

expr
        : abelian_expr
        | boolean_expr
        | block
        | if_expr
        | for_expr
        | while_expr
        | do_while_expr
        | switch_expr
        | BREAK
        | CONTINUE
        | RETURN expr
        | RETURN
        ;

abelian_expr
        : <assoc=right>abelian_expr POW abelian_expr           
        | ABELIAN_SUMOP expr                           
        | LOGICAL_NEGATION expr
        | NEW message
        | NEW type_name '[' expr ']'                            
        | abelian_expr ABELIAN_MULOP abelian_expr       
        | abelian_expr ABELIAN_SUMOP abelian_expr             
        | abelian_expr ABELIAN_RELOP abelian_expr                    
        | null_expr
        | /* this. */ message
        | JAVA_ID
        | JAVA_ID ABELIAN_INCREMENT_OP
        | ABELIAN_INCREMENT_OP JAVA_ID
        | constant
        | '(' abelian_expr ')'
        | abelian_expr '[' expr ']'
        | abelian_expr '[' expr ']' ABELIAN_INCREMENT_OP
        | ABELIAN_INCREMENT_OP expr '[' expr ']'
        | ABELIAN_INCREMENT_OP expr '.' JAVA_ID
        | abelian_expr '.' JAVA_ID ABELIAN_INCREMENT_OP
        | abelian_expr '.' message
        | abelian_expr '.' CLONE
        | abelian_expr '.' CLONE '(' ')'
        | abelian_expr '.' JAVA_ID
        | <assoc=right> abelian_expr ASSIGN expr
        ;

message
        : <assoc=right> JAVA_ID '(' argument_list ')'
        ;

boolean_expr
        : boolean_expr BOOLEAN_MULOP expr                       
        | boolean_expr BOOLEAN_SUMOP expr
        | constant      // 'true' / 'false'
        | abelian_expr
        ;

block
        : '{' expr_and_decl_list '}'
        | '{' '}'
        ;

expr_or_null
        : expr
        | /* null */
        ;

if_expr
        : 'if' '(' expr ')' expr
        | 'if' '(' expr ')' expr 'else' expr
        ;

for_expr
        : 'for' '(' object_decl expr ';' expr ')' expr  // O.K. — expr can be a block
        | 'for' '(' JAVA_ID ':' expr ')' expr
        ;

while_expr
        : 'while' '(' expr ')' expr
        ;

do_while_expr
        : 'do' expr 'while' '(' expr ')'
        ;

switch_expr
        : SWITCH '(' expr ')' '{'  ( switch_body )* '}'
        ;

switch_body
        : ( CASE constant | DEFAULT ) ':' expr_and_decl_list
        ;

null_expr
        : NULL
        ;

constant
        : STRING
        | INTEGER
        | FLOAT
        | BOOLEAN
        ;

argument_list
        : expr
        | argument_list ',' expr
        | /* null */
        ;


// Lexer rules

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

INTEGER : ('1' .. '9')+ ('0' .. '9')* | '0';

FLOAT : (('1' .. '9')* | '0') '.' ('0' .. '9')* ;

BOOLEAN : 'true' | 'false' ;

SWITCH : 'switch' ;

CASE : 'case' ;

DEFAULT : 'default' ;

BREAK : 'break' ;

CONTINUE : 'continue' ;

RETURN : 'return' ;

REQUIRES : 'requires' ;

NEW : 'new' ;

CLONE : 'clone' ;

NULL : 'null' ;

CONST : 'const' ;

ABELIAN_RELOP : '!=' | '==' | '>' | '<' | '>=' | '<=';

LOGICAL_NOT : '!' ;

POW : '**' ;

BOOLEAN_SUMOP : '||' | '^' ;

BOOLEAN_MULOP : '&&'  ;

ABELIAN_SUMOP : '+' | '-' ;

ABELIAN_MULOP : '*' | '/' | '%' ;

MINUS : '-' ;

PLUS : '+' ;

LOGICAL_NEGATION : '!' ;

ABELIAN_INCREMENT_OP : '++' | '--' ;

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

INLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ;

C_COMMENT: '/*' .*? '*/' -> channel(HIDDEN) ;

WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ -> channel(HIDDEN) ;

ASSIGN : '=' ;

答案 1 :(得分:0)

尝试从blockquote删除一元表达式,并将其添加到binop_expr规则中:

expr