基于 Nearley 实现的自定义公式编辑器

最近在做一个自定义公式的编辑器,可以支持基本数学运算和变量名称,同时支持CASE(判断),ROUND,CEIL,FLOOR运算,使用时用户将自己的自定义变量写入公式,规则校验通过之后只需要将特定格式的变量名进行替换即可成为一个标准的可计算的数学表达式,从而得到结果。

这里使用的 Nearley,一个js的解析器,通过自定义语法实现所需功能,以下是我实现的一个公式表达式的代码。

Copy
# 程序起点 main -> _ AS _ {% function(d) {return d[1]; } %} # 括号,CASE 运算 P -> "(" _ AS _ ")" {% function(d) {return d[2]; } %} | N {% id %} | ("CASE("|"case(") _ COMP _ "," _ AS _ (";" _ COMP _ "," _ AS):* _ ")" {% function(d) { let l = d[8].length; if (d[2]) { return d[6]; } let i; // 为每一个分号后面的数据做匹配 for (i = 0; i < l; i++) { if (d[8][i][2]) { return d[8][i][6]; } } return NaN; } %} # 乘法 和 除法 MD -> MD _ "*" _ P {% function(d) {return d[0]*d[4]; } %} | MD _ "/" _ P {% function(d) {return d[0]/d[4]; } %} | P {% id %} # 加法 和 减法 AS -> AS _ "+" _ MD {% function(d) {return d[0]+d[4]; } %} | AS _ "-" _ MD {% function(d) {return d[0]-d[4]; } %} | MD {% id %} # 比较 COMP -> AS _ ">" _ AS {% function(d) {return d[0] > d[4];} %} | AS _ ">=" _ AS {% function(d) {return d[0] >= d[4];} %} | AS _ "<" _ AS {% function(d) {return d[0] < d[4];} %} | AS _ "<=" _ AS {% function(d) {return d[0] <= d[4];} %} | AS _ "==" _ AS {% function(d) {return d[0] == d[4];} %} | AS _ "!=" _ AS {% function(d) {return d[0] != d[4];} %} # 数字或数字函数 N -> float {% id %} | ("ceiling"|"CEILING") _ P {% function(d) {return Math.ceil(d[2]); } %} # 进一取整 | ("floor"|"FLOOR") _ P {% function(d) {return Math.floor(d[2]); } %} # 舍一取整 | ("round"|"ROUND") _ P {% function(d) {return Math.round(d[2]); } %} # 四舍五入 | "${" VAR ("." VAR):* "}" {% function(d) { let l = d[2].length; let v = "${" + d[1]; for (let i = 0; i < l; i++) { v += "." + d[2][i][1]; } return v + "}"; } %} # 变量,支持.循环的多级变量 # 变量,可以是中文,英文,数字,下划线,中横线的组合 VAR -> [\u4e00-\u9fa5a-zA-Z\d_\-]:+ {% function(d) {return d[0].join(""); } %} # 有小数点的数字 float -> int "." unsigned_int {% function(d) {return parseFloat(d[0] + d[1] + d[2])} %} | int {% function(d) {return parseInt(d[0])} %} # 无符号整型(正数) unsigned_int -> [0-9]:+ {% function(d) {return d[0].join(""); } %} # 空白,比如空格和换行。这里重要的是后处理器是一个null返回函数。这是一个内存效率技巧。 _ -> [\s]:* {% function(d) {return null; } %} # 整型(包含负数) int -> ("-"|"+"):? [0-9]:+ {% function(d) { if (d[0]) { return parseInt(d[0][0]+d[1].join("")); } else { return parseInt(d[1].join("")); } } %}

使用时的代码示例:

Copy
CASE(${数量} > 0, ${金额}) * 1.5 ${明细.计划数} * ${单价}
posted @   凭栏知潇雨  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 想让你多爱自己一些的开源计时器
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析
点击右上角即可分享
微信分享提示