我面临着一个不明确的情况,输入字符串可以使用不同的规则进行解析,我需要考虑这两个选项,并为它们生成多个解析树。
为了简单起见,考虑到像"Alber Johanson"这样的人名,这个名字可以解析为
(fullName (firstName Alber) (lastName Johanson))
或解析为
(fullName (firstName Alber) (lastName Johan) (relation son))
首先,如何配置规则来处理第二种情况?因为它是第二个字符串的一部分,而不是一个单独的令牌。
其次,如何为输入字符串的所有可能选项生成解析树?
更新
这是我的语法示例,它只能用于解析第一种情况,而不能用于解析第二种情况
fullName: firstName lastName | firstName lastName relation;
firstName: NAME;
lastName: NAME;
relation: REL;
NAME: ('a'..'z'|'A'..'Z')+;
REL: 'son';
WHITESPACE : ('t' | ' ' | 'r' | 'n'| 'u0020' | 'u000C' )+ -> skip ;
ANTLR将不允许您按照自己想要的方式进行操作。然而,原因不是模棱两可,而是象征性的东西。
"Johanson"一词总是用NAME拼写,因为ANTLR的拼写策略是:
- 返回匹配时间最长的Token
- 在两个令牌匹配具有相同长度的情况下,首选第一个定义的令牌
令牌REL
永远不会发生,因为
- 任何后缀为"son"的单词都是
NAME
(最长匹配) - 任何前缀为"son"的单词都是
NAME
(最长匹配) - 然而,一个孤立的单词"son"是
NAME
(REL
匹配但不是第一次定义的)
回答您的第一个问题:它不能由ANTLR解析器处理,因为它依赖于在解析之前进行标记化。你有两个选择:
- 使用语法分析器生成器,允许您标记语法分析器定向(PEG语法分析器像parboiled,rats应该这样做)
- 丢弃令牌REL,并在访问解析树时重新分析姓氏
回答第二个问题:
以上两种选择都很难解决打印同一字符序列的可能解释的问题。
PEG解析器被设计为通过设计更喜欢第一种替代方案,如果找到有效的解释,它将不会进一步探索。
ANTLR还没有被设计成驱动解析器引导的lexer。如果您决定重新分析姓氏,那么使用纯java查找解释可能比编写新的lexer/parser来查找姓氏更容易。
从一长串评论中切换:
只要你定义词素来提取整个单词,并且有一个策略来确定当识别出两个单词时哪个词素获胜,你就会遇到这个问题。
为了避免这种情况,你必须使用不竞争的词汇。您可以使用字符作为词法来运行GLR解析器;对于简短的输入(例如,人名),这不会有任何问题。然后,您可以在语法中定义您的名称规则,而不是作为词位识别器,GLR解析器将提供所有可能的解释。
不,我不知道有什么好的基于Java的GLR解析器。这里有一个大列表:http://en.wikipedia.org/wiki/Comparison_of_parser_generators