Skip to content

Commit

Permalink
[Update] Add rest element of function parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
Siubaak committed Jul 24, 2019
1 parent c0751a1 commit 740d186
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 36 deletions.
23 changes: 18 additions & 5 deletions src_/compile/helper.ts
Expand Up @@ -3,7 +3,7 @@ import * as estree from 'estree'
import { OP } from '../share/const'
import compile from '.'
import { createSymbol } from '../share/utils'
import { ObjectPattern, ArrayPattern, AssignmentPattern, RestElement } from './pattern'
import { ObjectPattern, ArrayPattern, AssignmentPattern } from './pattern'

type FunctionDefinition = estree.FunctionDeclaration | estree.FunctionExpression | estree.ArrowFunctionExpression

Expand Down Expand Up @@ -32,9 +32,24 @@ export function compileFunc(node: FunctionDefinition, state: State) {
if (param.type === 'Identifier') {
state.opCodes.push({ op: OP.ALLOC, val: state.symbols.set(param.name, 'var').pointer })
} else if (param.type === 'RestElement') {

state.opCodes.push({ op: OP.REST, val: 'func' })
const value = param.argument
if (value.type === 'Identifier') {
state.opCodes.push({ op: OP.ALLOC, val: state.symbols.set(value.name, 'var').pointer })
// } 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)
}
} else {

compilePattern(param, state)
}
}
const body = node.body.type === 'BlockStatement' ? node.body.body : [node.body]
Expand Down Expand Up @@ -92,8 +107,6 @@ export function compilePattern(node: estree.Pattern, state: State) {
return ArrayPattern(node, state)
case 'AssignmentPattern':
return AssignmentPattern(node, state)
case 'RestElement':
return RestElement(node, state)
default:
throw new SyntaxError('Unexpected token')
}
Expand Down
11 changes: 2 additions & 9 deletions src_/compile/pattern.ts
Expand Up @@ -10,7 +10,6 @@ export function ObjectPattern(node: estree.ObjectPattern, state: State) {
let value: estree.Pattern = (property as estree.AssignmentProperty).value
state.opCodes.push({ op: OP.COPY })
if (property.type === 'Property') {
// key
const propKey = property.key
if (propKey.type === 'Identifier') {
state.opCodes.push({ op: OP.LOADK, val: propKey.name })
Expand All @@ -27,7 +26,7 @@ export function ObjectPattern(node: estree.ObjectPattern, state: State) {
compile(propKey, state)
}
}
state.opCodes.push({ op: OP.REST, val: i, type: 'obj' })
state.opCodes.push({ op: OP.REST, val: 'obj', remove: i })
value = (property as any).argument
}
if (value.type === 'Identifier') {
Expand Down Expand Up @@ -59,7 +58,7 @@ export function ArrayPattern(node: estree.ArrayPattern, state: State) {
let value: estree.Pattern = element
state.opCodes.push({ op: OP.COPY })
if (element.type === 'RestElement') {
state.opCodes.push({ op: OP.REST, val: i, type: 'arr' })
state.opCodes.push({ op: OP.REST, val: 'arr', remove: i })
value = element.argument
} else {
state.opCodes.push({ op: OP.LOADK, val: i })
Expand Down Expand Up @@ -107,9 +106,3 @@ export function AssignmentPattern(node: estree.AssignmentPattern, state: State)
compilePattern(node.left, state)
}
}

export function RestElement(node: estree.RestElement, state: State) {
if (node.argument.type === 'Identifier') {

}
}
2 changes: 2 additions & 0 deletions src_/compile/program.ts
@@ -1,9 +1,11 @@
import * as estree from 'estree'
import State from '../state'
import compile from '../compile'
import { OP } from '../share/const'

export function Program(program: estree.Program, state: State) {
for (let i = 0; i < program.body.length; i++) {
compile(program.body[i], state)
state.opCodes.push({ op: OP.GC })
}
}
5 changes: 4 additions & 1 deletion src_/compile/statement.ts
Expand Up @@ -10,7 +10,10 @@ export function ExpressionStatement(node: estree.ExpressionStatement, state: Sta

export function BlockStatement(node: estree.BlockStatement, state: State) {
state.symbols.pushScope()
for (let i = 0; i < node.body.length; i++) compile(node.body[i], state)
for (let i = 0; i < node.body.length; i++) {
compile(node.body[i], state)
state.opCodes.push({ op: OP.GC })
}
state.symbols.popScope()
}

Expand Down
43 changes: 27 additions & 16 deletions src_/jsvm/index.ts
Expand Up @@ -10,7 +10,9 @@ function step(state: State) {
switch (code.op) {
case OP.LOADK: stack.push(code.val); break
case OP.LOADV: stack.push(context[code.val].store); break
case OP.ALLOC: context[code.val] = { store: stack.pop() }; break
case OP.ALLOC: context[code.val] = {
store: stack.length > state.ebpList[state.ebpList.length - 1] ? stack.pop() : undefined
}; break
case OP.STORE: context[code.val].store = stack.pop(); break
case OP.BIOP: {
const right = stack.pop()
Expand Down Expand Up @@ -130,17 +132,17 @@ function step(state: State) {
break
}
case OP.REST: {
if (code.type === 'obj') {
const rmKeys = stack.splice(stack.length - code.val)
if (code.val === 'obj') {
const rmKeys = stack.splice(stack.length - code.remove)
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'

} else if (code.val === 'arr') {
stack.push(stack.pop().slice(code.remove))
} else { // code.val === 'func'
stack.push(stack.splice(state.ebpList[state.ebpList.length - 1]).reverse())
}
break
}
Expand Down Expand Up @@ -182,11 +184,11 @@ function step(state: State) {
state.pc = beginPc // offset pc to the function op codes
state.context = lexicalCtx // set the context as the lexical context of function

let s = SIGNAL.NONE
let signal = SIGNAL.NONE
let ret: any
while (state.pc < endPc) {
s = step(state)
if (s === SIGNAL.RET) {
signal = step(state)
if (signal === SIGNAL.RET) {
ret = stack.pop()
break
}
Expand Down Expand Up @@ -335,16 +337,20 @@ function step(state: State) {
}
}

let result: any
state.ebpList.push(stack.length)
if (code.catch) {
try {
stack.push(func.apply(obj, args))
result = func.apply(obj, args)
} catch (err) {
stack.push(err)
result = err
state.pc = code.catch.pc - 1
}
} else {
stack.push(func.apply(obj, args))
result = func.apply(obj, args)
}
stack.length = state.ebpList.pop()
stack.push(result)
break
}
case OP.NEW: {
Expand All @@ -361,16 +367,20 @@ function step(state: State) {
}
}

let result: any
state.ebpList.push(stack.length)
if (code.catch) {
try {
stack.push(new ctor(...args))
result = new ctor(...args)
} catch (err) {
stack.push(err)
result = err
state.pc = code.catch.pc - 1
}
} else {
stack.push(new ctor(...args))
result = new ctor(...args)
}
stack.length = state.ebpList.pop()
stack.push(result)
break
}
case OP.BRK: break
Expand All @@ -389,6 +399,7 @@ function step(state: State) {
throw stack.pop()
}
}
case OP.GC: stack.length = state.ebpList[state.ebpList.length - 1]; break
default: throw new Error('Unknown instruct code')
}
state.pc++
Expand Down
7 changes: 4 additions & 3 deletions src_/share/const.ts
Expand Up @@ -14,8 +14,8 @@ export enum OP {
MGET, // get member of object (no val)
MSET, // set member of object (no val)
REST, // get rest elements of object, array & function params
// (val: number of removed elements,
// type: 'obj' | 'arr' | 'func')
// (val: 'obj' | 'arr' | 'func',
// remove: number of removed elements)
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
Expand Down Expand Up @@ -46,7 +46,8 @@ export enum OP {
COPY, // copy the top of stack and push into stack (no val)
POP, // pop the top of stack (no val)
DBG, // debug (no val)
THROW, // throw (val: { pc: catch statement pc })
THROW, // throw (val: { pc: catch statement pc }),
GC, // clean stack after each statments (no val)
}

export enum SIGNAL {
Expand Down
2 changes: 2 additions & 0 deletions src_/state/index.ts
Expand Up @@ -4,6 +4,8 @@ import SymbolTable from './symbols'
export default class State {
readonly stack: any[] = []

readonly ebpList: number[] = [0]

readonly symbols: SymbolTable = new SymbolTable()

readonly opCodes: OpCode[] = []
Expand Down
6 changes: 4 additions & 2 deletions src_/test.ts
Expand Up @@ -2,6 +2,8 @@ import Sval from '.'

const interpreter = new Sval()
interpreter.run(`
const [a, ...g] = [1]
console.log(a, g)
function a(b, ...c) {
console.log(b, c)
}
a(1, 2, 3)
`)

0 comments on commit 740d186

Please sign in to comment.