项目中需要解析表达式,如 ( A > ( B ) ) || (C != (D + E * F - 2) )
。 公式存在一定规则: 为了确保公式的正确性,在生成公式中每一项时会使用括号进行包裹,如( A > ( B ) )
和 (C != (D + E * F - 2) )
, 同样内部比较符的右侧公式也会进行包裹如: ( B )
、 (D + E * F - 2)
。 并且公式中条件全为||
或者全为&&
。
第一想到的是抽象语法树(AST),使用到的类库:esprima
,作用和使用方法不多做介绍,可以查阅官网。(体验网址:https://esprima.org/demo/parse.html#)
通过Tokens
方式,将表达式从左向右解析为数组形式,事情就变得简单多了。
// 抽象语法树解析器
private esprima: Function;
...
ngOnInit() {
// 引入esprima类库, (需要install)
this.esprima = require('esprima');
}
private createSyntaxTree (expression: string) {
// 先将表达式分解为每一项(因为条件全为 || 或者 && 所以解析起来比较简单)
// 根据||或者&&进行分割,不存在条件时,将自身存入数组
let expressionList = (/\|\||&&/g).test(expression) ?
(/\|\|/g).test(expression) ? expression.split('||') : expression.split('&&') :
[expression];
// 对每个公式项进行处理
let compositionList = expressionList.map(item => {
// 去除每个公式项两端括号 如( A > ( B ) ) 外层括号
item = item.replace(/^\(|\)$/g, '');
// 解析生成抽象语法树
let AST = that.esprima.tokenize(item);
// 获取表达式中所有的变量(type 为 Identifier)
let variableList = AST.filter(_item => {
return _item .type == "Identifier"
});
// 获取比较符 (type 为 Punctuator)
let operator = AST.filter(item => {
return item.type == "Punctuator"
})[0].value;
// 获取内部公式的组成如 C != (D + E * F - 2) 中的 (D + E * F - 2)
let [, , ...innerFormulaComposition] = AST;
// 将组成元素拼接为公式,并去除两端括号,即生成:D + E * F - 2
let innerFormula = innerFormulaComposition.map(val => val.value)
.join('').replace(/^\(|\)$/g, '');
return {
// 被比较的变量
leftObj: variableList[0]
operator: operator,
innerFormula: innerFormula
}
})
}