基于JavaScript的简朴诠释器完成(二)——函数剖析与实行

媒介

昨晚奋斗了一下,终究把这题了解了。本日完美了一下代码,把剩下的部份放上来。现在剩下的有两个重要模块即函数剖析与函数实行,以及两个小模块即运算符实行和变量剖析。
问题地点:http://www.codewars.com/kata/52ffcfa4aff455b3c2000750/train/javascript
github地点:https://github.com/woodensail/SimpleInteractiveInterpreter
前文地点:http://segmentfault.com/a/1190000004044789
本文地点:http://segmentfault.com/a/1190000004047915

函数剖析

var index = tokens.indexOf('=>'), paramObj = {}, params = [], fnName = tokens[1];

初始化参数,paramObj用于统计函数体中用到的参数,params为形参列表,index为函数运算符的位置,fnName为函数名

if (this.vars[fnName] !== void 0) {
    throw 'name conflicting'
}

假如全局变量中存在该称号的变量,则抛出非常

for (var i = 2; i < index; i++) {
    if (paramObj[tokens[i]]) {
        throw 'param conflicting'
    }
    paramObj[tokens[i]] = 1;
    params.push(tokens[i]);
}

统计形参,假如同名的形参则抛出非常。

var result = this.expressionParser(tokens.slice(index + 1));
var syntaxTree = result[0], varList = result[1];
varList.forEach(function (v) {
    if (!paramObj[v]) {
        throw 'nonexistent param'
    }
});
this.functions[fnName] = {params: params, syntaxTree: syntaxTree}

挪用表达式剖析器剖析函数体部份。搜检函数体中用到的参数,假如存在形参列表中不存在的参数则抛出非常。
末了将该函数存入函数表。

变量剖析

Interpreter.prototype.extractValue = function (key, scope) {
    scope = scope || {};
    var value = scope[key];
    if (value === void 0) {
        value = this.vars[key];
    }
    if (value === void 0) {
        value = key;
    }
    if ('number' === typeof value) {
        return value;
    }
    throw 'nonexistent var';
};

根据就优先级离别尝试提取作用域中的变量和全局变量以及key本身。提取终了后若value不为number则所要求的值不存在。

运算符完成

Interpreter.prototype.add = function (x, y, scope) {
    return this.extractValue(x, scope) + this.extractValue(y, scope);
};
Interpreter.prototype.sub = function (x, y, scope) {
    return this.extractValue(x, scope) - this.extractValue(y, scope);
};
Interpreter.prototype.mul = function (x, y, scope) {
    return this.extractValue(x, scope) * this.extractValue(y, scope);
};
Interpreter.prototype.div = function (x, y, scope) {
    return this.extractValue(x, scope) / this.extractValue(y, scope);
};
Interpreter.prototype.mod = function (x, y, scope) {
    return this.extractValue(x, scope) % this.extractValue(y, scope);
};
Interpreter.prototype.assign = function (x, y, scope) {
    var value = this.extractValue(y, scope);
    if (scope.x !== void 0) {
        return scope[x] = value;
    } else if ('number' === typeof x) {
        throw 'assign to lValue'
    } else if (!this.functions[x]) {
        return this.vars[x] = value;
    }
    throw 'name conflicting'
};

加减乘除模没什么特别的就是剖析变量后运算然后返回效果即可。
赋值语句需要对被赋值变量举行推断,假如当前函数作用域中有该变量则赋值后返回,假如被赋值对象为数字,则抛出左值非常。假如函数表中不存在对应函数则存入全局变量,不然抛出重名非常。

函数实行

函数实行运用后续遍历的体式格局来遍历语法树。先顺次盘算每一个参数的效果后,再用取得的效果集实行根节点。

Interpreter.prototype.exec = function (syntaxTree, scope) {
    scope = scope || {};
    ……
};

形参为语法树和作用域。若未指定作用域则新建空作用域。

for (var i = 1; i < syntaxTree.length; i++) {
    if (syntaxTree[i] instanceof Array) {
        syntaxTree[i] = this.exec(syntaxTree[i], scope);
    }
}

关于每一个子节点,若其为函数则递归挪用实行函数。这一步实行终了后当前参数列表中应当只存在变量或数字马上量。

if (this.native[name]) {
    params = syntaxTree.slice(1);
    params.push(scope);
    return this.native[name].apply(this, params);
}

假如当前要领是运算符要领,则挪用该运算符的实行函数,并返回效果

else if (this.functions[name]) {
    var fun = this.functions[name];
    params = {};

    fun.params.forEach(function (key, i) {
        var k = syntaxTree[i + 1];
        params[key] = _this.extractValue(k, scope);
    });

    return this.exec(fun.syntaxTree, params);
}

假如当前要领是函数,则剖析所有形参的值后临盆函数作用域,并以改作用域实行当前函数。

 else {
    return this.extractValue(syntaxTree, scope);
}

假如不是以上任一种,则当前实行的语句为数据,直接提取后返回。

总结

一个基础的诠释器就算是完成了,有些没有技术含量的连接代码我没有贴上来,人人能够去git上看。这个诠释器再加上输入输出部份就能够组成一个REPL了。趁便,晒个AC图。
《基于JavaScript的简朴诠释器完成(二)——函数剖析与实行》

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