我的语法标识符关键字作为标识符
My grammar identifiers keywords as identifiers
我正在尝试解析 Jakarta 表达式语言中的表达式。总之,它是一个简化的 Java 表达式,添加了一些东西:
- 支持创建如下地图:
{"foo": "bar"}
- 支持创建列表和集合,例如:
[1,2,3,4]
{1,2,3,4}
- 使用一些标识符代替符号,例如:
foo gt bar
(foo > bar
)、foo mod bar
(foo % bar)
,等等。
我在最后一点挣扎,它总是将“mod”、“gt”、“ge”理解为标识符,而不是使用具有“%”、“>”的表达式", ">=".
我是 ANTLR 的新手。我的语法是基于 https://github.com/antlr/grammars-v4/tree/master/java/java and the JavaCC provided by: https://jakarta.ee/specifications/expression-language/4.0/jakarta-expression-language-spec-4.0.html#collected-syntax
中的 Java 语法
grammar ExpressionLanguageGrammar;
prog: compositeExpression;
compositeExpression: (dynamicExpression | deferredExpression | literalExpression)*;
dynamicExpression: '${' expression RCURL;
deferredExpression: '#{' expression RCURL;
literalExpression: literal;
literal: BOOL_LITERAL | FLOATING_POINT_LITERAL | INTEGER_LITERAL | StringLiteral | NULL;
mapData | listData | setData;
methodArguments: LPAREN expressionList? RPAREN;
expressionList: (expression ((COMMA expression)*));
lambdaExpressionOrCall: LPAREN lambdaExpression RPAREN methodArguments*;
lambdaExpression: lambdaParameters ARROW expression;
lambdaParameters: IDENTIFIER | (LPAREN (IDENTIFIER ((COMMA IDENTIFIER)*))? RPAREN);
mapEntry: expression COLON expression;
mapEntries: mapEntry (COMMA mapEntry)*;
expression
: primary
|'[' expressionList? ']'
| '{' expressionList? '}'
| '{' mapEntries? '}'
| expression bop='.' (IDENTIFIER | IDENTIFIER '(' expressionList? ')')
| expression ('[' expression ']')+
| prefix=('-' | '!' | NOT1 | EMPTY) expression
| expression bop=('*' | '/' | '%' | MOD1 | DIV1) expression
| expression bop=('+' | '-') expression
| expression bop=('<=' | '>=' | '>' | '<' | LE1 | GE1 | LT1 | GT1) expression
| expression bop=INSTANCEOF IDENTIFIER
| expression bop=('==' | '!=' | EQ1 | NE1) expression
| expression bop=('&&' | AND1) expression
| expression bop=('||' | OR1) expression
| <assoc=right> expression bop='?' expression bop=':' expression
| <assoc=right> expression
bop=('=' | '+=' | '-=' | '*=' | '/=')
expression
| lambdaExpression
| lambdaExpressionOrCall
;
primary
: '(' expression ')'
| literal
| IDENTIFIER
;
BOOL_LITERAL: TRUE | FALSE;
IDENTIFIER: LETTER (LETTER|DIGIT)*;
INTEGER_LITERAL: [0-9]+;
FLOATING_POINT_LITERAL: [0-9]+ '.' [0-9]* EXPONENT? | '.' [0-9]+ EXPONENT? | [0-9]+ EXPONENT?;
fragment EXPONENT: ('e'|'E') ('+'|'-')? [0-9]+;
StringLiteral: ('"' DoubleStringCharacter* '"'
| '\'' SingleStringCharacter* '\'') ;
fragment DoubleStringCharacter
: ~["\\r\n]
| '\' EscapeSequence
;
fragment SingleStringCharacter
: ~['\\r\n]
| '\' EscapeSequence
;
fragment EscapeSequence
: CharacterEscapeSequence
| '0'
| HexEscapeSequence
| UnicodeEscapeSequence
| ExtendedUnicodeEscapeSequence
;
fragment CharacterEscapeSequence
: SingleEscapeCharacter
| NonEscapeCharacter
;
fragment HexEscapeSequence
: 'x' HexDigit HexDigit
;
fragment UnicodeEscapeSequence
: 'u' HexDigit HexDigit HexDigit HexDigit
| 'u' '{' HexDigit HexDigit+ '}'
;
fragment ExtendedUnicodeEscapeSequence
: 'u' '{' HexDigit+ '}'
;
fragment SingleEscapeCharacter
: ['"\bfnrtv]
;
fragment NonEscapeCharacter
: ~['"\bfnrtv0-9xu\r\n]
;
fragment EscapeCharacter
: SingleEscapeCharacter
| [0-9]
| [xu]
;
fragment HexDigit
: [_0-9a-fA-F]
;
fragment DecimalIntegerLiteral
: '0'
| [1-9] [0-9_]*
;
fragment ExponentPart
: [eE] [+-]? [0-9_]+
;
fragment IdentifierPart
: IdentifierStart
| [\p{Mn}]
| [\p{Nd}]
| [\p{Pc}]
| '\u200C'
| '\u200D'
;
fragment IdentifierStart
: [\p{L}]
| [$_]
| '\' UnicodeEscapeSequence
;
LCURL: '{';
RCURL: '}';
LETTER: '\u0024' |
'\u0041'..'\u005a' |
'\u005f' |
'\u0061'..'\u007a' |
'\u00c0'..'\u00d6' |
'\u00d8'..'\u00f6' |
'\u00f8'..'\u00ff' |
'\u0100'..'\u1fff' |
'\u3040'..'\u318f' |
'\u3300'..'\u337f' |
'\u3400'..'\u3d2d' |
'\u4e00'..'\u9fff' |
'\uf900'..'\ufaff';
DIGIT: '\u0030'..'\u0039'|
'\u0660'..'\u0669'|
'\u06f0'..'\u06f9'|
'\u0966'..'\u096f'|
'\u09e6'..'\u09ef'|
'\u0a66'..'\u0a6f'|
'\u0ae6'..'\u0aef'|
'\u0b66'..'\u0b6f'|
'\u0be7'..'\u0bef'|
'\u0c66'..'\u0c6f'|
'\u0ce6'..'\u0cef'|
'\u0d66'..'\u0d6f'|
'\u0e50'..'\u0e59'|
'\u0ed0'..'\u0ed9'|
'\u1040'..'\u1049';
TRUE: 'true';
FALSE: 'false';
NULL: 'null';
DOT: '.';
LPAREN: '(';
RPAREN: ')';
LBRACK: '[';
RBRACK: ']';
COLON: ':';
COMMA: ',';
SEMICOLON: ';';
GT0: '>';
GT1: 'gt';
LT0: '<';
LT1: 'lt';
GE0: '>=';
GE1: 'ge';
LE0: '<=';
LE1: 'le';
EQ0: '==';
EQ1: 'eq';
NE0: '!=';
NE1: 'ne';
NOT0: '!';
NOT1: 'not';
AND0: '&&';
AND1: 'and';
OR0: '||';
OR1: 'or';
EMPTY: 'empty';
INSTANCEOF: 'instanceof';
MULT: '*';
PLUS: '+';
MINUS: '-';
QUESTIONMARK: '?';
DIV0: '/';
DIV1: 'div';
MOD0: '%';
MOD1: 'mod';
CONCAT: '+=';
ASSIGN: '=';
ARROW: '->';
DOLLAR: '$';
HASH: '#';
WS: [ \t\r\n]+ -> skip;
将它们的 Lexer 规则移动到 Identifier
的 Lexer 规则之前。
如果 ANTLR 有多个 Lexer 规则匹配相同长度的输入,它会选择匹配的语法中的第一个规则。
例如“mod”匹配到Identifier
和MOD1
,但是Identifier
是第一个,所以选择Identifier
。将 MOD1
规则移动到 Identifier
之前,它将匹配 MOD1
————-
顺便说一句,除非你关心“%”和“mod”有不同的标记值,否则你可以只定义一个规则:
MOD: ‘%’ | ‘mod’;
如果需要,您仍然可以获得令牌文本,但您只需在解析器规则中指定 MOD
而不是 (MOD0 | MOD1)
我正在尝试解析 Jakarta 表达式语言中的表达式。总之,它是一个简化的 Java 表达式,添加了一些东西:
- 支持创建如下地图:
{"foo": "bar"}
- 支持创建列表和集合,例如:
[1,2,3,4]
{1,2,3,4}
- 使用一些标识符代替符号,例如:
foo gt bar
(foo > bar
)、foo mod bar
(foo % bar)
,等等。
我在最后一点挣扎,它总是将“mod”、“gt”、“ge”理解为标识符,而不是使用具有“%”、“>”的表达式", ">=".
我是 ANTLR 的新手。我的语法是基于 https://github.com/antlr/grammars-v4/tree/master/java/java and the JavaCC provided by: https://jakarta.ee/specifications/expression-language/4.0/jakarta-expression-language-spec-4.0.html#collected-syntax
中的 Java 语法grammar ExpressionLanguageGrammar;
prog: compositeExpression;
compositeExpression: (dynamicExpression | deferredExpression | literalExpression)*;
dynamicExpression: '${' expression RCURL;
deferredExpression: '#{' expression RCURL;
literalExpression: literal;
literal: BOOL_LITERAL | FLOATING_POINT_LITERAL | INTEGER_LITERAL | StringLiteral | NULL;
mapData | listData | setData;
methodArguments: LPAREN expressionList? RPAREN;
expressionList: (expression ((COMMA expression)*));
lambdaExpressionOrCall: LPAREN lambdaExpression RPAREN methodArguments*;
lambdaExpression: lambdaParameters ARROW expression;
lambdaParameters: IDENTIFIER | (LPAREN (IDENTIFIER ((COMMA IDENTIFIER)*))? RPAREN);
mapEntry: expression COLON expression;
mapEntries: mapEntry (COMMA mapEntry)*;
expression
: primary
|'[' expressionList? ']'
| '{' expressionList? '}'
| '{' mapEntries? '}'
| expression bop='.' (IDENTIFIER | IDENTIFIER '(' expressionList? ')')
| expression ('[' expression ']')+
| prefix=('-' | '!' | NOT1 | EMPTY) expression
| expression bop=('*' | '/' | '%' | MOD1 | DIV1) expression
| expression bop=('+' | '-') expression
| expression bop=('<=' | '>=' | '>' | '<' | LE1 | GE1 | LT1 | GT1) expression
| expression bop=INSTANCEOF IDENTIFIER
| expression bop=('==' | '!=' | EQ1 | NE1) expression
| expression bop=('&&' | AND1) expression
| expression bop=('||' | OR1) expression
| <assoc=right> expression bop='?' expression bop=':' expression
| <assoc=right> expression
bop=('=' | '+=' | '-=' | '*=' | '/=')
expression
| lambdaExpression
| lambdaExpressionOrCall
;
primary
: '(' expression ')'
| literal
| IDENTIFIER
;
BOOL_LITERAL: TRUE | FALSE;
IDENTIFIER: LETTER (LETTER|DIGIT)*;
INTEGER_LITERAL: [0-9]+;
FLOATING_POINT_LITERAL: [0-9]+ '.' [0-9]* EXPONENT? | '.' [0-9]+ EXPONENT? | [0-9]+ EXPONENT?;
fragment EXPONENT: ('e'|'E') ('+'|'-')? [0-9]+;
StringLiteral: ('"' DoubleStringCharacter* '"'
| '\'' SingleStringCharacter* '\'') ;
fragment DoubleStringCharacter
: ~["\\r\n]
| '\' EscapeSequence
;
fragment SingleStringCharacter
: ~['\\r\n]
| '\' EscapeSequence
;
fragment EscapeSequence
: CharacterEscapeSequence
| '0'
| HexEscapeSequence
| UnicodeEscapeSequence
| ExtendedUnicodeEscapeSequence
;
fragment CharacterEscapeSequence
: SingleEscapeCharacter
| NonEscapeCharacter
;
fragment HexEscapeSequence
: 'x' HexDigit HexDigit
;
fragment UnicodeEscapeSequence
: 'u' HexDigit HexDigit HexDigit HexDigit
| 'u' '{' HexDigit HexDigit+ '}'
;
fragment ExtendedUnicodeEscapeSequence
: 'u' '{' HexDigit+ '}'
;
fragment SingleEscapeCharacter
: ['"\bfnrtv]
;
fragment NonEscapeCharacter
: ~['"\bfnrtv0-9xu\r\n]
;
fragment EscapeCharacter
: SingleEscapeCharacter
| [0-9]
| [xu]
;
fragment HexDigit
: [_0-9a-fA-F]
;
fragment DecimalIntegerLiteral
: '0'
| [1-9] [0-9_]*
;
fragment ExponentPart
: [eE] [+-]? [0-9_]+
;
fragment IdentifierPart
: IdentifierStart
| [\p{Mn}]
| [\p{Nd}]
| [\p{Pc}]
| '\u200C'
| '\u200D'
;
fragment IdentifierStart
: [\p{L}]
| [$_]
| '\' UnicodeEscapeSequence
;
LCURL: '{';
RCURL: '}';
LETTER: '\u0024' |
'\u0041'..'\u005a' |
'\u005f' |
'\u0061'..'\u007a' |
'\u00c0'..'\u00d6' |
'\u00d8'..'\u00f6' |
'\u00f8'..'\u00ff' |
'\u0100'..'\u1fff' |
'\u3040'..'\u318f' |
'\u3300'..'\u337f' |
'\u3400'..'\u3d2d' |
'\u4e00'..'\u9fff' |
'\uf900'..'\ufaff';
DIGIT: '\u0030'..'\u0039'|
'\u0660'..'\u0669'|
'\u06f0'..'\u06f9'|
'\u0966'..'\u096f'|
'\u09e6'..'\u09ef'|
'\u0a66'..'\u0a6f'|
'\u0ae6'..'\u0aef'|
'\u0b66'..'\u0b6f'|
'\u0be7'..'\u0bef'|
'\u0c66'..'\u0c6f'|
'\u0ce6'..'\u0cef'|
'\u0d66'..'\u0d6f'|
'\u0e50'..'\u0e59'|
'\u0ed0'..'\u0ed9'|
'\u1040'..'\u1049';
TRUE: 'true';
FALSE: 'false';
NULL: 'null';
DOT: '.';
LPAREN: '(';
RPAREN: ')';
LBRACK: '[';
RBRACK: ']';
COLON: ':';
COMMA: ',';
SEMICOLON: ';';
GT0: '>';
GT1: 'gt';
LT0: '<';
LT1: 'lt';
GE0: '>=';
GE1: 'ge';
LE0: '<=';
LE1: 'le';
EQ0: '==';
EQ1: 'eq';
NE0: '!=';
NE1: 'ne';
NOT0: '!';
NOT1: 'not';
AND0: '&&';
AND1: 'and';
OR0: '||';
OR1: 'or';
EMPTY: 'empty';
INSTANCEOF: 'instanceof';
MULT: '*';
PLUS: '+';
MINUS: '-';
QUESTIONMARK: '?';
DIV0: '/';
DIV1: 'div';
MOD0: '%';
MOD1: 'mod';
CONCAT: '+=';
ASSIGN: '=';
ARROW: '->';
DOLLAR: '$';
HASH: '#';
WS: [ \t\r\n]+ -> skip;
将它们的 Lexer 规则移动到 Identifier
的 Lexer 规则之前。
如果 ANTLR 有多个 Lexer 规则匹配相同长度的输入,它会选择匹配的语法中的第一个规则。
例如“mod”匹配到Identifier
和MOD1
,但是Identifier
是第一个,所以选择Identifier
。将 MOD1
规则移动到 Identifier
之前,它将匹配 MOD1
————-
顺便说一句,除非你关心“%”和“mod”有不同的标记值,否则你可以只定义一个规则:
MOD: ‘%’ | ‘mod’;
如果需要,您仍然可以获得令牌文本,但您只需在解析器规则中指定 MOD
而不是 (MOD0 | MOD1)