diff --git a/homework/1/rename.js b/homework/1/rename.js index 0bbd947..200eedc 100644 --- a/homework/1/rename.js +++ b/homework/1/rename.js @@ -3,23 +3,41 @@ const astring = require('astring'); const traverse = require('../../common/traverse'); function transform(root, originName, targetName) { - // 遍历所有节点 - return traverse((node, ctx, next) => { + // 遍历所有节点 + return traverse((node, ctx, next) => { - // TODO: 作业代码写在这里 - if (node.type === 'xxx') { - } + // TODO: 作业代码写在这里 - // 继续往下遍历 - return next(node, ctx) - })(root); + const nodeTypeArray = [ + 'FunctionDeclaration', + 'VariableDeclarator', + 'MemberExpression', + 'BinaryExpression' + ] + const keyTypeArray = [ + 'property', + 'label', + 'key' + ] + if (nodeTypeArray.includes(node.type)) { + for (const key in node) { + if (keyTypeArray.includes(key)) continue + if (node[key].type === 'Identifier' && node[key].name === originName) { + node[key].name = targetName; + } + } + } + + // 继续往下遍历 + return next(node, ctx) + })(root); } function rename(code, originName, targetName) { - const ast = acorn.parse(code, { - ecmaVersion: 5, - }) - return astring.generate(transform(ast, originName, targetName)) + const ast = acorn.parse(code, { + ecmaVersion: 5, + }) + return astring.generate(transform(ast, originName, targetName)) } module.exports = rename \ No newline at end of file diff --git a/homework/2/eval.js b/homework/2/eval.js index be527f5..2906fac 100644 --- a/homework/2/eval.js +++ b/homework/2/eval.js @@ -1,19 +1,111 @@ const acorn = require('acorn'); function evaluate(node, env) { - switch (node.type) { - case 'Literal': - // TODO: 补全作业代码 - } + switch (node.type) { + case 'Literal': + // TODO: 补全作业代码 + return node.value; + case 'Program': + evaluate(node.body, env); + case 'ExpressionStatement': + evaluate(node.expression, env); + case 'BinaryExpression': + case 'LogicalExpression': + //BinaryOperator { + // "==" | "!=" | "===" | "!==" + // | "<" | "<=" | ">" | ">=" + // | "<<" | ">>" | ">>>" + // | "+" | "-" | "*" | "/" | "%" + // | "**" | "||" | "^" | "&&" | "in" + // | "instanceof" + // | "|>" + // } + switch (node.operator) { + case '==': + return evaluate(node.left, env) == evaluate(node.right, env); + case '!=': + return evaluate(node.left, env) != evaluate(node.right, env); + case '===': + return evaluate(node.left, env) === evaluate(node.right, env); + case '!==': + return evaluate(node.left, env) !== evaluate(node.right, env); + case '<': + return evaluate(node.left, env) < evaluate(node.right, env); + case '<=': + return evaluate(node.left, env) <= evaluate(node.right, env); + case '>': + return evaluate(node.left, env) > evaluate(node.right, env); + case '>=': + return evaluate(node.left, env) >= evaluate(node.right, env); + case '<<': + return evaluate(node.left, env) << evaluate(node.right, env); + case '>>': + return evaluate(node.left, env) >> evaluate(node.right, env); + case '>>>': + return evaluate(node.left, env) >>> evaluate(node.right, env); + case '+': + return evaluate(node.left, env) + evaluate(node.right, env); + case '-': + return evaluate(node.left, env) - evaluate(node.right, env); + case '*': + return evaluate(node.left, env) * evaluate(node.right, env); + case '/': + return evaluate(node.left, env) / evaluate(node.right, env); + case '%': + return evaluate(node.left, env) % evaluate(node.right, env); + case '**': + return evaluate(node.left, env) ** evaluate(node.right, env); + case '||': + return evaluate(node.left, env) || evaluate(node.right, env); + case '^': + return evaluate(node.left, env) ^ evaluate(node.right, env); + case '&&': + return evaluate(node.left, env) && evaluate(node.right, env); + case 'in': + return evaluate(node.left, env) in evaluate(node.right, env); + case 'instanceof': + return evaluate(node.left, env) instanceof evaluate(node.right, env); + // case '|>': + // return evaluate(node.left, env) | > evaluate(node.right, env); + } + case 'ConditionalExpression': + return evaluate(node.test, env) ? evaluate(node.consequent, env) : evaluate(node.alternate, env); + case 'ObjectExpression': + let obj = {}; + for (let i in node.properties) { + obj[node.properties[i].key.name] = evaluate(node.properties[i].value, env) + } + return obj; + case 'Identifier': + return env[node.name]; + case 'ArrayExpression': + let arr = [] + for (i in node.elements) { + arr[i] = evaluate(node.elements[i], env) + } + return arr; + case 'CallExpression': + let callee = evaluate(node.callee, env); + let args = node.arguments.map(arg => evaluate(arg, env)); + return callee(...args); + case 'ArrowFunctionExpression': + return (...args) => { + let argEnv = {}; + for (let i in node.params) { + argEnv[node.params[i].name] = args[i]; + } + return evaluate(node.body, {...env, ...argEnv }) + } + } - throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`); + throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`); } function customerEval(code, env = {}) { - const node = acorn.parseExpressionAt(code, 0, { - ecmaVersion: 6 - }) - return evaluate(node, env) + const node = acorn.parseExpressionAt(code, 0, { + ecmaVersion: 6 + }) + return evaluate(node, env) } module.exports = customerEval \ No newline at end of file diff --git a/homework/3/eval.js b/homework/3/eval.js index 4ff65d2..051016b 100644 --- a/homework/3/eval.js +++ b/homework/3/eval.js @@ -1,19 +1,287 @@ const acorn = require('acorn'); +const Scope = require('./scope'); -function evaluate(node, env) { - switch (node.type) { - case 'Literal': - // TODO: 补全作业代码 - } +function evaluate(node, scope) { + if (!node) return; + switch (node.type) { + case 'Literal': + // TODO: 补全作业代码 + return node.value; - throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`); + case 'Identifier': // 变量名称 + return scope.get(node); + + case 'Program': {// 程序开始 + // 解释器运行时创建全局作用域 + let ret; + const globalScope = new Scope('global', null); + // 对程序中的函数以及变量进行声明提升 + hoisting(node, globalScope); + for (const body of node.body) { + if (body.type === 'FunctionDeclaration') continue; + ret = evaluate(body, globalScope); + }; + return ret; + } + + case 'ExpressionStatement': // 表达式语句 + return evaluate(node.expression, scope); + + case 'BinaryExpression': // 二元表达式 + case 'LogicalExpression': { //逻辑表达式 + switch (node.operator) { + case '==': return evaluate(node.left, scope) == evaluate(node.right, scope); + case '!=': return evaluate(node.left, scope) != evaluate(node.right, scope); + case '===': return evaluate(node.left, scope) === evaluate(node.right, scope); + case '!==': return evaluate(node.left, scope) !== evaluate(node.right, scope); + case '<': return evaluate(node.left, scope) < evaluate(node.right, scope); + case '<=': return evaluate(node.left, scope) <= evaluate(node.right, scope); + case '>': return evaluate(node.left, scope) > evaluate(node.right, scope); + case '>=': return evaluate(node.left, scope) >= evaluate(node.right, scope); + case '<<': return evaluate(node.left, scope) << evaluate(node.right, scope); + case '>>': return evaluate(node.left, scope) >> evaluate(node.right, scope); + case '>>>': return evaluate(node.left, scope) >>> evaluate(node.right, scope); + case '+': return evaluate(node.left, scope) + evaluate(node.right, scope); + case '-': return evaluate(node.left, scope) - evaluate(node.right, scope); + case '*': return evaluate(node.left, scope) * evaluate(node.right, scope); + case '/': return evaluate(node.left, scope) / evaluate(node.right, scope); + case '%': return evaluate(node.left, scope) % evaluate(node.right, scope); + case '**': return evaluate(node.left, scope) ** evaluate(node.right, scope); + case '||': return evaluate(node.left, scope) || evaluate(node.right, scope); + case '^': return evaluate(node.left, scope) ^ evaluate(node.right, scope); + case '&&': return evaluate(node.left, scope) && evaluate(node.right, scope); + case 'in': return evaluate(node.left, scope) in evaluate(node.right, scope); + case 'instanceof': return evaluate(node.left, scope) instanceof evaluate(node.right, scope); + } + } + + case 'UpdateExpression': { + switch (node.operator) { + case '++': + return scope.set(node.argument, scope.get(node.argument) + 1); + case '--': + return scope.set(node.argument, scope.get(node.argument) - 1); + } + } + + case 'UnaryExpression': { + switch (node.operator) { + case '-': return -evaluate(node.argument, scope); + case '+': return +evaluate(node.argument, scope); + case '!': return !evaluate(node.argument, scope); + case '~': return ~evaluate(node.argument, scope); + case 'typeof': return typeof evaluate(node.argument, scope); + } + } + + case 'AssignmentExpression': { // 赋值表达式 + switch (node.operator) { + case '=': return scope.set(node.left, evaluate(node.right, scope)); + case '+=': return scope.set(node.left, scope.get(node.left) + evaluate(node.right, scope)); + case '-=': return scope.set(node.left, scope.get(node.left) - evaluate(node.right, scope)); + case '*=': return scope.set(node.left, scope.get(node.left) * evaluate(node.right, scope)); + case '/=': return scope.set(node.left, scope.get(node.left) / evaluate(node.right, scope)); + } + } + + case 'MemberExpression': { + return scope.get(node); + } + + case 'ConditionalExpression': // 三元表达式 + return evaluate(node.test, scope) ? evaluate(node.consequent, scope) : evaluate(node.alternate, scope); + + case 'SequenceExpression': // 序列表达式 + return node.expressions.reduce((_, expression) => evaluate(expression, scope), {}); + + case 'ObjectExpression': { // 对象表达式 + let obj = {}; + node.properties.forEach((_, i) => { + obj[node.properties[i].key.name] = evaluate(node.properties[i].value, scope); + }) + return obj; + } + + case 'ArrayExpression': {// 数组表达式 + let arr = [] + node.elements.forEach((_, i) => { + arr[i] = evaluate(node.elements[i], scope); + }) + return arr; + } + + case 'CallExpression': {// 调用表达式 + let callee = evaluate(node.callee, scope); + let args = node.arguments.map(arg => evaluate(arg, scope)); + return callee(...args); + } + + case 'FunctionExpression': // 函数表达式 + case 'ArrowFunctionExpression': { // 箭头函数表达式 + return (...args) => { + const functionScope = new Scope('function', scope); + node.params.forEach(function (par, i) { + functionScope.init('let', par.name, args[i]); + }) + return evaluate(node.body, functionScope); + } + } + + case 'BlockStatement': { // 块 + let ret; + hoisting(node, scope); // 对函数和变量进行声明提升 + for (const body of node.body) { + if (body.type === 'FunctionDeclaration') continue; + ret = evaluate(body, scope); + if (ret && ret.type) { + switch (ret.type) { + case 'continue': + case 'break': + return ret; + } + } + }; + return ret; + } + + case 'IfStatement': {// if + if (evaluate(node.test, scope)) { + const ifScope = new Scope('block', scope); + return evaluate(node.consequent, ifScope); + } else { + const ifScope = new Scope('block', scope); + return evaluate(node.alternate, ifScope); + } + } + + case 'SwitchStatement': {// switch + let ret; + const switchScope = new Scope('block', scope); + node.cases.reduce((_, cases) => { + if (evaluate(node.discriminant, switchScope) === evaluate(cases.test, switchScope)) { + cases.consequent.reduce((_, cons) => { ret = evaluate(cons, switchScope) }, {}); + } + }, {}); + return ret; + } + + case 'LabeledStatement': { + scope.controlFlowLabel = node.label.name; + return evaluate(node.body, scope); + } + + case 'ForStatement': {// for 循环 + let ret; + let label = scope.controlFlowLabel; + const forScope = new Scope('block', scope); + for (evaluate(node.init, forScope); evaluate(node.test, forScope); evaluate(node.update, forScope)) { + ret = evaluate(node.body, forScope); + if (ret && ret.type) { + switch (ret.type) { + case 'continue': + if (ret.label === label) { continue } else { return ret }; + case 'break': + if (ret.label === label) { break } else { return ret }; + } + return; + } + } + return; + } + + case 'WhileStatement': {// while 循环 + let ret; + let label = scope.controlFlowLabel; + const whileScope = new Scope('block', scope); + while (evaluate(node.test, whileScope)) { + ret = evaluate(node.body, whileScope); + if (ret && ret.type) { + switch (ret) { + case 'continue': + if (ret.label === label) { continue } else { return ret }; + case 'break': + if (ret.label === label) { continue } else { return ret }; + } + return; + } + } + return; + } + + case 'ReturnStatement': // return + return evaluate(node.argument, scope); + + case 'ContinueStatement': {// continue + let ret = {}; + ret.type = 'continue'; + if (node.label) { + ret.label = node.label.name; + } + return ret; + } + + case 'BreakStatement': {// break + let ret = {}; + ret.type = 'break'; + if (node.label) { + ret.label = node.label.name; + } + return ret; + } + + case 'VariableDeclaration': { // 变量声明(变量初始化) + node.declarations.forEach((dec) => scope.init(node.kind, dec.id.name, evaluate(dec.init, scope))); + return; + } + + case 'TryStatement': { + try { + const tryScope = new Scope('block', scope); + evaluate(node.block, tryScope); + } catch (err) { + let ret; + const catchScope = new Scope('block', scope); + catchScope.init('let', node.handler.param.name, err); + ret = evaluate(node.handler.body, catchScope); + return ret; + } finally { + const finallyScope = new Scope('block', scope); + evaluate(node.finalizer, finallyScope); + } + } + + case 'ThrowStatement': { + throw evaluate(node.argument, scope); + } + } + throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`); +} + +function hoisting(node, scope) { // 变量以及函数声明提升 + node.body.forEach((node) => { + switch (node.type) { + case 'VariableDeclaration': { // 变量声明 + node.declarations.forEach((dec) => scope.declare(node.kind, dec.id.name, undefined)); + return; + } + + case 'FunctionDeclaration': { // 函数声明 + return scope.init('var', node.id.name, function (...args) { + node.params.forEach((param, i) => { + scope.init('let', param.name, args[i]) + }) + return evaluate(node.body, scope); + }); + } + } + }) } function customerEval(code, env = {}) { - const node = acorn.parse(code, 0, { - ecmaVersion: 6 - }) - return evaluate(node, env) + const node = acorn.parse(code, { + ecmaVersion: 6 + }) + return evaluate(node, env) } module.exports = customerEval \ No newline at end of file diff --git a/homework/3/scope.js b/homework/3/scope.js new file mode 100644 index 0000000..a93bd20 --- /dev/null +++ b/homework/3/scope.js @@ -0,0 +1,146 @@ +class Scope { + constructor(type, parent) { + // 解释器运行起来的时候,就需要定义一个全局作用域 global + this.type = type; // global || function || block + this.parent = parent; + this.variables = {}; + this.controlFlowLabel = {}; + } + + declare(kind, name, init) { + // kind:var / const / let 不同情况定义的位置也不相同 + switch (kind) { + case 'var': { + if (this.type == 'function' || this.type == 'global') { + this.variables[name] = { + kind: kind, + value: init, + initialized: true + } + } else { + this.parent.declare(kind, name, init); + } + return; + } + case 'const': + case 'let': { + if (this.variables[name]) { + throw new SyntaxError(`Identifier '${name}' has already been declared`); + } else { + this.variables[name] = { + kind: kind, + value: init, + initialized: false + } + } + return; + } + } + return; + } + + init(kind, name, init) { + switch (kind) { + case 'var': { + if (this.type == 'function' || this.type == 'global') { + this.variables[name] = { + kind: kind, + value: init, + initialized: true + } + } else { + this.parent.init(kind, name, init); + } + return; + } + case 'const': + case 'let': { + this.variables[name] = { + kind: kind, + value: init, + initialized: true + } + return; + } + } + } + + get(node) { + // 如果当前作用域里面没有找到该变量,就去父节点作用域里面找 + // 找不到就返回 "name" is not defined + if (node.type === 'MemberExpression') { + if (this.variables[node.object.name]) { + if (this.variables[node.object.name].value[node.property.value]) { // 判断是否是数组类型 + return this.variables[node.object.name].value[node.property.value]; + } else if (this.variables[node.object.name].value[node.property.name]) { + return this.variables[node.object.name].value[node.property.name]; + } else { + return undefined; + } + } else { + if (this.parent != null) { + return this.parent.get(node); + } else { + throw new ReferenceError(`'${node.name}' is not defined`); + } + } + } else { + if (this.variables[node.name]) { + if (this.variables[node.name].kind === 'var') { + return this.variables[node.name].value; + } else if (this.variables[node.name].initialized){ + return this.variables[node.name].value; + } else { + throw new ReferenceError(`Cannot access '${node.name}' before initialization`); + } + } else { + if (this.parent != null) { + return this.parent.get(node); + } else { + throw new ReferenceError(`'${node.name}' is not defined`); + } + } + } + } + + set(node, value) { + if (node.type === 'MemberExpression') { + if (this.variables[node.object.name]) { + if (this.variables[node.object.name].value[node.property.value]) { + this.variables[node.object.name].value[node.property.value] = value; + } else { + this.variables[node.object.name].value[node.property.name] = value; + } + } else { + if (this.parent != null) { + return this.parent.set(node, value); + } else { + this.variables[node.object.name] = { + kind: null, + value: {} + } + this.variables[node.object.name].value[node.property.name] = value; + } + } + } else { + if (this.variables[node.name]) { + if (this.variables[node.name].kind === 'const') { + throw new TypeError('Assignment to constant variable'); + } else { + return this.variables[node.name].value = value; + } + } else { + if (this.parent != null) { + return this.parent.set(node, value); + } else { + this.variables[node.name] = { + kind: 'var', + value: value + } + // throw new ReferenceError(`${node.name} is not defined`); + } + } + } + } +} +module.exports = Scope; \ No newline at end of file