写一个整数四则运算的解析器——语法分析部分

构建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语法树就可以了

原文地址:https://www.cnblogs.com/amiezhang/p/11070618.html