我是新来的,因为我正在寻找替代我的长朋友flex& amp;野牛(使用lex / yacc超过20年)。主要是改变的原因是恕我直言的C ++支持不佳。
但在寻找替代情境时,敏感性将是主要的选择标准。
我是否正确地理解ANTLR(无论是3还是4,因为4还没有C ++支持)没有任何上下文敏感性(意味着自动上下文敏感性,而不是手动谓词)?我说的是纯语法上下文,而不是函数上下文,比如在C ++语言中整理函数和构造函数,这不是解析器的职责。
以下是一个简单的例子:
假设:
语法可能不是100%正确但可以理解
stmt:DEFINE SYSTEM ID' {' istmt'}' | DEFINE COMPONENT ID' {' istmt'}' ;
定义:'定义' ;
SYSTEM:' system' ;
组件:'组件' ;
ID:[A-Za-z] [A-Za-z] *;
INT:[0-9] +
输入:
定义系统定义{...
这两个定义将被普通词法分析者作为DEFINE返回,作为第一个最长的匹配。第二个"定义"解析器可以将当前上下文的有效标记传递给词法分析器,然后选择这些中的第一个匹配(如果有的话)。即当涉及到ID时,词法分析器将识别"定义"首先作为DEFINE,但这目前不是有效的,所以它需要下一场比赛同样的#34; word"这将是ID并返回它在有效列表中。如果在有效列表中找不到匹配项,则会返回通常的第一个匹配项,而不是错误处理/恢复。
当然有更复杂的例子在第一级无法解决,但是因为任何候选人总是会使用相同的词汇词来期待buzilding并且减少可能的pathes树将在大多数情况下最终得到一个分支识别要使用的正确令牌。
可以通过选项以及词法分析器级别启用上下文敏感性。如果需要,我还可以详细说明一个更复杂的例子。
答案 0 :(得分:3)
在Antlr中,词法分析器(通常)在解析器运行之前运行完成,因此解析器无法影响词汇上下文。
如果默认令牌值空间中的重叠和ID特定的重叠是有限的,那么只需将重叠成员包含在ID定义的一部分中(为方便起见添加标签):
stmt: DEFINE SYSTEM id=( DEFINE | SYSTEM | COMPONENT | ID ) '{' istmt '}'
| DEFINE COMPONENT id=( DEFINE | SYSTEM | COMPONENT | ID ) '{' istmt '}'
;
如果重叠较大或不方便,Antlr 4提供了词法模式,允许在输入流中的上下文定义的点处显式限制允许的语法。使用词法分析谓词,Antlr 3可以实现基本相同的功能。
至少从Antlr 2开始,已经存在解耦词法分析器和解析器的设计决策,并且可能是为了分离关注点和性能原因。在词法分析器驱动词法分析器的情况下,词法分析器可能必须在解析器能够满足规则之前在不同的上下文中多次重新输入部分输入文本。 Antlr词法分子在一次通过中处理整个输入文本时,尽其所能,并在很大程度上取得成功。只是以不同的方式采取了不同的方法。
更新:示例
如果SYSTEM和COMPONENT足以定义ID的出现,并且ID被定义为单个单词,则词法分析器变为:
// mode default:
DEFINE : 'define' ;
SYSTEM : 'system' -> pushMode(id);
COMPONENT : 'component' -> pushMode(id);
LBRACE : '{' ;
RBRACE : '}' ;
WS : [ /t/r/n] -> channel(HIDDEN);
mode id:
ID : [A-Za-z][A-Za-z]* -> popMode();
IDWS : [ /t/r/n] -> channel(HIDDEN);
// or, if more than a single ID word were allowed:
mode id:
ID : [A-Za-z][A-Za-z]*
IDLBRACE : '{' -> type(LBRACE), popMode();
IDWS : [ /t/r/n] -> channel(HIDDEN);
解析器不知道词法模式,所以
stmt: DEFINE ( SYSTEM | COMPONENT ) ID LBRACE istmt RBRACE ;
答案 1 :(得分:0)
这就是我解决 keywords
和 Tokens
歧义的方法。
src/MyGrammar.g4
CONTEXT_KEYWORD_TYPE: 'type' ;
CONTEXT_KEYWORD_CONST: 'const' ;
# This token should come after all KEYWORDS
IDENTIFIER: [_a-zA-Z] [a-zA-Z0-9]* ;
# This token should come after all KEYWORDS and INDENTIFIER
RENDER_ALL_KEYWORDS: 'itWillBeReplaced';
value: ...;
typeDefinition: CONTEXT_KEYWORD_TYPE '=' '{' key=(IDENTIFIER | RENDER_ALL_KEYWORDS) ':' value '}';
在我的第一个构建步骤中,我将 src/MyGrammar.g4
转换为 src/gen/MyGrammar.g4
(用 RENDER_ALL_KEYWORDS
分隔的所有 CONTEXT_KEYWORD_...
的列表替换 |
):
src/gen/MyGrammar.g4
...
typeDefinition: CONTEXT_KEYWORD_TYPE '=' '{' key=(IDENTIFIER | CONTEXT_KEYWORD_TYPE | CONTEXT_KEYWORD_CONST
) ':' value '}';
然后我编译`src/gen/MyGrammar.g4'作为我的源语法文件。
在 TypeScript MyLangTreeListener
上下文中具有属性 _key
:
export class MyLangRootListener implements MyLangListener {
enterTypeDefinition(ctx: TypeDefinitionContext): void {
ctx._key;
}
}
这样我就有了与 IDENTIFIER
标记不冲突的上下文关键字。我只需要添加 RENDER_ALL_KEYWORDS
并使用语法 myTokenName=(IDENTIFIER | RENDER_ALL_KEYWORDS)
。
添加新的上下文特定关键字时无需更新所有内容。