我是antlr的新手,在正确解析源代码时遇到了一些问题,这是我的语法:
compilationUnit
: (assignment | declarationList | definitionList)* EOF
;
block
: LC RC
;
assignment: typeSpecifier? IDENTIFIER '=' expression ';';
expression
: INTEGER
;
statementList
:
;
declarationList
: declaration
| declarationList declaration
;
declaration
: functionDeclaration SEMICOLON
;
functionDeclaration
: typeSpecifier? functionName functionArgs
;
definitionList
: functionDefinition
;
functionDefinition: functionDeclaration block;
functionName: IDENTIFIER;
functionArgs: LP RP;
typeSpecifier: VOID | INT;
TYPE_SPECIFIER
: VOID
| INT
;
IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]*;
INTEGER: [1-9][0-9]*;
STRING_LITERAL: '"' ~('"')* '"';
VOID: 'void';
INT: 'int';
STAR: '*';
LP: '(';
RP: ')';
LC: '{';
RC: '}';
LSQRB: '[';
RSQRB: ']';
SEMICOLON: ';';
WS: [ trn]+ -> skip;
NEWLINE
: ( 'r' 'n'?
| 'n'
)
-> skip
;
BLOCK_COMMENT
: '/*' .*? '*/'
-> skip
;
LINE_COMMENT
: '//' ~[rn]*
-> skip
;
问题是typeSpecifier不能正确匹配,除非我把它改成lexer规则,所以如果我输入这样的东西:
void b();
int a = 1;
它返回:
line 1:0 extraneous input 'void' expecting {<EOF>, IDENTIFIER, 'void', 'int'}
line 2:0 extraneous input 'int' expecting {<EOF>, IDENTIFIER, 'void', 'int'}
但是,如果我将typeSpecifier重命名为TYPE_SPECIFIER,它会对其进行无错误的解析,问题是,对于分配int a = 1
,我无法区分节点和终端节点,标识符也有同样的问题,因此它将返回:
'int' = <class 'antlr4.tree.Tree.TerminalNodeImpl'>
'a' = <class 'antlr4.tree.Tree.TerminalNodeImpl'>
'=' = <class 'antlr4.tree.Tree.TerminalNodeImpl'>
'1' = <class 'core.CParser.CParser.ExpressionContext'>
';' = <class 'antlr4.tree.Tree.TerminalNodeImpl'>
我想让它返回更像的东西
'int' = <class 'antlr4.tree.Tree.TypeSpecifier'>
'a' = <class 'antlr4.tree.Tree.Identifier'>
'=' = <class 'antlr4.tree.Tree.AssignEq'> #or something like that
'1' = <class 'core.CParser.CParser.ExpressionContext'>
';' = <class 'antlr4.tree.Tree.SemiCol'>
这是我的python访问者代码:
from core.CParser import CParser
from core.CListener import CListener
from io import FileIO
from antlr4.tree.Tree import TerminalNodeImpl
class Listener(CListener):
def __init__(self, output):
self.output: FileIO = output
def add_newline(self):
self.output.write('n')
def enterDeclaration(self, ctx: CParser.DeclarationContext):
...
def enterFunctionDeclaration(self,
ctx: CParser.FunctionDeclarationContext):
for child in ctx.getChildren():
if isinstance(child, TerminalNodeImpl):
self.output.write(child.getText() + ' ')
if isinstance(child, CParser.FunctionNameContext):
self.output.write(child.getText())
if isinstance(child, CParser.FunctionArgsContext):
self.output.write(child.getText())
self.output.write(';')
self.add_newline()
def enterAssignment(self, ctx: CParser.AssignmentContext):
for child in ctx.getChildren():
if isinstance(child, TerminalNodeImpl):
self.output.write(child.getText() + ' ')
if isinstance(child, CParser.ExpressionContext):
self.output.write(child.getText())
self.add_newline()
def enterBlock(self, ctx: CParser.BlockContext):
print(ctx.getText())
提前感谢:(
您拥有以下两项:
typeSpecifier: VOID | INT;
TYPE_SPECIFIER
: VOID
| INT
;
然而,您从未在任何解析器规则中使用过TYPE_SPECIFIER
令牌。(TYPE_SPECIFIER
Lexer规则将是指定的令牌类型;如果有这个规则,你永远不会看到VOID
或INT
令牌。你实际上是在让它们成为碎片规则。(
删除TYPE_SPECIFIER
Lexer规则(你一开始就有正确的想法。(
但是,您需要将IDENTIFIER
规则移动到Lexer规则中的任何关键字下方(在Lexer的"平局"中,ANTLR将使用第一个定义的规则,因此您永远不会看到关键字规则,因为您的关键字也将匹配更通用的IDENTIFIERS规则(。
---
此外,定义一个以EOF
结尾的开始规则是一个非常好的主意,以确保所有输入都被解析。