将lexer规则更改为parser规则|python时,Antlr4解析问题



我是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_SPECIFIERLexer规则将是指定的令牌类型;如果有这个规则,你永远不会看到VOIDINT令牌。你实际上是在让它们成为碎片规则。(

删除TYPE_SPECIFIERLexer规则(你一开始就有正确的想法。(

但是,您需要将IDENTIFIER规则移动到Lexer规则中的任何关键字下方(在Lexer的"平局"中,ANTLR将使用第一个定义的规则,因此您永远不会看到关键字规则,因为您的关键字也将匹配更通用的IDENTIFIERS规则(。

---

此外,定义一个以EOF结尾的开始规则是一个非常好的主意,以确保所有输入都被解析。

最新更新