From a9b211deadf7243eff2ba62b59b376022da170fc Mon Sep 17 00:00:00 2001 From: deastre Date: Tue, 18 Jan 2022 17:40:11 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=E5=AE=8C=E6=88=90=E4=BA=86=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/1/rename.js | 37 +++++++++++++++++++++++++++++++------ homework/1/rename.test.js | 30 +++--------------------------- homework/1/sourceCode.js | 11 +++++++++++ 3 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 homework/1/sourceCode.js diff --git a/homework/1/rename.js b/homework/1/rename.js index 0bbd947..fbc3b9e 100644 --- a/homework/1/rename.js +++ b/homework/1/rename.js @@ -5,18 +5,43 @@ const traverse = require('../../common/traverse'); function transform(root, originName, targetName) { // 遍历所有节点 return traverse((node, ctx, next) => { - - // TODO: 作业代码写在这里 - if (node.type === 'xxx') { + switch(node.type) { + case 'VariableDeclaration': + let variableDeclarator = node.type.declarations[0]; + if (variableDeclarator.name === originName) { + variableDeclarator.name = targetName; + } + break; + case 'FunctionDeclaration': + if (node.id.name === originName) { + node.id.name = targetName; + } + break; + // case 'ObjectExpression': + // if (node.properties[0].key.name === originName) { + // node.properties[0].key.name = targetName; + // } + // break; + case 'BinaryExpression': + if (node.left.name === originName) { + node.left.name = targetName; + } + else if (node.right.name === originName) { + node.right.name = targetName; + } + break; + case 'MemberExpression' && !node.object.object: + if (node.object.name === originName) { + node.object.name === targetName; + } + break; } - - // 继续往下遍历 return next(node, ctx) })(root); } function rename(code, originName, targetName) { - const ast = acorn.parse(code, { + const ast = acorn.parse(code, { ecmaVersion: 5, }) return astring.generate(transform(ast, originName, targetName)) diff --git a/homework/1/rename.test.js b/homework/1/rename.test.js index c0f7d1d..9bd0710 100644 --- a/homework/1/rename.test.js +++ b/homework/1/rename.test.js @@ -1,6 +1,7 @@ const acorn = require('acorn'); const traverse = require('../../common/traverse') const rename = require('./rename') +const sourceCode = require('./sourceCode') function toStandard(code) { const root = acorn.parse(code, { ecmaVersion: 5 }) @@ -14,30 +15,5 @@ function toStandard(code) { return target } -test('测试重命名变量', () => { - const sourceCode = ` -function foo() { - foo: while(true) { - var foo = { - foo: foo.foo.foo[foo + foo] - }; - break foo; - } -} -` - - const targetCode = ` -function bar() { - foo: while(true) { - var bar = { - foo: bar.foo.foo[bar + bar] - }; - break foo; - } -} -`; - - const result = rename(sourceCode, 'foo', 'bar'); - - expect(toStandard(result)).toStrictEqual(toStandard(targetCode)); -}) \ No newline at end of file +const res = rename(sourceCode, 'foo', 'bar'); +console.log(res); \ No newline at end of file diff --git a/homework/1/sourceCode.js b/homework/1/sourceCode.js new file mode 100644 index 0000000..20fa7f9 --- /dev/null +++ b/homework/1/sourceCode.js @@ -0,0 +1,11 @@ +function foo() { + const a = 1; + foo: while(true) { + var foo = { + foo: foo.foo.foo[foo + foo] + }; + break foo; + } +} + +module.exports = foo; \ No newline at end of file From 57eb04c116fa2476f502b01d34a7ad1a04f55a9e Mon Sep 17 00:00:00 2001 From: deastre Date: Tue, 18 Jan 2022 17:49:31 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=E4=BA=86rename.test.?= =?UTF-8?q?js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/1/rename.test.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/homework/1/rename.test.js b/homework/1/rename.test.js index 9bd0710..dd393fc 100644 --- a/homework/1/rename.test.js +++ b/homework/1/rename.test.js @@ -15,5 +15,30 @@ function toStandard(code) { return target } -const res = rename(sourceCode, 'foo', 'bar'); -console.log(res); \ No newline at end of file +test('测试重命名变量', () => { + const sourceCode = ` +function foo() { + foo: while(true) { + var foo = { + foo: foo.foo.foo[foo + foo] + }; + break foo; + } +} +` + + const targetCode = ` +function bar() { + foo: while(true) { + var bar = { + foo: bar.foo.foo[bar + bar] + }; + break foo; + } +} +`; + + const result = rename(sourceCode, 'foo', 'bar'); + + expect(toStandard(result)).toStrictEqual(toStandard(targetCode)); +}) \ No newline at end of file From 7eb5d89beafd7a649ab89b6593691ed6470feb64 Mon Sep 17 00:00:00 2001 From: deastre Date: Tue, 18 Jan 2022 18:22:43 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E4=BA=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/1/rename.js | 39 ++++++++++++++++++++++++--------------- homework/1/rename.test.js | 1 - homework/1/sourceCode.js | 1 - homework/1/test.js | 7 +++++++ 4 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 homework/1/test.js diff --git a/homework/1/rename.js b/homework/1/rename.js index fbc3b9e..91a9ea4 100644 --- a/homework/1/rename.js +++ b/homework/1/rename.js @@ -5,16 +5,21 @@ const traverse = require('../../common/traverse'); function transform(root, originName, targetName) { // 遍历所有节点 return traverse((node, ctx, next) => { - switch(node.type) { + switch(node.type) + { case 'VariableDeclaration': - let variableDeclarator = node.type.declarations[0]; - if (variableDeclarator.name === originName) { - variableDeclarator.name = targetName; + { + let variableDeclarator = node.declarations[0]; + if (variableDeclarator.id.name === originName) { + variableDeclarator.id.name = targetName; + } } break; case 'FunctionDeclaration': - if (node.id.name === originName) { - node.id.name = targetName; + { + if (node.id.name === originName) { + node.id.name = targetName; + } } break; // case 'ObjectExpression': @@ -23,16 +28,20 @@ function transform(root, originName, targetName) { // } // break; case 'BinaryExpression': - if (node.left.name === originName) { - node.left.name = targetName; - } - else if (node.right.name === originName) { - node.right.name = targetName; + { + if (node.left.name === originName) { + node.left.name = targetName; + } + if (node.right.name === originName) { + node.right.name = targetName; + } } break; - case 'MemberExpression' && !node.object.object: - if (node.object.name === originName) { - node.object.name === targetName; + case 'MemberExpression': + { + if (!node.object.object && node.object.name === originName) { + node.object.name = targetName; + } } break; } @@ -41,7 +50,7 @@ function transform(root, originName, targetName) { } function rename(code, originName, targetName) { - const ast = acorn.parse(code, { + const ast = acorn.parse(code, { ecmaVersion: 5, }) return astring.generate(transform(ast, originName, targetName)) diff --git a/homework/1/rename.test.js b/homework/1/rename.test.js index dd393fc..c0f7d1d 100644 --- a/homework/1/rename.test.js +++ b/homework/1/rename.test.js @@ -1,7 +1,6 @@ const acorn = require('acorn'); const traverse = require('../../common/traverse') const rename = require('./rename') -const sourceCode = require('./sourceCode') function toStandard(code) { const root = acorn.parse(code, { ecmaVersion: 5 }) diff --git a/homework/1/sourceCode.js b/homework/1/sourceCode.js index 20fa7f9..a18dd66 100644 --- a/homework/1/sourceCode.js +++ b/homework/1/sourceCode.js @@ -1,5 +1,4 @@ function foo() { - const a = 1; foo: while(true) { var foo = { foo: foo.foo.foo[foo + foo] diff --git a/homework/1/test.js b/homework/1/test.js new file mode 100644 index 0000000..0f2b659 --- /dev/null +++ b/homework/1/test.js @@ -0,0 +1,7 @@ +const rename = require('./rename') +const sourceCode = require('./sourceCode') + + +let result = rename(sourceCode, 'foo', 'bar'); + +console.log(result); \ No newline at end of file From 1d79a10eddd67bb1dafbc21db1e56494c3c3d62c Mon Sep 17 00:00:00 2001 From: deastre Date: Thu, 27 Jan 2022 16:56:17 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BA=86=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/2/eval.js | 139 +++++++++++++++++++++++++++++++++++++++ homework/2/eval.test.js | 31 +++++++++ homework/2/sourceCode.js | 3 + homework/2/test.js | 6 ++ 4 files changed, 179 insertions(+) create mode 100644 homework/2/eval.js create mode 100644 homework/2/eval.test.js create mode 100644 homework/2/sourceCode.js create mode 100644 homework/2/test.js diff --git a/homework/2/eval.js b/homework/2/eval.js new file mode 100644 index 0000000..b31f3f6 --- /dev/null +++ b/homework/2/eval.js @@ -0,0 +1,139 @@ +const acorn = require('acorn'); + +function evaluate(node, env) { + switch (node.type) + { + case 'Literal': + return node.value; + case 'Identifier': + return env[node.name]; + case 'BinaryExpression': + { + 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 'instanceof': + return evaluate(node.left, env) instanceof evaluate(node.right, env); + } + } + case 'LogicalExpression': + { + switch(node.operator) + { + case '&&': + return evaluate(node.left, env) && evaluate(node.right, env); + case '||': + return evaluate(node.left, env) || evaluate(node.right, env); + } + } + case 'UnaryExpression': { + switch(node.operator) + { + case '!': + return !evaluate(node.argument, env); + case '~': + return ~evaluate(node.argument, env); + case 'typeof': + return typeof evaluate(node.argument, env); + } + } + case 'UpdateExpression': { + const flag = node.prefix; + const argument = node.argument; + switch(node.operator) + { + case '++': + { + if (flag) { + return ++evaluate(argument, env); + } else { + return evaluate(argument, env)++; + } + } + case '--': + { + if (flag) { + return --evaluate(argument, env); + } else { + return evaluate(argument, env)--; + } + } + } + } + case 'ConditionalExpression': + { + if (evaluate(node.test, env)) { + return evaluate(node.consequent, env); + } else { + return evaluate(node.alternate, env); + } + } + case 'CallExpression': { + const c = evaluate(node.callee, env); + const args = node.arguments.map(arg => evaluate(arg, env)); + return c(...args); + } + case 'ObjectExpression': + return node.properties.reduce((preProperty, curProperty) => ( + {...preProperty, [curProperty.key.name] : evaluate(curProperty.value, env)}), {}); + case 'ArrayExpression': + return node.elements.map((element) => evaluate(element, env)); + case 'BlockStatement': + return node.body.reduce((preStatement, curStatement) => evaluate(curStatement, env), undefined); + case 'ArrowFunctionStatement': + return (...args) => { + return evaluate(node.body, { + ...env, + ...node.params.reduce((preParam, curParam, curIdx)=>({...preParam, [curParam.name]: args[curIdx]}), {}) + }) + } + } + + 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) +} + +module.exports = customerEval \ No newline at end of file diff --git a/homework/2/eval.test.js b/homework/2/eval.test.js new file mode 100644 index 0000000..ff60206 --- /dev/null +++ b/homework/2/eval.test.js @@ -0,0 +1,31 @@ +const customerEval = require('./eval'); + +function throwError() { + throw new Error('error') +} + +const baseEnv = { throwError } + +test('测试表达式 - 初级挑战', () => { + const sourceCodeList = [ + '1 - 2 + 3 * 10 / 5', + 'true && true || throwError()', + 'false && throwError() || true', + 'true ? 1 : throwError()', + 'false ? throwError() : 2', + '({ a: 1 + 2 + 3, b: 4 + 5, c: [1, 2, 3] })', + ]; + for (sourceCode of sourceCodeList) { + expect(customerEval(sourceCode, baseEnv)).toStrictEqual(eval(sourceCode)); + } +}) + +test('测试表达式 - 终极挑战', () => { + const sourceCode = '(f => (x => f (y => x (x) (y))) (x => f (y => x(x)(y))))(f => n => n <= 1 ? n : n * f(n - 1))(10)' + expect(customerEval(sourceCode)).toStrictEqual(eval(sourceCode)); +}) + +test('测试表达式 - 超纲挑战(下节课会讲)', () => { + const sourceCode = '(n => ((x => n = x)(n + 2), (y => n + y)(3)))(1)' + expect(customerEval(sourceCode)).toStrictEqual(eval(sourceCode)); +}) \ No newline at end of file diff --git a/homework/2/sourceCode.js b/homework/2/sourceCode.js new file mode 100644 index 0000000..e834f2c --- /dev/null +++ b/homework/2/sourceCode.js @@ -0,0 +1,3 @@ +const foo = '({ a: 1 + 2 + 3, b: 4 + 5, c: [1, 2, 3] })'; + +module.exports = foo; \ No newline at end of file diff --git a/homework/2/test.js b/homework/2/test.js new file mode 100644 index 0000000..fdde79d --- /dev/null +++ b/homework/2/test.js @@ -0,0 +1,6 @@ +const customerEval = require('./eval') + + +const result = customerEval('((x,y)=>x+y)(2,3)'); + +console.log(result); \ No newline at end of file