From 6e981d93c6990257ed222c9ce06883d499e3e9bb Mon Sep 17 00:00:00 2001 From: Reuben Thomas Date: Mon, 5 Aug 2024 15:43:18 +0100 Subject: [PATCH] Ark: re-jig property access Rename ArkProperty to ArkField, and add ArkSetField, rather than using ArkSet to set fields. Similarly, rename ArkSetPropertyInst to ArkSetFieldInst, and remove ArkSetInst, which is no longer required. --- src/ark/code.ts | 8 +++++++- src/ark/compiler/index.ts | 6 +++--- src/ark/flatten.ts | 35 +++++++++++++++-------------------- src/ark/interpreter.ts | 4 ++-- src/ark/reader.ts | 18 +++++++++++++++--- src/ark/serialize.ts | 9 +++++---- src/ursa/compiler.ts | 10 +++++++--- 7 files changed, 54 insertions(+), 36 deletions(-) diff --git a/src/ark/code.ts b/src/ark/code.ts index dd865cf..a0520da 100644 --- a/src/ark/code.ts +++ b/src/ark/code.ts @@ -133,12 +133,18 @@ export class ArkObjectLiteral extends ArkExp { } } -export class ArkProperty extends ArkLvalue { +export class ArkField extends ArkLvalue { constructor(public obj: ArkExp, public prop: string) { super() } } +export class ArkSetField extends ArkExp { + constructor(public field: ArkField, public exp: ArkExp) { + super() + } +} + export class ArkListLiteral extends ArkExp { constructor(public list: ArkExp[]) { super() diff --git a/src/ark/compiler/index.ts b/src/ark/compiler/index.ts index 0ff8b9a..265217d 100644 --- a/src/ark/compiler/index.ts +++ b/src/ark/compiler/index.ts @@ -28,7 +28,7 @@ import { ArkLaunchBlockOpenInst, ArkLaunchBlockCloseInst, ArkLetBlockOpenInst, ArkLocalInst, ArkCaptureInst, ArkListLiteralInst, ArkLiteralInst, ArkMapLiteralInst, ArkObjectLiteralInst, ArkPropertyInst, ArkReturnInst, ArkYieldInst, - ArkSetInst, ArkSetPropertyInst, + ArkSetNamedLocInst, ArkSetFieldInst, } from '../flatten.js' import { jsGlobals, ArkBoolean, ArkBooleanVal, ArkList, ArkMap, ArkNull, @@ -191,14 +191,14 @@ export function flatToJs(insts: ArkInsts, file: string | null = null): CodeWithS 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 ArkSetInst) { + } 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`, 'throw new JsRuntimeError(\'Assignment to different type\')\n', '}\n', letAssign(inst.id, `${inst.lexpId.description} = ${inst.valId.description}`), ]) - } else if (inst instanceof ArkSetPropertyInst) { + } else if (inst instanceof ArkSetFieldInst) { return sourceNode(letAssign(inst.id, `${inst.lexpId.description}.set('${inst.prop}', ${inst.valId.description})`)) } else if (inst instanceof ArkObjectLiteralInst) { const objInits: string[] = [] diff --git a/src/ark/flatten.ts b/src/ark/flatten.ts index 3f28ef8..052437f 100644 --- a/src/ark/flatten.ts +++ b/src/ark/flatten.ts @@ -9,7 +9,7 @@ import { ArkAnd, ArkAwait, ArkBreak, ArkCall, ArkCapture, ArkContinue, ArkDebugInfo, ArkExp, ArkFn, ArkGenerator, ArkIf, ArkInvoke, ArkLaunch, ArkLet, ArkListLiteral, ArkLiteral, ArkLocal, ArkLoop, ArkMapLiteral, ArkNamedLoc, - ArkObjectLiteral, ArkOr, ArkProperty, ArkReturn, ArkSequence, ArkSet, ArkYield, + ArkObjectLiteral, ArkOr, ArkField, ArkReturn, ArkSequence, ArkSet, ArkSetField, ArkYield, } from './code.js' import {ArkBoolean, ArkNull, ArkVal} from './data.js' @@ -245,25 +245,20 @@ export class ArkInvokeInst extends ArkInst { } } -export class ArkSetInst extends ArkInst { - constructor(sourceLoc: Interval | undefined, public lexpId: symbol, public valId: symbol) { - super(sourceLoc) - } -} -export class ArkSetNamedLocInst extends ArkSetInst { +export class ArkSetNamedLocInst extends ArkInst { constructor( sourceLoc: Interval | undefined, - lexpId: symbol, + public lexpId: symbol, public lexpIndex: number, - valId: symbol, + public valId: symbol, ) { - super(sourceLoc, lexpId, valId) + super(sourceLoc) } } export class ArkSetLocalInst extends ArkSetNamedLocInst {} export class ArkSetCaptureInst extends ArkSetNamedLocInst {} -export class ArkSetPropertyInst extends ArkInst { +export class ArkSetFieldInst extends ArkInst { constructor( sourceLoc: Interval | undefined, public lexpId: symbol, @@ -379,14 +374,6 @@ export function expToInsts( ]) } else if (exp instanceof ArkSet) { const insts = expToInsts(exp.exp, innerLoop, innerFn) - if (exp.lexp instanceof ArkProperty) { - const objInsts = expToInsts(exp.lexp.obj, innerLoop, innerFn) - return new ArkInsts([ - ...objInsts.insts, - ...insts.insts, - new ArkSetPropertyInst(exp.lexp.sourceLoc, objInsts.id, exp.lexp.prop, insts.id), - ]) - } let SetInst if (exp.lexp instanceof ArkLocal) { SetInst = ArkSetLocalInst @@ -399,6 +386,14 @@ export function expToInsts( ...insts.insts, new SetInst(exp.sourceLoc, Symbol.for(exp.lexp.debug.name!), exp.lexp.index, insts.id), ]) + } else if (exp instanceof ArkSetField) { + const insts = expToInsts(exp.exp, innerLoop, innerFn) + const objInsts = expToInsts(exp.field.obj, innerLoop, innerFn) + return new ArkInsts([ + ...objInsts.insts, + ...insts.insts, + new ArkSetFieldInst(exp.field.sourceLoc, objInsts.id, exp.field.prop, insts.id), + ]) } else if (exp instanceof ArkObjectLiteral) { const insts: ArkInst[] = [] const valMap = new Map([...exp.properties.entries()].map( @@ -476,7 +471,7 @@ export function expToInsts( ) } else if (exp instanceof ArkLoop) { return loopBlock(exp.sourceLoc, exp.localsDepth, exp.body, innerFn) - } else if (exp instanceof ArkProperty) { + } else if (exp instanceof ArkField) { const objInsts = expToInsts(exp.obj, innerLoop, innerFn) return new ArkInsts([ ...objInsts.insts, diff --git a/src/ark/interpreter.ts b/src/ark/interpreter.ts index ae4f081..aa36664 100644 --- a/src/ark/interpreter.ts +++ b/src/ark/interpreter.ts @@ -14,7 +14,7 @@ import { ArkLaunchBlockCloseInst, ArkLaunchBlockOpenInst, ArkLetBlockCloseInst, ArkLetBlockOpenInst, ArkLetCopyInst, ArkListLiteralInst, ArkLiteralInst, ArkLocalInst, ArkLoopBlockCloseInst, ArkLoopBlockOpenInst, ArkMapLiteralInst, ArkObjectLiteralInst, ArkPropertyInst, ArkReturnInst, - ArkSetCaptureInst, ArkSetLocalInst, ArkSetNamedLocInst, ArkSetPropertyInst, ArkYieldInst, + ArkSetCaptureInst, ArkSetLocalInst, ArkSetNamedLocInst, ArkSetFieldInst, ArkYieldInst, } from './flatten.js' import { ArkAbstractObjectBase, ArkBoolean, ArkList, ArkMap, ArkNull, ArkNullVal, @@ -331,7 +331,7 @@ function* doEvalFlat(outerArk: ArkState): Operation { mem.set(inst.id, result) ref.set(result) inst = inst.next - } else if (inst instanceof ArkSetPropertyInst) { + } else if (inst instanceof ArkSetFieldInst) { const result = mem.get(inst.valId)! const obj = mem.get(inst.lexpId)! as ArkObject if (obj.get(inst.prop) === ArkUndefined) { diff --git a/src/ark/reader.ts b/src/ark/reader.ts index b794625..d8cd2a7 100644 --- a/src/ark/reader.ts +++ b/src/ark/reader.ts @@ -16,7 +16,8 @@ import { ArkExp, ArkLvalue, ArkIf, ArkAnd, ArkOr, ArkSequence, ArkLoop, ArkBreak, ArkContinue, ArkSet, ArkLocal, ArkCapture, ArkListLiteral, ArkObjectLiteral, ArkMapLiteral, ArkFn, ArkGenerator, ArkReturn, ArkYield, - ArkProperty, ArkLet, ArkCall, ArkInvoke, ArkLiteral, ArkBoundVar, ArkNamedLoc, + ArkField, ArkLet, ArkCall, ArkInvoke, ArkLiteral, ArkBoundVar, ArkNamedLoc, + ArkSetField, } from './code.js' import {expToInst} from './flatten.js' import {ArkState} from './interpreter.js' @@ -142,7 +143,7 @@ export function symRef(env: Environment, name: string): ArkLvalue { if (env.externalSyms.get(name) === ArkUndefined) { throw new ArkCompilerError(`Undefined symbol ${name}`) } - lexp = new ArkProperty(new ArkLiteral(env.externalSyms), name) + lexp = new ArkField(new ArkLiteral(env.externalSyms), name) } lexp.debug.name = name lexp.debug.env = JSON.stringify(env) @@ -200,7 +201,7 @@ function doCompile(env: Environment, value: unknown): ArkExp { throw new ArkCompilerError("Invalid 'prop'") } const compiled = doCompile(env, value[2]) - return new ArkProperty(compiled, value[1]) + return new ArkField(compiled, value[1]) } case 'set': { if (value.length !== 3) { @@ -216,6 +217,17 @@ function doCompile(env: Environment, value: unknown): ArkExp { const compiledVal = doCompile(env, value[2]) return new ArkSet(compiledRef, compiledVal) } + case 'set-field': { + if (value.length !== 4 || typeof value[2] !== 'string') { + throw new ArkCompilerError("Invalid 'set-field'") + } + const compiledRef = doCompile(env, value[1]) + if (!(compiledRef instanceof ArkLvalue)) { + throw new ArkCompilerError('Invalid lvalue') + } + const compiledVal = doCompile(env, value[3]) + return new ArkSetField(new ArkField(compiledRef, value[2]), compiledVal) + } case 'list': { const elems = listToVals(env, value.slice(1)) return new ArkListLiteral(elems) diff --git a/src/ark/serialize.ts b/src/ark/serialize.ts index b644461..cef082e 100644 --- a/src/ark/serialize.ts +++ b/src/ark/serialize.ts @@ -8,9 +8,8 @@ import { ArkUndefined, NativeObject, } from './data.js' import { - ArkExp, ArkSequence, - ArkAnd, ArkOr, ArkIf, ArkLoop, ArkBreak, ArkContinue, ArkInvoke, - ArkSet, ArkLet, ArkCall, ArkFn, ArkGenerator, ArkReturn, ArkProperty, + ArkExp, ArkSequence, ArkAnd, ArkOr, ArkIf, ArkLoop, ArkBreak, ArkContinue, ArkInvoke, + ArkSet, ArkSetField, ArkLet, ArkCall, ArkFn, ArkGenerator, ArkReturn, ArkField, ArkLiteral, ArkListLiteral, ArkMapLiteral, ArkObjectLiteral, ArkYield, } from './code.js' @@ -57,7 +56,9 @@ export function valToJs(val: ArkVal | ArkExp, externalSyms = globals) { 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) { + } else if (val instanceof ArkSetField) { + return ['set-field', doValToJs(val.field.obj), val.field.prop, doValToJs(val.exp)] + } else if (val instanceof ArkField) { if (val.obj instanceof ArkLiteral && val.obj.val === externalSyms) { // Serialize globals as simply their name. return val.prop diff --git a/src/ursa/compiler.ts b/src/ursa/compiler.ts index 3b3362f..ad3896a 100644 --- a/src/ursa/compiler.ts +++ b/src/ursa/compiler.ts @@ -19,8 +19,9 @@ import { import { ArkBoundVar, ArkExp, ArkLvalue, ArkLiteral, ArkSequence, ArkIf, ArkLoop, ArkAnd, ArkOr, ArkObjectLiteral, ArkListLiteral, ArkMapLiteral, - ArkCall, ArkInvoke, ArkLet, ArkFn, ArkGenerator, ArkProperty, ArkSet, ArkReturn, ArkYield, + ArkCall, ArkInvoke, ArkLet, ArkFn, ArkGenerator, ArkField, ArkSet, ArkReturn, ArkYield, ArkBreak, ArkContinue, ArkAwait, ArkLaunch, ArkCapture, ArkFnType, ArkNamedLoc, + ArkSetField, } from '../ark/code.js' import {ArkState, ArkRuntimeError} from '../ark/interpreter.js' import { @@ -140,7 +141,7 @@ function makeProperty( object: ParserNonterminalNode, property: ParserNode, ) { - return addLoc(new ArkProperty(object.toExp(a), property.sourceString), exp) + return addLoc(new ArkField(object.toExp(a), property.sourceString), exp) } function makeIfChain(ifs: ArkIf[]): ArkIf { @@ -221,7 +222,7 @@ semantics.addOperation('toLet(a)', { // For path x.y.z, compile `let z = x.use("y", "z")` const innerEnv = this.args.a.env.push([new Location(ident.sourceString, false)]) const libValue = path[0].toExp({...this.args.a, env: innerEnv}) - const useProperty = addLoc(new ArkProperty(libValue, 'use'), this) + const useProperty = addLoc(new ArkField(libValue, 'use'), this) const useCallArgs = path.slice(1).map((id) => new ArkLiteral(ArkString(id.sourceString))) const useCall = addLoc(new ArkCall(useProperty, useCallArgs), this) const index = this.args.a.env.top().locals.length @@ -534,6 +535,9 @@ semantics.addOperation('toExp(a)', { if (compiledLvalue instanceof ArkNamedLoc && !compiledLvalue.isVar) { throw new UrsaCompilerError(lvalue.source, "Cannot assign to non-'var'") } + if (compiledLvalue instanceof ArkField) { + return addLoc(new ArkSetField(compiledLvalue, compiledValue), this) + } return addLoc(new ArkSet(compiledLvalue, compiledValue), this) },