一个 JavaScript 实现的表达式语法解析器
const TokenType = {
/**
* 表达式中词的类型
* @type {string}
*/
OR: "or", // 只支持布尔值或者表达式进行逻辑运算
AND: "and",
GT: ">", // greater than 只支持数值比较大小
GE: ">:", // greater equal than
LT: "<=", // less than
LE: "<=:", // less equal than
EQUAL: ":", // 支持数值、字符串、布尔值 比较是否相等
PLUS: "+", // 只支持数值进行+-*/运算
MINUS: "-",
MULTIPLY: "*",
DIVIDE: "/",
INDEX: "index", // 指标,相当于字面量,index 中的值可以是数值类型和字符串类型,字符串类型必须使用 '' 引起来
FIELD: "field", // 字段,相当于变量
LEFT_PARENTHESIS: "(",
RIGHT_PARENTHESIS: ")",
}
const ASTNodeType = {
/**
* 抽象语法树节点代表的表达式值的类型
* @type {string}
*/
NUMERIC: "numeric", // 数值类型
LOGICAL: "logical", // 布尔类型
STRING: "string", // 字符串类型
}
let parser = {
/**
* 表达式语法分析器的 CFG
* 表达式运算符优先级 or < and < 关系运算符 < 加减法 < 乘除法 < 小括号
* orExpr -> andExpr (or andExpr)*
* andExpr -> relExpr (and relExpr)*
* relExpr -> addExpr (> addExpr)* | addExpr (>= addExpr)* | addExpr (< addExpr)*
* | addExpr (<= addExpr)* | addExpr (= addExpr)*
* addExpr -> mulExpr (+ mulExpr)* | mulExpr (- mulExpr)*
* mulExpr -> priExpr (* priExpr)* | priExpr (/ priExpr)*
* priExpr -> index | field | (exp)
*
* 语法检查方面:
* 1. 双元运算符两侧 node 不能为 null
* 2. 运算符两侧 node 的数据类型必须严格和运算符匹配
* 3. () 必须配对
* @param tokens
*/
tokens: {
cursor: 0,
arrToken: [],
getCursor() {
return this.cursor;
}, peek() {
/**
* 预读下一个 token,cursor 不变
*/
return this.arrToken[this.cursor] === undefined ? null : this.arrToken[this.cursor];
}, read() {
/**
* 读取下一个 token,cursor 加 1
* @type {number}
*/
return this.arrToken[this.cursor] === undefined ? null : this.arrToken[this.cursor++];
}
},
hasLeftBrackets: 0,
nodeTree: null,
orExpr: function () {
/**
* 解析 or 表达式
* or 表达式 CFG 如下:orExpr -> andExpr (or andExpr)*
* 表示 or 运算符表达式由 1 个或者多个 and 表达式组成,多个 and 表达式之间由 or 运算符连接
*/
let child1 = this.andExpr();
let node = child1;
if (child1 != null) {
let token = this.tokens.peek();
while (token != null && token.type === TokenType.OR) {
token = this.tokens.read(); // 读出 or 运算符
let child2 = this.andExpr(); // 计算下级节点
this.checkNodeType(child1, child2, token.text, ASTNodeType.LOGICAL)
node = {
type: ASTNodeType.LOGICAL,
text: token.text,
children: [],
};
node.children.push(child1);
node.children.push(child2);
child1 = node;
token = this.tokens.peek();
}
}
return node;
},
andExpr: function () {
/**
* 解析 and 表达式
* and 表达式 CFG 如下:andExpr -> relExpr (and relExpr)*
* 表示 and 运算符表达式由 1 个或者多个 关系表达式组成,多个 关系表达式之间由 and 运算符连接
*/
let child1 = this.relExpr();
let node = child1;
if (child1 != null) {
let token = this.tokens.peek();
while (token != null && token.type === TokenType.AND) {
token = this.tokens.read(); //读出 and 运算符
let child2 = this.relExpr(); //计算下级节点
this.checkNodeType(child1