我正在为表达式写一些解析器。目前,我只希望它识别二进制乘法( myid * myid )和类似C的dereed Persed Pointer( * 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:
[u0020u000Cu0009u000Bu000C]
;
fragment NewLineCharacter:
[u000Au000D]
;
编辑:应评论者的要求添加语法的新版本。
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 : '//' ~[ 00A 00D]* -> 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 : [u0020u000Cu0009u000Bu000C];
fragment Operator:
'*'
| '/'
| '%'
| '+'
| '-'
| '<<'
| '>>'
| '&'
| '^'
| '|'
;
规则
expression
: monoOperatedExpression (binaryOperator monoOperatedExpression)?
;
在binaryOperator
之后不允许=
。因此,运行时报告说,它不知道在消耗BINARY_OP
后使用什么规则。
语法可以通过一些显着的重组固定,最好是简化。
1-可以通过忽略Whitespace/Newline处理。
WS : [ trn] -> skip;
c家族和类似python的语言是上下文的自由语言,具有一些众所周知的上下文敏感角案例。ANTLR是一种免费的解析器,具有许多方便能力来处理上下文敏感性。因此,忽略(或隐藏)的空格应该是默认的。
2-根据定义,请歧义*
的使用:
STAR_EQUAL : '*=' ;
STAR : '*' ;
EQUAL : '=' ;
这可以确保任何单个STAR
仅可被视为指针标记或乘法运算符(序列星WS等于您的语言无效,或者可能具有某些自定义含义)。
3-使用解析器规则递归:考虑C语法中的表达处理规则,特别是从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
;
以这种方式提出,语法将更加可读和可维护。
随着这些更改,完成语法的修订是一个简单的操作练习(意思是,尝试并发布遇到的任何问题)。
奖励 - ANTLR是自上而下的解析器。因此,将解析器规则放在顶部,组织到狭窄。其次是Lexer规则,也以相同的方式(任何Lexer模式)排序,然后在最底部进行片段规则。
这种秩序减轻了您和其他人的语法的认知能力。例如,使树转储更容易/更快地理解。还将简化最终分为分裂语法的任务(如果语法具有任何显着的复杂性,则建议使用模式)。
)。完整的语法
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 : [ trn]+ -> skip;
ERRCHAR
: . -> channel(HIDDEN)
;
fragment Comment
: '/*' .*? '*/'
| '//' ~[rn]*
;
生成但未测试。报告未处理的任何角案件。