From 54ff11e4247a8c9efa6924f13d8baae02d98b4d9 Mon Sep 17 00:00:00 2001 From: Jake Coxon Date: Tue, 6 Aug 2024 23:53:01 +0100 Subject: [PATCH 1/6] Stuff --- src/compiler.ts | 20 ++++-- src/compiler_iterator.ts | 82 ++++++++++++++--------- src/defs.ts | 5 +- tests/fixtures/iterator_expansion.rad | 93 +++++++++++++++------------ 4 files changed, 123 insertions(+), 77 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 369b414..59930c5 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -74,6 +74,7 @@ export const BytecodeDefault: ParseTreeTable = { not: (out, node) => (visitParseNode(out, node.expr), pushBytecode(out, node.token, { type: 'not' })), evalfunc: (out, node) => (node.typeArgs.map(x => writeMeta(out, x)), visitAll(out, node.args), pushBytecode(out, node.token, { type: "evalfunc", func: node.func })), + concurrency: (out, node) => (visitAll(out, node.fns), pushBytecode(out, node.token, { type: 'concurrency', count: node.fns.length })), note: (out, node) => { if (node.expr instanceof ParseCall) { @@ -298,6 +299,7 @@ export const BytecodeSecondOrder: ParseTreeTable = { }, evalfunc: (out, node) => (node.typeArgs.map(x => writeMeta(out, x)), visitAll(out, node.args), pushBytecode(out, node.token, { type: "evalfunc", func: node.func })), + concurrency: (out, node) => (visitAll(out, node.fns), pushBytecode(out, node.token, { type: 'concurrency', count: node.fns.length })), note: (out, node) => { if (node.expr instanceof ParseCall) { @@ -586,9 +588,10 @@ export function createBytecodeVmAndExecuteTask(ctx: TaskContext, subCompilerStat return ( TaskDef(executeVmTask, { vm }) .mapRejected(error => { - if (error.info) { - if (!(error.info as any).location) (error.info as any).location = vm.location; - (error.info as any).subCompilerState = subCompilerState + const info = error.info as any + if (info) { + if (!info.location) info.location = vm.location; + if (!info.subCompilerState) info.subCompilerState = subCompilerState } return error }) @@ -889,6 +892,11 @@ const instructions: InstructionMapping = { vm.stack.push(new IfAst(resultType, vm.location, cond, trueBody, falseBody)) }, evalfunc: (vm, { func }) => { return func(vm) }, + concurrency: (vm, { count }) => { + const values = popValues(vm, count).reverse() + const fnctx: CompilerFunctionCallContext = { location: vm.location, compilerState: vm.context.subCompilerState, resultAst: undefined, typeCheckResult: undefined } + return Task.concurrency(values.map(x => createCallAstFromValue(fnctx, x, [], []))) + }, listast: (vm, { count }) => { const values = expectAsts(popValues(vm, count)) const elementType = getCommonType(values.map(x => x.type)) @@ -1032,6 +1040,7 @@ const instructions: InstructionMapping = { return TaskDef(resolveScope, vm.scope, name).chainFn((task, binding) => { compilerAssert(binding instanceof Binding, "Expected binding got $binding", { binding }) const ast = propagatedLiteralAst(expectAst(popStack(vm))) + compilerAssert(binding.type === ast.type, "Type mismatch got $got expected $expected", { got: ast.type, expected: binding.type }) vm.stack.push(new SetAst(VoidType, vm.location, binding, ast)) return Task.success() }); @@ -1333,7 +1342,10 @@ function executeVmTask(ctx: TaskContext, { vm } : { vm: Vm }, p: void): Task { }) ) } -const arrayConstructorFinish = (vm: Vm, constructor: ArrayConstructorCompiler) => { + +const arrayConstructorFinish = new ExternalFunction('arrayConstructorFinish', VoidType, (ctx, values) => { + const [constructor] = values; + compilerAssert(constructor instanceof ArrayConstructorCompiler, "Expected array constructor", { constructor }) compilerAssert(constructor.arrayConstructor, "Expected array constructor", { constructor }) - const location = vm.location const binding = constructor.constructorBinding compilerAssert(binding, "Expected list binding", { constructor }) - const let_ = new LetAst(VoidType, location, binding, constructor.arrayConstructor) - const bindingAst = new BindingAst(binding.type, location, binding) - vm.stack.push(createStatements(location, [let_, ...constructor.calls, bindingAst])) -} + const let_ = new LetAst(VoidType, ctx.location, binding, constructor.arrayConstructor) + const bindingAst = new BindingAst(binding.type, ctx.location, binding) + return createStatements(ctx.location, [let_, ...constructor.calls, bindingAst]) +}) export const listConstructorSugar = (out: BytecodeWriter, node: ParseList) => { const listConstructorIden = new ParseFreshIden(node.token, new FreshBindingToken('list')) @@ -203,23 +205,9 @@ export const listConstructorSugar = (out: BytecodeWriter, node: ParseList) => { return new ParseFunction(node.token, fn) } }).filter(x => x) as ParseFunction[] - - const compilation = (vm: Vm) => { - const values = popValues(vm, fns.length).reverse() - const constructor = popStack(vm) - compilerAssert(constructor instanceof ArrayConstructorCompiler, "Expected array constructor", { constructor }) - - const fnctx: CompilerFunctionCallContext = { location: node.token.location, compilerState: vm.context.subCompilerState, resultAst: undefined, typeCheckResult: undefined } - const compileFns = values.map((fn, i) => createCallAstFromValue(fnctx, fn, [], [])) - const compileFnsTask = Task.concurrency([...compileFns]) - return compileFnsTask.chainFn((task, _) => { - compilerAssert(constructor instanceof ArrayConstructorCompiler, "Expected array constructor", { constructor }) - arrayConstructorFinish(vm, constructor) - return Task.success() - }) - } - visitParseNode(out, new ParseEvalFunc(node.token, compilation, [...fns], [listConstructorIden])) + visitParseNode(out, new ParseCompTime(node.token, new ParseConcurrency(node.token, fns))) + visitParseNode(out, new ParseMeta(node.token, new ParseCall(node.token, new ParseValue(node.token, arrayConstructorFinish), [listConstructorIden], []))) } @@ -739,16 +727,50 @@ export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, a compilerAssert(!expansion.fold, "Fold not supported in this context") - const iden = new ParseFreshIden(node.token, new FreshBindingToken('first')) - const set = new ParseSet(node.token, iden, result) - - expansion.loopBodyNode = new ParseStatements(node.token, [set, new ParseBreak(node.token, expansion.breakIden, null)]) + const objIden = new ParseFreshIden(node.token, new FreshBindingToken('obj')) + // const iden = new ParseFreshIden(node.token, new FreshBindingToken('first')) + const tcResult = new ParseEvalFunc(createAnonymousToken(''), (vm: Vm) => { + const value = vm.stack.pop() + const obj = vm.stack.pop() as any + compilerAssert(isAst(value), "Expected ast", { value, obj }) + obj.type = value.type + obj.binding = new Binding("", value.type) + vm.stack.push(new SetAst(VoidType, node.token.location, obj.binding, value)) + // compilerAssert(false, "Not implemented", { obj, value }) + // return Task.of(null) + }, [new ParseQuote(node.token, result)], [objIden]); + const set = new ParseStatements(node.token, [ + new ParseMeta(node.token, tcResult) + ]) + const break_ = new ParseBreak(node.token, expansion.breakIden, null) + expansion.loopBodyNode = new ParseStatements(node.token, [set, break_]) // TODO: Only supports numbers at the moment const reduce = compileExpansionToParseNode(out, expansion, node) - const let_ = new ParseLet(node.token, iden, null, new ParseNumber(createAnonymousToken('0'))) - const value = new ParseStatements(node.token, [let_, reduce, iden]) - visitParseNode(out, value) + // const let_ = new ParseLet(node.token, iden, null, new ParseNumber(createAnonymousToken('0'))) + const letobj = new ParseLetConst(node.token, objIden, new ParseEvalFunc(createAnonymousToken(''), (vm: Vm) => { + // compilerAssert(false, "Not implemented", { stack: vm.stack }) + vm.stack.push({ thing: 1 }) + }, [], [])) + const stmts = new ParseStatements(node.token, [letobj, reduce]) + // const thing = new ParseMeta(node.token, new ParseCall(createAnonymousToken(''), new ParseIdentifier(createAnonymousToken('thing')), [], [] + visitParseNode(out, new ParseEvalFunc(node.token, (vm) => { + const obj = vm.stack.pop() + const stmts = vm.stack.pop() + compilerAssert(isAst(stmts), "Expected ast", { stmts }) + const binding = obj.binding + compilerAssert(binding instanceof Binding, "Expected binding", { obj }) + + const newStmts = createStatements(vm.location, [ + new LetAst(binding.type, vm.location, binding, new DefaultConsAst(binding.type, vm.location)), + stmts, + new BindingAst(binding.type, vm.location, binding) + ]) + + vm.stack.push(newStmts) + // compilerAssert(false, "Not implemented", { stmts, obj }) + + }, [stmts, objIden], [])) } export const expandFuncMinSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") diff --git a/src/defs.ts b/src/defs.ts index 09f9e29..0054805 100644 --- a/src/defs.ts +++ b/src/defs.ts @@ -173,6 +173,7 @@ export class ParseFreshIden extends ParseNodeType { key = 'freshiden' as cons export class ParseConstructor extends ParseNodeType { key = 'constructor' as const; constructor(public token: Token, public type: ParseNode, public args: ParseNode[]) { super();} } export class ParseCompilerIden extends ParseNodeType { key = 'compileriden' as const; constructor(public token: Token, public value: string) { super();} } export class ParseEvalFunc extends ParseNodeType { key = 'evalfunc' as const; constructor(public token: Token, public func: (vm: Vm) => void | Task, public args: ParseNode[], public typeArgs: ParseNode[]) { super();} } +export class ParseConcurrency extends ParseNodeType { key = 'concurrency' as const; constructor(public token: Token, public fns: ParseNode[]) { super();} } export type ParseNode = ParseStatements | ParseLet | ParseSet | ParseOperator | ParseIdentifier | ParseNumber | ParseMeta | ParseCompTime | ParseLetConst | ParseCall | ParseList | ParseOr | ParseAnd | @@ -180,7 +181,8 @@ export type ParseNode = ParseStatements | ParseLet | ParseSet | ParseOperator | ParseOpEq | ParseWhile | ParseWhileExpr | ParseForExpr | ParseNot | ParseField | ParseExpand | ParseListComp | ParseDict | ParsePostCall | ParseSymbol | ParseNote | ParseSlice | ParseSubscript | ParseTuple | ParseClass | ParseNil | ParseBoolean | ParseElse | ParseMetaIf | ParseMetaFor | ParseMetaWhile | ParseBlock | ParseImport | - ParseCompilerIden | ParseValue | ParseConstructor | ParseQuote | ParseBytecode | ParseFreshIden | ParseFold | ParseNamedArg | ParseEvalFunc + ParseCompilerIden | ParseValue | ParseConstructor | ParseQuote | ParseBytecode | ParseFreshIden | ParseFold | + ParseNamedArg | ParseEvalFunc | ParseConcurrency // Void types mean that in secondOrder compilation, the AST doesn't return an AST export const isParseVoid = (ast: ParseNode) => ast.key == 'letconst' || ast.key === 'function' || ast.key === 'class' || ast.key === 'comptime' || ast.key === 'metawhile'; @@ -244,6 +246,7 @@ export type BytecodeInstr = { type: 'jump', address: number } | { type: 'jumpf', address: number } | { type: 'evalfunc', func: (vm: Vm) => void | Task } | + { type: 'concurrency', count: number } | { type: 'halt' } diff --git a/tests/fixtures/iterator_expansion.rad b/tests/fixtures/iterator_expansion.rad index 994a3c0..1087eba 100644 --- a/tests/fixtures/iterator_expansion.rad +++ b/tests/fixtures/iterator_expansion.rad @@ -79,32 +79,32 @@ fn test_shorthand(): numbers := [40, 20, 10, 32, 12, 3124] - x1 := @all(numbers[:] > 9) - assert(x1 == true) - print(x1) + # x1 := @all(numbers[:] > 9) + # assert(x1 == true) + # print(x1) - x2 := @all(numbers[:] < 1000) - assert(x2 == false) - print(x2) + # x2 := @all(numbers[:] < 1000) + # assert(x2 == false) + # print(x2) - y1 := @any(numbers[:] == 20) - assert(y1 == true) - print(y1) + # y1 := @any(numbers[:] == 20) + # assert(y1 == true) + # print(y1) - y2 := @any(numbers[:] == 1000) - assert(y2 == false) - print(y2) + # y2 := @any(numbers[:] == 1000) + # assert(y2 == false) + # print(y2) z1 := @sum(numbers[1:]) assert(z1 == 3198) print(z1) - w1 := @sum(bar[:]) - print(w1) - assert(w1 == 204) + # w1 := @sum(bar[:]) + # print(w1) + # assert(w1 == 204) - assert(@any(squares[:] == 81) == true) - assert(@all(factorial[:] < 1000) == false) + # assert(@any(squares[:] == 81) == true) + # assert(@all(factorial[:] < 1000) == false) fn test_min_max(): numbers := [40, 20, 10, 32, 12, 3124] @@ -136,7 +136,7 @@ fn test_concat2(): numbers := [40, 20, 10, 32, 12, 3124] - concat1 :: @concat(foo[:], bar[:], numbers[:], 2, 3, 4) + concat1 :: @concat(2, 3, 4.2) x3 := @sum(concat1[:]) print(x3) assert(x3 == 3496) @@ -201,10 +201,11 @@ fn test_last(): fn test_first(): - numbers := [40, 20, 10, 32, 12, 3124] + numbers := [40.0, 20, 10, 32, 12, 3124] + x := @first(numbers[:]) print(x) - assert(x == 40) + # assert(x == 40) fn test_range(): rng := Range(10, 20) @@ -329,27 +330,35 @@ fn test_iterator_to_array(): print(arr[:]) ... +fn test_array(): + x := 0.2 + arr := [ 3, 1, 1] + # assert(@sum(arr[:]) == 43545640) + print(arr[:]) ... + + fn main(): - test_fold() - test_shorthand() - test_concat() - test_concat2() - test_concat3() - test_concat4() - test_concat5() - test_concat6() - test_min_max() - test_people() - test_last() + # test_fold() + # test_shorthand() + # test_concat() + # test_concat2() + # test_concat3() + # test_concat4() + # test_concat5() + # test_concat6() + # test_min_max() + # test_people() + # test_last() test_first() - test_range() - test_stuff() - test_for_loop() - test_if() - # test_sideeffect() - test_template!int() - test_template!float() - test_while() - test_flatmap() - test_custom_iterator() - test_iterator_to_array() + # test_range() + # test_stuff() + # test_for_loop() + # test_if() + # # test_sideeffect() + # test_template!int() + # test_template!float() + # test_while() + # test_flatmap() + # test_custom_iterator() + # test_iterator_to_array() + # test_array() From 2fb5ef491e60d72db19eb4274514bce4c5e8793f Mon Sep 17 00:00:00 2001 From: Jake Coxon Date: Wed, 7 Aug 2024 14:06:19 +0100 Subject: [PATCH 2/6] Deferred let binding --- src/compiler_iterator.ts | 79 ++++++++++++++------------- tests/fixtures/iterator_expansion.rad | 5 ++ 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/compiler_iterator.ts b/src/compiler_iterator.ts index 0d12f65..35841f0 100644 --- a/src/compiler_iterator.ts +++ b/src/compiler_iterator.ts @@ -718,6 +718,37 @@ export const expandFuncLastSugar = (out: BytecodeWriter, noteNode: ParseNote, ar const value = new ParseStatements(node.token, [let_, reduce, iden]) visitParseNode(out, value) } + +export class DeferredLetBinding { + type: Type + binding: Binding | null + constructor() {} +} + +const createDeferObject = new ExternalFunction('createDeferObject', CompileTimeObjectType, (ctx, values) => { + return new CompTimeObjAst(CompileTimeObjectType, ctx.location, new DeferredLetBinding()) +}) +const deferToSetAst = new ExternalFunction('deferToSetAst', VoidType, (ctx, values) => { + let [value, defer] = values + if (defer instanceof CompTimeObjAst) defer = defer.value + compilerAssert(defer instanceof DeferredLetBinding, "Expected deferred let binding", { defer }) + compilerAssert(isAst(value), "Expected ast", { value, defer }) + defer.type = value.type + defer.binding = new Binding("", value.type) + return new SetAst(VoidType, ctx.location, defer.binding, value) +}) +const deferToBindingAst = new ExternalFunction('deferToBindingAst', VoidType, (ctx, values) => { + let [stmts, defer] = values + if (defer instanceof CompTimeObjAst) defer = defer.value + compilerAssert(defer instanceof DeferredLetBinding, "Expected deferred let binding", { defer }) + compilerAssert(isAst(stmts), "Expected ast", { stmts, defer }) + const binding = defer.binding + compilerAssert(binding, "Expected binding", { defer }) + const let_ = new LetAst(binding.type, ctx.location, binding, new DefaultConsAst(binding.type, ctx.location)) + const bindingAst = new BindingAst(binding.type, ctx.location, binding) + return createStatements(ctx.location, [let_, stmts, bindingAst]) +}) + export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") const node = args[0] @@ -727,50 +758,20 @@ export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, a compilerAssert(!expansion.fold, "Fold not supported in this context") - const objIden = new ParseFreshIden(node.token, new FreshBindingToken('obj')) - // const iden = new ParseFreshIden(node.token, new FreshBindingToken('first')) - const tcResult = new ParseEvalFunc(createAnonymousToken(''), (vm: Vm) => { - const value = vm.stack.pop() - const obj = vm.stack.pop() as any - compilerAssert(isAst(value), "Expected ast", { value, obj }) - obj.type = value.type - obj.binding = new Binding("", value.type) - vm.stack.push(new SetAst(VoidType, node.token.location, obj.binding, value)) - // compilerAssert(false, "Not implemented", { obj, value }) - // return Task.of(null) - }, [new ParseQuote(node.token, result)], [objIden]); - const set = new ParseStatements(node.token, [ - new ParseMeta(node.token, tcResult) - ]) + const deferIden = new ParseFreshIden(node.token, new FreshBindingToken('obj')) + const tcResult = new ParseCall(createAnonymousToken(''), + new ParseValue(node.token, deferToSetAst), [new ParseQuote(node.token, result), deferIden], []) + const set = new ParseMeta(node.token, tcResult) const break_ = new ParseBreak(node.token, expansion.breakIden, null) expansion.loopBodyNode = new ParseStatements(node.token, [set, break_]) - // TODO: Only supports numbers at the moment const reduce = compileExpansionToParseNode(out, expansion, node) - // const let_ = new ParseLet(node.token, iden, null, new ParseNumber(createAnonymousToken('0'))) - const letobj = new ParseLetConst(node.token, objIden, new ParseEvalFunc(createAnonymousToken(''), (vm: Vm) => { - // compilerAssert(false, "Not implemented", { stack: vm.stack }) - vm.stack.push({ thing: 1 }) - }, [], [])) + const letobj = new ParseLetConst(node.token, deferIden, new ParseCall(createAnonymousToken(''), new ParseValue(node.token, createDeferObject), [], [])) const stmts = new ParseStatements(node.token, [letobj, reduce]) - // const thing = new ParseMeta(node.token, new ParseCall(createAnonymousToken(''), new ParseIdentifier(createAnonymousToken('thing')), [], [] - visitParseNode(out, new ParseEvalFunc(node.token, (vm) => { - const obj = vm.stack.pop() - const stmts = vm.stack.pop() - compilerAssert(isAst(stmts), "Expected ast", { stmts }) - const binding = obj.binding - compilerAssert(binding instanceof Binding, "Expected binding", { obj }) - - const newStmts = createStatements(vm.location, [ - new LetAst(binding.type, vm.location, binding, new DefaultConsAst(binding.type, vm.location)), - stmts, - new BindingAst(binding.type, vm.location, binding) - ]) - - vm.stack.push(newStmts) - // compilerAssert(false, "Not implemented", { stmts, obj }) - - }, [stmts, objIden], [])) + const call_ = new ParseCall(createAnonymousToken(''), new ParseValue(node.token, deferToBindingAst), [ + new ParseQuote(createAnonymousToken(''), stmts), new ParseQuote(createAnonymousToken(''), deferIden)], []) + const resultBinding = new ParseMeta(createAnonymousToken(''), call_) + visitParseNode(out, resultBinding) } export const expandFuncMinSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") diff --git a/tests/fixtures/iterator_expansion.rad b/tests/fixtures/iterator_expansion.rad index 1087eba..8c91972 100644 --- a/tests/fixtures/iterator_expansion.rad +++ b/tests/fixtures/iterator_expansion.rad @@ -205,6 +205,11 @@ fn test_first(): x := @first(numbers[:]) print(x) + + people := [Person(10), Person(20), Person(30), Person(40), Person(50)] + p := @first(people[1:]) + + print(p.age) # assert(x == 40) fn test_range(): From 3378ac2eab03a95a302cdad7f0a201ea69584a16 Mon Sep 17 00:00:00 2001 From: Jake Coxon Date: Wed, 7 Aug 2024 16:45:41 +0100 Subject: [PATCH 3/6] Try defer helper --- src/compiler_iterator.ts | 62 +++++++++++++++++++++++++++++----------- src/defs.ts | 2 +- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/compiler_iterator.ts b/src/compiler_iterator.ts index 35841f0..11b0f01 100644 --- a/src/compiler_iterator.ts +++ b/src/compiler_iterator.ts @@ -722,19 +722,27 @@ export const expandFuncLastSugar = (out: BytecodeWriter, noteNode: ParseNote, ar export class DeferredLetBinding { type: Type binding: Binding | null + letAst: LetAst constructor() {} } const createDeferObject = new ExternalFunction('createDeferObject', CompileTimeObjectType, (ctx, values) => { return new CompTimeObjAst(CompileTimeObjectType, ctx.location, new DeferredLetBinding()) }) -const deferToSetAst = new ExternalFunction('deferToSetAst', VoidType, (ctx, values) => { +const deferAssignSet = new ExternalFunction('deferAssignSet', VoidType, (ctx, values) => { let [value, defer] = values if (defer instanceof CompTimeObjAst) defer = defer.value compilerAssert(defer instanceof DeferredLetBinding, "Expected deferred let binding", { defer }) compilerAssert(isAst(value), "Expected ast", { value, defer }) - defer.type = value.type - defer.binding = new Binding("", value.type) + compilerAssert(value instanceof LetAst, "Expected let ast", { value, defer }) + Object.assign(defer, { letAst: value, type: value.type, binding: value.binding }) +}) +const deferToSetAst = new ExternalFunction('deferToSetAst', VoidType, (ctx, values) => { + let [value, defer] = values + if (defer instanceof CompTimeObjAst) defer = defer.value + compilerAssert(isAst(value), "Expected ast", { value, defer }) + compilerAssert(defer instanceof DeferredLetBinding, "Expected deferred let binding", { defer }) + compilerAssert(defer.binding, "Expected binding", { defer }) return new SetAst(VoidType, ctx.location, defer.binding, value) }) const deferToBindingAst = new ExternalFunction('deferToBindingAst', VoidType, (ctx, values) => { @@ -744,11 +752,40 @@ const deferToBindingAst = new ExternalFunction('deferToBindingAst', VoidType, (c compilerAssert(isAst(stmts), "Expected ast", { stmts, defer }) const binding = defer.binding compilerAssert(binding, "Expected binding", { defer }) - const let_ = new LetAst(binding.type, ctx.location, binding, new DefaultConsAst(binding.type, ctx.location)) const bindingAst = new BindingAst(binding.type, ctx.location, binding) - return createStatements(ctx.location, [let_, stmts, bindingAst]) + return createStatements(ctx.location, [defer.letAst, stmts, bindingAst]) }) +const createDefaultLetFromType = new ExternalFunction('createDefaultLetFromType', VoidType, (ctx, values) => { + let [value] = values + compilerAssert(isAst(value), "Expected ast", { value }) + const binding = new Binding("defer", value.type) + const let_ = new LetAst(binding.type, ctx.location, binding, new DefaultConsAst(binding.type, ctx.location)) + return let_ +}) + +const deferredLetHelper = (token: Token, result: ParseNode) => { + const deferIden = new ParseFreshIden(token, new FreshBindingToken('obj')) + return { + setter: () => { + const toLet_ = new ParseCall(token, + new ParseValue(token, createDefaultLetFromType), [new ParseQuote(token, result)], []) + const tcResult = new ParseCall(createAnonymousToken(''), + new ParseValue(token, deferAssignSet), [toLet_, deferIden], []) + const call_ = new ParseCall(createAnonymousToken(''), + new ParseValue(token, deferToSetAst), [new ParseQuote(token, result), deferIden], []) + return new ParseStatements(token, [new ParseCompTime(token, tcResult), new ParseMeta(token, call_),]) + }, + result: (original: ParseNode) => { + const letobj = new ParseLetConst(token, deferIden, new ParseCall(createAnonymousToken(''), new ParseValue(token, createDeferObject), [], [])) + const stmts = new ParseStatements(token, [letobj, original]) + const call_ = new ParseCall(createAnonymousToken(''), new ParseValue(token, deferToBindingAst), [ + new ParseQuote(createAnonymousToken(''), stmts), new ParseQuote(createAnonymousToken(''), deferIden)], []) + return new ParseMeta(createAnonymousToken(''), call_) + } + } +} + export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") const node = args[0] @@ -758,20 +795,11 @@ export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, a compilerAssert(!expansion.fold, "Fold not supported in this context") - const deferIden = new ParseFreshIden(node.token, new FreshBindingToken('obj')) - const tcResult = new ParseCall(createAnonymousToken(''), - new ParseValue(node.token, deferToSetAst), [new ParseQuote(node.token, result), deferIden], []) - const set = new ParseMeta(node.token, tcResult) + const deferredLet_ = deferredLetHelper(node.token, result) const break_ = new ParseBreak(node.token, expansion.breakIden, null) - expansion.loopBodyNode = new ParseStatements(node.token, [set, break_]) - + expansion.loopBodyNode = new ParseStatements(node.token, [deferredLet_.setter(), break_]) const reduce = compileExpansionToParseNode(out, expansion, node) - const letobj = new ParseLetConst(node.token, deferIden, new ParseCall(createAnonymousToken(''), new ParseValue(node.token, createDeferObject), [], [])) - const stmts = new ParseStatements(node.token, [letobj, reduce]) - const call_ = new ParseCall(createAnonymousToken(''), new ParseValue(node.token, deferToBindingAst), [ - new ParseQuote(createAnonymousToken(''), stmts), new ParseQuote(createAnonymousToken(''), deferIden)], []) - const resultBinding = new ParseMeta(createAnonymousToken(''), call_) - visitParseNode(out, resultBinding) + visitParseNode(out, deferredLet_.result(reduce)) } export const expandFuncMinSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") diff --git a/src/defs.ts b/src/defs.ts index 0054805..3490ac1 100644 --- a/src/defs.ts +++ b/src/defs.ts @@ -1017,7 +1017,7 @@ export function bytecodeToString(bytecodeProgram: BytecodeProgram) { const instr = (instr: BytecodeInstr) => { const { type, ...args } = instr; const values = Object.entries(args) - .map(([k, v]) => `${k}: ${typeof v === 'function' ? '' : v}`) + .map(([k, v]) => `${k}: ${typeof v === 'function' ? '' : typeof v === 'string' ? v : Inspect(v)}`) .join(", "); return `${type.padStart("beginblockast".length, " ")} ${values}`; }; From da2e2c43fa55c7ae059fa48f1f0e3b94b360d0cf Mon Sep 17 00:00:00 2001 From: Jake Coxon Date: Wed, 7 Aug 2024 17:12:33 +0100 Subject: [PATCH 4/6] Try make it more streamlined --- src/compiler_iterator.ts | 63 +++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/compiler_iterator.ts b/src/compiler_iterator.ts index 11b0f01..744c44d 100644 --- a/src/compiler_iterator.ts +++ b/src/compiler_iterator.ts @@ -756,34 +756,44 @@ const deferToBindingAst = new ExternalFunction('deferToBindingAst', VoidType, (c return createStatements(ctx.location, [defer.letAst, stmts, bindingAst]) }) -const createDefaultLetFromType = new ExternalFunction('createDefaultLetFromType', VoidType, (ctx, values) => { +const createFreshLet = new ExternalFunction('createFreshLet', VoidType, (ctx, values) => { let [value] = values compilerAssert(isAst(value), "Expected ast", { value }) const binding = new Binding("defer", value.type) - const let_ = new LetAst(binding.type, ctx.location, binding, new DefaultConsAst(binding.type, ctx.location)) - return let_ + return new LetAst(binding.type, ctx.location, binding, value) }) -const deferredLetHelper = (token: Token, result: ParseNode) => { - const deferIden = new ParseFreshIden(token, new FreshBindingToken('obj')) - return { - setter: () => { - const toLet_ = new ParseCall(token, - new ParseValue(token, createDefaultLetFromType), [new ParseQuote(token, result)], []) - const tcResult = new ParseCall(createAnonymousToken(''), - new ParseValue(token, deferAssignSet), [toLet_, deferIden], []) - const call_ = new ParseCall(createAnonymousToken(''), - new ParseValue(token, deferToSetAst), [new ParseQuote(token, result), deferIden], []) - return new ParseStatements(token, [new ParseCompTime(token, tcResult), new ParseMeta(token, call_),]) - }, - result: (original: ParseNode) => { - const letobj = new ParseLetConst(token, deferIden, new ParseCall(createAnonymousToken(''), new ParseValue(token, createDeferObject), [], [])) - const stmts = new ParseStatements(token, [letobj, original]) - const call_ = new ParseCall(createAnonymousToken(''), new ParseValue(token, deferToBindingAst), [ - new ParseQuote(createAnonymousToken(''), stmts), new ParseQuote(createAnonymousToken(''), deferIden)], []) - return new ParseMeta(createAnonymousToken(''), call_) - } - } +const createDefaultFromType = new ExternalFunction('createDefaultFromType', VoidType, (ctx, values) => { + let [type] = values + compilerAssert(isType(type), "Expected type", { type }) + return new DefaultConsAst(type, ctx.location) +}) + +const typeOf = new ExternalFunction('typeOf', VoidType, (ctx, values) => { + let [value] = values + compilerAssert(isAst(value), "Expected ast", { value }) + return value.type +}) + +const deferredHelperSetter = (token: Token, deferIden: ParseFreshIden, result: ParseNode) => { + const typeOf_ = new ParseCall(token, new ParseValue(token, typeOf), [new ParseQuote(token, result)], []) + const default_ = new ParseCall(token, + new ParseValue(token, createDefaultFromType), [typeOf_], []) + const toLet_ = new ParseCall(token, + new ParseValue(token, createFreshLet), [default_], []) + const tcResult = new ParseCall(createAnonymousToken(''), + new ParseValue(token, deferAssignSet), [toLet_, deferIden], []) + const call_ = new ParseCall(createAnonymousToken(''), + new ParseValue(token, deferToSetAst), [new ParseQuote(token, result), deferIden], []) + return new ParseStatements(token, [new ParseCompTime(token, tcResult), new ParseMeta(token, call_),]) +} + +const deferredHelperResult = (token: Token, deferIden: ParseFreshIden, original: ParseNode) => { + const letobj = new ParseLetConst(token, deferIden, new ParseCall(createAnonymousToken(''), new ParseValue(token, createDeferObject), [], [])) + const stmts = new ParseStatements(token, [letobj, original]) + const call_ = new ParseCall(createAnonymousToken(''), new ParseValue(token, deferToBindingAst), [ + new ParseQuote(createAnonymousToken(''), stmts), new ParseQuote(createAnonymousToken(''), deferIden)], []) + return new ParseMeta(createAnonymousToken(''), call_) } export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { @@ -795,11 +805,12 @@ export const expandFuncFirstSugar = (out: BytecodeWriter, noteNode: ParseNote, a compilerAssert(!expansion.fold, "Fold not supported in this context") - const deferredLet_ = deferredLetHelper(node.token, result) + const deferIden = new ParseFreshIden(node.token, new FreshBindingToken('defer')) const break_ = new ParseBreak(node.token, expansion.breakIden, null) - expansion.loopBodyNode = new ParseStatements(node.token, [deferredLet_.setter(), break_]) + const deferSet_ = deferredHelperSetter(node.token, deferIden, result) + expansion.loopBodyNode = new ParseStatements(node.token, [deferSet_, break_]) const reduce = compileExpansionToParseNode(out, expansion, node) - visitParseNode(out, deferredLet_.result(reduce)) + visitParseNode(out, deferredHelperResult(node.token, deferIden, reduce)) } export const expandFuncMinSugar = (out: BytecodeWriter, noteNode: ParseNote, args: ParseNode[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") From 69c2f8d9a756c9eb006aa0783bf2235e89eb59dd Mon Sep 17 00:00:00 2001 From: Jake Coxon Date: Wed, 7 Aug 2024 17:56:11 +0100 Subject: [PATCH 5/6] Array constructor append value partial function --- src/compiler.ts | 11 +++- src/compiler_iterator.ts | 32 +++++++---- src/defs.ts | 2 +- tests/fixtures/iterator_expansion.rad | 82 +++++++++++++-------------- 4 files changed, 73 insertions(+), 54 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 59930c5..1a40315 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -123,7 +123,8 @@ export const BytecodeDefault: ParseTreeTable = { visitAll(out, node.typeArgs) visitAll(out, node.args); if (node.left instanceof ParseValue) { // Internal desugar results in a fixed value sometimes - pushBytecode(out, node.token, { type: "callobj", callable: node.left.value, count: node.args.length, tcount: node.typeArgs.length }) + visitParseNode(out, node.left) + pushBytecode(out, node.token, { type: "callobj", count: node.args.length, tcount: node.typeArgs.length }) return; } if (node.left instanceof ParseIdentifier) { @@ -138,6 +139,11 @@ export const BytecodeDefault: ParseTreeTable = { compilerAssert(false, "Call with field not implemented yet", { node }) return; } + if (node.left instanceof ParseFunction) { + visitParseNode(out, node.left) + pushBytecode(out, node.token, { type: "callobj", count: node.args.length, tcount: node.typeArgs.length }) + return; + } compilerAssert(false, "Call with non-identifier not implemented yet", { left: node.left}) }, postcall: (out, node) => { @@ -1229,7 +1235,8 @@ const instructions: InstructionMapping = { }) ) }, - callobj: (vm, { callable, count, tcount }) => { + callobj: (vm, { count, tcount }) => { + const callable = popStack(vm) const values = popValues(vm, count); const typeArgs = popValues(vm, tcount || 0); return TaskDef(callFunctionFromValueTask, vm, callable, typeArgs, values) diff --git a/src/compiler_iterator.ts b/src/compiler_iterator.ts index 744c44d..6911da8 100644 --- a/src/compiler_iterator.ts +++ b/src/compiler_iterator.ts @@ -170,6 +170,27 @@ const arrayConstructorFinish = new ExternalFunction('arrayConstructorFinish', Vo return createStatements(ctx.location, [let_, ...constructor.calls, bindingAst]) }) +const appendValuePartialFn = (() => { + const token = createAnonymousToken('') + const consIdenParam = new ParseIdentifier(createAnonymousToken('cons')) + const exprParam = new ParseIdentifier(createAnonymousToken('expr')) + + const exprQuote = new ParseQuote(token, exprParam) + const iden = new ParseFreshIden(token, new FreshBindingToken('elem')) + const let_ = new ParseLet(token, iden, null, exprQuote) + const call = new ParseCall(token, new ParseValue(token, arrayConstructorTypeCheck), [consIdenParam, iden], []) + const call2 = new ParseCall(token, new ParseValue(token, arrayConstructorCreateAppend), [consIdenParam, iden], []) + const call3 = new ParseCall(token, new ParseValue(token, arrayConstructorAddAppendCall), [consIdenParam, call2], []) + const meta_ = new ParseMeta(token, new ParseStatements(token, [let_, call, call3])) + const decl = createAnonymousParserFunctionDecl('appendValue', token, [], meta_) + + const params: ParserFunctionParameter[] = [ + { name: consIdenParam, storage: null, type: null }, + { name: exprParam, storage: null, type: null } + ] + return createAnonymousParserFunctionDecl('appendValuePartial', token, params, new ParseFunction(token, decl)) +})() + export const listConstructorSugar = (out: BytecodeWriter, node: ParseList) => { const listConstructorIden = new ParseFreshIden(node.token, new FreshBindingToken('list')) const numExprs = node.exprs.length @@ -193,16 +214,7 @@ export const listConstructorSugar = (out: BytecodeWriter, node: ParseList) => { const fn = createAnonymousParserFunctionDecl('appendIterator', node.token, [], meta_) return new ParseFunction(node.token, fn) } else { - const exprQuote = new ParseQuote(node.token, expr) - const iden = new ParseFreshIden(node.token, new FreshBindingToken('elem')) - const let_ = new ParseLet(node.token, iden, null, exprQuote) - const call = new ParseCall(node.token, new ParseValue(node.token, arrayConstructorTypeCheck), [listConstructorIden, iden], []) - const call2 = new ParseCall(node.token, new ParseValue(node.token, arrayConstructorCreateAppend), [listConstructorIden, iden], []) - const call3 = new ParseCall(node.token, new ParseValue(node.token, arrayConstructorAddAppendCall), [listConstructorIden, call2], []) - const meta_ = new ParseMeta(node.token, new ParseStatements(node.token, [let_, call, call3])) - - const fn = createAnonymousParserFunctionDecl('appendValue', node.token, [], meta_) - return new ParseFunction(node.token, fn) + return new ParseCall(node.token, new ParseFunction(node.token, appendValuePartialFn), [listConstructorIden, new ParseQuote(node.token, expr)], []) } }).filter(x => x) as ParseFunction[] diff --git a/src/defs.ts b/src/defs.ts index 3490ac1..2737900 100644 --- a/src/defs.ts +++ b/src/defs.ts @@ -204,7 +204,7 @@ export type BytecodeInstr = { type: 'dictast', count: number } | { type: 'closure', id: number } | { type: 'call', name: string, count: number, tcount: number } | - { type: 'callobj', callable: unknown, count: number, tcount: number } | + { type: 'callobj', count: number, tcount: number } | { type: 'compilerfn', name: string, count: number, tcount: number } | { type: 'return', r: boolean } | { type: 'namedarg' } | diff --git a/tests/fixtures/iterator_expansion.rad b/tests/fixtures/iterator_expansion.rad index 8c91972..3d9e576 100644 --- a/tests/fixtures/iterator_expansion.rad +++ b/tests/fixtures/iterator_expansion.rad @@ -79,32 +79,32 @@ fn test_shorthand(): numbers := [40, 20, 10, 32, 12, 3124] - # x1 := @all(numbers[:] > 9) - # assert(x1 == true) - # print(x1) + x1 := @all(numbers[:] > 9) + assert(x1 == true) + print(x1) - # x2 := @all(numbers[:] < 1000) - # assert(x2 == false) - # print(x2) + x2 := @all(numbers[:] < 1000) + assert(x2 == false) + print(x2) - # y1 := @any(numbers[:] == 20) - # assert(y1 == true) - # print(y1) + y1 := @any(numbers[:] == 20) + assert(y1 == true) + print(y1) - # y2 := @any(numbers[:] == 1000) - # assert(y2 == false) - # print(y2) + y2 := @any(numbers[:] == 1000) + assert(y2 == false) + print(y2) z1 := @sum(numbers[1:]) assert(z1 == 3198) print(z1) - # w1 := @sum(bar[:]) - # print(w1) - # assert(w1 == 204) + w1 := @sum(bar[:]) + print(w1) + assert(w1 == 204) - # assert(@any(squares[:] == 81) == true) - # assert(@all(factorial[:] < 1000) == false) + assert(@any(squares[:] == 81) == true) + assert(@all(factorial[:] < 1000) == false) fn test_min_max(): numbers := [40, 20, 10, 32, 12, 3124] @@ -136,7 +136,7 @@ fn test_concat2(): numbers := [40, 20, 10, 32, 12, 3124] - concat1 :: @concat(2, 3, 4.2) + concat1 :: @concat(foo[:], bar[:], numbers[:], 2, 3, 4) x3 := @sum(concat1[:]) print(x3) assert(x3 == 3496) @@ -343,27 +343,27 @@ fn test_array(): fn main(): - # test_fold() - # test_shorthand() - # test_concat() - # test_concat2() - # test_concat3() - # test_concat4() - # test_concat5() - # test_concat6() - # test_min_max() - # test_people() - # test_last() + test_fold() + test_shorthand() + test_concat() + test_concat2() + test_concat3() + test_concat4() + test_concat5() + test_concat6() + test_min_max() + test_people() + test_last() test_first() - # test_range() - # test_stuff() - # test_for_loop() - # test_if() - # # test_sideeffect() - # test_template!int() - # test_template!float() - # test_while() - # test_flatmap() - # test_custom_iterator() - # test_iterator_to_array() - # test_array() + test_range() + test_stuff() + test_for_loop() + test_if() + # test_sideeffect() + test_template!int() + test_template!float() + test_while() + test_flatmap() + test_custom_iterator() + test_iterator_to_array() + test_array() From 95a0341557b7144f0c4cfcc4a1dd21f4f4339c7e Mon Sep 17 00:00:00 2001 From: Jake Coxon Date: Thu, 8 Aug 2024 20:38:53 +0100 Subject: [PATCH 6/6] Fix function definition lookup --- src/compiler.ts | 2 -- src/compiler_functions.ts | 8 +++++--- src/defs.ts | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 1a40315..8fc2b2b 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1717,7 +1717,6 @@ export const runTopLevelTask = (ctx: TaskContext, stmts: ParseStatements, module const createInitializerFunctionTask = (ctx: TaskContext) => { const decl: ParserFunctionDecl = { - id: undefined, debugName: ``, token: createAnonymousToken(''), functionMetaName: null, name: null, typeParams: [], params: [], keywords: [], anonymous: true, returnType: null, body: null, annotations: [], variadic: false @@ -1743,7 +1742,6 @@ const createInitializerFunctionTask = (ctx: TaskContext) => { const createEntryFunctionTask = (ctx: TaskContext) => { const decl: ParserFunctionDecl = { - id: undefined, debugName: ``, token: createAnonymousToken(''), functionMetaName: null, name: null, typeParams: [], params: [], keywords: [], anonymous: true, returnType: null, body: null, annotations: [], variadic: false diff --git a/src/compiler_functions.ts b/src/compiler_functions.ts index 2b20849..cf05923 100644 --- a/src/compiler_functions.ts +++ b/src/compiler_functions.ts @@ -5,18 +5,20 @@ import { Task, TaskDef, Unit } from "./tasks"; export const insertFunctionDefinition = (compilerState: GlobalCompilerState, decl: ParserFunctionDecl) => { - if (decl.id !== undefined) return compilerState.functionDefinitions[decl.id]; + const existing = compilerState.functionDefinitionsByDeclaration.get(decl) + if (existing) return existing - decl.id = compilerState.functionDefinitions.length; + const id = compilerState.functionDefinitions.length; const keywords = decl.keywords.map(x => x instanceof ParseNote ? x.expr.token.value : x.token.value) const inline = !!decl.anonymous || keywords.includes('inline') const funcDef = new FunctionDefinition( - decl.id, decl.debugName, + id, decl.debugName, decl.name, decl.typeParams, decl.params, decl.returnType, decl.body, inline, decl.annotations) funcDef.variadic = decl.variadic funcDef.keywords.push(...keywords) + compilerState.functionDefinitionsByDeclaration.set(decl, funcDef) if (funcDef.keywords.includes("external")) { compilerAssert(decl.name, "Expected name") diff --git a/src/defs.ts b/src/defs.ts index 2737900..2759052 100644 --- a/src/defs.ts +++ b/src/defs.ts @@ -72,7 +72,7 @@ export type ParserFunctionParameter = { } // These are reference types that id will be filled in later. export type ParserFunctionDecl = { - id: number | undefined, debugName: string, anonymous?: boolean, + debugName: string, anonymous?: boolean, token: Token, functionMetaName: ParseIdentifier | null, name: ParseIdentifier | null, typeParams: ParseNode[], params: ParserFunctionParameter[], returnType: ParseNode | null, body: ParseNode | null, keywords: ParseNode[], @@ -863,6 +863,7 @@ export type Logger = { log: (...args: any[]) => void } export type GlobalCompilerState = { compiledFunctions: Map; functionDefinitions: FunctionDefinition[], + functionDefinitionsByDeclaration: Map, classDefinitions: ClassDefinition[], moduleLoader: ModuleLoader methods: WeakMap, @@ -933,6 +934,7 @@ export const createDefaultGlobalCompiler = () => { const globalCompiler: GlobalCompilerState = { compiledFunctions: new Map(), functionDefinitions: [], + functionDefinitionsByDeclaration: new Map(), classDefinitions: [], allWaitingEvents: [], globalLets: [],