Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

王铭沣 homework-3 #60

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
947 changes: 935 additions & 12 deletions final/eval.js

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions final/hoisting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const Scope = require('./scope')
const eval = require('./eval')
// 由于引入 evaluate 出现了些问题,所以该文件暂时废弃
// const evaluate = eval.evaluate
console.log(eval, 'eval');

function hoisting(node, scope) {
switch (node.type) {
case 'Program':{
node.body.forEach(v => {
if (v.type === 'VariableDeclaration') hoisting(v,scope)
})
return
}
case 'FunctionExpression': {
node.body.body.forEach(v => {
if (v.type === 'VariableDeclaration') hoisting(v,scope)
if (v.type === 'FunctionDeclaration') hoisting(v,scope)
})
return
}
case 'VariableDeclaration':{
node.declarations.forEach(v => {
if (node.kind === 'var')
scope.declare(node.kind, v.id.name)
})
return
}
case 'FunctionDeclaration':{
let f = function (...args) {
let childScope = new Scope({}, scope, 'function')
node.params.map((v, index) => {
childScope.variables[v.name] = args[index]
childScope.isDefine[v.name] = 'let'
})
return evaluate(node.body, childScope)
}
Object.defineProperty(f, 'name', { value: node.id.name })
Object.defineProperty(f, 'length', { value: node.params.length })
scope.declare('var', node.id.name)
scope.set(node.id.name, f)
return f
}
}
}

module.exports = hoisting
110 changes: 110 additions & 0 deletions final/scope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
class Scope {
constructor(initial, parent = null, type = 'global') {
this.isDefine = {}
this.variables = {}
for (let key in initial) {
this.variables[key] = initial[key]
this.isDefine[key] = 'context'
}
this.type = type // function | block
this.parent = parent
}
declare(kind, name) { // var | const | let
if (this.findThis(name) === 'context' && kind === 'let') throw new Error()
if (this.isDefine[name] === 'context') {
if (kind === 'let' || kind === 'const') return new Error()
}
if (this.isDefine[name] === 'context' && this.type === 'global') return false
if (kind === 'const') kind = '-const'
switch (this.type) {
case 'global':
this.isDefine[name] = kind
break;
case 'function':
this.isDefine[name] = kind
break;
case 'block':
if (kind === 'var') { // 在块级作用域中var,得交给上一级作用域
this.parent.declare(kind, name)
} else {
this.isDefine[name] = kind
}
break;
}
return true
}
find(name) {
if (this.isDefine[name]) {
return this.isDefine[name]
} else {
if (this.parent === null) {
if (name === 'this') return {}
// return false
return 'notDefined'
} else {
return this.parent.find(name)
}
}
}
findThis(name) {
// 特判一下这个name是否和context冲突
// 说实话,我也觉得写得太丑了这里
if (this.type === 'block' || this.type === 'function') {
if (!(this.type === 'block' && this.parent && this.parent.type === 'global')) {
return 'notDefined'
}
}
if (this.isDefine[name]) {
return this.isDefine[name]
} else {
if (this.parent === null) {
if (name === 'this') return {}
return 'notDefined'
} else {
return this.parent.find(name)
}
}
}
get(name) {
if (this.isDefine[name]) {
return this.variables[name]
} else {
if (this.parent === null) {
if (name === 'this') return undefined
throw new Error('not define: ' + name)
return undefined
} else {
return this.parent.get(name)
}
}
}
set(name, value) {
if (this.isDefine[name]) {
if (this.isDefine[name] === '-const') {
this.isDefine[name] = 'const'
this.variables[name] = value
} else
if (this.isDefine[name] === 'context') {
return
// throw new TypeError('context can not be rewrite')
} else
if (this.isDefine[name] === 'const') {
throw new TypeError('Assignment to constant variable')
// return new TypeError('Assignment to constant variable')
} else {
this.variables[name] = value
}
} else {
if (this.parent === null) {
return {
type: 'notDefine'
}
throw new Error('error:not declare ' + name)
} else {
this.parent.set(name, value)
}
}
}
}

module.exports = Scope
1 change: 0 additions & 1 deletion final/test/arrow-function/new.target.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ module.exports = {target: target, Person: Person};
`,
scope
);

t.true(target === Person);
});
1 change: 1 addition & 0 deletions final/test/async-function/async-function.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ module.exports = get;

t.deepEqual(result, "data");
});

2 changes: 0 additions & 2 deletions final/test/generator/GeneratorFunctionExpression.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ function* get(){
var a = 123;
yield a;
}

module.exports = get;
`,
scope
);

const generator = get();
t.deepEqual(generator.next(), { done: false, value: 123 });
t.deepEqual(generator.next(), { done: true, value: undefined });
Expand Down
18 changes: 18 additions & 0 deletions final/test/homework-3.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const test = require("ava");
const { customEval, Scope } = require("../eval");


test("测试声明与控制流 - 超纲挑战", t => {
const scope = new Scope();

const sourceCode =
'(() => { loop1: for (var i = 0; i < 3; i++) { loop2: for (var m = 1; m < 3; m++) { if (m % 2 === 0) { break loop1; } loop3: for (var y = 1; y < 10; y++) { if (y % 5 === 0) { continue loop2; } } } } return { i, m, y } })()';

const ans = customEval(
`
module.exports = (() => { loop1: for (var i = 0; i < 3; i++) { loop2: for (var m = 1; m < 3; m++) { if (m % 2 === 0) { break loop1; } loop3: for (var y = 1; y < 10; y++) { if (y % 5 === 0) { continue loop2; } } } } return { i, m, y } })()
`,
scope
);
t.deepEqual(ans, eval(sourceCode))
});
40 changes: 20 additions & 20 deletions final/test/let-const/VariableDeclaration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,23 +127,23 @@ module.exports = {name: name}
});

// FIXME: let and const should have block scope
// test("block scope", t => {
// const scope = new Scope({});

// const { a, b } = customEval(
// `
// var a = 1;
// var b;
// {
// // should have block scope
// const a = 2;
// b =a;
// }
// module.exports = {a:a, b:b}
// `,
// scope
// );

// t.deepEqual(a, 1);
// t.deepEqual(b, 2);
// });
test("block scope", t => {
const scope = new Scope({});

const { a, b } = customEval(
`
var a = 1;
var b;
{
// should have block scope
const a = 2;
b =a;
}
module.exports = {a:a, b:b}
`,
scope
);

t.deepEqual(a, 1);
t.deepEqual(b, 2);
});
18 changes: 7 additions & 11 deletions final/test/stack-track.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ const { customEval, Scope } = require('../eval');
test("not defined", t => {
const scope = new Scope();

try {
t.throws(function () {
customEval(
`function get(){
var a = 123;
console.log(b);
}

get();`,
var a = 123;
console.log(b);
}
get();`,
scope
);
t.fail("it should throw an error");
} catch (err) {
// ignore
}
});
})
});
4 changes: 2 additions & 2 deletions final/test/switch/SwitchStatement.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ module.exports = t;
scope
);

// t.deepEqual(func(1), [1, 2, 3, 4, 5]);
// t.deepEqual(func(2), [1, 2, 3, 4, 5]);
t.deepEqual(func(1), [1, 2, 3, 4, 5]);
t.deepEqual(func(2), [1, 2, 3, 4, 5]);

// the will loop will be continue
t.deepEqual(func(0), []);
Expand Down
29 changes: 28 additions & 1 deletion homework/1/rename.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,39 @@ const acorn = require('acorn');
const astring = require('astring');
const traverse = require('../../common/traverse');



function transform(root, originName, targetName) {

// 替换变量名
function replace(name){
if (name === originName) return targetName
}

// 遍历所有节点
return traverse((node, ctx, next) => {

// TODO: 作业代码写在这里
if (node.type === 'xxx') {
if (node.type === 'VariableDeclarator') {
node.id.name = replace(node.id.name)
}
if (node.type === 'MemberExpression') {
if (node.object.type === 'Identifier') {
node.object.name = replace(node.object.name)
}
}
if (node.type === 'BinaryExpression') {
if (node.left.type === 'Identifier') {
node.left.name = replace(node.left.name)
}
if (node.right.type === 'Identifier') {
node.right.name = replace(node.right.name)
}
}
if (node.type === 'FunctionDeclaration') {
if (node.id.type === 'Identifier') {
node.id.name = replace(node.id.name)
}
}

// 继续往下遍历
Expand Down
Loading