我是lexing和解析的新手,如果标题不够明确,我很抱歉。
基本上,我正在使用Jison来解析一些文本,我试图让词法分析器理解缩进。这是有问题的:
(\r\n|\r|\n)+\s* %{
parser.indentCount = parser.indentCount || [0];
var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length;
if (indentation > parser.indentCount[0]) {
parser.indentCount.unshift(indentation);
return 'INDENT';
}
var tokens = [];
while (indentation < parser.indentCount[0]) {
tokens.push('DEDENT');
parser.indentCount.shift();
}
if (tokens.length) {
return tokens;
}
if (!indentation.length) {
return 'NEWLINE';
}
%}
到目前为止,几乎所有这些都按预期工作。一个问题是我尝试返回DEDENT
标记数组的行。似乎Jison只是将该数组转换为字符串,这导致我得到像Expecting ........, got DEDENT,DEDENT
这样的解析错误。
我希望能够解决这个问题,手动将一些DEDENT
令牌推入堆栈。也许使用类似this.pushToken('DEDENT')
的函数或类似的东西。但是Jison文档并不是那么好,我可以使用一些帮助。
有什么想法吗?
编辑:
在查看生成的解析器代码之后,我似乎已经能够解决这个问题了。这似乎有用......
if (tokens.length) {
var args = arguments;
tokens.slice(1).forEach(function () {
lexer.performAction.apply(this, args);
}.bind(this));
return 'DEDENT';
}
这使得词法分析器使用我们在堆栈中的每个DEDENT
的完全相同的输入来执行另一个动作,从而允许它添加到适当的dedents中。然而,感觉很糟糕,我担心会出现无法预料的问题。
如果有人对更好的方法有任何想法,我仍然会喜欢它。
答案 0 :(得分:1)
几天之后,我最终找到了更好的答案。这是它的样子:
(\r\n|\r|\n)+[ \t]* %{
parser.indentCount = parser.indentCount || [0];
parser.forceDedent = parser.forceDedent || 0;
if (parser.forceDedent) {
parser.forceDedent -= 1;
this.unput(yytext);
return 'DEDENT';
}
var indentation = yytext.replace(/^(\r\n|\r|\n)+/, '').length;
if (indentation > parser.indentCount[0]) {
parser.indentCount.unshift(indentation);
return 'INDENT';
}
var dedents = [];
while (indentation < parser.indentCount[0]) {
dedents.push('DEDENT');
parser.indentCount.shift();
}
if (dedents.length) {
parser.forceDedent = dedents.length - 1;
this.unput(yytext);
return 'DEDENT';
}
return `NEWLINE`;
%}
首先,我修改了我的捕获正则表达式,以确保在一系列非换行符空格之后我不会无意中捕获额外的换行符。
接下来,我们确保有2&#34;全球&#34;变量。 indentCount
将跟踪我们当前的缩进长度。 forceDedent
会强制我们返回DEDENT
,如果它的值大于0。
接下来,我们有一个条件来测试forceDedent
上的真值。如果我们有一个,我们将它减1并使用unput
函数确保我们至少再一次迭代这个相同的模式,但是对于这个迭代,我们将返回一个DEDENT
。
如果我们还没有回来,我们会得到当前缩进的长度。
如果当前缩进大于我们最近的缩进,我们会在indentCount
变量上跟踪该缩进并返回INDENT
。
如果我们还没有回来,那么现在是时候为可能的受访者做准备了。我们制作一个数组来跟踪它们。
当我们检测到dedent时,用户可能会尝试一次关闭1个或多个块。因此,我们需要在用户关闭的块中包含DEDENT
。我们设置了一个循环,并说只要当前缩进小于我们最近的缩进,我们就会在列表中添加DEDENT
并将项目从indentCount
移开
如果我们跟踪任何dedents,我们需要确保所有这些都被lexer返回。因为词法分析器一次只能返回1个令牌,所以我们在这里返回1,但我们也会设置forceDedent
变量以确保我们还返回其余部分。为了确保我们再次迭代这个模式并插入那些dedents,我们将使用unput
函数。
在任何其他情况下,我们只会返回NEWLINE
。