From c0751a1c1b40197ba9362a0e41d3ae7868ac9355 Mon Sep 17 00:00:00 2001 From: baakqiu Date: Wed, 24 Jul 2019 17:13:21 +0800 Subject: [PATCH] [Update] Add rest element of object & array --- package-lock.json | 41 ++++++++++++++++------ src_/compile/pattern.ts | 78 ++++++++++++++++++++++++----------------- src_/jsvm/index.ts | 15 ++++++++ src_/share/const.ts | 6 +++- src_/state/index.ts | 2 +- src_/state/symbols.ts | 2 +- src_/test.ts | 4 +-- 7 files changed, 99 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 769ed66..05c9d96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2895,7 +2895,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2916,12 +2917,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2936,17 +2939,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3063,7 +3069,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3075,6 +3082,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3089,6 +3097,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3096,12 +3105,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3120,6 +3131,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3200,7 +3212,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3212,6 +3225,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3297,7 +3311,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3333,6 +3348,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3352,6 +3368,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3395,12 +3412,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src_/compile/pattern.ts b/src_/compile/pattern.ts index 96fc20b..6fa8abb 100644 --- a/src_/compile/pattern.ts +++ b/src_/compile/pattern.ts @@ -7,8 +7,9 @@ import { compilePattern } from './helper'; export function ObjectPattern(node: estree.ObjectPattern, state: State) { for (let i = 0; i < node.properties.length; i++) { const property = node.properties[i] + let value: estree.Pattern = (property as estree.AssignmentProperty).value + state.opCodes.push({ op: OP.COPY }) if (property.type === 'Property') { - state.opCodes.push({ op: OP.COPY }) // key const propKey = property.key if (propKey.type === 'Identifier') { @@ -17,28 +18,35 @@ export function ObjectPattern(node: estree.ObjectPattern, state: State) { compile(propKey, state) } state.opCodes.push({ op: OP.MGET }) - // value - const value = property.value - if (value.type === 'Identifier') { - if (state.symbols.type) { - state.opCodes.push({ op: OP.ALLOC, val: state.symbols.set(value.name).pointer }) + } else { // property.type === 'RestElement' + for (let j = 0; j < i; j++) { + const propKey = node.properties[j].key + if (propKey.type === 'Identifier') { + state.opCodes.push({ op: OP.LOADK, val: propKey.name }) } else { - state.opCodes.push({ op: OP.STORE, val: state.symbols.get(value.name).pointer }) + compile(propKey, state) } - } else if (value.type === 'MemberExpression') { - compile(value.object, state) - const property = value.property - if (property.type === 'Identifier') { - state.opCodes.push({ op: OP.LOADK, val: property.name }) - } else { // node.computed === true - compile(property, state) - } - state.opCodes.push({ op: OP.MSET }) + } + state.opCodes.push({ op: OP.REST, val: i, type: 'obj' }) + value = (property as any).argument + } + if (value.type === 'Identifier') { + if (state.symbols.type) { + state.opCodes.push({ op: OP.ALLOC, val: state.symbols.set(value.name).pointer }) } else { - compilePattern(value, state) + state.opCodes.push({ op: OP.STORE, val: state.symbols.get(value.name).pointer }) } - } else { // property.type === 'RestElement' - + } else if (value.type === 'MemberExpression') { + compile(value.object, state) + const prop = value.property + if (prop.type === 'Identifier') { + state.opCodes.push({ op: OP.LOADK, val: prop.name }) + } else { // node.computed === true + compile(prop, state) + } + state.opCodes.push({ op: OP.MSET }) + } else { + compilePattern(value, state) } } state.opCodes.push({ op: OP.POP }) @@ -48,28 +56,32 @@ export function ArrayPattern(node: estree.ArrayPattern, state: State) { for (let i = 0; i < node.elements.length; i++) { const element = node.elements[i] if (!element) continue // for the case: let [ , x] = [1, 2] + let value: estree.Pattern = element state.opCodes.push({ op: OP.COPY }) - state.opCodes.push({ op: OP.LOADK, val: i }) - state.opCodes.push({ op: OP.MGET }) - if (element.type === 'Identifier') { + if (element.type === 'RestElement') { + state.opCodes.push({ op: OP.REST, val: i, type: 'arr' }) + value = element.argument + } else { + state.opCodes.push({ op: OP.LOADK, val: i }) + state.opCodes.push({ op: OP.MGET }) + } + if (value.type === 'Identifier') { if (state.symbols.type) { - state.opCodes.push({ op: OP.ALLOC, val: state.symbols.set(element.name).pointer }) + state.opCodes.push({ op: OP.ALLOC, val: state.symbols.set(value.name).pointer }) } else { - state.opCodes.push({ op: OP.STORE, val: state.symbols.get(element.name).pointer }) + state.opCodes.push({ op: OP.STORE, val: state.symbols.get(value.name).pointer }) } - } else if (element.type === 'MemberExpression') { - compile(element.object, state) - const property = element.property - if (property.type === 'Identifier') { - state.opCodes.push({ op: OP.LOADK, val: property.name }) + } else if (value.type === 'MemberExpression') { + compile(value.object, state) + const prop = value.property + if (prop.type === 'Identifier') { + state.opCodes.push({ op: OP.LOADK, val: prop.name }) } else { // node.computed === true - compile(property, state) + compile(prop, state) } state.opCodes.push({ op: OP.MSET }) - } else if (element.type === 'RestElement') { - } else { - compilePattern(element, state) + compilePattern(value, state) } } state.opCodes.push({ op: OP.POP }) diff --git a/src_/jsvm/index.ts b/src_/jsvm/index.ts index 87a239b..a6d90ed 100644 --- a/src_/jsvm/index.ts +++ b/src_/jsvm/index.ts @@ -129,6 +129,21 @@ function step(state: State) { object[key] = value break } + case OP.REST: { + if (code.type === 'obj') { + const rmKeys = stack.splice(stack.length - code.val) + const object = Object.assign({}, stack.pop()) + for (let i = 0; i < rmKeys.length; i++) { + delete object[rmKeys[i]] + } + stack.push(object) + } else if (code.type === 'arr') { + stack.push(stack.pop().slice(code.val)) + } else { // code.type === 'func' + + } + break + } case OP.KOVS: { const kovs = [] if (code.val) { diff --git a/src_/share/const.ts b/src_/share/const.ts index 60c29ab..040855e 100644 --- a/src_/share/const.ts +++ b/src_/share/const.ts @@ -13,7 +13,9 @@ export enum OP { OBJ, // create object (val: array of property kinds) MGET, // get member of object (no val) MSET, // set member of object (no val) - DEL, // delete property () + REST, // get rest elements of object, array & function params + // (val: number of removed elements, + // type: 'obj' | 'arr' | 'func') KOVS, // get enumerable properties or iterable values of an object for for-in or for-of statement // (val: true for keys and false for values) CLS, // declare a class @@ -30,9 +32,11 @@ export enum OP { // length: function.length) CALL, // invoke functions or methods // (val: number of parameters, + // spread: array of spread element indexes, // catch: { pc: catch statement pc }) NEW, // create an object by constructor // (val: number of parameters, + // spread: array of spread element indexes, // catch: { pc: catch statement pc }) BRK, // break (val: label) CONTI, // continue (val: label) diff --git a/src_/state/index.ts b/src_/state/index.ts index c5e2b03..d735dfc 100644 --- a/src_/state/index.ts +++ b/src_/state/index.ts @@ -1,5 +1,5 @@ import { OpCode } from '../share/const' -import SymbolTable, { VarType } from './symbols' +import SymbolTable from './symbols' export default class State { readonly stack: any[] = [] diff --git a/src_/state/symbols.ts b/src_/state/symbols.ts index 5d21502..51d7bc4 100644 --- a/src_/state/symbols.ts +++ b/src_/state/symbols.ts @@ -1,6 +1,6 @@ import { hasOwn } from '../share/utils' -export type VarType = 'var' | 'let' | 'const' +type VarType = 'var' | 'let' | 'const' interface VarSymbol { type: VarType diff --git a/src_/test.ts b/src_/test.ts index a56c436..5919906 100644 --- a/src_/test.ts +++ b/src_/test.ts @@ -2,6 +2,6 @@ import Sval from '.' const interpreter = new Sval() interpreter.run(` -const { a = 1, b } = { b: 2 } -console.log(a, b) +const [a, ...g] = [1] +console.log(a, g) `) \ No newline at end of file