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


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

  • 相关阅读:
    那些不能错过的Xcode插件
    iOS开发过程中使用Core Data应避免的十个错误
    define和typedef
    #号运算符
    第三方移动后端服务开发
    9款优秀的APP免费测试框架
    网络协议初探(二)
    iphone第三方库
    HTTP状态码
    JDK8新特性:在JDK8中,默认添加final
  • 原文地址:https://www.cnblogs.com/amiezhang/p/11070618.html
Copyright © 2020-2023  润新知