为什么antlr4将我的句子解析为两个语句?

时间:2017-09-21 22:02:17

标签: parsing antlr whitespace grammar antlr4

我正在为表达式编写一个小解析器。目前我只想让它识别二进制乘法( myId * myId )和类似C的解引用指针( * myId ),以及一些赋值语句( myId * = myId )。

使解析器抛出错误的输入是:

x *= y;

...解析器使用此消息和解析树失败:

[line 1:1 mismatched input ' *' expecting {';', NEWLINE}]
(sourceFile (statement (expressionStatement (expression (monoOperatedExpression (atomicExpression x))))  * =  ) (statement (expressionStatement (expression (monoOperatedExpression (atomicExpression y)))) ;) <EOF>)

我一直在摸不着头脑,但我看不出我的语法有什么问题(见下文)。有什么提示吗?提前谢谢。

grammar Sable;

options {}

@header {
    package org.sable.parser;
}

ASSIGNMENT_OP:
    '='
    ;

BINARY_OP:
    '*'
    ;

WS_BUT_NOT_NEWLINE:
    WhiteSpaceButNotNewLineCharacter
    ;

NEWLINE:
    ('\u000D' '\u000A')
    | '\u000A'
    ;

WSA_BINARY_OP:
    (WS_BUT_NOT_NEWLINE+ BINARY_OP WS_BUT_NOT_NEWLINE+)
    | BINARY_OP
    ;

WSA_PREFIX_OP:
    (WS_BUT_NOT_NEWLINE+ '*' )
    ;

WS  :  WhiteSpaceCharacter+ -> skip
    ;

IDENTIFIER:
    (IdentifierHead IdentifierCharacter*)
    | ('`'(IdentifierHead IdentifierCharacter*)'`')
    ;

// NOTE: a file with zero statements is allowed because
// it can contain just comments.
sourceFile:
    statement* EOF;

statement:
    expressionStatement (';' | NEWLINE);

// Req. not existing any valid expression starting from
// an equals sign or any other assignment operator.
expressionStatement:
    expression (assignmentOperator expression)?;

expression:
    monoOperatedExpression (binaryOperator monoOperatedExpression)?
    ;

monoOperatedExpression:
    atomicExpression
    ;

binaryOperator:
    WSA_BINARY_OP
    ;

atomicExpression:
    IDENTIFIER ('<' type (',' type)* '>')? //TODO: can this be a lsv?
    ;

type:
    IDENTIFIER
    ;

assignmentOperator:
    ASSIGNMENT_OP
    ;

fragment DecimalDigit:
    '0'..'9'
    ;

fragment IdentifierHead:
    'a'..'z'
    | 'A'..'Z'
    ;
fragment IdentifierCharacter:
    DecimalDigit
    | IdentifierHead
    ;

fragment WhiteSpaceCharacter:
    WhiteSpaceButNotNewLineCharacter
    | NewLineCharacter;

fragment WhiteSpaceButNotNewLineCharacter:
    [\u0020\u000C\u0009u000B\u000C]
    ;

fragment NewLineCharacter:
    [\u000A\u000D]
    ;

编辑:根据评论者的要求添加新版语法。

grammar Sable;

options {}

@header {
    package org.sable.parser;
}

//
// PARSER RULES.

sourceFile              : statement* EOF;
statement               : expressionStatement (SEMICOLON | NEWLINE);
expressionStatement     : expression (ASSIGNMENT_OPERATOR expression)?;

expression:
    expression WSA_OPERATOR expression
    | expression OPERATOR expression
    | OPERATOR expression
    | expression OPERATOR
    | atomicExpression
    ;

atomicExpression:
    IDENTIFIER ('<' type (',' type)* '>')? //TODO: can this be a lsv?
    ;

type                    : IDENTIFIER;


//
// LEXER RULES.

COMMENT                 : '/*' .*? '*/'                    -> channel(HIDDEN);
LINE_COMMENT            : '//' ~[\000A\000D]*              -> channel(HIDDEN);

ASSIGNMENT_OPERATOR     : Operator? '=';

// WSA = White Space Aware token.
// These are tokens that occurr in a given whitespace context.
WSA_OPERATOR:
    (WhiteSpaceNotNewline+ Operator WhiteSpaceNotNewline+)
    ;

OPERATOR         : Operator;

// Newline chars are defined apart because they carry meaning as a statement
// delimiter.
NEWLINE:
    ('\u000D' '\u000A')
    | '\u000A'
    ;

WS                      : WhiteSpaceNotNewline -> skip;

SEMICOLON               : ';';


IDENTIFIER:
    (IdentifierHead IdentifierCharacter*)
    | ('`'(IdentifierHead IdentifierCharacter*)'`')
    ;

fragment DecimalDigit   :'0'..'9';

fragment IdentifierHead:
    'a'..'z'
    | 'A'..'Z'
    | '_'
    | '\u00A8'
    | '\u00AA'
    | '\u00AD'
    | '\u00AF' |
    '\u00B2'..'\u00B5' |
    '\u00B7'..'\u00BA'  |
    '\u00BC'..'\u00BE' |
    '\u00C0'..'\u00D6' |
    '\u00D8'..'\u00F6' |
    '\u00F8'..'\u00FF' |
    '\u0100'..'\u02FF' |
    '\u0370'..'\u167F' |
    '\u1681'..'\u180D' |
    '\u180F'..'\u1DBF' |
    '\u1E00'..'\u1FFF' |
    '\u200B'..'\u200D' |
    '\u202A'..'\u202E' |
    '\u203F'..'\u2040' |
    '\u2054' |
    '\u2060'..'\u206F' |
    '\u2070'..'\u20CF' |
    '\u2100'..'\u218F' |
    '\u2460'..'\u24FF' |
    '\u2776'..'\u2793' |
    '\u2C00'..'\u2DFF' |
    '\u2E80'..'\u2FFF' |
    '\u3004'..'\u3007' |
    '\u3021'..'\u302F' |
    '\u3031'..'\u303F' |
    '\u3040'..'\uD7FF' |
    '\uF900'..'\uFD3D' |
    '\uFD40'..'\uFDCF' |
    '\uFDF0'..'\uFE1F' |
    '\uFE30'..'\uFE44' |
    '\uFE47'..'\uFFFD'
    ;
fragment IdentifierCharacter:
    DecimalDigit
    | '\u0300'..'\u036F'
    | '\u1DC0'..'\u1DFF'
    | '\u20D0'..'\u20FF'
    | '\uFE20'..'\uFE2F'
    | IdentifierHead
    ;
// Non-newline whitespaces are defined apart because they carry meaning in
// certain contexts, e.g. within space-aware operators.
fragment WhiteSpaceNotNewline    : [\u0020\u000C\u0009u000B\u000C];

fragment Operator:
    '*'
    | '/'
    | '%'
    | '+'
    | '-'
    | '<<'
    | '>>'
    | '&'
    | '^'
    | '|'
    ;

1 个答案:

答案 0 :(得分:4)

规则

expression
    : monoOperatedExpression (binaryOperator monoOperatedExpression)?
    ;

不允许在=之后binaryOperator。因此,运行时报告在消耗BINARY_OP之后它不知道下一个要使用的规则。

语法可以通过一些重要的重组来修复,最好是简化。

1 - 忽略它可以大大简化空白/换行处理。

WS : [ \t\r\n] -> skip;

C系列和类似Python的语言是无上下文语言,具有一些众所周知的上下文敏感的极端情况。 ANTLR是一种无上下文解析器,具有许多处理上下文敏感性的便利功能。因此,忽略(或隐藏)空格应该是默认值。

2 - 根据定义消除*的使用歧义:

STAR_EQUAL : '*=' ;
STAR       : '*'  ;
EQUAL      : '='  ;

这可确保任何单个STAR仅可用作指针标记或乘法运算符(序列STAR WS EQUAL在您的语言中无效或可能具有某些自定义含义)。

3 - 使用解析器规则递归:考虑C grammar中的表达式处理规则,特别是从expression规则开始。简化模式是:

expression     // list of all valid syntaxes for an `expression`
    : LPAREN expression RPAREN
    | expression ( COMMA expression )*
    | expression op expression 
    | unitary_op expression 
    | expression unitary_op 
    | << any other valid syntax >>
    | atom
    ;

 unitary_op : 2PLUS | 2DASH | .... ;
 op         : STAR_EQUAL | STAR | EQUAL | .... ;

 atom
    : STAR? IDENTIFIER   // pointer usage
    | NUMBER
    ;

以这种方式呈现,语法将更具可读性和可维护性。

通过这些更改,完成语法修订仍然是OP的一个简单练习(意思是,尝试并发布遇到的任何问题)。

奖金 - ANTLR是一个自上而下的解析器。因此,将解析器规则放在顶部,组织范围广泛缩小。遵循词法分析器规则,也以相同的方式排序,任何词法分析器模式,然后在最底部使用片段规则。

这种排序可以减轻您和其他人理解语法的认知负担。例如,使树转储更容易/更快地理解。还将简化最终划分为分割语法的任务(如果语法具有任何显着的复杂性,则推荐使用,如果存在模式则需要)。

完整语法

grammar Sable;

@header {
    package org.sable.parser.gen;
}

sable
    : statement* EOF
    ;

statement
    : expression? SEMI
    ;

expression
    : LPAREN expression RPAREN
    | COMMA expression
    | expression op expression
    | unitary_op expression
    | expression unitary_op
    | STAR? IDENTIFIER
    | NUMBER
    ;

 unitary_op
    : DPLUS | DMINUS
    ;

 op : STAR_EQUAL | DIV_EQUAL | PLUS_EQUAL | MINUS_EQUAL | EQUAL
    | STAR | DIV | PLUS | MINUS
    ;


COMMENT     : Comment -> skip ;

STAR_EQUAL  : '*=' ;
DIV_EQUAL   : '/=' ;
PLUS_EQUAL  : '+=' ;
MINUS_EQUAL : '-=' ;
EQUAL       : '='  ;

STAR        : '*'  ; // mult or pointer
DIV         : '/'  ;
PLUS        : '+'  ;
MINUS       : '-'  ;

DPLUS       : '++' ;
DMINUS      : '--' ;

COMMA       : ','  ;
DOT         : '.'  ;
SEMI        : ';'  ;

LPAREN      : '('  ;
RPAREN      : ')'  ;
LBRACE      : '{'  ;
RBRACE      : '}'  ;
LBRACK      : '['  ;
RBRACK      : ']'  ;
LANGLE      : '<'  ;
RANGLE      : '>'  ;

NUMBER      : [0-9]+ ('.' [0-9]+)? ([eE] [+-]? [0-9]+)? ;
IDENTIFIER  : [a-zA-Z_][a-zA-Z0-9_-]*  ;

WS          : [ \t\r\n]+    -> skip;

ERRCHAR
    :   .   -> channel(HIDDEN)
    ;

fragment Comment
    :   '/*' .*? '*/'
    |   '//' ~[\r\n]*
    ;

生成但未经测试。报告任何未处理的角落案件。