Skip to content

Commit 73f4559

Browse files
committed
es2015 VariableDeclaration
1 parent a1c28d1 commit 73f4559

File tree

4 files changed

+190
-140
lines changed

4 files changed

+190
-140
lines changed

src/evaluate/es2015.ts

Lines changed: 118 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -5,122 +5,50 @@ import Signal from '../signal';
55
import Environment from '../environment';
66
import { slice } from '../tool';
77

8+
export function VariableDeclaration(env: Environment<ESTree.VariableDeclaration>) {
9+
for (const declarator of env.node.declarations) {
10+
const v = declarator.init ? env.evaluate(declarator.init) : undefined;
11+
declarePatternValue({ node: declarator.id, v, env, scope: env.scope, kind: env.node.kind });
12+
}
13+
}
14+
815
export function ObjectExpression(env: Environment<ESTree.ObjectExpression>) {
916
const obj: { [key: string]: any } = {};
1017

11-
for (const property of env.node.properties) {
18+
for (const prop of env.node.properties) {
1219
let key: string;
13-
if (!property.computed) {
14-
if (property.key.type === 'Literal') {
15-
key = property.key.value as string;
16-
} else if (property.key.type === 'Identifier') {
17-
key = property.key.name;
20+
if (!prop.computed) {
21+
if (prop.key.type === 'Identifier') {
22+
key = prop.key.name;
1823
} else {
19-
throw new Error(`evil-eval: [ObjectExpression] Unsupported property key type "${property.key.type}"`);
24+
key = (<ESTree.Literal>prop.key).value as string;
2025
}
26+
2127
} else {
22-
if (property.key.type === 'Identifier') {
23-
const value = env.scope.get(property.key.name);
28+
if (prop.key.type === 'Identifier') {
29+
const value = env.scope.get(prop.key.name);
2430
key = value.v;
2531
} else {
26-
key = env.evaluate(property.key);
32+
key = env.evaluate(prop.key);
2733
}
2834
}
2935

30-
const value = env.evaluate(property.value);
31-
if (property.kind === 'init') {
36+
const value = env.evaluate(prop.value);
37+
if (prop.kind === 'init') {
3238
obj[key] = value;
33-
} else if (property.kind === 'get') {
39+
} else if (prop.kind === 'get') {
3440
Object.defineProperty(obj, key, { get: value });
35-
} else if (property.kind === 'set') {
41+
} else if (prop.kind === 'set') {
3642
Object.defineProperty(obj, key, { set: value });
3743
} else {
38-
throw new Error(`evil-eval: [ObjectExpression] Unsupported property kind "${property.kind}"`);
44+
throw new Error(`evil-eval: [ObjectExpression] Unsupported property kind "${prop.kind}"`);
3945
}
4046
}
4147

4248
return obj;
4349
}
4450

45-
interface AssignPatternValueOptions<T> {
46-
node: T;
47-
v: any;
48-
env: Environment<ESTree.Node>;
49-
scope: Scope;
50-
kind?: 'var' | 'let' | 'const';
51-
}
52-
53-
function assignPatternValue(options: AssignPatternValueOptions<ESTree.Pattern>) {
54-
if (options.node.type === 'ObjectPattern') {
55-
assignObjectPatternValue(<AssignPatternValueOptions<ESTree.ObjectPattern>>options);
56-
} else if (options.node.type === 'ArrayPattern') {
57-
assignArrayPatternValue(<AssignPatternValueOptions<ESTree.ArrayPattern>>options);
58-
} else {
59-
throw new Error(`evil-eval`);
60-
}
61-
}
62-
function assignObjectPatternValue({ node, v: vObj, env, scope, kind }: AssignPatternValueOptions<ESTree.ObjectPattern>) {
63-
for (const prop of node.properties) {
64-
let key: string;
65-
if (!prop.computed) {
66-
if (prop.key.type === 'Literal') {
67-
key = prop.key.value as string;
68-
} else {
69-
key = (<ESTree.Identifier>prop.key).name;
70-
}
71-
} else {
72-
key = env.evaluate(prop.key);
73-
}
74-
75-
const v = vObj[key];
76-
77-
if (prop.value.type === 'Identifier') {
78-
if (v === null || v === undefined) {
79-
throw new TypeError(`Cannot destructure property \`${key}\` of 'undefined' or 'null'.`)
80-
}
81-
if (kind) {
82-
scope.declare(prop.value.name, v, kind);
83-
} else {
84-
const value = scope.get(prop.value.name, true);
85-
value.v = v;
86-
}
87-
} else {
88-
assignPatternValue({ node: prop.value, v, env, scope, kind });
89-
}
90-
}
91-
}
92-
93-
function assignArrayPatternValue({ node, v: vArr, env, scope, kind }: AssignPatternValueOptions<ESTree.ArrayPattern>) {
94-
for (let i = 0, l = node.elements.length; i < l; i++) {
95-
const element = node.elements[i];
96-
97-
let v = vArr[i];
98-
if (element.type === 'Identifier') {
99-
if (v === undefined) {
100-
throw new TypeError(`Cannot read property 'Symbol(Symbol.iterator)' of undefined`);
101-
} else if (v === null) {
102-
throw new TypeError(`Cannot read property 'Symbol(Symbol.iterator)' of object`);
103-
}
104-
if (kind) {
105-
scope.declare(element.name, v, kind);
106-
} else {
107-
const value = scope.get(element.name, true);
108-
value.v = v;
109-
}
110-
} else if (element.type === 'RestElement') {
111-
const name = (<ESTree.Identifier>element.argument).name;
112-
v = slice.call(vArr, i);
113-
if (kind) {
114-
scope.declare(name, v, kind);
115-
} else {
116-
const value = scope.get(name, true);
117-
value.v = v;
118-
}
119-
} else {
120-
assignPatternValue({ node: element, v, env, scope, kind });
121-
}
122-
}
123-
}
51+
// -- es2015 new feature --
12452

12553
export function ForOfStatement(env: Environment<ESTree.ForOfStatement>) {
12654
const { left, right, body } = env.node;
@@ -133,23 +61,14 @@ export function ForOfStatement(env: Environment<ESTree.ForOfStatement>) {
13361
}
13462

13563
const id = left.declarations[0].id;
136-
if (id.type === 'Identifier') {
137-
// for (let it of list);
138-
scope.declare(id.name, v, left.kind);
139-
} else {
140-
// for (let { id } of list);
141-
assignPatternValue({ node: id, v, env, scope, kind: left.kind });
142-
}
64+
// for (let it of list);
65+
// for (let { id } of list);
66+
declarePatternValue({ node: id, v, env, scope, kind: left.kind });
14367

14468
} else {
145-
if (left.type === 'Identifier') {
146-
// for (if of list);
147-
const value = scope.get(left.name, true);
148-
value.v = v;
149-
} else {
150-
// for ({ id } of list);
151-
assignPatternValue({ node: left, v, env, scope });
152-
}
69+
// for (it of list);
70+
// for ({ id } of list);
71+
declarePatternValue({ node: left, v, env, scope });
15372
}
15473

15574
const signal: Signal = env.evaluate(body, { scope });
@@ -260,3 +179,93 @@ export function ExportDefaultDeclaration(env: Environment<ESTree.ExportDefaultDe
260179
export function ExportAllDeclaration(env: Environment<ESTree.ExportAllDeclaration>) {
261180
throw new Error(`evil-eval: "${env.node.type}" not implemented`);
262181
}
182+
183+
// -- private --
184+
185+
interface DeclarePatternValueOptions<T> {
186+
node: T;
187+
v: any;
188+
env: Environment<ESTree.Node>;
189+
scope: Scope;
190+
kind?: 'var' | 'let' | 'const';
191+
}
192+
193+
function declarePatternValue(options: DeclarePatternValueOptions<ESTree.Pattern>) {
194+
if (options.node.type === 'Identifier') {
195+
if (options.kind) {
196+
options.scope.declare(options.node.name, options.v, options.kind);
197+
} else {
198+
const value = options.scope.get(options.node.name, true);
199+
value.v = options.v;
200+
}
201+
} else if (options.node.type === 'ObjectPattern') {
202+
declareObjectPatternValue(<DeclarePatternValueOptions<ESTree.ObjectPattern>>options);
203+
} else if (options.node.type === 'ArrayPattern') {
204+
declareArrayPatternValue(<DeclarePatternValueOptions<ESTree.ArrayPattern>>options);
205+
} else {
206+
throw new Error(`evil-eval: Not support to declare pattern value of node type "${options.node.type}"`);
207+
}
208+
}
209+
210+
function declareObjectPatternValue({ node, v: vObj, env, scope, kind }: DeclarePatternValueOptions<ESTree.ObjectPattern>) {
211+
for (const prop of node.properties) {
212+
let key: string;
213+
if (!prop.computed) {
214+
if (prop.key.type === 'Identifier') {
215+
key = prop.key.name;
216+
} else {
217+
key = (<ESTree.Literal>prop.key).value as string;
218+
}
219+
} else {
220+
key = env.evaluate(prop.key);
221+
}
222+
223+
const v = vObj[key];
224+
225+
if (prop.value.type === 'Identifier') {
226+
if (v === null || v === undefined) {
227+
throw new TypeError(`Cannot destructure property \`${key}\` of 'undefined' or 'null'.`)
228+
}
229+
if (kind) {
230+
scope.declare(prop.value.name, v, kind);
231+
} else {
232+
const value = scope.get(prop.value.name, true);
233+
value.v = v;
234+
}
235+
} else {
236+
declarePatternValue({ node: prop.value, v, env, scope, kind });
237+
}
238+
}
239+
}
240+
241+
function declareArrayPatternValue({ node, v: vArr, env, scope, kind }: DeclarePatternValueOptions<ESTree.ArrayPattern>) {
242+
for (let i = 0, l = node.elements.length; i < l; i++) {
243+
const element = node.elements[i];
244+
245+
let v = vArr[i];
246+
if (element.type === 'Identifier') {
247+
if (v === undefined) {
248+
throw new TypeError(`Cannot read property 'Symbol(Symbol.iterator)' of undefined`);
249+
} else if (v === null) {
250+
throw new TypeError(`Cannot read property 'Symbol(Symbol.iterator)' of object`);
251+
}
252+
if (kind) {
253+
scope.declare(element.name, v, kind);
254+
} else {
255+
const value = scope.get(element.name, true);
256+
value.v = v;
257+
}
258+
} else if (element.type === 'RestElement') {
259+
const name = (<ESTree.Identifier>element.argument).name;
260+
v = slice.call(vArr, i);
261+
if (kind) {
262+
scope.declare(name, v, kind);
263+
} else {
264+
const value = scope.get(name, true);
265+
value.v = v;
266+
}
267+
} else {
268+
declarePatternValue({ node: element, v, env, scope, kind });
269+
}
270+
}
271+
}

src/evaluate/es5.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as ESTree from 'estree';
22
import Scope from '../scope';
3-
import { Value } from '../value';
3+
import { Value, createMemberValue } from '../value';
44
import Signal from '../signal';
55
import Environment from '../environment';
66
import * as Tool from '../tool';
@@ -352,7 +352,7 @@ const UnaryExpressionOperatorEvaluateMap = {
352352
const argument = env.node.argument;
353353
if (argument.type === 'MemberExpression') {
354354
const obj = env.evaluate(argument.object);
355-
const name = Tool.getPropertyName(argument, env);
355+
const name = getPropertyName(argument, env);
356356
return delete obj[name];
357357
} else if (argument.type === 'Identifier') {
358358
return false;
@@ -370,7 +370,7 @@ const UpdateExpressionOperatorEvaluateMap = {
370370
'--': (value: Value, prefix: boolean) => prefix ? --value.v : value.v--
371371
};
372372
export function UpdateExpression(env: Environment<ESTree.UpdateExpression>) {
373-
const value = Tool.getIdentifierOrMemberExpressionValue(env.node.argument, env);
373+
const value = getIdentifierOrMemberExpressionValue(env.node.argument, env);
374374
return UpdateExpressionOperatorEvaluateMap[env.node.operator](value, env.node.prefix);
375375
}
376376

@@ -421,7 +421,7 @@ const AssignmentExpressionOperatorEvaluateMap = {
421421
};
422422
export function AssignmentExpression(env: Environment<ESTree.AssignmentExpression>) {
423423
const node = env.node;
424-
const value = Tool.getIdentifierOrMemberExpressionValue(node.left, env, node.operator === '=');
424+
const value = getIdentifierOrMemberExpressionValue(node.left, env, node.operator === '=');
425425
return AssignmentExpressionOperatorEvaluateMap[node.operator](value, env.evaluate(node.right));
426426
}
427427

@@ -437,7 +437,7 @@ export function LogicalExpression(env: Environment<ESTree.LogicalExpression>) {
437437

438438
export function MemberExpression(env: Environment<ESTree.MemberExpression>) {
439439
const obj = env.evaluate(env.node.object);
440-
const name = Tool.getPropertyName(env.node, env);
440+
const name = getPropertyName(env.node, env);
441441
return obj[name];
442442
}
443443

@@ -472,3 +472,27 @@ export function SequenceExpression(env: Environment<ESTree.SequenceExpression>)
472472
}
473473
return last;
474474
}
475+
476+
// -- private --
477+
478+
export function getPropertyName(node: ESTree.MemberExpression, ctx: Environment<ESTree.Node>): string {
479+
if (node.computed) {
480+
return ctx.evaluate(node.property);
481+
} else {
482+
return (<ESTree.Identifier>node.property).name;
483+
}
484+
}
485+
486+
export function getIdentifierOrMemberExpressionValue(node: ESTree.Pattern | ESTree.Expression, env: Environment<ESTree.Node>, assignment = false) {
487+
if (node.type === 'Identifier') {
488+
return env.scope.get(node.name, assignment);
489+
490+
} else if (node.type === 'MemberExpression') {
491+
const obj = env.evaluate(node.object);
492+
const name = getPropertyName(node, env);
493+
return createMemberValue(obj, name);
494+
495+
} else {
496+
throw new Error(`evil-eval: Not support to get value of node type "${node.type}"`);
497+
}
498+
}

src/tool.ts

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,2 @@
1-
import * as ESTree from 'estree';
2-
import Environment from './environment';
3-
import { Value, createMemberValue } from './value';
4-
5-
export function getPropertyName(node: ESTree.MemberExpression, ctx: Environment<ESTree.Node>): string {
6-
if (node.computed) {
7-
return ctx.evaluate(node.property);
8-
} else {
9-
return (<ESTree.Identifier>node.property).name;
10-
}
11-
}
12-
13-
export function getIdentifierOrMemberExpressionValue(node: ESTree.Pattern | ESTree.Expression, env: Environment<ESTree.Node>, assignment = false) {
14-
if (node.type === 'Identifier') {
15-
return env.scope.get(node.name, assignment);
16-
17-
} else if (node.type === 'MemberExpression') {
18-
const obj = env.evaluate(node.object);
19-
const name = getPropertyName(node, env);
20-
return createMemberValue(obj, name);
21-
22-
} else {
23-
throw new Error(`evil-eval: Not support to get value of node type "${node.type}"`);
24-
}
25-
}
26-
271
export const slice = Array.prototype.slice;
282
export const hop = Object.prototype.hasOwnProperty;

0 commit comments

Comments
 (0)