1

我一直在研究用 ANTLR 解析键值数据格式。非常简单,但键代表层次结构。

我的输入语法的简化示例:

/a/b/c=2
/a/b/d/e=3
/a/b/d/f=4

在我看来,这代表一棵结构如下的树:

(a (b (= c 2) (d (= e 3) (= f 4))))

我能得到的最接近的是使用以下语法:

/* Parser Rules */
start: (component NEWLINE?)* EOF -> (component)*;

component: FORWARD_SLASH ALPHA_STRING component -> ^(ALPHA_STRING component)
  | FORWARD_SLASH ALPHA_STRING EQUALS value -> ^(EQUALS ALPHA_STRING value);

value: ALPHA_STRING;

/* Lexer Rules */
NEWLINE : '\r'? '\n';
ALPHA_STRING : ('a'..'z'|'A'..'Z'|'0'..'9')+;
EQUALS : '=';
FORWARD_SLASH : '/';

产生:

(a (b (= c 2))) (a (b (d (= e 3)))) (a (b (d (= f 4))))

我不确定我是否对像 ANTLR 这样的通用工具提出了太多要求,这是我可以通过这种方法得到的。也就是说,从这里我使用树的各个部分并手动创建我想要的数据结构。

那么 - 我可以直接从语法中生成我想要的树结构吗?如果是这样,怎么做?如果不是,为什么不呢 - 它是 ANTLR 的技术限制,还是与所涉及的语言类型有关?

4

2 回答 2

2

我不确定我是否对像 ANTLR 这样的通用工具要求太多...

我认为您对令牌解析器的要求太多。对于 input /a/b/c=2,令牌解析器看到:

FORWARD_SLASH ALPHA_STRING FORWARD_SLASH ALPHA_STRING FORWARD_SLASH ALPHA_STRING EQUALS ALPHA_STRING

在这种情况下,有趣的是令牌本身中的文本,令牌解析器对此并不关心。您至少需要使用手动编码的操作来挖掘令牌、存储它们、组织它们并以所需的排列方式将它们吐出。

...也就是说,从这里我消耗树的部分并手动创建我想要的数据结构。

您可以选择使用一个或多个 ANTLR 树解析器来帮助您完成任务,但它们也关注令牌类型而不是令牌文本。最终我认为你仍然需要在某个地方编写一个动作。

使用您的语法和使用相同标记词汇的自定义树语法,我能够减少这种情况(使用根节点提供帮助):

(START (a (b (= c 2))) (a (b (d (= e 3)))) (a (b (d (= f 4)))))

对此:

(START (a (b (= c 2) (d (= e 3)))) (a (b (d (= f 4)))))

不错的开始(如果您有兴趣,我可以发布树语法),但这需要语义谓词。如果没有我的一些编码,ANTLR 就无法做到这一点。

那么 - 我可以直接从语法中生成我想要的树结构吗?...如果不是,为什么不呢 - 它是 ANTLR 的技术限制,还是与所涉及的语言类型有关?

这是某种技术上的限制:在词法分析之后,ANTLR 正确(即,不是您可以注入的代码)对令牌而不是它们可能包含的文本进行操作1。如果文本“a”映射到令牌A,文本“b”映射到令牌B(等等),树解析器会给你一些现在不能的杠杆作用,但我认为你仍然需要编写一些动作和/或语义谓词来获得你想要的。


1除了能够使用自定义文本创建标记外,但这与此问题无关。

于 2012-11-22T23:08:47.873 回答
1

您可以做的不是使用 AST,而是定义自己的树和操作。然后,您不需要每次触发“组件”规则时都创建新树,而只需向其添加新节点即可。我希望这个想法很清楚?

于 2012-11-22T11:13:49.027 回答