Skip to content
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
956 changes: 944 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
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