zoukankan      html  css  js  c++  java
  • 写一个整数四则运算的解析器——语法分析部分

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

  • 相关阅读:
    HDU 1102 Constructing Roads
    HDU 1285 确定比赛名次。
    最小生成树 HDU 各种畅通工程的题,prim和kru的模板题
    HDU Jungle Roads 1301 最小生成树、
    并查集小结(转)
    HDU hdu 2094 产生冠军 拓扑排序 判定环
    模运算(转)
    拓扑排序(主要是确定环和加法) HDU 2647 Reward
    HDU 1372 Knight Moves 简单BFS
    用计算机模型浅析人与人之间沟通方式 (一)如何谈话
  • 原文地址:https://www.cnblogs.com/amiezhang/p/11070618.html
Copyright © 2011-2022 走看看