From f5ae33a0446421a50fbac07cae4f18c9ff805815 Mon Sep 17 00:00:00 2001 From: yinyuanling <1318661135@qq.com> Date: Sun, 27 Feb 2022 14:16:18 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=B0=B9=E8=BF=9C=E7=8E=B2=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A=E4=BA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/2/eval.js | 89 ++++++++++++++++++++++++++++++++++++++++- homework/2/eval.test.js | 2 +- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/homework/2/eval.js b/homework/2/eval.js index be527f5..fc3666f 100644 --- a/homework/2/eval.js +++ b/homework/2/eval.js @@ -3,7 +3,92 @@ const acorn = require('acorn'); function evaluate(node, env) { switch (node.type) { case 'Literal': - // TODO: 补全作业代码 + // TODO: 补全作业代码 + return node.value + case 'Identifier': + return env[node.name] + case 'BinaryExpression': { + if (node.operator === '+') { + return evaluate(node.left, env) + evaluate(node.right, env) + } else if (node.operator === '-') { + return evaluate(node.left, env) - evaluate(node.right, env) + } else if (node.operator === '*') { + return evaluate(node.left, env) * evaluate(node.right, env) + } else if (node.operator === '/') { + return evaluate(node.left, env) / evaluate(node.right, env) + } else if (node.operator === '==') { + return evaluate(node.left, env) == evaluate(node.right, env) + } else if (node.operator === '!=') { + return evaluate(node.left, env) != evaluate(node.right, env) + } else if (node.operator === '===') { + return evaluate(node.left, env) === evaluate(node.right, env) + } else if (node.operator === '!==') { + return evaluate(node.left, env) !== evaluate(node.right, env) + } else if (node.operator === '<') { + return evaluate(node.left, env) < evaluate(node.right, env) + } else if (node.operator === '<=') { + return evaluate(node.left, env) <= evaluate(node.right, env) + } else if (node.operator === '>') { + return evaluate(node.left, env) > evaluate(node.right, env) + } else if (node.operator === '>=') { + return evaluate(node.left, env) >= evaluate(node.right, env) + } else if (node.operator === '<<') { + return evaluate(node.left, env) << evaluate(node.right, env) + } else if (node.operator === '>>') { + return evaluate(node.left, env) >> evaluate(node.right, env) + } else if (node.operator === '>>>') { + return evaluate(node.left, env) >>> evaluate(node.right, env) + } else if (node.operator === '%') { + return evaluate(node.left, env) % evaluate(node.right, env) + } else if (node.operator === '|') { + return evaluate(node.left, env) | evaluate(node.right, env) + } else if (node.operator === '^') { + return evaluate(node.left, env) ^ evaluate(node.right, env) + } else if (node.operator === '&') { + return evaluate(node.left, env) & evaluate(node.right, env) + } else if (node.operator === 'in') { + return evaluate(node.left, env) in evaluate(node.right, env) + } else if (node.operator === 'instanceof') { + return evaluate(node.left, env) instanceof evaluate(node.right, env) + } + } + case 'ConditionalExpression': { + if (evaluate(node.test, env)) { + return evaluate(node.consequent, env) + } else { + return evaluate(node.alternate, env) + } + } + case 'ArrowFunctionExpression': { + return function (...args) { + const argsenv = {} + const params = node.params + for (let i = 0; i < params.length; i++) { + argsenv[params[i].name] = args[i] + } + return evaluate(node.body, {...env, ...argsenv}) + } + } + case 'LogicalExpression': { + if (node.operator === '||') { + return evaluate(node.left, env) || evaluate(node.right, env) + } else if (node.operator === '&&') { + return evaluate(node.left, env) && evaluate(node.right, env) + } + } + case 'CallExpression': { + return evaluate(node.callee, env)(...node.arguments.map(arg => evaluate(arg, env))) + } + case 'ArrayExpression': { + return [...node.elements.map(arg => evaluate(arg, env))] + } + case 'ObjectExpression': { + let obj = {} + node.properties.forEach(property => { + obj[property.key.name] = evaluate(property.value, env) + }) + return obj + } } throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`); @@ -16,4 +101,4 @@ function customerEval(code, env = {}) { return evaluate(node, env) } -module.exports = customerEval \ No newline at end of file +module.exports = customerEval diff --git a/homework/2/eval.test.js b/homework/2/eval.test.js index 8e6422a..022abb1 100644 --- a/homework/2/eval.test.js +++ b/homework/2/eval.test.js @@ -29,4 +29,4 @@ test('测试表达式 - 终极挑战', t => { test('测试表达式 - 超纲挑战(下节课会讲)', t => { const sourceCode = '(n => ((x => n = x)(n + 2), (y => n + y)(3)))(1)' t.deepEqual(customerEval(sourceCode), eval(sourceCode)); -}) \ No newline at end of file +}) From 125e18c9f0e836631296cfb820de29d7cab67ad1 Mon Sep 17 00:00:00 2001 From: yinyuanling <1318661135@qq.com> Date: Tue, 1 Mar 2022 12:26:50 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20=E5=A7=93=E5=90=8D?= =?UTF-8?q?=EF=BC=9A=E5=B0=B9=E8=BF=9C=E7=8E=B2=20pull=20request=20?= =?UTF-8?q?=EF=BC=9A#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/2/eval.js | 1 + 1 file changed, 1 insertion(+) diff --git a/homework/2/eval.js b/homework/2/eval.js index fc3666f..396f912 100644 --- a/homework/2/eval.js +++ b/homework/2/eval.js @@ -101,4 +101,5 @@ function customerEval(code, env = {}) { return evaluate(node, env) } +const test = customerEval('(()=> {return 1})()') module.exports = customerEval From d911feb71c6787cc01e3da2839495d170fc4f870 Mon Sep 17 00:00:00 2001 From: yinyuanling <1318661135@qq.com> Date: Tue, 1 Mar 2022 12:34:10 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20=E5=A7=93=E5=90=8D?= =?UTF-8?q?=EF=BC=9A=E5=B0=B9=E8=BF=9C=E7=8E=B2=20pull=20request=20?= =?UTF-8?q?=EF=BC=9A#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/1/rename.js | 105 +++++++++++++++++- homework/1/rename.test.js | 3 +- homework/3/eval.js | 225 +++++++++++++++++++++++++++++++++++++- package.json | 4 +- 4 files changed, 326 insertions(+), 11 deletions(-) diff --git a/homework/1/rename.js b/homework/1/rename.js index 0bbd947..00f25fc 100644 --- a/homework/1/rename.js +++ b/homework/1/rename.js @@ -5,11 +5,96 @@ const traverse = require('../../common/traverse'); function transform(root, originName, targetName) { // 遍历所有节点 return traverse((node, ctx, next) => { - // TODO: 作业代码写在这里 - if (node.type === 'xxx') { + if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') { + if (node.id) { + if (node.id.type === 'Identifier' && node.id.name === originName) { + node.id.name = targetName + } + } + if (node.params.length > 0) { + for(child in node.params) { + if (child.type === 'Identifier' && child.name === originName) { + child.name = targetName + } + } + } + } + if (node.type === 'VariableDeclarator') { + if (node.id.type === 'Identifier' && node.id.name === originName) { + node.id.name = targetName + } + } + if (node.type === 'ArrayExpression') { + if (node.elements.length > 0) { + for(child in node.elements) { + if (child.type === 'Identifier' && child.name === originName) { + child.name = targetName + } + } + } + } + if (node.type === 'ObjectExpression') { + if (node.properties.length > 0) { + node.properties.forEach(child => { + if (child.value.type === 'Identifier' && child.name === originName) { + child.name = targetName + } + }) + } + } + if (node.type === 'UnaryExpression' || node.type === 'UpdateExpression') { + if (node.argument) { + if (node.argument.type === 'Identifier' && node.argument.name === originName) { + node.argument.name = targetName + } + } + } + if (node.type === 'BinaryExpression' || node.type === 'AssignmentExpression' || node.type === 'LogicalExpression') { + if (node.left.type === 'Identifier' && node.left.name === originName) { + node.left.name = targetName + } + if (node.right.type === 'Identifier' && node.right.name === originName) { + node.right.name = targetName + } + } + if (node.type === 'MemberExpression') { + if (node.object.type === 'Identifier' && node.object.name === originName) { + node.object.name = targetName + } + } + if (node.type === 'ConditionalExpression') { + if (node.test.type === 'Identifier' && node.test.name === originName) { + node.test.name = targetName + } + if (node.alternate.type === 'Identifier' && node.alternate.name === originName) { + node.alternate.name = targetName + } + if (node.consequent.type === 'Identifier' && node.consequent.name === originName) { + node.consequent.name = targetName + } + } + if (node.type === 'CallExpression' || node.type === 'NewExpression') { + if (node.callee.type === 'Identifier' && node.callee.name === originName) { + node.callee.name = targetName + } + if (node.arguments.length > 0) { + for (child in node.arguments) { + if (child.type === 'Identifier' && child.name === originName) { + child.name = targetName + } + } + } + } + if (node.type === 'SequenceExpression') { + if (node.expressions.length > 0) { + for (child in node.expressions) { + if (child.type === 'Identifier' && child.name === originName) { + child.name = targetName + } + } + } } - // 继续往下遍历 return next(node, ctx) })(root); @@ -22,4 +107,16 @@ function rename(code, originName, targetName) { return astring.generate(transform(ast, originName, targetName)) } -module.exports = rename \ No newline at end of file +// const sourceCode = ` +// function foo() { +// foo: while(true) { +// var foo = { +// foo: foo.foo.foo[foo + foo] +// }; +// break foo; +// } +// } +// ` +// const result = rename(sourceCode, 'foo', 'bar'); +// console.log(result) +module.exports = rename diff --git a/homework/1/rename.test.js b/homework/1/rename.test.js index 8387009..09606ae 100644 --- a/homework/1/rename.test.js +++ b/homework/1/rename.test.js @@ -39,6 +39,5 @@ function bar() { `; const result = rename(sourceCode, 'foo', 'bar'); - t.deepEqual(toStandard(result), toStandard(targetCode)) -}) \ No newline at end of file +}) diff --git a/homework/3/eval.js b/homework/3/eval.js index 4ff65d2..e360c15 100644 --- a/homework/3/eval.js +++ b/homework/3/eval.js @@ -1,9 +1,220 @@ const acorn = require('acorn'); -function evaluate(node, env) { +// 处理常量 +class SimpleValue { + constructor (value, kind = '') { + this.value = value + this.kind = kind + } + + set (value) { + // 禁止重新对const类型变量赋值 + if (this.kind === 'const') { + throw new TypeError('Assignment to constant variable') + } else { + this.value = value + } + } + + get () { + return this.value + } +} +const standardMap = { + console: new SimpleValue(console) +} +class Scope { + constructor (type, parentScope) { + this.variables = {} + + // 作用域类型,区分函数作用域function和块级作用域block + this.type = type // function/block//let/var/const + // 父级作用域 + this.parentScope = parentScope + // 全局作用域 + this.globalDeclaration = standardMap + // 当前作用域的变量空间 + this.declaration = Object.create(null) + } + + /* + * get/set方法用于获取/设置当前作用域中对应name的变量值 + 符合JS语法规则,优先从当前作用域去找,若找不到则到父级作用域去找,然后到全局作用域找。 + 如果都没有,就报错 + */ + get (name) { + if (this.declaration[name]) { + console.log(this.declaration[name]) + return this.declaration[name] + } else if (this.parentScope) { + return this.parentScope.get(name) + } else if (this.globalDeclaration[name]) { + return this.globalDeclaration[name] + } + throw new ReferenceError(`${name} is not defined`) + } + + set (name, value) { + if (this.declaration[name]) { + this.declaration[name] = value + } else if (this.parentScope[name]) { + this.parentScope.set(name, value) + } else { + throw new ReferenceError(`${name} is not defined`) + } + } + + /** + * 根据变量的kind调用不同的变量定义方法 + */ + declare (name, value, kind) { + if (kind === 'var') { + return this.varDeclare(name, value) + } else if (kind === 'let') { + return this.letDeclare(name, value) + } else if (kind === 'const') { + return this.constDeclare(name, value) + } else { + throw new Error(`canjs: Invalid Variable Declaration Kind of "${kind}"`) + } + } + + varDeclare (name, value) { + let scope = this + // 若当前作用域存在非函数类型的父级作用域时,就把变量定义到父级作用域 + while (scope.parentScope && scope.type !== 'function') { + scope = scope.parentScope + } + this.declaration[name] = new SimpleValue(value, 'var') + return this.declaration[name] + } + + letDeclare (name, value) { + // 不允许重复定义 + if (this.declaration[name]) { + throw new SyntaxError(`Identifier ${name} has already been declared`) + } + this.declaration[name] = new SimpleValue(value, 'let') + return this.declaration[name] + } + + constDeclare (name, value) { + // 不允许重复定义 + if (this.declaration[name]) { + throw new SyntaxError(`Identifier ${name} has already been declared`) + } + this.declaration[name] = new SimpleValue(value, 'const') + return this.declaration[name] + } +} + +function evaluate(node, scope) { switch (node.type) { case 'Literal': - // TODO: 补全作业代码 + // TODO: 补全作业代码 + return node.value + case 'Identifier': + return scope.get(node.name).value + case 'ReturnStatement': { + return evaluate(node.argument, scope) + } + case 'ExpressionStatement': { + return evaluate(node.expression, scope) + } + case 'ArrowFunctionExpression': { + return function (...args) { + const argsScope = {} + const params = node.params + for (let i = 0; i < params.length; i++) { + argsScope[params[i].name] = args[i] + } + return evaluate(node.body, {...scope, ...argsScope}) + } + } + case 'BinaryExpression': { + if (node.operator === '+') { + return evaluate(node.left, scope) + evaluate(node.right, scope) + } else if (node.operator === '-') { + return evaluate(node.left, scope) - evaluate(node.right, scope) + } else if (node.operator === '*') { + return evaluate(node.left, scope) * evaluate(node.right, scope) + } else if (node.operator === '/') { + return evaluate(node.left, scope) / evaluate(node.right, scope) + } else if (node.operator === '==') { + return evaluate(node.left, scope) == evaluate(node.right, scope) + } else if (node.operator === '!=') { + return evaluate(node.left, scope) != evaluate(node.right, scope) + } else if (node.operator === '===') { + return evaluate(node.left, scope) === evaluate(node.right, scope) + } else if (node.operator === '!==') { + return evaluate(node.left, scope) !== evaluate(node.right, scope) + } else if (node.operator === '<') { + return evaluate(node.left, scope) < evaluate(node.right, scope) + } else if (node.operator === '<=') { + return evaluate(node.left, scope) <= evaluate(node.right, scope) + } else if (node.operator === '>') { + return evaluate(node.left, scope) > evaluate(node.right, scope) + } else if (node.operator === '>=') { + return evaluate(node.left, scope) >= evaluate(node.right, scope) + } else if (node.operator === '<<') { + return evaluate(node.left, scope) << evaluate(node.right, scope) + } else if (node.operator === '>>') { + return evaluate(node.left, scope) >> evaluate(node.right, scope) + } else if (node.operator === '>>>') { + return evaluate(node.left, scope) >>> evaluate(node.right, scope) + } else if (node.operator === '%') { + return evaluate(node.left, scope) % evaluate(node.right, scope) + } else if (node.operator === '|') { + return evaluate(node.left, scope) | evaluate(node.right, scope) + } else if (node.operator === '^') { + return evaluate(node.left, scope) ^ evaluate(node.right, scope) + } else if (node.operator === '&') { + return evaluate(node.left, scope) & evaluate(node.right, scope) + } else if (node.operator === 'in') { + return evaluate(node.left, scope) in evaluate(node.right, scope) + } else if (node.operator === 'instanceof') { + return evaluate(node.left, scope) instanceof evaluate(node.right, scope) + } + } + case 'CallExpression': { + return evaluate(node.callee, scope)(...node.arguments.map(arg => evaluate(arg, scope))) + } + case 'BlockStatement': { + // 有花括号 + const child = new Scope('Block', scope) + for (const state of node.body) { + evaluate(state, child) + } + } + case 'Declarations': { + // 名字 + scope.declare(node.name) + // init,初始化值set进scope里 + scope.set(node.name, evaluate(node.init, scope)) + } + case 'VariableDeclaration': { + // const child = new Scope(node.kind, scope) + // child.declare() + for (const val of node.declarations) { + return scope.declare (val.id.name, evaluate(val.init, scope), node.kind) + } + + } + + // 赋值的话直接set + + case 'IfStatement': { + if (evaluate(node.test, scope)) { + // 后稷 + return evaluate(node.consequent, scope) + } + } + case 'WhileStatement': { + while (node.test) { + return evaluate(node.body, scope) + } + } + } throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`); @@ -13,7 +224,13 @@ function customerEval(code, env = {}) { const node = acorn.parse(code, 0, { ecmaVersion: 6 }) - return evaluate(node, env) + return evaluate(node.body[0], env) } -module.exports = customerEval \ No newline at end of file +const temp1 = '(() => { let a = 3; if (a > 0) { return 1 } else { return 0 } })()' +const temp11 = (() => { let a = 3; if (a > 0) { return 1 } else { return 0 } })() +const temp2 = '(()=> {let a = 0; let b = 3; return 1})()' +const test = customerEval(temp1) +console.log(test) +// console.log(temp11) +module.exports = customerEval diff --git a/package.json b/package.json index d2ad3f4..34695cc 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,15 @@ "license": "MIT", "dependencies": { "acorn": "^8.7.0", + "acorn-bigint": "^1.0.0", + "acorn-jsx": "^5.3.2", "astring": "^1.8.1", "ava": "^4.0.1" }, "scripts": { "test-final": "ava final/test", "test-homework-1": "ava homework/1/rename.test.js", - "test-homework-2": "ava homework/2/eval.test.js", + "test-homework-2": "ava homework/2/eval.test.js", "test-homework-3": "ava homework/3/eval.test.js" } }