我写了一些解析器组合器来构建一个带有源位置信息的AST。分析表明至少30%的执行花费在反复解析不必要的信息以生成源位置数据上。我需要帮助找到一种提高性能的替代方法。
假设我正在解析一种仅由两个令牌组成的语言:'A'和'B'。为了生成AST节点,我当前定义了一个组合器,它提取位置并构建节点:
// Parse body and build resulting node from function
// nodeFactory called with position and result from body.
var node = function(body, nodeFactory) {
return bind(
locParser, // get the current location in the token stream
body,
prevEnd, // get the previous end position in the token stream
function(o, x, c) {
return always(f(Location(o, c), x));
});
};
var a = node(char('A'), buildNodeA);
var b = node(char('B'), buildNodeB);
这种方法的问题是locParser
总是运行,无论身体是否成功:
var element = either(a, b);
var program = many(element);
run(program, "AAABBA");
a
一旦明确其body
将失败,就会失败。
对于“B”代币,locParser
的第一个a
运行,当body
失败时,locParser
运行b
。嵌套和更多分支,这成为一个性能问题。
该位置包含整个消费输入范围:
var c = node(next(char(' '), char('C')), buildNodeC);
'C'节点的位置从空格字符的开头开始。
var d = node(between(char('('), char(')'), char('D')), buildNodeD);
'D'节点的位置从'('开头'到''结尾')开始。这两种情况都由node
正确处理。
如何重构这些解析器以快速失败但仍能访问正确的位置数据?
我更希望保持每个解析器都是自包含的并避免重复逻辑。此外,构建后不得修改节点。