Skip to content

Commit

Permalink
Ark: add ArkInvoke instruction for method invocation
Browse files Browse the repository at this point in the history
Also add corresponding ArkInvokeInst to the flat code.

This is a preliminary to separating methods from fields.
  • Loading branch information
rrthomas committed Aug 17, 2024
1 parent d816b94 commit 5b98332
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 35 deletions.
6 changes: 6 additions & 0 deletions src/ark/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export class ArkCall extends ArkExp {
}
}

export class ArkInvoke extends ArkExp {
constructor(public obj: ArkExp, public prop: string, public args: ArkExp[]) {
super()
}
}

export abstract class ArkLvalue extends ArkExp {}

export abstract class ArkNamedLoc extends ArkLvalue {
Expand Down
7 changes: 5 additions & 2 deletions src/ark/compiler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import {
expToInsts, ArkInsts,
ArkInst, ArkAwaitInst,
ArkBlockCloseInst, ArkBlockOpenInst, ArkIfBlockOpenInst, ArkLoopBlockOpenInst,
ArkBreakInst, ArkCallInst, ArkContinueInst, ArkElseBlockInst, ArkElseBlockCloseInst,
ArkFnBlockOpenInst, ArkFnBlockCloseInst, ArkGeneratorBlockOpenInst, ArkLetCopyInst,
ArkBreakInst, ArkCallInst, ArkInvokeInst, ArkContinueInst,
ArkElseBlockInst, ArkElseBlockCloseInst, ArkFnBlockOpenInst, ArkFnBlockCloseInst,
ArkGeneratorBlockOpenInst, ArkLetCopyInst,
ArkLaunchBlockOpenInst, ArkLaunchBlockCloseInst, ArkLetBlockOpenInst,
ArkLocalInst, ArkCaptureInst, ArkListLiteralInst, ArkLiteralInst, ArkMapLiteralInst,
ArkObjectLiteralInst, ArkPropertyInst, ArkReturnInst, ArkYieldInst,
Expand Down Expand Up @@ -188,6 +189,8 @@ export function flatToJs(insts: ArkInsts, file: string | null = null): CodeWithS
return sourceNode(letAssign(inst.id, inst.argId.description!))
} else if (inst instanceof ArkCallInst) {
return sourceNode(letAssign(inst.id, `yield* ${inst.fnId.description}.body(${inst.argIds.map((id) => id.description).join(', ')})`))
} else if (inst instanceof ArkInvokeInst) {
return sourceNode(letAssign(inst.id, `yield* ${inst.objId.description}.get('${inst.prop}').body(${inst.argIds.map((id) => id.description).join(', ')})`))
} else if (inst instanceof ArkSetNamedLocInst) {
return sourceNode([
`if (${inst.lexpId.description} !== ArkUndefined && ${inst.lexpId.description}.constructor !== ArkNullVal && ${inst.valId.description}.constructor !== ${inst.lexpId.description}.constructor) {\n`,
Expand Down
27 changes: 24 additions & 3 deletions src/ark/flatten.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {Interval} from 'ohm-js'

import {
ArkAnd, ArkAwait, ArkBreak, ArkCall, ArkCapture, ArkContinue, ArkDebugInfo,
ArkExp, ArkFn, ArkGenerator, ArkIf, ArkLaunch, ArkLet, ArkListLiteral, ArkLiteral,
ArkLocal, ArkLoop, ArkMapLiteral, ArkNamedLoc, ArkObjectLiteral,
ArkOr, ArkProperty, ArkReturn, ArkSequence, ArkSet, ArkYield,
ArkExp, ArkFn, ArkGenerator, ArkIf, ArkInvoke, ArkLaunch, ArkLet,
ArkListLiteral, ArkLiteral, ArkLocal, ArkLoop, ArkMapLiteral, ArkNamedLoc,
ArkObjectLiteral, ArkOr, ArkProperty, ArkReturn, ArkSequence, ArkSet, ArkYield,
} from './code.js'
import {ArkBoolean, ArkNull, ArkVal} from './data.js'

Expand Down Expand Up @@ -233,6 +233,18 @@ export class ArkCallInst extends ArkInst {
}
}

export class ArkInvokeInst extends ArkInst {
constructor(
sourceLoc: Interval | undefined,
public objId: symbol,
public prop: string,
public argIds: symbol[],
public name?: string,
) {
super(sourceLoc)
}
}

export class ArkSetNamedLocInst extends ArkInst {
constructor(
sourceLoc: Interval | undefined,
Expand Down Expand Up @@ -351,6 +363,15 @@ export function expToInsts(
...fnInsts.insts,
new ArkCallInst(exp.fn.sourceLoc, fnInsts.id, argIds, exp.fn.debug.name),
])
} else if (exp instanceof ArkInvoke) {
const argInsts = exp.args.map((exp) => expToInsts(exp, innerLoop, innerFn))
const argIds = argInsts.map((insts) => insts.id)
const objInsts = expToInsts(exp.obj, innerLoop, innerFn)
return new ArkInsts([
...argInsts.map((i) => i.insts).flat(),
...objInsts.insts,
new ArkInvokeInst(exp.obj.sourceLoc, objInsts.id, exp.prop, argIds, `${exp.obj.debug.name}.exp.prop`),
])
} else if (exp instanceof ArkSet) {
const insts = expToInsts(exp.exp, innerLoop, innerFn)
if (exp.lexp instanceof ArkProperty) {
Expand Down
15 changes: 13 additions & 2 deletions src/ark/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {Interval} from 'ohm-js'
import {
ArkAwaitInst, ArkBlockCloseInst, ArkBlockOpenInst, ArkBreakInst, ArkCallableBlockOpenInst,
ArkCallInst, ArkCaptureInst, ArkContinueInst, ArkElseBlockCloseInst, ArkElseBlockInst,
ArkFnBlockOpenInst, ArkGeneratorBlockOpenInst, ArkIfBlockOpenInst, ArkInst,
ArkFnBlockOpenInst, ArkGeneratorBlockOpenInst, ArkIfBlockOpenInst, ArkInst, ArkInvokeInst,
ArkLaunchBlockCloseInst, ArkLaunchBlockOpenInst, ArkLetBlockCloseInst, ArkLetBlockOpenInst,
ArkLetCopyInst, ArkListLiteralInst, ArkLiteralInst, ArkLocalInst, ArkLoopBlockCloseInst,
ArkLoopBlockOpenInst, ArkMapLiteralInst, ArkObjectLiteralInst, ArkPropertyInst, ArkReturnInst,
Expand Down Expand Up @@ -119,7 +119,7 @@ async function evalFlat(outerArk: ArkState): Promise<ArkVal> {

function* call(
ark: ArkState,
inst: ArkCallInst,
inst: ArkCallInst | ArkInvokeInst,
callable: ArkCallable,
args: ArkVal[],
): Generator<Instruction, [ArkState, ArkInst | undefined]> {
Expand Down Expand Up @@ -310,6 +310,17 @@ function* doEvalFlat(outerArk: ArkState): Operation<ArkVal> {
const callable = mem.get(inst.fnId)! as ArkCallable
const args = inst.argIds.map((id) => mem.get(id)!);
[ark, inst] = yield* call(ark, inst, callable, args)
} else if (inst instanceof ArkInvokeInst) {
const obj = mem.get(inst.objId)!
if (!(obj instanceof ArkAbstractObjectBase)) {
throw new ArkRuntimeError(ark, 'Invalid object', inst.sourceLoc)
}
const method = obj.get(inst.prop) as ArkCallable
if (method === ArkUndefined) {
throw new ArkRuntimeError(ark, 'Invalid property', inst.sourceLoc)
}
const args = inst.argIds.map((id) => mem.get(id)!);
[ark, inst] = yield* call(ark, inst, method, args)
} else if (inst instanceof ArkSetNamedLocInst) {
const result = mem.get(inst.valId)!
let ref: ArkRef
Expand Down
10 changes: 9 additions & 1 deletion src/ark/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
ArkExp, ArkLvalue, ArkIf, ArkAnd, ArkOr, ArkSequence, ArkLoop, ArkBreak, ArkContinue,
ArkSet, ArkLocal, ArkCapture, ArkListLiteral, ArkObjectLiteral, ArkMapLiteral,
ArkFn, ArkGenerator, ArkReturn, ArkYield,
ArkProperty, ArkLet, ArkCall, ArkLiteral, ArkBoundVar, ArkNamedLoc,
ArkProperty, ArkLet, ArkCall, ArkInvoke, ArkLiteral, ArkBoundVar, ArkNamedLoc,
} from './code.js'
import {expToInst} from './flatten.js'
import {ArkState} from './interpreter.js'
Expand Down Expand Up @@ -300,6 +300,14 @@ function doCompile(env: Environment, value: unknown): ArkExp {
}
return new Constructor()
}
case 'invoke': {
if (value.length < 3 || typeof value[2] !== 'string') {
throw new ArkCompilerError("Invalid 'invoke'")
}
const compiledObj = doCompile(env, value[1])
const args = listToVals(env, value.slice(3))
return new ArkInvoke(compiledObj, value[2], args)
}
default: {
const compiledFn = doCompile(env, value[0])
const args = listToVals(env, value.slice(1))
Expand Down
4 changes: 3 additions & 1 deletion src/ark/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from './data.js'
import {
ArkExp, ArkSequence,
ArkAnd, ArkOr, ArkIf, ArkLoop, ArkBreak, ArkContinue,
ArkAnd, ArkOr, ArkIf, ArkLoop, ArkBreak, ArkContinue, ArkInvoke,
ArkSet, ArkLet, ArkCall, ArkFn, ArkGenerator, ArkReturn, ArkProperty,
ArkLiteral, ArkListLiteral, ArkMapLiteral, ArkObjectLiteral, ArkYield,
} from './code.js'
Expand Down Expand Up @@ -53,6 +53,8 @@ export function valToJs(val: ArkVal | ArkExp, externalSyms = globals) {
return ['let', [...val.boundVars.map((bv) => [bv.name, bv.isVar, doValToJs(bv.init)])], doValToJs(val.body)]
} else if (val instanceof ArkCall) {
return [doValToJs(val.fn), ...val.args.map(doValToJs)]
} else if (val instanceof ArkInvoke) {
return ['invoke', doValToJs(val.obj), val.prop, ...val.args.map(doValToJs)]
} else if (val instanceof ArkSet) {
return ['set', doValToJs(val.lexp), doValToJs(val.exp)]
} else if (val instanceof ArkProperty) {
Expand Down
51 changes: 25 additions & 26 deletions src/ursa/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ import {
ArkVal, ArkNull, ArkBoolean, ArkNumber, ArkString,
} from '../ark/data.js'
import {
ArkBoundVar, ArkExp, ArkLvalue, ArkLiteral,
ArkSequence, ArkIf, ArkLoop, ArkAnd, ArkOr,
ArkBoundVar, ArkExp, ArkLvalue, ArkLiteral, ArkSequence, ArkIf, ArkLoop, ArkAnd, ArkOr,
ArkObjectLiteral, ArkListLiteral, ArkMapLiteral,
ArkCall, ArkLet, ArkFn, ArkGenerator, ArkProperty, ArkSet, ArkReturn, ArkYield,
ArkCall, ArkInvoke, ArkLet, ArkFn, ArkGenerator, ArkProperty, ArkSet, ArkReturn, ArkYield,
ArkBreak, ArkContinue, ArkAwait, ArkLaunch, ArkCapture, ArkFnType, ArkNamedLoc,
} from '../ark/code.js'
import {ArkState, ArkRuntimeError} from '../ark/interpreter.js'
Expand Down Expand Up @@ -359,7 +358,7 @@ semantics.addOperation<ArkExp>('toExp(a)', {
[new ArkBoundVar(forVar, false, innerIndex, addLoc(new ArkCall(addLoc(symRef(loopEnv, '_for'), iterator), []), this))],
new ArkSequence([
new ArkIf(
addLoc(new ArkCall(new ArkProperty(compiledForVar, 'equals'), [new ArkLiteral(ArkNull())]), this),
addLoc(new ArkInvoke(compiledForVar, 'equals', [new ArkLiteral(ArkNull())]), this),
new ArkBreak(),
),
compiledForBody,
Expand All @@ -376,139 +375,139 @@ semantics.addOperation<ArkExp>('toExp(a)', {

UnaryExp_bitwise_not(_not, exp) {
return addLoc(
new ArkCall(new ArkProperty(exp.toExp(this.args.a), 'bitwiseNot'), []),
new ArkInvoke(exp.toExp(this.args.a), 'bitwiseNot', []),
this,
)
},
UnaryExp_pos(_plus, exp) {
return addLoc(
new ArkCall(new ArkProperty(exp.toExp(this.args.a), 'pos'), []),
new ArkInvoke(exp.toExp(this.args.a), 'pos', []),
this,
)
},
UnaryExp_neg(_minus, exp) {
return addLoc(
new ArkCall(new ArkProperty(exp.toExp(this.args.a), 'neg'), []),
new ArkInvoke(exp.toExp(this.args.a), 'neg', []),
this,
)
},

ExponentExp_power(left, _power, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'exp'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'exp', [right.toExp(this.args.a)]),
this,
)
},

ProductExp_times(left, _times, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'mul'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'mul', [right.toExp(this.args.a)]),
this,
)
},
ProductExp_divide(left, _divide, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'div'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'div', [right.toExp(this.args.a)]),
this,
)
},
ProductExp_mod(left, _mod, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'mod'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'mod', [right.toExp(this.args.a)]),
this,
)
},

SumExp_plus(left, _plus, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'add'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'add', [right.toExp(this.args.a)]),
this,
)
},
SumExp_minus(left, _minus, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'sub'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'sub', [right.toExp(this.args.a)]),
this,
)
},

CompareExp_eq(left, _eq, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'equals'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'equals', [right.toExp(this.args.a)]),
this,
)
},
CompareExp_neq(left, _neq, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'notEquals'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'notEquals', [right.toExp(this.args.a)]),
this,
)
},
CompareExp_lt(left, _lt, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'lt'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'lt', [right.toExp(this.args.a)]),
this,
)
},
CompareExp_leq(left, _leq, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'leq'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'leq', [right.toExp(this.args.a)]),
this,
)
},
CompareExp_gt(left, _gt, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'gt'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'gt', [right.toExp(this.args.a)]),
this,
)
},
CompareExp_geq(left, _geq, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'geq'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'geq', [right.toExp(this.args.a)]),
this,
)
},

BitwiseExp_and(left, _and, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'bitwiseAnd'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'bitwiseAnd', [right.toExp(this.args.a)]),
this,
)
},
BitwiseExp_or(left, _or, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'bitwiseOr'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'bitwiseOr', [right.toExp(this.args.a)]),
this,
)
},
BitwiseExp_xor(left, _xor, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'bitwiseXor'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'bitwiseXor', [right.toExp(this.args.a)]),
this,
)
},
BitwiseExp_lshift(left, _lshift, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'shiftLeft'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'shiftLeft', [right.toExp(this.args.a)]),
this,
)
},
BitwiseExp_arshift(left, _arshift, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'shiftRight'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'shiftRight', [right.toExp(this.args.a)]),
this,
)
},
BitwiseExp_lrshift(left, _lrshift, right) {
return addLoc(
new ArkCall(new ArkProperty(left.toExp(this.args.a), 'shiftRightArith'), [right.toExp(this.args.a)]),
new ArkInvoke(left.toExp(this.args.a), 'shiftRightArith', [right.toExp(this.args.a)]),
this,
)
},

LogicNotExp_not(_not, exp) {
return addLoc(
new ArkCall(new ArkProperty(exp.toExp(this.args.a), 'not'), []),
new ArkInvoke(exp.toExp(this.args.a), 'not', []),
this,
)
},
Expand Down

0 comments on commit 5b98332

Please sign in to comment.