我有一个编译的语法,我想用它来将输入序列转换成XML。请注意,在我的情况下,我有一个很大的语法,其中包含许多规则,并且我想避免在代码中覆盖每个语法规则。
我将使用一个示例来避免混淆。让我们有以下语法
grammar expr;
prog: stat+ ;
stat: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;
expr: expr ('*'|'/') expr
| INT
| ID
| '(' expr ')'
;
ID : [a-zA-Z]+ ; // match identifiers
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace
输入序列
A = 10
B = A * A
预期输出
<prog>
<stat>
A =
<expr> 10
</expr>
\r\n
</stat>
<stat>
B =
<expr>
<expr>A</expr>
*
<expr> A</expr>
</expr>
\r\n
</stat>
</prog>
对应于解析树
当前,我使用一种创建
ParseTree
的方法,并使用toStringTree
方法生成以下字符串(prog (stat A = (expr 10) \r\n) (stat B = (expr (expr A) * (expr A)) \r\n))
然后我将其转换为上面显示的XML(我使用适用于任何语法的简单通用代码)。我发现这种方法很假。没有
toStringTree
是否可以解决它?我想避免需要覆盖访问者中的每个语法规则。 (我有数百个)。编辑
我基本上需要某种通用的ParseTree序列化为XML格式。主要目标是我不必为每个规则都用Java编写特殊的序列化方法。
最佳答案
这种方法可能适合您的需求。我为终端符号加上了额外的标记t
,以提高可读性,同时也跳过了带有空格的符号。但是,如果需要的话,调整输出应该不是一个大问题。
final exprLexer lexer = new exprLexer(CharStreams.fromString("A=10\nB = A * A\n"));
final CommonTokenStream tokens = new CommonTokenStream(lexer);
final exprParser parser = new exprParser(tokens);
final ParseTree tree = parser.prog();
ParseTreeWalker.DEFAULT.walk(new exprBaseListener()
{
final String INDENT = " ";
int level = 0;
@Override
public void enterEveryRule(final ParserRuleContext ctx)
{
System.out.printf("%s<%s>%n", indent(), parser.getRuleNames()[ctx.getRuleIndex()]);
++level;
super.enterEveryRule(ctx);
}
@Override
public void exitEveryRule(final ParserRuleContext ctx)
{
--level;
System.out.printf("%s</%s>%n", indent(), parser.getRuleNames()[ctx.getRuleIndex()]);
super.exitEveryRule(ctx);
}
@Override
public void visitTerminal(final TerminalNode node)
{
final String value = node.getText();
if (!value.matches("\\s+"))
{
System.out.printf("%s<t>%s</t>%n", indent(), node.getText());
}
super.visitTerminal(node);
}
private String indent()
{
return String.join("", Collections.nCopies(level, INDENT));
}
}, tree);