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

张昱 Homework3 #45

Open
wants to merge 7 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
7 changes: 5 additions & 2 deletions homework/1/rename.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ function transform(root, originName, targetName) {
return traverse((node, ctx, next) => {

// TODO: 作业代码写在这里
if (node.type === 'xxx') {
for (const key in node) {
if (node[key].type !== 'Identifier' || node[key].name !== originName) continue;
if (['property', 'label', 'key'].includes(key)) continue;
node[key].name = targetName
}

// 继续往下遍历
return next(node, ctx)
})(root);
Expand Down
62 changes: 61 additions & 1 deletion homework/2/eval.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,67 @@ 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': {
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 'in': return evaluate(node.left, env) in 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 'ConditionalExpression': {
if (evaluate(node.test, env)) return evaluate(node.consequent, env);
else return evaluate(node.alternate, env);
}
case 'ObjectExpression': {
return node.properties.reduce((argsEnv, property) => ({ ...argsEnv, [property.key.name]: evaluate(property.value, env) }), {});
}
case 'ArrayExpression': {
return node.elements.map(el => evaluate(el, env));
}
case 'ArrowFunctionExpression': {
const args = node.params.map(e => e.name);
return function (...args) {
const argsEnv = node.params.reduce((argsEnv, param, idx) => ({ ...argsEnv, [param.name]: args[idx] }), { ...env });
return evaluate(node.body, { ...argsEnv });
};
}
case 'CallExpression': {
return evaluate(node.callee, env)(...node.arguments.map(arg => evaluate(arg, env)));
}
case 'SequenceExpression': {
return node.expressions.reduce((_, expression) => evaluate(expression, env));
}
case 'AssignmentExpression': {
env[node.left.name] = evaluate(node.right, env);
return evaluate(node.right, env);
}
}

throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`);
Expand Down
16 changes: 8 additions & 8 deletions homework/3/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 作业3 —— 实现基本完整的 ES5 解释器
## 作业要求:
1. `npm install` 安装依赖;
2. 补全 `eval.js` 里面的代码中的 `evaluate(node, env)` 函数,使其能将传入的表达式 AST 节点运算出结果;
3. `yarn test-homework-3` 可以执行本作业的测试用例,是作业通过 `eval.test.js` 中的测试用例 ;
4. 要求实现 ES6 let、 const 和箭头函数语法
5. 选择实现 break / continue / lable 语法
# 作业3 —— 实现基本完整的 ES5 解释器

## 作业要求:
1. `npm install` 安装依赖;
2. 补全 `eval.js` 里面的代码中的 `evaluate(node, env)` 函数,使其能将传入的表达式 AST 节点运算出结果;
3. `yarn test-homework-3` 可以执行本作业的测试用例,是作业通过 `eval.test.js` 中的测试用例 ;
4. 要求实现 ES6 let、 const 和箭头函数语法
5. 选择实现 break / continue / lable 语法
262 changes: 261 additions & 1 deletion homework/3/eval.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,275 @@
const acorn = require('acorn');
const Scope = require('./scope');

function evaluate(node, env) {
switch (node.type) {
case 'Literal':
// TODO: 补全作业代码
// TODO: 补全作业代码
return node.value;
case 'Identifier':
return env.get(node.name);
// case 'RegExpLiteral':
case 'Program':
const arr = node.body.map(e => e ? evaluate(e, env) : undefined);
return arr ? arr[arr.length - 1] : undefined;
case 'ExpressionStatement':
return evaluate(node.expression, env);
// case 'Directive':
case 'BlockStatement': {
const scope = new Scope('block', env);
let ret;
for (const e of node.body) {
ret = evaluate(e, scope);
if (ret && ret.kind === 'return') return ret.value;
if (ret && ret.kind === 'break') return ret;
if (ret && ret.kind === 'continue') return;
}
return ret;
}
case 'EmptyStatement':
return;
case 'DebuggerStatement':
return { kind: 'debugger' };
case 'WithStatement':
return { kind: 'with' };
case 'ReturnStatement':
return { kind: 'return', value: evaluate(node.argument, env) };
case 'LabeledStatement':
return { kind: 'label' };
case 'BreakStatement':
return { kind: 'break' };
case 'ContinueStatement':
return { kind: 'continue' };
case 'IfStatement': {
const scope = new Scope('block', env);
if (evaluate(node.test, scope)) {
return evaluate(node.consequent, scope);
}
return node.alternate ? evaluate(node.alternate, scope) : null;
}
case 'SwitchStatement': {
const disc = evaluate(node.discriminant, env);
const cases = node.cases;
let ret;
for (const el of cases) {
if (evaluate(el.test, env) === disc) {
for (const e of el.consequent) {
ret = evaluate(e, env);
if (ret && ret.kind === 'continue') return 'continue';
}
}
}
return ret;
}
case 'ThrowStatement':
throw evaluate(node.argument, env);
case 'TryStatement': {
let ret;
try {
ret = evaluate(node.block, env);
} catch (error) {
const param = node.handler.param.name;
env.declare('let', param);
env.set(param, error);
ret = evaluate(node.handler, env);
} finally {
if (node.finalizer) evaluate(node.finalizer, env);
}
return ret;
}
case 'CatchClause':
return evaluate(node.body, env);
case 'WhileStatement': {
const scope = new Scope('block', env);
let ret;
while (evaluate(node.test, scope)) {
ret = evaluate(node.body, scope);
}
return ret;
}
case 'DoWhileStatement': {
let ret;
do {
ret = evaluate(node.body, env);
if (ret && ret.kind === 'break') break;
if (ret && ret.kind === 'return') return ret.value;
} while (evaluate(node.test, env));
return;
}
case 'ForStatement': {
const scope = new Scope('block', env);
evaluate(node.init, scope);
let ret;
while (evaluate(node.test, scope)) {
ret = evaluate(node.body, scope);
evaluate(node.update, scope);
}
return ret;
}
// case 'ForInStatement':
case 'FunctionDeclaration': {
env.declare('const', node.id.name);
return env.set(node.id.name, function (...params) {
const scope = new Scope('function', env);
const args = node.params.map(e => e.name);
args.forEach((arg, idx) => {
scope.declare('let', arg);
scope.set(arg, params[idx]);
});
return evaluate(node.body, scope);
})
}
case 'VariableDeclaration':
return node.declarations.forEach(e => {
env.declare(node.kind, e.id.name);
if (e.init) env.set(e.id.name, evaluate(e.init, env));
});
// case 'ThisExpression':
case 'ArrayExpression':
return node.elements.map(el => evaluate(el, env));
case 'ObjectExpression':
return node.properties.reduce((obj, e) => ({ ...obj, [e.key.name]: evaluate(e.value, env) }), {});
// case 'Property':
// kind: "init" | "get" | "set";
case 'FunctionExpression': {
const scope = new Scope('function', env);
const args = node.params.map(e => e.name);
const fun = function (...params) {
args.forEach((arg, idx) => {
scope.declare('let', arg);
scope.set(arg, params[idx]);
});
return evaluate(node.body, scope);
};
if (node.id) {
scope.declare('const', node.id.name);
scope.set(node.id.name, fun);
}
return fun;
}
// case 'UnaryExpression':
// case 'UnaryOperator':
// "-" | "+" | "!" | "~" | "typeof" | "void" | "delete"
case 'UpdateExpression': {
const prefix = node.prefix;
const name = node.argument.name;
const value = env.get(name);
switch (node.operator) {
case '++':
env.set(name, value + 1);
return prefix ? value + 1 : value;
case '--':
env.set(name, value - 1);
return prefix ? value - 1 : value;
}
}
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 'in': return evaluate(node.left, env) in evaluate(node.right, env);
case 'instanceof': return evaluate(node.left, env) instanceof evaluate(node.right, env);
}
case 'AssignmentExpression':
if (node.left.type === 'Identifier') {
const param = node.left.name;
const value = env.get(param);
switch (node.operator) {
case '=': return env.set(param, evaluate(node.right, env));
case '+=': return env.set(param, value + evaluate(node.right, env));
case '-=': return env.set(param, value - evaluate(node.right, env));
case '*=': return env.set(param, value * evaluate(node.right, env));
case '/=': return env.set(param, value / evaluate(node.right, env));
case '%=': return env.set(param, value % evaluate(node.right, env));
}
} else {
const obj = node.left.object.name;
const prop = node.left.property.name;
let value = evaluate(node.right, env);
switch (node.operator) {
case '=': break;
case '+=': value += env.get(obj); break;
case '-=': value -= env.get(obj); break;
case '*=': value *= env.get(obj); break;
case '/=': value /= env.get(obj); break;
case '%=': value %= env.get(obj); break;
case '<<=': value <<= env.get(obj); break;
case '>>=': value >>= env.get(obj); break;
case '>>>=': value >>>= env.get(obj); break;
case '|=': value |= env.get(obj); break;
case '^=': value ^= env.get(obj); break;
case '&=': value &= env.get(obj); break;
}
env.set(obj, { ...env.get('obj'), [prop]: value });
return value;
}
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 'MemberExpression': {
const name = node.object.name;
const value = env.get(name);
const prop = node.property.name;
if (value instanceof Array) {
switch (prop) {
case 'push': return (i) => {
const ret = value.push(i);
env.set(name, value);
return ret;
};
case 'pop': return (i) => {
const ret = value.pop(i);
env.set(name, value);
return ret;
};
}
} else return value[prop];
}
case 'ConditionalExpression':
if (evaluate(node.test, env)) return evaluate(node.consequent, env);
else return evaluate(node.alternate, env);
case 'CallExpression':
return evaluate(node.callee, env)(...node.arguments.map(arg => evaluate(arg, env)));
// // case 'NewExpression':
case 'SequenceExpression':
return node.expressions.reduce((_, expression) => evaluate(expression, env));
case 'ArrowFunctionExpression': {
const args = node.params.map(e => e.name);
const scope = new Scope('function', env);
return (...params) => {
args.forEach((arg, idx) => {
scope.declare('let', arg);
scope.set(arg, params[idx]);
});
return evaluate(node.body, scope);
};
}
}

throw new Error(`Unsupported Syntax ${node.type} at Location ${node.start}:${node.end}`);
}

function customerEval(code, env = {}) {
env = new Scope();
const node = acorn.parse(code, 0, {
ecmaVersion: 6
})
Expand Down
Loading