ANTLR访问者根节点中的self.visitChildren(ctx)在Python中返回None



正如标题所说:当从解析树传播值时,当我在那里调用self.visitChildren(ctx)时,根节点返回None。我可以看到其他节点向上传播值,但根节点是唯一接收None的节点。

我使用的是ANTLR 4.10.1和Python antlr4-python3-runtime 4.10。

当我输入393939393和CTRL-D时,它打印以下内容:

Number: 393939393
Atom: {'type': 'number', 'value': '393939393'}
SExpr: None
None

我尝试对S-Expression解析器进行以下轻微修改,然后显示我的访问者脚本:

/*
Port to Antlr4 by Tom Everett
*/
grammar sexpr;
sexpr
/*   : item* EOF */
: item EOF
;
item
: atom
| list_
/*   | LPAREN item DOT item RPAREN */
;
list_
: LPAREN item* RPAREN
;
atom
: string
| symbol
| number
/*   | DOT */
;
string: STRING ;
symbol: SYMBOL ;
number: NUMBER ;

STRING
: '"' ('\' . | ~ ('\' | '"'))* '"'
;
WHITESPACE
: (' ' | 'n' | 't' | 'r')+ -> skip
;
NUMBER
: ('+' | '-')? (DIGIT)+ ('.' (DIGIT)+)?
;
SYMBOL
: SYMBOL_START (SYMBOL_START | DIGIT)*
;
LPAREN
: '('
;
RPAREN
: ')'
;
DOT
: '.'
;
fragment SYMBOL_START
: ('a' .. 'z')
| ('A' .. 'Z')
| '+'
| '-'
| '*'
| '/'
| '.'
;
fragment DIGIT
: ('0' .. '9')
;

Python 3 ANTLR访问者代码

#!/usr/bin/python3
import sys
from antlr4 import *
from sexprLexer import sexprLexer
from sexprParser import sexprParser
from sexprVisitor import sexprVisitor
class SExprVisitor(sexprVisitor):
def visitSexpr(self, ctx):
r = self.visitChildren(ctx)
print("SExpr: %s" % r)
return r

def visitItem(self, ctx):
r = self.visitChildren(ctx)
return r
def visitAtom(self, ctx):
r = self.visitChildren(ctx)
print("Atom: %s" % r)
return r
def visitString(self, ctx):
print("String: %s" % ctx.getText())
return {'type':'string', 'value':ctx.getText()}
def visitNumber(self, ctx):
print("Number: %s" % ctx.getText())
return {'type':'number', 'value':ctx.getText()}
def visitSymbol(self, ctx):
print("Symbol: %s" % ctx.getText())
return {'type':'symbol', 'value':ctx.getText()}
def visitor_main(argv):
input_stream = StdinStream()
lexer = sexprLexer(input_stream)
stream = CommonTokenStream(lexer)
parser = sexprParser(stream)
tree = parser.sexpr()
visitor = SExprVisitor()
output = visitor.visit(tree)
print(output)

def main(argv):
visitor_main(argv)
if __name__ == '__main__':
main(sys.argv)

根节点以EOF令牌结束,这是访问者调用visitChildren后返回的内容。如果您在访问者:

中包含此方法
def visitTerminal(self, ctx):
# The `EOF` will now return this instead of `None`
return '???'

您将看到???visitSexpr返回。

要修复它,只需调用visitSexpr中的item:

def visitSexpr(self, ctx):
r = self.visitChildren(ctx.item())
print("SExpr: %s" % r)
return r

您可以使用[...]而不是旧的v3语法'?' .. '?':

来使您的语法更紧凑一些。
grammar sexpr;
sexpr
/*   : item* EOF */
: item EOF
;
item
: atom
| list_
/*   | LPAREN item DOT item RPAREN */
;
list_
: LPAREN item* RPAREN
;
atom
: string
| symbol
| number
/*   | DOT */
;
string: STRING ;
symbol: SYMBOL ;
number: NUMBER ;

STRING
: '"' ('\' . | ~[\"])* '"'
;
WHITESPACE
: [ trn]+ -> skip
;
NUMBER
:[+-]? (DIGIT)+ ('.' (DIGIT)+)?
;
SYMBOL
: SYMBOL_START (SYMBOL_START | DIGIT)*
;
LPAREN
: '('
;
RPAREN
: ')'
;
DOT
: '.'
;
fragment SYMBOL_START
: [a-zA-Z+-*/.]
;
fragment DIGIT
: [0-9]
;

编辑

我不明白你对[…]中紧凑语法的评论

我的意思是旧的v3语法'a' .. 'z'可以在v4中写成[a-z],使其更紧凑。

如果我使用item*而不是item遍历项目的最佳方法是什么?…

你在评论中添加的内容可能会起作用。您可以稍微更改语法,以创建其他规则使用的items : item*;规则。您也可以使用替代标签,这样您就不需要额外的规则,如string,symbolnumber

快速演示:

/*
Port to Antlr4 by Tom Everett
*/
grammar sexpr;
sexpr
: items EOF
;
items
: item*
;
item
: atom    #item_atom
| list_   #item_list
;
list_
: LPAREN items RPAREN
;
atom
: STRING    #atom_string
| SYMBOL    #atom_symbol
| NUMBER    #atom_number
;
STRING
: '"' ('\' . | ~[\"])* '"'
;
WHITESPACE
: [ trn]+ -> skip
;
NUMBER
:[+-]? (DIGIT)+ ('.' (DIGIT)+)?
;
SYMBOL
: SYMBOL_START (SYMBOL_START | DIGIT)*
;
LPAREN
: '('
;
RPAREN
: ')'
;
DOT
: '.'
;
fragment SYMBOL_START
: [a-zA-Z+-*/.]
;
fragment DIGIT
: [0-9]
;

如果你现在运行:

import sys
from antlr4 import *
from sexprLexer import sexprLexer
from sexprParser import sexprParser
from sexprVisitor import sexprVisitor

class SExprVisitor(sexprVisitor):
def visitSexpr(self, ctx):
return self.visit(ctx.items())
def visitItems(self, ctx):
items = []
for item in ctx.item():
items.append(self.visit(item))
return items
def visitItem_atom(self, ctx):
return self.visit(ctx.atom())
def visitItem_list(self, ctx):
return self.visit(ctx.list_())
def visitList_(self, ctx):
return self.visit(ctx.items())
def visitAtom_string(self, ctx):
return {'type': 'string', 'value': ctx.getText()}
def visitAtom_number(self, ctx):
return {'type': 'number', 'value': ctx.getText()}
def visitAtom_symbol(self, ctx):
return {'type': 'symbol', 'value': ctx.getText()}

def visitor_main(argv):
lexer = sexprLexer(InputStream('("Q" 42 /7)'))
stream = CommonTokenStream(lexer)
parser = sexprParser(stream)
tree = parser.sexpr()
visitor = SExprVisitor()
output = visitor.visit(tree)
print(output)

def main(argv):
visitor_main(argv)

if __name__ == '__main__':
main(sys.argv)

打印以下内容:

[[{'type': 'string', 'value': '"Q"'}, {'type': 'number', 'value': '42'}, {'type': 'symbol', 'value': '/7'}]]

最新更新