使用PEG.js实现文档缩进



我正在研究一种叫做gaiman的类似于ruby的语言,我使用PEG.js来生成解析器。

你知道是否有一种方法实现heredocs与适当的缩进?

xxx =  <<<END
hello
world
END

输出应该是:

"hello
world"

我需要这个,因为这段代码看起来不太好:

def foo(arg) {
if arg == "here" then
return <<<END
xxx
xxx
END
end
end

这是一个用户希望返回的函数:

"xxx
xxx"

我希望代码看起来像这样:

def foo(arg) {
if arg == "here" then
return <<<END
xxx
xxx
END
end
end

如果我剪掉所有的行,用户将不能在他想要的时候使用带前导空格的字符串。有人知道PEG.js是否允许这个吗?

我还没有heredocs的任何代码,只是想确定我想要的东西是否可能。

编辑:

所以我试图实现heredocs,问题是PEG不允许反向引用。

heredoc = "<<<" marker:[w]+ "n" text:[sS]+ marker {
return text.join('');
}

表示没有定义标记。至于修剪,我想我可以使用location()函数

我不认为这是一个合理的期望解析器生成器;几乎没有人能面对这样的挑战。

首先,识别here-string语法本质上是上下文敏感的,因为结束分隔符必须是<<<令牌后提供的分隔符的精确副本。因此,您将需要一个自定义词法分析器,这意味着您需要一个允许您使用自定义词法分析器的解析器生成器。(因此,假设您想要无扫描程序的解析器的解析器生成器可能不是最佳选择。)

识别here-string标记的结尾应该不会太困难,尽管您不能使用单个正则表达式来完成。我的方法是使用一个自定义的扫描函数,它将这里的字符串分解成一系列行,并将它们连接起来,直到到达只包含结束分隔符的行。

一旦你已经识别了文字的文本,所有你需要规范化的空格在你想要的方式是<<<开始的列号。这样,就可以对字符串字面值中的每一行进行修剪。所以你只需要一个词法扫描器,准确报道令牌的位置。修剪通常不会在生成的词法扫描程序中完成;相反,它可能是相关的语义动作。(同样,它也可以是语法中的语义动作。但它总是你写的代码)

当您修剪文字时,您需要处理不可能的情况,因为用户没有遵守缩进要求。你需要对制表符做点什么;要使这些正确可能意味着您需要一个词法扫描器来计算可见列位置而不是字符偏移。

我不知道如果peg.js符合这些要求,因为我不使用它。(我确实看了文档,但没有看到任何关于如何合并自定义扫描仪功能的指示。但这并不意味着没有办法做到这一点。)我希望上面的讨论至少能让你检查你想要使用的解析器生成器的详细文档,否则就会找到一个不同的解析器生成器,在这个用例中为你工作。

这是Peggy中heredocs的实现,它是不再维护的PEG.js的后继版本。这段代码是基于GitHub的问题。

heredoc = "<<<" begin:marker "n" text:($any_char+ "n")+ _ end:marker (
&{ return begin === end; }
/ '' { error(`Expected matched marker "${begin}", but marker "${end}" was found`); }
) {
const loc = location();
const min = loc.start.column - 1;
const re = new RegExp(`\s{${min}}`);
return text.map(line => {
return line[0].replace(re, '');
}).join('n');
}
any_char = (!"n" .)
marker_char = (!" " !"n" .)
marker "Marker" = $marker_char+
_ "whitespace"
= [ tnr]* { return []; }

编辑:上面的代码不能与herdoc之后的另一段代码一起工作,下面是更好的语法:

{ let heredoc_begin = null; }
heredoc = "<<<" beginMarker "n" text:content endMarker {
const loc = location();
const min = loc.start.column - 1;
const re = new RegExp(`^\s{${min}}`, 'mg');
return {
type: 'Literal',
value: text.replace(re, '')
};
}
__ = (!"n" !" " .)
marker 'Marker' = $__+
beginMarker = m:marker { heredoc_begin = m; }
endMarker = "n" " "* end:marker &{ return heredoc_begin === end; }
content = $(!endMarker .)*

相关内容

  • 没有找到相关文章

最新更新