2016-10-22 144 views
1

如果标题不够清晰,我很乐意进行分析和解析。如何用Jison词法分析器返回多个标记

基本上,我使用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添加。然而,感觉很糟糕,我担心可能会有不可预见的问题。

如果有人有更好的方法来做到这一点,我仍然会喜欢它。

回答

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

接下来,我们有条件测试forceDedent上的真值。如果我们有一个,我们将它减1并使用unput函数来确保我们至少再次迭代这个相同的模式,但是对于这个迭代,我们将返回一个DEDENT

如果我们还没有返回,我们会得到我们当前缩进的长度。

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

如果我们还没有返回,是时候准备可能的dedent。我们将制作一个数组来追踪它们。

当我们检测到一个缩进时,用户可能试图一次关闭一个或多个块。所以我们需要包含一个DEDENT作为用户关闭的块。我们设置了一个循环,并说只要当前缩进比我们最近的缩进少,我们就会将DEDENT添加到我们的列表中,并将其中的一个项目从indentCount中移出。

如果我们追踪任何dedent,我们需要确保他们所有人都被词法分析器返回。由于词法分析器一次只能返回1个标记,因此我们将在这里返回1,但我们也将设置我们的forceDedent变量以确保我们也返回其余的标记。为了确保我们再次迭代这个模式,并且可以插入这些缩进,我们将使用unput函数。我们只会返回NEWLINE