媒介
昨晚奋斗了一下,终究把这题了解了。本日完美了一下代码,把剩下的部份放上来。现在剩下的有两个重要模块即函数剖析与函数实行,以及两个小模块即运算符实行和变量剖析。
问题地点: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图。