在上一篇文章中,我们通过AST完成了微信小程序组件的多端编译,在这篇文章中,让我们更深入一点,通过AST完成一个javascript元循环求值器结构
结构结构一个元循环求值器,完整的应该包含以下内容:

tokenizer:对代码文本进行词法和语法分析,将代码分割成若干个token

parser:根据token,生成AST树

evaluate:根据AST树节点的type,执行对应的apply方法

apply:根据环境,执行实际的求值计算

scope:当前代码执行的环境
tokenizer:对代码文本进行词法和语法分析,将代码分割成若干个tokenparser:根据token,生成AST树evaluate:根据AST树节点的type,执行对应的apply方法apply:根据环境,执行实际的求值计算scope:当前代码执行的环境代码目录
代码目录
代码目录
根据结构看,我将代码目录大致拆分为以下几个文件

parser

eval

scope
parserevalscopetokenizer和parser这两个过程不是本文的重点,我统一放在了parser中,交由 @babel/parser 来处理。
evaluate和apply这两个过程我统一放在了eval文件中处理,一会我们重点看下这部分。
scope则放入scope文件。
evaluate-apply
evaluate-applyevaluate-apply这其实是一个递归计算的过程。
首先,evaluate 接收两个参数,node 当前遍历的AST树节点和 scope 当前环境。然后,evaluate去根据 node 的 type 属性,判断该节点是什么类型。判断出类型后,执行 apply 去求值这个节点所代表的表达式。apply 中会再次递归的执行 evaluate 去计算当前节点的子节点。最终,执行完整颗AST树。
我们来看下具体代码吧

const evaluate = (node: t.Node, scope) => {
const evalFunc = evaluateMap[node.type];
if (!evalFunc) {
throw `${node.loc} ${node.type} 还未实现`;
}
return evalFunc(node, scope);
}

const evaluate = (node: t.Node, scope) => {
const evalFunc = evaluateMap[node.type];
if (!evalFunc) {
throw `${node.loc} ${node.type} 还未实现`;
}
return evalFunc(node, scope);
}
以上就是evaluate具体做的事。
其中,evaluateMap 是目前实现的内容集合,我们来看下具体的代码

const evaluateMap: EvaluateMap = {
File(node: t.File, scope) {
evaluate(node.program, scope);
},

Program(node: t.Program, scope) {
for (const n of node.body) {

evaluate(n, scope);
}
},

Identifier(node: t.Identifier, scope) {
const $var = scope.$find(node.name);
if (!$var) {

throw `[Error] ${node.loc}, '${node.name}' 未定义`;
}
return $var.$get();
},

StringLiteral(node: t.StringLiteral, scope) {
return node.value;
},

NumericLiteral(node: t.NumericLiteral, scope) {
return node.value;
},

BooleanLiteral(node: t.BooleanLiteral, scope) {
return node.value;
},

NullLiteral(node: t.NullLiteral, scope) {
return null;
},

BlockStatement(block: t.BlockStatement, scope) {
const blockScope = scope.shared ? scope : new Scope('block', scope);
for (const node of block.body) {

const res = evaluate(node, blockScope);

if (res === BREAK || res === CONTINUE || res === RETURN) {

return res;

}
}
},

DebuggerStatement(node: t.DebuggerStatement, scope) {
debugger;
},

ExpressionStatement(node: t.ExpressionStatement, scope) {
evaluate(node.expression, scope);
},

ReturnStatement(node: t.ReturnStatement, scope) {
RETURN.result = (node.argument ? evaluate(node.argument, scope) : void 0);
return RETURN;
},

BreakStatement(node: t.BreakStatement, scope) {
return BREAK;
},

ContinueStatement(node: t.ContinueStatement, scope) {
return CONTINUE;
},

IfStatement(node: t.IfStatement, scope) {
if (evaluate(node.test, scope)) {

return evaluate(node.consequent, scope);
}

if (node.alternate) {

const ifScope = new Scope('block', scope, true);

return evaluate(node.alternate, ifScope)
}
},

SwitchStatement(node: t.SwitchStatement, scope) {
const discriminant = evaluate(node.discriminant, scope);
const switchScope = new Scope('switch', scope);
for (const ca of node.cases){

if (ca.test === null || evaluate(ca.test, switchScope) === discriminant) {

const res = evaluate(ca, switchScope);

if (res === BREAK) {

break;

} else if (res === RETURN) {

return res;

}

}
}
},

SwitchCase(node: t.SwitchCase, scope) {
for (const item of node.consequent) {

const res = evaluate(item, scope);

if (res === BREAK || res === RETURN) {

return res;

}
}
},

ThrowStatement(node: t.ThrowStatement, scope) {
throw evaluate(node.argument, scope);
},

TryStatement(node: t.TryStatement, scope) {
try {

return evaluate(node.block, scope);
} catch (error) {

if (node.handler) {

const catchScope = new Scope('block', scope, true);

catchScope.$let((node.handler.param).name, error);

return evaluate(node.handler, catchScope);

} else {

throw error;

}
} finally {

if (node.finalizer) {

return evaluate(node.finalizer, scope);

}
}
},

CatchClause(node: t.CatchClause, scope) {
return evaluate(node.body, scope);
},

WhileStatement(node: t.WhileStatement, scope) {
while (evaluate(node.test, scope)) {

const whileScope = new Scope('loop', scope, true);

const res = evaluate(node.body, whileScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

ForStatement(node: t.ForStatement, scope) {
for (

const forScope = new Scope('loop', scope),

initVal = evaluate(node.init, forScope);

evaluate(node.test, forScope);

evaluate(node.update, forScope)
) {

const res = evaluate(node.body, forScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

ForInStatement(node: t.ForInStatement, scope) {
const kind = (node.left).kind;
const decl = (node.left).declarations[0];
const name = (decl.id).name;

for (const value in evaluate(node.right, scope)) {

const forScope = new Scope('loop', scope, true);

scope.$define(kind, name, value);

const res = evaluate(node.body, forScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

ForOfStatement(node: t.ForOfStatement, scope) {
const kind = (node.left).kind;
const decl = (node.left).declarations[0];
const name = (decl.id).name;

for (const value of evaluate(node.right, scope)) {

const forScope = new Scope('loop', scope, true);

scope.$define(kind, name, value);

const res = evaluate(node.body, forScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

FunctionDeclaration(node: t.FunctionDeclaration, scope) {
const func = evaluateMap.FunctionExpression(node, scope);
scope.$var(node.id.name, func);
},

VariableDeclaration(node: t.VariableDeclaration, scope) {
const { kind, declarations } = node;
for (const decl of declarations) {

const varName = (decl.id).name;

const value = decl.init ? evaluate(decl.init, scope) : void 0;

if (!scope.$define(kind, varName, value)) {

throw `[Error] ${name} 重复定义`

}
}
},

ThisExpression(node: t.ThisExpression, scope) {
const _this = scope.$find('this');
return _this ? _this.$get() : null;
},

ArrayExpression(node: t.ArrayExpression, scope) {
return node.elements.map(item => evaluate(item, scope));
},

ObjectExpression(node: t.ObjectExpression, scope) {
let res = Object.create(null);
node.properties.forEach((prop) => {

let key;

let value;

if(prop.type === 'ObjectProperty'){

key = prop.key.name;

value = evaluate(prop.value, scope);

res[key] = value;

}else if (prop.type === 'ObjectMethod'){

const kind = prop.kind;

key = prop.key.name;

value = evaluate(prop.body, scope);

if(kind === 'method') {

res[key] = value;

}else if(kind === 'get') {

Object.defineProperty(res, key, { get: value });

}else if(kind === 'set') {

Object.defineProperty(res, key, { set: value });

}

}else if(prop.type === 'SpreadElement'){

const arg = evaluate(prop.argument, scope);

res = Object.assign(res, arg);

}
});
return res;
},

FunctionExpression(node: t.FunctionExpression, scope) {
return function (...args: any) {

const funcScope = new Scope('function', scope, true);

node.params.forEach((param: t.Identifier, idx) => {

const { name: paramName } = param;

funcScope.$let(paramName, args[idx]);

});

funcScope.$const('this', this);

funcScope.$const('arguments', arguments);

const res = evaluate(node.body, funcScope);

if (res === RETURN) {

return res.result;

}
}
},

ArrowFunctionExpression(node: t.ArrowFunctionExpression, scope) {
return (...args) => {

const funcScope = new Scope('function', scope, true);

node.params.forEach((param: t.Identifier, idx) => {

const { name: paramName } = param;

funcScope.$let(paramName, args[idx]);

});

const _this = funcScope.$find('this');

funcScope.$const('this', _this ? _this.$get() : null);

funcScope.$const('arguments', args);

const res = evaluate(node.body, funcScope);

if (res === RETURN) {

return res.result;

}
}
},

UnaryExpression(node: t.UnaryExpression, scope) {
const expressionMap = {

'~': () => ~evaluate(node.argument, scope),

'+': () => +evaluate(node.argument, scope),

'-': () => -evaluate(node.argument, scope),

'!': () => !evaluate(node.argument, scope),

'void': () => void evaluate(node.argument, scope),

'typeof': () => {

if (node.argument.type === 'Identifier') {

const $var = scope.$find(node.argument.name);

const value = $var ? $var.$get() : void 0;

return typeof value;

}

return typeof evaluate(node.argument, scope);

},

'delete': () => {

if (node.argument.type === 'MemberExpression') {

const { object, property, computed } = node.argument;

const obj = evaluate(object, scope);

let prop;

if (computed) {

prop = evaluate(property, scope);

} else {

prop = property.name;

}

return delete obj[prop];

} else {

throw '[Error] 出现错误'

}

},
}
return expressionMap[node.operator]();
},

UpdateExpression(node: t.UpdateExpression, scope) {
const { prefix, argument, operator } = node;
let $var: IVariable;
if (argument.type === 'Identifier') {

$var = scope.$find(argument.name);

if (!$var) throw `${argument.name} 未定义`;
} else if (argument.type === 'MemberExpression') {

const obj = evaluate(argument.object, scope);

let prop;

if (argument.computed) {

prop = evaluate(argument.property, scope);

} else {

prop = argument.property.name;

}

$var = {

$set(value: any) {

obj[prop] = value;

return true;

},

$get() {

return obj[prop];

}

}
} else {

throw '[Error] 出现错误'
}

const expressionMap = {

'++': v => {

$var.$set(v + 1);

return prefix ? ++v : v++

},

'--': v => {

$var.$set(v - 1);

return prefix ? --v : v--

},
}

return expressionMap[operator]($var.$get());
},

BinaryExpression(node: t.BinaryExpression, scope) {
const { left, operator, right } = node;
const expressionMap = {

'==': (a, b) => a == b,

'===': (a, b) => a === b,

'>': (a, b) => a > b,

'<': (a, b) => a < b,

'!=': (a, b) => a != b,

'!==': (a, b) => a !== b,

'>=': (a, b) => a >= b,

'<=': (a, b) => a <= b,

'<<': (a, b) => a << b,

'>>': (a, b) => a >> b,

'>>>': (a, b) => a >>> b,

'+': (a, b) => a + b,

'-': (a, b) => a - b,

'*': (a, b) => a * b,

'/': (a, b) => a / b,

'&': (a, b) => a & b,

'%': (a, b) => a % b,

'|': (a, b) => a | b,

'^': (a, b) => a ^ b,

'in': (a, b) => a in b,

'instanceof': (a, b) => a instanceof b,
}
return expressionMap[operator](evaluate(left, scope), evaluate(right, scope));
},

AssignmentExpression(node: t.AssignmentExpression, scope) {
const { left, right, operator } = node;
let $var: IVariable;

if (left.type === 'Identifier') {

$var = scope.$find(left.name);

if(!$var) throw `${left.name} 未定义`;
} else if (left.type === 'MemberExpression') {

const obj = evaluate(left.object, scope);

let prop;

if (left.computed) {

prop = evaluate(left.property, scope);

} else {

prop = left.property.name;

}

$var = {

$set(value: any) {

obj[prop] = value;

return true;

},

$get() {

return obj[prop];

}

}
} else {

throw '[Error] 出现错误'
}

const expressionMap = {

'=': v => { $var.$set(v); return $var.$get() },

'+=': v => { $var.$set($var.$get() + v); return $var.$get() },

'-=': v => { $var.$set($var.$get() - v); return $var.$get() },

'*=': v => { $var.$set($var.$get() * v); return $var.$get() },

'/=': v => { $var.$set($var.$get() / v); return $var.$get() },

'%=': v => { $var.$set($var.$get() % v); return $var.$get() },

'<<=': v => { $var.$set($var.$get() << v); return $var.$get() },

'>>=': v => { $var.$set($var.$get() >> v); return $var.$get() },

'>>>=': v => { $var.$set($var.$get() >>> v); return $var.$get() },

'|=': v => { $var.$set($var.$get() | v); return $var.$get() },

'&=': v => { $var.$set($var.$get() & v); return $var.$get() },

'^=': v => { $var.$set($var.$get() ^ v); return $var.$get() },
}

return expressionMap[operator](evaluate(right, scope));
},

LogicalExpression(node: t.LogicalExpression, scope) {
const { left, right, operator } = node;
const expressionMap = {

'&&': () => evaluate(left, scope) && evaluate(right, scope),

'||': () => evaluate(left, scope) || evaluate(right, scope),
}
return expressionMap[operator]();
},

MemberExpression(node: t.MemberExpression, scope) {
const { object, property, computed } = node;
const obj = evaluate(object, scope);
let prop;
if (computed) {

prop = evaluate(property, scope);
} else {

prop = property.name;
}
return obj[prop];
},

ConditionalExpression(node: t.ConditionalExpression, scope) {
const { test, consequent, alternate } = node;
return evaluate(test, scope) ? evaluate(consequent, scope) : evaluate(alternate, scope);
},

CallExpression(node: t.CallExpression, scope) {
const func = evaluate(node.callee, scope);
const args = node.arguments.map(arg => evaluate(arg, scope));
let _this;
if (node.callee.type === 'MemberExpression') {

_this = evaluate(node.callee.object, scope);
} else {

const $var = scope.$find('this');

_this = $var ? $var.$get() : null;
}
return func.apply(_this, args);
},

NewExpression(node: t.NewExpression, scope) {
const func = evaluate(node.callee, scope);
const args = node.arguments.map(arg => evaluate(arg, scope));
return new (func.bind(func, ...args));
},

SequenceExpression(node: t.SequenceExpression, scope) {
let last;
node.expressions.forEach(expr => {

last = evaluate(expr, scope);
})
return last;
},
}


const evaluateMap: EvaluateMap = {
File(node: t.File, scope) {
evaluate(node.program, scope);
},

Program(node: t.Program, scope) {
for (const n of node.body) {

evaluate(n, scope);
}
},

Identifier(node: t.Identifier, scope) {
const $var = scope.$find(node.name);
if (!$var) {

throw `[Error] ${node.loc}, '${node.name}' 未定义`;
}
return $var.$get();
},

StringLiteral(node: t.StringLiteral, scope) {
return node.value;
},

NumericLiteral(node: t.NumericLiteral, scope) {
return node.value;
},

BooleanLiteral(node: t.BooleanLiteral, scope) {
return node.value;
},

NullLiteral(node: t.NullLiteral, scope) {
return null;
},

BlockStatement(block: t.BlockStatement, scope) {
const blockScope = scope.shared ? scope : new Scope('block', scope);
for (const node of block.body) {

const res = evaluate(node, blockScope);

if (res === BREAK || res === CONTINUE || res === RETURN) {

return res;

}
}
},

DebuggerStatement(node: t.DebuggerStatement, scope) {
debugger;
},

ExpressionStatement(node: t.ExpressionStatement, scope) {
evaluate(node.expression, scope);
},

ReturnStatement(node: t.ReturnStatement, scope) {
RETURN.result = (node.argument ? evaluate(node.argument, scope) : void 0);
return RETURN;
},

BreakStatement(node: t.BreakStatement, scope) {
return BREAK;
},

ContinueStatement(node: t.ContinueStatement, scope) {
return CONTINUE;
},

IfStatement(node: t.IfStatement, scope) {
if (evaluate(node.test, scope)) {

return evaluate(node.consequent, scope);
}

if (node.alternate) {

const ifScope = new Scope('block', scope, true);

return evaluate(node.alternate, ifScope)
}
},

SwitchStatement(node: t.SwitchStatement, scope) {
const discriminant = evaluate(node.discriminant, scope);
const switchScope = new Scope('switch', scope);
for (const ca of node.cases){

if (ca.test === null || evaluate(ca.test, switchScope) === discriminant) {

const res = evaluate(ca, switchScope);

if (res === BREAK) {

break;

} else if (res === RETURN) {

return res;

}

}
}
},

SwitchCase(node: t.SwitchCase, scope) {
for (const item of node.consequent) {

const res = evaluate(item, scope);

if (res === BREAK || res === RETURN) {

return res;

}
}
},

ThrowStatement(node: t.ThrowStatement, scope) {
throw evaluate(node.argument, scope);
},

TryStatement(node: t.TryStatement, scope) {
try {

return evaluate(node.block, scope);
} catch (error) {

if (node.handler) {

const catchScope = new Scope('block', scope, true);

catchScope.$let((node.handler.param).name, error);

return evaluate(node.handler, catchScope);

} else {

throw error;

}
} finally {

if (node.finalizer) {

return evaluate(node.finalizer, scope);

}
}
},

CatchClause(node: t.CatchClause, scope) {
return evaluate(node.body, scope);
},

WhileStatement(node: t.WhileStatement, scope) {
while (evaluate(node.test, scope)) {

const whileScope = new Scope('loop', scope, true);

const res = evaluate(node.body, whileScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

ForStatement(node: t.ForStatement, scope) {
for (

const forScope = new Scope('loop', scope),

initVal = evaluate(node.init, forScope);

evaluate(node.test, forScope);

evaluate(node.update, forScope)
) {

const res = evaluate(node.body, forScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

ForInStatement(node: t.ForInStatement, scope) {
const kind = (node.left).kind;
const decl = (node.left).declarations[0];
const name = (decl.id).name;

for (const value in evaluate(node.right, scope)) {

const forScope = new Scope('loop', scope, true);

scope.$define(kind, name, value);

const res = evaluate(node.body, forScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

ForOfStatement(node: t.ForOfStatement, scope) {
const kind = (node.left).kind;
const decl = (node.left).declarations[0];
const name = (decl.id).name;

for (const value of evaluate(node.right, scope)) {

const forScope = new Scope('loop', scope, true);

scope.$define(kind, name, value);

const res = evaluate(node.body, forScope);

if (res === CONTINUE) continue;

if (res === BREAK) break;

if (res === RETURN) return res;
}
},

FunctionDeclaration(node: t.FunctionDeclaration, scope) {
const func = evaluateMap.FunctionExpression(node, scope);
scope.$var(node.id.name, func);
},

VariableDeclaration(node: t.VariableDeclaration, scope) {
const { kind, declarations } = node;
for (const decl of declarations) {

const varName = (decl.id).name;

const value = decl.init ? evaluate(decl.init, scope) : void 0;

if (!scope.$define(kind, varName, value)) {

throw `[Error] ${name} 重复定义`

}
}
},

ThisExpression(node: t.ThisExpression, scope) {
const _this = scope.$find('this');
return _this ? _this.$get() : null;
},

ArrayExpression(node: t.ArrayExpression, scope) {
return node.elements.map(item => evaluate(item, scope));
},

ObjectExpression(node: t.ObjectExpression, scope) {
let res = Object.create(null);
node.properties.forEach((prop) => {

let key;

let value;

if(prop.type === 'ObjectProperty'){

key = prop.key.name;

value = evaluate(prop.value, scope);

res[key] = value;

}else if (prop.type === 'ObjectMethod'){

const kind = prop.kind;

key = prop.key.name;

value = evaluate(prop.body, scope);

if(kind === 'method') {

res[key] = value;

}else if(kind === 'get') {

Object.defineProperty(res, key, { get: value });

}else if(kind === 'set') {

Object.defineProperty(res, key, { set: value });

}

}else if(prop.type === 'SpreadElement'){

const arg = evaluate(prop.argument, scope);

res = Object.assign(res, arg);

}
});
return res;
},

FunctionExpression(node: t.FunctionExpression, scope) {
return function (...args: any) {

const funcScope = new Scope('function', scope, true);

node.params.forEach((param: t.Identifier, idx) => {

const { name: paramName } = param;

funcScope.$let(paramName, args[idx]);

});

funcScope.$const('this', this);

funcScope.$const('arguments', arguments);

const res = evaluate(node.body, funcScope);

if (res === RETURN) {

return res.result;

}
}
},

ArrowFunctionExpression(node: t.ArrowFunctionExpression, scope) {
return (...args) => {

const funcScope = new Scope('function', scope, true);

node.params.forEach((param: t.Identifier, idx) => {

const { name: paramName } = param;

funcScope.$let(paramName, args[idx]);

});

const _this = funcScope.$find('this');

funcScope.$const('this', _this ? _this.$get() : null);

funcScope.$const('arguments', args);

const res = evaluate(node.body, funcScope);

if (res === RETURN) {

return res.result;

}
}
},

UnaryExpression(node: t.UnaryExpression, scope) {
const expressionMap = {

'~': () => ~evaluate(node.argument, scope),

'+': () => +evaluate(node.argument, scope),

'-': () => -evaluate(node.argument, scope),

'!': () => !evaluate(node.argument, scope),

'void': () => void evaluate(node.argument, scope),

'typeof': () => {

if (node.argument.type === 'Identifier') {

const $var = scope.$find(node.argument.name);

const value = $var ? $var.$get() : void 0;

return typeof value;

}

return typeof evaluate(node.argument, scope);

},

'delete': () => {

if (node.argument.type === 'MemberExpression') {

const { object, property, computed } = node.argument;

const obj = evaluate(object, scope);

let prop;

if (computed) {

prop = evaluate(property, scope);

} else {

prop = property.name;

}

return delete obj[prop];

} else {

throw '[Error] 出现错误'

}

},
}
return expressionMap[node.operator]();
},

UpdateExpression(node: t.UpdateExpression, scope) {
const { prefix, argument, operator } = node;
let $var: IVariable;
if (argument.type === 'Identifier') {

$var = scope.$find(argument.name);

if (!$var) throw `${argument.name} 未定义`;
} else if (argument.type === 'MemberExpression') {

const obj = evaluate(argument.object, scope);

let prop;

if (argument.computed) {

prop = evaluate(argument.property, scope);

} else {

prop = argument.property.name;

}

$var = {

$set(value: any) {

obj[prop] = value;

return true;

},

$get() {

return obj[prop];

}

}
} else {

throw '[Error] 出现错误'
}

const expressionMap = {

'++': v => {

$var.$set(v + 1);

return prefix ? ++v : v++

},

'--': v => {

$var.$set(v - 1);

return prefix ? --v : v--

},
}

return expressionMap[operator]($var.$get());
},

BinaryExpression(node: t.BinaryExpression, scope) {
const { left, operator, right } = node;
const expressionMap = {

'==': (a, b) => a == b,

'===': (a, b) => a === b,

'>': (a, b) => a > b,

'<': (a, b) => a < b,

'!=': (a, b) => a != b,

'!==': (a, b) => a !== b,

'>=': (a, b) => a >= b,

'<=': (a, b) => a <= b,

'<<': (a, b) => a << b,

'>>': (a, b) => a >> b,

'>>>': (a, b) => a >>> b,

'+': (a, b) => a + b,

'-': (a, b) => a - b,

'*': (a, b) => a * b,

'/': (a, b) => a / b,

'&': (a, b) => a & b,

'%': (a, b) => a % b,

'|': (a, b) => a | b,

'^': (a, b) => a ^ b,

'in': (a, b) => a in b,

'instanceof': (a, b) => a instanceof b,
}
return expressionMap[operator](evaluate(left, scope), evaluate(right, scope));
},

AssignmentExpression(node: t.AssignmentExpression, scope) {
const { left, right, operator } = node;
let $var: IVariable;

if (left.type === 'Identifier') {

$var = scope.$find(left.name);

if(!$var) throw `${left.name} 未定义`;
} else if (left.type === 'MemberExpression') {

const obj = evaluate(left.object, scope);

let prop;

if (left.computed) {

prop = evaluate(left.property, scope);

} else {

prop = left.property.name;

}

$var = {

$set(value: any) {

obj[prop] = value;

return true;

},

$get() {

return obj[prop];

}

}
} else {

throw '[Error] 出现错误'
}

const expressionMap = {

'=': v => { $var.$set(v); return $var.$get() },

'+=': v => { $var.$set($var.$get() + v); return $var.$get() },

'-=': v => { $var.$set($var.$get() - v); return $var.$get() },

'*=': v => { $var.$set($var.$get() * v); return $var.$get() },

'/=': v => { $var.$set($var.$get() / v); return $var.$get() },

'%=': v => { $var.$set($var.$get() % v); return $var.$get() },

'<<=': v => { $var.$set($var.$get() << v); return $var.$get() },

'>>=': v => { $var.$set($var.$get() >> v); return $var.$get() },

'>>>=': v => { $var.$set($var.$get() >>> v); return $var.$get() },

'|=': v => { $var.$set($var.$get() | v); return $var.$get() },

'&=': v => { $var.$set($var.$get() & v); return $var.$get() },

'^=': v => { $var.$set($var.$get() ^ v); return $var.$get() },
}

return expressionMap[operator](evaluate(right, scope));
},

LogicalExpression(node: t.LogicalExpression, scope) {
const { left, right, operator } = node;
const expressionMap = {

'&&': () => evaluate(left, scope) && evaluate(right, scope),

'||': () => evaluate(left, scope) || evaluate(right, scope),
}
return expressionMap[operator]();
},

MemberExpression(node: t.MemberExpression, scope) {
const { object, property, computed } = node;
const obj = evaluate(object, scope);
let prop;
if (computed) {

prop = evaluate(property, scope);
} else {

prop = property.name;
}
return obj[prop];
},

ConditionalExpression(node: t.ConditionalExpression, scope) {
const { test, consequent, alternate } = node;
return evaluate(test, scope) ? evaluate(consequent, scope) : evaluate(alternate, scope);
},

CallExpression(node: t.CallExpression, scope) {
const func = evaluate(node.callee, scope);
const args = node.arguments.map(arg => evaluate(arg, scope));
let _this;
if (node.callee.type === 'MemberExpression') {

_this = evaluate(node.callee.object, scope);
} else {

const $var = scope.$find('this');

_this = $var ? $var.$get() : null;
}
return func.apply(_this, args);
},

NewExpression(node: t.NewExpression, scope) {
const func = evaluate(node.callee, scope);
const args = node.arguments.map(arg => evaluate(arg, scope));
return new (func.bind(func, ...args));
},

SequenceExpression(node: t.SequenceExpression, scope) {
let last;
node.expressions.forEach(expr => {

last = evaluate(expr, scope);
})
return last;
},
}

以上,evaluate-apply 这个过程就完了。
scope
scope
scope
我们再来看下 scope 该如何实现。

class Scope implements IScope {
public readonly variables: EmptyObj = Object.create(null);

constructor(
private readonly scopeType: ScopeType,
private parent: Scope = null,
public readonly shared = false,
) { }
}


class Scope implements IScope {
public readonly variables: EmptyObj = Object.create(null);

constructor(
private readonly scopeType: ScopeType,
private parent: Scope = null,
public readonly shared = false,
) { }
}

我们构造一个类来模拟 scope。可以看到,Scope 类包含了以下4个属性:

variables:当前环境下存在的变量

scopeType:当前环境的type

parent:当前环境的父环境

shared:有些时候不需要重复构造子环境,故用此标识
variables:当前环境下存在的变量scopeType:当前环境的typeparent:当前环境的父环境shared:有些时候不需要重复构造子环境,故用此标识接下来我们看下该如何在环境中声明变量
首先构造一个类来模拟变量

class Variable implements IVariable {
constructor(
private kind: Kind,
private value: any
){ }

$get() {
return this.value
}

$set(value: any) {
if (this.kind === 'const') {

return false
}
this.value = value;
return true;
}
}


class Variable implements IVariable {
constructor(
private kind: Kind,
private value: any
){ }

$get() {
return this.value
}

$set(value: any) {
if (this.kind === 'const') {

return false
}
this.value = value;
return true;
}
}

这个类中有两个属性和两个方法

kind 用于标识该变量是通过 var、let 还是 const 声明

value 表示该变量的值

$get 和 $set 分别用于获取和设置该变量的值
kind 用于标识该变量是通过 var、let 还是 const 声明value 表示该变量的值$get 和 $set 分别用于获取和设置该变量的值有了 Variable 类之后,我们就可以编写 Scope 类中的声明变量的方法了。
let 和 const 的声明方式基本一样

$const(varName: string, value: any) {
const variable = this.variables[varName];
if (!variable) {
this.variables[varName] = new Variable('const', value);
return true;
}
return false;
}

$let(varName: string, value: any) {
const variable = this.variables[varName];
if (!variable) {
this.variables[varName] = new Variable('let', value);
return true;
}
return false;
}


$const(varName: string, value: any) {
const variable = this.variables[varName];
if (!variable) {
this.variables[varName] = new Variable('const', value);
return true;
}
return false;
}

$let(varName: string, value: any) {
const variable = this.variables[varName];
if (!variable) {
this.variables[varName] = new Variable('let', value);
return true;
}
return false;
}

var 的声明方式稍微有一点差异,因为js中,除了在 function 中,用var 声明的变量是会被声明到父级作用域的(js的历史遗留坑)。我们看下代码

$var(varName: string, value: any) {
let scope: Scope = this;
while (!!sco