使用regex macher加速并简化多个if/else



我有用JavaScript编写的基于Scheme的lips解释器,在解析器中我有一个解析值的函数。内部S-表达式:

function parse_argument(arg) {
var regex = arg.match(re_re);
if (regex) {
return new RegExp(regex[1], regex[2]);
} else if (arg.match(/^"[sS]*"$/)) {
return parse_string(arg);
} else if (arg.match(char_re)) {
return parse_character(arg);
} else if (arg.match(rational_re)) {
return parse_rational(arg);
} else if (arg.match(complex_re)) {
return parse_complex(arg);
} else if (arg.match(int_re)) {
return parse_integer(arg);
} else if (arg.match(float_re)) {
return parse_float(arg);
} else if (arg === 'nil') {
return nil;
} else if (['+nan.0', '-nan.0'].includes(arg)) {
return LNumber(NaN);
} else if (['true', '#t', '#true'].includes(arg)) {
return true;
} else if (['false', '#f', '#false'].includes(arg)) {
return false;
} else if (arg.match(/^#[iexobd]/)) {
throw new Error('Invalid numeric constant');
} else {
// characters with more than one codepoint
var m = arg.match(/#\(.+)/);
if (m && ucs2decode(m[1]).length === 1) {
return parse_character(arg);
}
return parse_symbol(arg);
}
}

解析时经常调用这个函数,所以去掉if.else可能会加快代码的速度,但我不确定它是否可以简化或加快。

你知道有什么方法可以让这个功能运行得更快吗?正如您所看到的,符号在末尾,而解析符号是最常见的对象。

@georg的答案很好,ff OP想要的只是为各种各样的词位提取词位文本。这将所有单独的正则表达式组合到一个正则表达式中,这将有效地处理条件。

然而,如果OP想生成特殊的代码来说明哪个词法类型("token"(已经被识别(请参阅他的识别器,了解nil/true/false;我怀疑是其他的令牌,但OP没有提供足够的代码(,那么OP需要的是一个传统的词法生成器,用于对令牌进行分类。

不同之处在于@georg的union regext有一个单独的"接受";state("非常复杂的regex匹配其(并集(子项之一"(与lexer生成器相比,每个子语句都有一个接受状态。具有单独的接受状态允许相应的单独操作,例如生成特定于词位的令牌代码和可选地进行值转换(例如读取浮点数字文本并生成浮点值(。使用lexer生成器是几乎每个编译器/解释器都使用的标准解决方案,因为它具有这种能力。

OP可以查找任何";Javascript lexer生成器";谷歌,但这里有一个:https://github.com/sormy/flex-js我没有具体的经验。

注意:如果OP采用这种方式,他应该将所有的词法放入词法生成器中,特别是"("one_answers"(";对于LISP,而不是仅将其用于值。然后每一次对";get_token";例程可以返回下一个令牌。

我通常更喜欢有一个大的正则表达式,将所有标记"或"运算在一起,这样正则表达式引擎就可以自己进行分支。这(应该(更快,也有助于简化后续的parse_xxx内容,因为您已经有了所需的子匹配。玩具示例:

let LEXEMS = String.raw`
(?<num>
[0-9]+
(?<frac> . [0-9]+) ?
)
|
(?<ident>
w+
)
|
(?<op>
[+ * / -]
)
|
s+
|
(? <ERROR> S)
`;
let lexer = new RegExp(LEXEMS.replace(/s+/g, ''), 'g')
//
program = "abc + 123.456 - 123"
for (let m of program.matchAll(lexer)) {
let g = m.groups;
if (g.num)
console.log('num', g.num, 'frac=', g.frac)
if (g.ident)
console.log('ident', g.ident)
// etc

}

最新更新