重学前端进修笔记(二十八)--经由过程四则运算的诠释器疾速明白编译道理

笔记申明

重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时候开的一个专栏,
天天10分钟,重构你的前端学问系统,笔者重要整顿进修历程的一些要点笔记以及感悟,完整的能够到场winter的专栏进修【原文有winter的语音】,若有侵权请联络我,邮箱:kaimo313@foxmail.com。

一、剖析

依据编译道理相干的学问,将其分红几个步骤。

  • 定义四则运算:产出四则运算的词法定义和语法定义。
  • 词法剖析:把输入的字符串流变成 token。
  • 语法剖析:把 token 变成笼统语法树 AST。
  • 诠释实行:后序遍历 AST,实行得出效果。

二、定义四则运算

2.1、定义词法

  • Token

    • Number: 1 2 3 4 5 6 7 8 9 0 的组合
    • Operator: +、-、*、/ 之一
  • Whitespace<sp>
  • LineTerminator<LE> <CR>

2.2、定义语法

语法定义多半采纳 BNF。
巴科斯范式(BNF:
Backus-Naur Form 的缩写)是由 John Backus 和 Peter Naur 初次引入一种形式化标记来形貌给定言语的语法(最早用于形貌ALGOL 60 编程言语)。JavaScript 规范内里就是一种跟 BNF 相似的自创语法。

1、加法是由若干个乘法再由加号或许减号连接成的:

<Expression> ::=
    <AdditiveExpression><EOF>

<AdditiveExpression> ::=
    <MultiplicativeExpression>
    |<AdditiveExpression><+><MultiplicativeExpression>
    |<AdditiveExpression><-><MultiplicativeExpression>

2、可把一般数字当做乘法的一种惯例:

<MultiplicativeExpression> ::=
    <Number>
    |<MultiplicativeExpression><*><Number>
    |<MultiplicativeExpression></><Number>

上面就是四则运算的定义。

三、词法剖析:状况机

词法剖析:把字符流变成 token 流,有两种计划,一种是状况机,一种是正则表达式,它们是等效的。

3.1、完成状况机

// 能够发生四种输入元素,个中只要两种 token,状况机的第一个状况就是依据第一个输入字符来推断进入了哪一种状况

var token = [];
function start(char) {
    if(char === '1' || char === '2'|| char === '3'
        || char === '4'|| char === '5'|| char === '6'|| char === '7'
        || char === '8'|| char === '9'|| char === '0') {
        token.push(char);
        return inNumber;
    }
    if(char === '+' || char === '-' || char === '*' || char === '/') {
        emmitToken(char, char);
        return start
    }
    if(char === ' ') {
        return start;
    }
    if(char === '\r' || char === '\n') {
        return start;
    }
}
function inNumber(char) {
    if ( char === '1' || char === '2' || char === '3'
        || char === '4'|| char === '5' || char === '6' || char === '7'
        || char === '8' || char === '9' || char === '0') {
        token.push(char);
        return inNumber;
    } else {
        emmitToken("Number", token.join(""));
        token = [];
        // put back char
        return start(char);
    }
}

// 用函数示意状况,用 if 示意状况的迁徙关联,用 return 值示意下一个状况。

3.2、运转状况机

function emmitToken(type, value) {
    console.log(value);
}

var input = "1024 + 2 * 256"

var state = start;

for(var c of input.split(''))
    state = state(c);

state(Symbol('EOF'))

// 输出效果
1024
+
2
*
256

四、语法剖析:LL

LL 语法剖析依据每个发生式来写一个函数。

4.1、写好函数名

function AdditiveExpression( ){

}
function MultiplicativeExpression(){

}

4.2、假定已拿到 token

var tokens = [{
    type:"Number",
    value: "1024"
}, {
    type:"+",
    value: "+"
}, {
    type:"Number",
    value: "2"
}, {
    type:"*",
    value: "*"
}, {
    type:"Number",
    value: "256"
}, {
    type:"EOF"
}];

4.3、AdditiveExpression处置惩罚

1、三种状况

<AdditiveExpression> ::=
    <MultiplicativeExpression>
    |<AdditiveExpression><+><MultiplicativeExpression>
    |<AdditiveExpression><-><MultiplicativeExpression>

2、AdditiveExpression 的写法

AdditiveExpression 的写法是根传入的节点,应用发生式合成新的节点。

function AdditiveExpression(source){
    if(source[0].type === "MultiplicativeExpression") {
        let node = {
            type:"AdditiveExpression",
            children:[source[0]]
        }
        source[0] = node;
        return node;
    }
    if(source[0].type === "AdditiveExpression" && source[1].type === "+") {
        let node = {
            type:"AdditiveExpression",
            operator:"+",
            children:[source.shift(), source.shift(), MultiplicativeExpression(source)]
        }
        source.unshift(node);
    }
    if(source[0].type === "AdditiveExpression" && source[1].type === "-") {
        let node = {
            type:"AdditiveExpression",
            operator:"-",
            children:[source.shift(), source.shift(), MultiplicativeExpression(source)]
        }
        source.unshift(node);
    }
}

4.4、函数 Expression 处置惩罚

1、把剖析好的 token 传给的顶层处置惩罚函数 Expression。

Expression(tokens);

2、假如 Expression 收到第一个 token,是个 Number,就需要对发生式的首项层层睁开,依据一切能够性挪用响应的处置惩罚函数,这个历程在编译道理中称为求 closure

function Expression(source){
    if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "EOF" ) {
        let node = {
            type:"Expression",
            children:[source.shift(), source.shift()]
        }
        source.unshift(node);
        return node;
    }
    AdditiveExpression(source);
    return Expression(source);
}
function AdditiveExpression(source){
    if(source[0].type === "MultiplicativeExpression") {
        let node = {
            type:"AdditiveExpression",
            children:[source[0]]
        }
        source[0] = node;
        return AdditiveExpression(source);
    }
    if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "+") {
        let node = {
            type:"AdditiveExpression",
            operator:"+",
            children:[]
        }
        node.children.push(source.shift());
        node.children.push(source.shift());
        MultiplicativeExpression(source);
        node.children.push(source.shift());
        source.unshift(node);
        return AdditiveExpression(source);
    }
    if(source[0].type === "AdditiveExpression" && source[1] && source[1].type === "-") {
        let node = {
            type:"AdditiveExpression",
            operator:"-",
            children:[]
        }
        node.children.push(source.shift());
        node.children.push(source.shift());
        MultiplicativeExpression(source);
        node.children.push(source.shift());
        source.unshift(node);
        return AdditiveExpression(source);
    }
    if(source[0].type === "AdditiveExpression")
        return source[0];
    MultiplicativeExpression(source);
    return AdditiveExpression(source);
}
function MultiplicativeExpression(source){
    if(source[0].type === "Number") {
        let node = {
            type:"MultiplicativeExpression",
            children:[source[0]]
        }
        source[0] = node;
        return MultiplicativeExpression(source);
    }
    if(source[0].type === "MultiplicativeExpression" && source[1] && source[1].type === "*") {
        let node = {
            type:"MultiplicativeExpression",
            operator:"*",
            children:[]
        }
        node.children.push(source.shift());
        node.children.push(source.shift());
        node.children.push(source.shift());
        source.unshift(node);
        return MultiplicativeExpression(source);
    }
    if(source[0].type === "MultiplicativeExpression"&& source[1] && source[1].type === "/") {
        let node = {
            type:"MultiplicativeExpression",
            operator:"/",
            children:[]
        }
        node.children.push(source.shift());
        node.children.push(source.shift());
        node.children.push(source.shift());
        source.unshift(node);
        return MultiplicativeExpression(source);
    }
    if(source[0].type === "MultiplicativeExpression")
        return source[0];

    return MultiplicativeExpression(source);
};

var source = [{
    type:"Number",
    value: "3"
}, {
    type:"*",
    value: "*"
}, {
    type:"Number",
    value: "300"
}, {
    type:"+",
    value: "+"
}, {
    type:"Number",
    value: "2"
}, {
    type:"*",
    value: "*"
}, {
    type:"Number",
    value: "256"
}, {
    type:"EOF"
}];
var ast = Expression(source);

console.log(ast);
// 输出效果 children: Array(1) children: Array(3)还能够继承睁开。。。
{
    type: "Expression",
    children: [
        {
            type: "AdditiveExpression",
            operator: "+",
            children: [
                {
                    type: "AdditiveExpression",
                    children: Array(1)
                },
                {
                    type: "+",
                    value: "+"
                },
                {
                    type: "MultiplicativeExpression",
                    operator: "*",
                    children: Array(3)
                }
            ]
        },
        {
            type: "EOF"
        }
    ]
}

五、诠释实行

得到了 AST 以后,只需要对这个树做遍历操纵实行即可。

// 依据差别的节点范例和别的信息,用 if 离别处置惩罚即可:
function evaluate(node) {
    if(node.type === "Expression") {
        return evaluate(node.children[0])
    }
    if(node.type === "AdditiveExpression") {
        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 === "MultiplicativeExpression") {
        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.value);
    }
}

个人总结

这下完整懵逼了 _(:3」∠)_

    原文作者:凯小默
    原文地址: https://segmentfault.com/a/1190000019401278
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞