写一个整数四则运算的解析器——语法分析部分
构建AST
1. 先用BNF定义3个语法
运算表达式 <Expression> ::= <AddExpression><EOF> 加法表达式 <AddExpression> ::= <MultipleExpression> | <AddExpression><+><MultiplicativeExpression> | <AddExpression><-><MultiplicativeExpression> 乘法表达式 <MultipleExpression> ::= <Number> | <MultipleExpression><*><Number> | <MultipleExpression></><Number>
2.按照定义的语法来编写解析函数
function expression(source) { // source[0]位置尽可能多地聚合出一个AddExpression addExpression(source) // 符合Expression语法定义 if (source[0].type === 'AddExpression' && source[1] && source[1].type === 'EOF') { let node = { type: "Expression", children: [source.shift(), source.shift()] } source.unshift(node) } // 出口,如果第0项不是Expression,则是抽象语法树解析错误 if (source[0].type === 'Expression') { return source[0] } else { throw new Error('解析AST不正确') } } function addExpression(source) { // MultipleExpression --> AddExpression if (source[0].type === 'MultipleExpression') { let node = { type: "AddExpression", children: [source[0]] } source.shift(); source.unshift(node) // 继续递归,寻求合并 return addExpression(source); } // AddExpression聚合 if (source[0].type === 'AddExpression' && source[1] && (source[1].type === '+' || source[1].type === '-')) { let node = { type: "AddExpression", operator: source[1].type, children: [source.shift(), source.shift()] } multipleExpression(source) node.children.push(source.shift()) source.unshift(node) // 继续递归,寻求合并 return addExpression(source); } // source[0]位置尽可能多地聚合出一个MultipleExpression multipleExpression(source) // 递归出口,如果第0项不是AddExpression,则一直递归自己 if (source[0].type === 'AddExpression') { return source } // 递归自己 return addExpression(source) } function multipleExpression(source) { // number --> MultipleExpression if (source[0].type === 'number') { let node = { type: "MultipleExpression", children: [source[0]] } source.shift(); source.unshift(node) // 继续递归,寻求合并 return multipleExpression(source); } // MultipleExpression聚合 if (source[0].type === 'MultipleExpression' && source[1] && (source[1].type === '*' || source[1].type === '/')) { let node = { type: "MultipleExpression", operator: source[1].type, children: [source.shift(), source.shift()] } node.children.push(source.shift()) source.unshift(node) // 继续递归,寻求合并 return multipleExpression(source); } // 递归出口,如果第0项不是MultipleExpression,则一直递归自己 if (source[0].type === 'MultipleExpression') { return source } }
3. 调用
// 上节词法分析获取的词 let tokenList = [ {"type": "number","token": "123"}, {"type": "*","token": "*"}, {"type": "number","token": "656"}, {"type": "-","token": "-"}, {"type": "number","token": "644"}, {"type": "+","token": "+"}, {"type": "number","token": "3131"}, {"type": "EOF","token": "EOF"} ] const ast = expression(tokenList) // 打印出解析出来的ast console.log(ast)
解析执行
得到上述的ast后,我们要做的就很简单了,就是递归执行
function evaluate(node) { if (node.type === 'Expression') { return evaluate(node.children[0]) } if (node.type === 'AddExpression') { if (node.operator === '+') { return evaluate(node.children[0]) + evaluate(node.children[2]) } if (node.operator === '-') { return evaluate(node.children[0]) - evaluate(node.children[2]) } return evaluate(node.children[0]) } if (node.type === 'MultipleExpression') { if (node.operator === '*') { return evaluate(node.children[0]) * evaluate(node.children[2]) } if (node.operator === '/') { return evaluate(node.children[0]) / evaluate(node.children[2]) } return evaluate(node.children[0]) } if (node.type === 'number') { return Number(node.token) } }
到此整个四则运算的解析过程已经完成了。
我们看到,其实整个过程就是词法分析、语法分析、解析执行
词法分析我们可以用比较麻烦的状态机,或者相对简洁的正则
相对比较困难的是语法分析,首先我们要定义语法,然后根据定义的语法,把词构建成AST抽象语法树
解析执行过程是最简单的了,就是根据语法定义,递归处理AST语法树就可以了