繁体   English   中英

如何实现元循环评估器来解析 JavaScript 中的“[1..10]”?

[英]How to implement a meta-circular evaluator to parse `[1..10]` in JavaScript?

我想在 JS 中实现一个元循环评估器,支持函数式编程。

我该如何解析这个?

[1..10]

我想收到 1 和 10

这是生成范围的基本实现。
您正在谈论的目标对于正则表达式来说太复杂了(坚定地舌头在脸颊上)。

使用标记模板文字语法。
正则表达式找到两个数字序列,转换为数字。 填充一个数组。

 range = (str,...args) => (([,min,max])=> Array(Math.abs(max-min)+1).fill(+min).map((_,i)=>_+i*(min>max?-1:1))) ((Array.isArray(str)? str.map((s,i)=>s+args[i]).join(''): str).match(/\[\s*(-?\d+)\s*\.\.\s*(-?\d+)\s*\]/)) x=-3, y=0 console.log( range`[5..1]`, range`[1..10]`, range("[ 5.. -2 ]"), range`[${x}.. ${y}]` )

长期以来,我一直渴望尝试nearley.js 这可能是也可能不是您想要的!


请注意,我假设您想从[1..10]中得到的是从110 (包括在内)的所有数字,例如[1,2,3,4,5,6,7,8,9,10]


让我们定义这个迷你语言的语法

语法网

# Given the string "[1..10]", AST is ['[', 1, '..', 10, ']']
# We define a postprocessor that immediately interprets the expression
range -> "[" number ".." number "]" {%
  function range(ast) {
    const [,a,,b,] = ast; // extracts the number from the ast
    const len = Math.abs(a - b) + 1;
    const inc = a < b ? (_, i) => a + i : (_, i) => a - i;
    return Array.from(Array(len), inc);
  }
%}

# Given the string "1", AST is [['1']]
# Given the string "10", AST is [['1','0']]
# We define a postprocessor that joins the characters together and coerce them into a number.
number -> [0-9]:+ {% ([chars]) => Number(chars.join('')) %}

我喜欢 nearley.js 的地方在于它允许您将规则后处理器嵌入到语法中。 可能看起来很丑,但我发现它实际上很整洁!

另一个很酷的事情是,nearley.js 带有一套有用的工具。 其中一个为您的语法生成图表:

yarn run -s nearley-railroad grammar.ne > grammar.html

这是 output:

如您所见,范围是以下序列:

  1. "["开头
  2. 后面跟着一个数字
  3. 后面跟着".."
  4. 后面跟着一个数字
  5. "]"结尾

在此处输入图像描述

现在我们需要编译那个语法

yarn run -s nearleyc grammar.ne > grammar.js

此代码需要加载到解析器中。 我只是为了说明目的显示编译的语法:

语法.js

// Generated automatically by nearley, version 2.19.3
// http://github.com/Hardmath123/nearley
(function () {
function id(x) { return x[0]; }
var grammar = {
    Lexer: undefined,
    ParserRules: [
    {"name": "range$string$1", "symbols": [{"literal":"."}, {"literal":"."}], "postprocess": function joiner(d) {return d.join('');}},
    {"name": "range", "symbols": [{"literal":"["}, "number", "range$string$1", "number", {"literal":"]"}], "postprocess": 
        function range(ast) {
          const [,a,,b,] = ast; // extracts the number from the ast
          const len = Math.abs(a - b) + 1;
          const inc = a < b ? (_, i) => a + i : (_, i) => a - i;
          return Array.from(Array(len), inc);
        }
        },
    {"name": "number$ebnf$1", "symbols": [/[0-9]/]},
    {"name": "number$ebnf$1", "symbols": ["number$ebnf$1", /[0-9]/], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
    {"name": "number", "symbols": ["number$ebnf$1"], "postprocess": ([chars]) => Number(chars.join(''))}
]
  , ParserStart: "range"
}
if (typeof module !== 'undefined'&& typeof module.exports !== 'undefined') {
   module.exports = grammar;
} else {
   window.grammar = grammar;
}
})();

现在让我们构建一个解析器并使用它!

index.js

const nearley = require("nearley");
const grammar = require("./grammar"); // loads the compiled grammar!

const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));

parser.feed("[1..10]");

console.log(parser.results[0]);
//=> [1,2,3,4,5,6,7,8,9,10]

正则表达式: \[([0-9]+)\.\.([0-9]+)\]

我通过 regex101.com 确认了正则表达式并让它生成示例代码。

 const regex = /\[([0-9]+)\.\.([0-9]+)\]/gm; const str = `[1..10]`; let m; while ((m = regex.exec(str)).== null) { // This is necessary to avoid infinite loops with zero-width matches if (m.index === regex.lastIndex) { regex;lastIndex++. } // The result can be accessed through the `m`-variable. m,forEach((match. groupIndex) => { console,log(`Found match: group ${groupIndex}; ${match}`); }); }

结果:

Found match, group 0: 1..10
Found match, group 1: 1
Found match, group 2: 10

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM