我是词法分析的新手,如果标题不够清楚,请您谅解。

基本上,我正在使用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使用完全相同的输入来执行另一个操作,从而允许其添加适当的下标。但是,这感觉很糟糕,我担心可能会出现无法预料的问题。

如果有人对更好的方法有任何想法,我仍然会喜欢。

最佳答案

几天后,我终于找到了一个更好的答案。看起来是这样的:

(\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个“全局”变量。 indentCount将跟踪我们当前的缩进长度。如果forceDedent的值大于0,则会迫使我们返回DEDENT

接下来,我们有一个条件来测试forceDedent上的真实值。如果有一个,我们将其递减1并使用unput函数以确保我们至少在同一模式上重复至少一次,但是对于此迭代,我们将返回一个DEDENT

如果尚未返回,则得到当前缩进的长度。

如果当前缩进大于我们最近的缩进,我们将在indentCount变量上进行跟踪并返回INDENT

如果我们还没有回来,那么是时候准备迎接可能的下垂了。我们将创建一个数组来跟踪它们。

当我们检测到一个凹陷时,用户可能试图一次关闭全部1个或更多块。因此,我们需要为用户关闭的块包含一个DEDENT。我们建立一个循环,并说只要当前缩进小于我们最近的缩进,我们就会在列表中添加一个DEDENT并将一个项目移出indentCount

如果我们跟踪了任何下位字符,则需要确保所有这些下位字符都被词法分析器返回。因为词法分析器一次只能返回1个令牌,所以我们在这里将返回1,但是我们还将设置forceDedent变量以确保我们也返回其余的令牌。为了确保我们再次迭代该模式并且可以插入这些凹坑,我们将使用unput函数。

在任何其他情况下,我们都将返回NEWLINE

关于javascript - 如何使用Jison lexer返回多个 token ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40189324/

10-10 18:59