我已经编写了一个可以解决这个问题的生成器,但我想知道实现偏离规则的最佳方法.
简而言之:在这种情况下,偏离规则意味着缩进被识别为语法元素.
以下是伪代码的越位规则,用于制作以可用形式捕获缩进的标记器,我不想按语言限制答案:
token NEWLINE matches r"\n\ *" increase line count pick up and store the indentation level remember to also record the current level of parenthesis procedure layout tokens level = stack of indentation levels push 0 to level last_newline = none per each token if it is NEWLINE put it to last_newline and get next token if last_newline contains something extract new_level and parenthesis_count from last_newline - if newline was inside parentheses, do nothing - if new_level > level.top push new_level to level emit last_newline as INDENT token and clear last_newline - if new_level == level.top emit last_newline and clear last_newline - otherwise while new_level < level.top pop from level if new_level > level.top freak out, indentation is broken. emit last_newline as DEDENT token clear last_newline emit token while level.top != 0 emit token as DEDENT token pop from level comments are ignored before they are getting into the layouter layouter lies between a lexer and a parser
这个布局在时间上不会生成多个NEWLINE,并且在出现缩进时不会生成NEWLINE.因此,解析规则仍然非常简单.我觉得这很好,但告知是否有更好的方法来实现它.
在使用它一段时间之后,我注意到在DEDENT之后无论如何都可以发出新行,这样你就可以将表达式与NEWLINE分开,同时将INDENT DEDENT保留为表达式的预告片.
在过去的几年里,我已经为几个以缩进为中心的特定领域语言编写了标记器和解析器,而且你所拥有的东西看起来对我来说非常合理,不管是什么值得的.如果我没有弄错的话,你的方法与Python的方法非常相似,例如,它似乎应该承担一些重量.
将NEWLINE NEWLINE INDENT转换为INDENT,然后它才能进入解析器,这似乎是正确的做事方式 - 在解析器中始终向前看是痛苦的(IME)!我实际上已经完成了这个步骤作为一个单独的层,结果是一个三步过程:第一步结合你的词法分析器和外行设备减去所有NEWLINE前瞻性的东西(这使得它非常简单),第二个(也非常简单) )图层折叠连续NEWLINE并将NEWLINE INDENT转换为INDENT(或实际上,COLON NEWLINE INDENT到INDENT,因为在这种情况下所有缩进的块总是以冒号开头),然后解析器是第三个阶段.但是对我来说,按照你描述它们的方式做事情也很有意义,特别是如果你想把词法分析器和唱法分开,
我确实有一个应用程序需要对缩进规则更加灵活,基本上让解析器在需要时强制执行它们 - 以下需要在某些上下文中有效,例如:
this line introduces an indented block of literal text: this line of the block is indented four spaces but this line is only indented two spaces
这对于INDENT/DEDENT令牌来说效果不是很好,因为你最终需要为每一段缩进生成一个INDENT,并且在回来的路上需要生成相同数量的DEDENT,除非你向前看以找出缩进级别的位置最终会成为现实,而你似乎并不想要一个令牌化器.在那种情况下,我尝试了一些不同的东西,最后只是在每个NEWLINE令牌中存储一个计数器,该计数器给出了后续逻辑行的缩进(正或负)的变化.(每个标记还存储所有尾随空格,以防需要保留;对于NEWLINE,存储的空白包括EOL本身,任何插入的空白行和下一个逻辑行上的缩进.)根本没有单独的INDENT或DEDENT标记.让解析器处理这个问题比嵌套INDENTs和DEDENTs要多得多,而且很可能是一个复杂的语法,需要一个花哨的解析器生成器,但它并不像我担心的那么糟糕,无论是.同样,解析器无需向前看NEWLINE以查看此方案中是否存在INDENT.
尽管如此,我认为你同意在tokenizer/layouter中允许和保留所有类似疯狂的空白,并让解析器决定什么是文字,什么代码是一个不寻常的要求!例如,如果您只是想解析Python代码,那么您当然不希望您的解析器背负该缩进计数器.你做事的方式几乎肯定是你的应用程序和其他许多其他方法的正确方法.虽然如果有其他人想到如何最好地做这类事情,我显然很想听听他们......