我正在为表达式编写一个小解析器。目前我只想让它识别二进制乘法( 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:
'*'
| '/'
| '%'
| '+'
| '-'
| '<<'
| '>>'
| '&'
| '^'
| '|'
;
答案 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]*
;
生成但未经测试。报告任何未处理的角落案件。