diff --git a/src/compiler.ts b/src/compiler.ts index 369b414..0df3112 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) { @@ -122,11 +123,13 @@ 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) { - pushBytecode(out, node.token, { type: "call", name: node.left.token.value, count: node.args.length, tcount: node.typeArgs.length }); + if (node.left instanceof ParseIdentifier || node.left instanceof ParseFreshIden) { + const name = node.left instanceof ParseFreshIden ? node.left.freshBindingToken.identifier : node.left.token.value + pushBytecode(out, node.token, { type: "call", name, count: node.args.length, tcount: node.typeArgs.length }); return; } if (node.left instanceof ParseCompilerIden) { @@ -137,6 +140,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) => { @@ -298,6 +306,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) { @@ -474,8 +483,9 @@ export const BytecodeSecondOrder: ParseTreeTable = { const name = new ParseFreshIden(node.token, new FreshBindingToken('tmpfn')) visitParseNode(out, new ParseLetConst(node.token, name, node.left)) pushBytecode(out, node.token, { type: 'pop' }) + node.typeArgs.forEach(x => writeMeta(out, x)); visitAll(out, node.args) - pushBytecode(out, node.token, { type: "callast", name: name.freshBindingToken.identifier, count: node.args.length, tcount: 0 }); + pushBytecode(out, node.token, { type: "callast", name: name.freshBindingToken.identifier, count: node.args.length, tcount: node.typeArgs.length }); return } compilerAssert(false, "Call with non-identifier not implemented yet", { left: node.left}) @@ -586,9 +596,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 }) @@ -728,7 +739,7 @@ const letLocalAst = (vm: Vm, name: string, type: Type | null, value: Ast | null) compilerAssert(type || value, "Expected type or initial value for let binding $name", { name }); compilerAssert(!Object.hasOwn(vm.scope, name), `Already defined $name`, { name }); let inferType = type || value!.type - compilerAssert(inferType !== VoidType, "Expected type for local $name but got $inferType", { name, inferType }) + compilerAssert(inferType !== VoidType, "Expected type for local $name but got $inferType", { name, inferType, value }) inferType = propagateLiteralType(inferType, value) const binding = new Binding(name, inferType) setScopeValueAndResolveEvents(vm.scope, name, binding) // This is for globals usually. Locals should be in order @@ -889,6 +900,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 +1048,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() }); @@ -1220,7 +1237,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) @@ -1333,7 +1351,10 @@ function executeVmTask(ctx: TaskContext, { vm } : { vm: Vm }, p: void): Task { const decl: ParserFunctionDecl = { - id: undefined, debugName: ``, token: createAnonymousToken(''), functionMetaName: null, name: null, typeParams: [], params: [], keywords: [], anonymous: true, returnType: null, body: null, annotations: [], variadic: false @@ -1724,7 +1744,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..2ce73c1 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") @@ -302,8 +304,9 @@ function functionInlineTask(ctx: TaskContext, { location, func, typeArgs, parent func.typeParams.forEach((typeParam, i) => { compilerAssert(typeParam instanceof ParseIdentifier, "Not implemented") - const typeArg = typeArgs[i] || result.substitutions[typeParam.token.value] - compilerAssert(typeArg, "Type arg not found $name", { name: typeParam.token.value, typeArgs }) + let typeArg = typeArgs[i] + if (typeArg === undefined) typeArg = result.substitutions[typeParam.token.value] + compilerAssert(typeArg !== undefined, "Type arg not found $name", { name: typeParam.token.value, typeArgs }) templateScope[typeParam.token.value] = typeArg }); @@ -312,7 +315,7 @@ function functionInlineTask(ctx: TaskContext, { location, func, typeArgs, parent .chainFn((task, ast) => { compilerAssert(isAst(ast), "Expected ast got $ast", { ast }); - ctx.globalCompiler.logger.log(textColors.cyan(`Compiled inline ${func.debugName}`)) + // ctx.globalCompiler.logger.log(textColors.cyan(`Compiled inline ${func.debugName}`)) // TODO: This is basically what endblockast instruction does. can we fuse it? let type = propagatedLiteralAst(ast).type if (breakBlock.type) { diff --git a/src/compiler_iterator.ts b/src/compiler_iterator.ts index 8198644..a8af959 100644 --- a/src/compiler_iterator.ts +++ b/src/compiler_iterator.ts @@ -1,6 +1,6 @@ import { BytecodeSecondOrder, compileFunctionPrototype, getCommonType, getOperatorTable, loadModule, popStack, popValues, propagateLiteralType, propagatedLiteralAst, pushBytecode, resolveScope, unknownToAst, visitParseNode } from "./compiler" import { compileExportedFunctionTask, createCallAstFromValue, createCallAstFromValueAndPushValue, createMethodCall, insertFunctionDefinition } from "./compiler_functions" -import { Ast, BytecodeWriter, Closure, CompiledClass, ConstructorAst, ExternalFunction, FieldAst, FreshBindingToken, ParameterizedType, ParseBlock, ParseBytecode, ParseCall, ParseCompilerIden, ParseConstructor, ParseElse, ParseExpand, ParseFor, ParseFunction, ParseIdentifier, ParseIf, ParseLet, ParseList, ParseListComp, ParseMeta, ParseNode, ParseNumber, ParseOpEq, ParseOperator, ParseQuote, ParseSet, ParseSlice, ParseStatements, ParseSubscript, ParseValue, ParseWhile, Scope, SourceLocation, SubCompilerState, Token, TupleTypeConstructor, VoidType, compilerAssert, createAnonymousParserFunctionDecl, createAnonymousToken, ParseFreshIden, ParseAnd, ParseFold, ParseForExpr, ParseWhileExpr, Module, pushSubCompilerState, createScope, TaskContext, CompilerError, AstType, OperatorAst, CompilerFunction, CallAst, RawPointerType, SubscriptAst, IntType, expectType, SetSubscriptAst, ParserFunctionParameter, FunctionType, Binding, StringType, ValueFieldAst, LetAst, BindingAst, createStatements, StringAst, FloatType, DoubleType, CompilerFunctionCallContext, Vm, expectAst, NumberAst, Type, UserCallAst, hashValues, NeverType, IfAst, BoolType, VoidAst, LoopObject, CompileTimeObjectType, u64Type, FunctionDefinition, ParserFunctionDecl, StatementsAst, isTypeScalar, IntLiteralType, FloatLiteralType, isAst, isType, isTypeCheckError, InterleaveAst, ContinueInterAst, CompTimeObjAst, ParseEvalFunc, SetAst, DefaultConsAst, WhileAst, BoolAst, isArray, ExpansionSelector, ParseNote, ExpansionCompilerState, ParseBoolean, ParseOr, ParseBreak, filterNotNull, ParseTuple, ParseNot, ParseLetConst } from "./defs" +import { Ast, BytecodeWriter, Closure, CompiledClass, ConstructorAst, ExternalFunction, FieldAst, FreshBindingToken, ParameterizedType, ParseBlock, ParseBytecode, ParseCall, ParseCompilerIden, ParseConstructor, ParseElse, ParseExpand, ParseFor, ParseFunction, ParseIdentifier, ParseIf, ParseLet, ParseList, ParseListComp, ParseMeta, ParseNode, ParseNumber, ParseOpEq, ParseOperator, ParseQuote, ParseSet, ParseSlice, ParseStatements, ParseSubscript, ParseValue, ParseWhile, Scope, SourceLocation, SubCompilerState, Token, TupleTypeConstructor, VoidType, compilerAssert, createAnonymousParserFunctionDecl, createAnonymousToken, ParseFreshIden, ParseAnd, ParseFold, ParseForExpr, ParseWhileExpr, Module, pushSubCompilerState, createScope, TaskContext, CompilerError, AstType, OperatorAst, CompilerFunction, CallAst, RawPointerType, SubscriptAst, IntType, expectType, SetSubscriptAst, ParserFunctionParameter, FunctionType, Binding, StringType, ValueFieldAst, LetAst, BindingAst, createStatements, StringAst, FloatType, DoubleType, CompilerFunctionCallContext, Vm, expectAst, NumberAst, Type, UserCallAst, hashValues, NeverType, IfAst, BoolType, VoidAst, LoopObject, CompileTimeObjectType, u64Type, FunctionDefinition, ParserFunctionDecl, StatementsAst, isTypeScalar, IntLiteralType, FloatLiteralType, isAst, isType, isTypeCheckError, InterleaveAst, ContinueInterAst, CompTimeObjAst, ParseEvalFunc, SetAst, DefaultConsAst, WhileAst, BoolAst, isArray, ExpansionSelector, ParseNote, ExpansionCompilerState, ParseBoolean, ParseOr, ParseBreak, filterNotNull, ParseTuple, ParseNot, ParseLetConst, ParseConcurrency, ParseCompTime, ParseNil } from "./defs" import { Event, Task, TaskDef, isTask } from "./tasks" @@ -158,15 +158,38 @@ const createConstructor = (vm: Vm, constructor: ArrayConstructorCompiler) => { }) ) } -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]) +}) + +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')) @@ -191,35 +214,12 @@ 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[] - - 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], []))) } @@ -284,47 +284,201 @@ const createArraySetterIterator = (token: Token, subCompilerState: SubCompilerSt return { closure, yieldParam, indexIdentifier, subscriptIterator, valueIdentifier } } -const createIteratorSliceLoop = (token: Token, subCompilerState: SubCompilerState, selector: { node: ParseNode, start: ParseNode | null, end: ParseNode | null, step: ParseNode | null, setterIdentifier: ParseNode | null, indexIdentifier: ParseFreshIden | null }) => { + +const letIn = (token: Token, node: ParseNode, f: (iden: ParseFreshIden) => ParseNode[]) => { + const iden = new ParseFreshIden(token, new FreshBindingToken('let_in')) + const let_ = new ParseLet(token, iden, null, node) + return new ParseStatements(token, [let_, ...f(iden)]) +} + +const metaIf = (token: Token, cond: ParseNode, then: ParseNode, else_: ParseNode | null) => new ParseMeta(token, new ParseIf(token, false, cond, new ParseQuote(token, then), + new ParseElse(token, else_ ? new ParseQuote(token, else_) : new ParseNil(token)))) + +const createIteratorSliceLoopOld = (token: Token, subCompilerState: SubCompilerState, selector: { node: ParseNode, start: ParseNode | null, end: ParseNode | null, step: ParseNode | null, setterIdentifier: ParseNode | null, indexIdentifier: ParseFreshIden | null }) => { const consumeParam = new ParseFreshIden(token, new FreshBindingToken('consume')) const yieldParam = new ParseFreshIden(token, new FreshBindingToken('yield')) - const indexIdentifier = selector.indexIdentifier + const indexIdentifier = new ParseFreshIden(token, new FreshBindingToken('index')) compilerAssert(indexIdentifier) const fnParams: ParserFunctionParameter[] = [ { name: consumeParam, storage: null, type: null }, { name: yieldParam, storage: null, type: null }, ] + const startIden = new ParseFreshIden(token, new FreshBindingToken('start')) + const endIden = new ParseFreshIden(token, new FreshBindingToken('end')) + const stepIden = new ParseFreshIden(token, new FreshBindingToken('step')) + // const indexIden = new ParseFreshIden(token, new FreshBindingToken('index')) + + const letStartNode = metaIf(token, startIden, + new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('start')), null, startIden), null) + const letEndNode = metaIf(token, endIden, + new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('end')), null, endIden), null) + const letStepNode = metaIf(token, stepIden, + new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('step')), null, stepIden), null) + const letIndexNode = new ParseLet(token, indexIdentifier, null, new ParseNumber(createAnonymousToken('0'))) + const incNode = new ParseOpEq(createAnonymousToken("+="), indexIdentifier, new ParseNumber(createAnonymousToken('1'))) + const consumedValue = new ParseFreshIden(token, new FreshBindingToken('slice')) + const trueNode = new ParseBoolean(createAnonymousToken('true')) + + + const loopCondNode = metaIf(token, endIden, new ParseOperator(createAnonymousToken("<"), [indexIdentifier, endIden]), trueNode) + + const yieldCondsIden = new ParseFreshIden(token, new FreshBindingToken('yield_conds')) + const letYieldConds = new ParseLetConst(token, yieldCondsIden, new ParseNil(createAnonymousToken(''))) + const foo1 = new ParseCompTime(token, new ParseIf(token, true, startIden, + new ParseSet(createAnonymousToken(''), yieldCondsIden, + new ParseQuote(token, new ParseOperator(createAnonymousToken(">="), [indexIdentifier, startIden]))), null) + ) + const check = metaIf(token, startIden, new ParseOperator(createAnonymousToken("-"), [indexIdentifier, startIden]), indexIdentifier) + + const modCond = new ParseQuote(token, new ParseOperator(createAnonymousToken("=="), [ + new ParseOperator(createAnonymousToken("mod"), [check, stepIden]), + new ParseNumber(createAnonymousToken('0'))])) + const modCondIf = letIn(token, modCond, (modCond) => { + return [new ParseSet(token, yieldCondsIden, new ParseIf(token, true, yieldCondsIden, + new ParseQuote(token, new ParseAnd(createAnonymousToken("and"), [yieldCondsIden, modCond])), + new ParseElse(token, modCond)))]}) + const foo2 = new ParseCompTime(token, new ParseIf(token, false, stepIden, modCondIf, null)) + + const yieldCall: ParseNode = new ParseCall(createAnonymousToken(''), yieldParam, [consumedValue], []) + const wrappedYieldCall = new ParseMeta(token, letIn(token, new ParseQuote(token, yieldCall), (yieldCall) => [ + new ParseIf(token, false, yieldCondsIden, + new ParseQuote(token, new ParseIf(token, false, yieldCondsIden, yieldCall, null)), + new ParseElse(token, new ParseQuote(token, yieldCall))) + ])) - const letStartNode = selector.start ? new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('start')), null, selector.start) : null - const letEndNode = selector.end ? new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('end')), null, selector.end) : null - const letStepNode = selector.step ? new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('step')), null, selector.step) : null + const consumeCall = new ParseLet(token, consumedValue, null, new ParseCall(createAnonymousToken(''), consumeParam, [], [])) + const loopBody = new ParseStatements(token, filterNotNull([consumeCall, wrappedYieldCall, incNode])) + const loop = new ParseWhile(token, loopCondNode, loopBody) + + const fnBody = new ParseStatements(createAnonymousToken(''), filterNotNull([letStartNode, letEndNode, letStepNode, letIndexNode, letYieldConds, foo1, foo2, loop])) + const decl = createAnonymousParserFunctionDecl(`array_iterator_slice`, createAnonymousToken(''), fnParams, fnBody) + + // TODO: Make this a function that is compiled once and reused + // It would be good to have some instructions in the bytecode for manipulating arrays (create/append/reduce) + // but maybe we need to improve the metaprogramming system first + // Also How about [... if ...] sugar? + + const consumeParam2 = new ParseFreshIden(token, new FreshBindingToken('consume')) + const yieldParam2 = new ParseFreshIden(token, new FreshBindingToken('yield')) + const fnParams2: ParserFunctionParameter[] = [{ name: consumeParam2, storage: null, type: null }, { name: yieldParam2, storage: null, type: null }] + const closureCall = new ParseCall(token, new ParseFunction(createAnonymousToken(''), decl), [consumeParam2, yieldParam2], []) + // compilerAssert(selector.indexIdentifier, "Expected index identifier", { selector }) + const lets = [ + // new ParseLetConst(token, indexIden, new ParseQuote(token, selector.indexIdentifier)), + new ParseLetConst(token, startIden, selector.start ?? new ParseNil(createAnonymousToken(''))), + new ParseLetConst(token, endIden, selector.end ?? new ParseNil(createAnonymousToken(''))), + new ParseLetConst(token, stepIden, selector.step ?? new ParseNil(createAnonymousToken('')))] + const fnBody2 = new ParseStatements(createAnonymousToken(''), [...lets, closureCall]) + const decl2 = createAnonymousParserFunctionDecl(`array_iterator_slice_partial`, createAnonymousToken(''), fnParams2, fnBody2) + const funcDef2 = insertFunctionDefinition(subCompilerState.globalCompiler, decl2) + const closure2 = new Closure(funcDef2, subCompilerState.scope, subCompilerState) + + return { closure: closure2, yieldParam, indexIdentifier } +} + + +const createIteratorSliceLoopPartialFn = (() => { + const token = createAnonymousToken('') + + // It would be good to have some instructions in the bytecode for manipulating arrays (create/append/reduce) + // but maybe we need to improve the metaprogramming system first + // Also How about [... if ...] sugar? + + const consumeParam = new ParseFreshIden(token, new FreshBindingToken('consume')) + const yieldParam = new ParseFreshIden(token, new FreshBindingToken('yield')) + const indexIdentifier = new ParseFreshIden(token, new FreshBindingToken('index')) + compilerAssert(indexIdentifier) + const fnParams: ParserFunctionParameter[] = [ + { name: consumeParam, storage: null, type: null }, + { name: yieldParam, storage: null, type: null }, + ] + const startIden = new ParseIdentifier(createAnonymousToken('start')) + const endIden = new ParseIdentifier(createAnonymousToken('end')) + const stepIden = new ParseIdentifier(createAnonymousToken('step')) + // const indexIden = new ParseFreshIden(token, new FreshBindingToken('index')) + + const letStartNode = metaIf(token, startIden, + new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('start')), null, startIden), null) + const letEndNode = metaIf(token, endIden, + new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('end')), null, endIden), null) + const letStepNode = metaIf(token, stepIden, + new ParseLet(token, new ParseFreshIden(token, new FreshBindingToken('step')), null, stepIden), null) const letIndexNode = new ParseLet(token, indexIdentifier, null, new ParseNumber(createAnonymousToken('0'))) - const incNode = new ParseOpEq(createAnonymousToken("+="), letIndexNode.left, new ParseNumber(createAnonymousToken('1'))) + const incNode = new ParseOpEq(createAnonymousToken("+="), indexIdentifier, new ParseNumber(createAnonymousToken('1'))) const consumedValue = new ParseFreshIden(token, new FreshBindingToken('slice')) const trueNode = new ParseBoolean(createAnonymousToken('true')) - const loopCondNode = letEndNode ? new ParseOperator(createAnonymousToken("<"), [letIndexNode.left, letEndNode.left]) : trueNode - let yieldCall: ParseNode = new ParseCall(createAnonymousToken(''), yieldParam, [consumedValue], []) - const yieldConds: ParseNode[] = [] - if (letStartNode) { - yieldConds.push(new ParseOperator(createAnonymousToken(">="), [letIndexNode.left, letStartNode.left])) - } - if (letStepNode) { - let check: ParseNode = letIndexNode.left - if (letStartNode) check = new ParseOperator(createAnonymousToken("-"), [check, letStartNode.left]) - const mod = new ParseOperator(createAnonymousToken("mod"), [check, letStepNode.left]) - yieldConds.push(new ParseOperator(createAnonymousToken("=="), [mod, new ParseNumber(createAnonymousToken('0'))])) - } - const condNode = yieldConds.length > 0 ? yieldConds.reduce((prev, curr) => new ParseAnd(createAnonymousToken('and'), [prev, curr])) : null - if (condNode) yieldCall = new ParseIf(token, false, condNode, yieldCall, null) + + const loopCondNode = metaIf(token, endIden, new ParseOperator(createAnonymousToken("<"), [indexIdentifier, endIden]), trueNode) + + const yieldCondsIden = new ParseFreshIden(token, new FreshBindingToken('yield_conds')) + const letYieldConds = new ParseLetConst(token, yieldCondsIden, new ParseNil(createAnonymousToken(''))) + const foo1 = new ParseCompTime(token, new ParseIf(token, true, startIden, + new ParseSet(createAnonymousToken(''), yieldCondsIden, + new ParseQuote(token, new ParseOperator(createAnonymousToken(">="), [indexIdentifier, startIden]))), null) + ) + const check = metaIf(token, startIden, new ParseOperator(createAnonymousToken("-"), [indexIdentifier, startIden]), indexIdentifier) + + const modCond = new ParseQuote(token, new ParseOperator(createAnonymousToken("=="), [ + new ParseOperator(createAnonymousToken("mod"), [check, stepIden]), + new ParseNumber(createAnonymousToken('0'))])) + const modCondIf = letIn(token, modCond, (modCond) => { + return [new ParseSet(token, yieldCondsIden, new ParseIf(token, true, yieldCondsIden, + new ParseQuote(token, new ParseAnd(createAnonymousToken("and"), [yieldCondsIden, modCond])), + new ParseElse(token, modCond)))]}) + const foo2 = new ParseCompTime(token, new ParseIf(token, false, stepIden, modCondIf, null)) + + const yieldCall: ParseNode = new ParseCall(createAnonymousToken(''), yieldParam, [consumedValue], []) + const wrappedYieldCall = new ParseMeta(token, letIn(token, new ParseQuote(token, yieldCall), (yieldCall) => [ + new ParseIf(token, false, yieldCondsIden, + new ParseQuote(token, new ParseIf(token, false, yieldCondsIden, yieldCall, null)), + new ParseElse(token, new ParseQuote(token, yieldCall))) + ])) + const consumeCall = new ParseLet(token, consumedValue, null, new ParseCall(createAnonymousToken(''), consumeParam, [], [])) - const loopBody = new ParseStatements(token, filterNotNull([consumeCall, yieldCall, incNode])) + const loopBody = new ParseStatements(token, filterNotNull([consumeCall, wrappedYieldCall, incNode])) const loop = new ParseWhile(token, loopCondNode, loopBody) - const fnBody = new ParseStatements(createAnonymousToken(''), filterNotNull([letStartNode, letEndNode, letStepNode, letIndexNode, loop])) - const decl = createAnonymousParserFunctionDecl(`array_iterator`, createAnonymousToken(''), fnParams, fnBody) - const funcDef = insertFunctionDefinition(subCompilerState.globalCompiler, decl) - const closure = new Closure(funcDef, subCompilerState.scope, subCompilerState) - return { closure, yieldParam, indexIdentifier } + const fnBody = new ParseStatements(createAnonymousToken(''), filterNotNull([letStartNode, letEndNode, letStepNode, letIndexNode, letYieldConds, foo1, foo2, loop])) + const decl = createAnonymousParserFunctionDecl(`array_iterator_slice`, createAnonymousToken(''), fnParams, fnBody) + + { + const consumeParam2 = new ParseFreshIden(token, new FreshBindingToken('consume')) + const yieldParam2 = new ParseFreshIden(token, new FreshBindingToken('yield')) + const fnParams2: ParserFunctionParameter[] = [ + { name: consumeParam2, storage: null, type: null }, + { name: yieldParam2, storage: null, type: null }, + // TODO: Support passing null values, otherwise this doesn't work, so I had to make them typeArgs + // { name: startIden, storage: null, type: null }, + // { name: endIden, storage: null, type: null }, + // { name: stepIden, storage: null, type: null } + ] + const closureCall = new ParseCall(token, new ParseFunction(createAnonymousToken(''), decl), [consumeParam2, yieldParam2], []) + const fnBody2 = new ParseStatements(createAnonymousToken(''), [closureCall]) + const decl2 = createAnonymousParserFunctionDecl(`array_iterator_slice_partial`, createAnonymousToken(''), fnParams2, fnBody2) + decl2.typeParams = [startIden, endIden, stepIden] + return decl2 + } +})() + +const createIteratorSliceLoop = (ctx: CompilerFunctionCallContext, token: Token, subCompilerState: SubCompilerState, consume: Closure, originalYieldFn: Closure, selector: { node: ParseNode, start: ParseNode | null, end: ParseNode | null, step: ParseNode | null, setterIdentifier: ParseNode | null, indexIdentifier: ParseFreshIden | null }) => { + + // TODO: Make this inlined + + const consumeParam2 = new ParseFreshIden(token, new FreshBindingToken('consume')) + const yieldParam2 = new ParseFreshIden(token, new FreshBindingToken('yield')) + const fnParams2: ParserFunctionParameter[] = [{ name: consumeParam2, storage: null, type: null }, { name: yieldParam2, storage: null, type: null }] + const start = new ParseMeta(token, selector.start ? selector.start : new ParseNil(createAnonymousToken(''))) + const end = new ParseMeta(token, selector.end ? selector.end : new ParseNil(createAnonymousToken(''))) + const step = new ParseMeta(token, selector.step ? selector.step : new ParseNil(createAnonymousToken(''))) + const closureCall = new ParseCall(token, new ParseFunction(createAnonymousToken(''), createIteratorSliceLoopPartialFn), [consumeParam2, yieldParam2], [start, end, step]) + + const fnBody2 = new ParseStatements(createAnonymousToken(''), [closureCall]) + const decl2 = createAnonymousParserFunctionDecl(`array_iterator_slice_loop`, createAnonymousToken(''), fnParams2, fnBody2) + const funcDef2 = insertFunctionDefinition(subCompilerState.globalCompiler, decl2) + const closure2 = new Closure(funcDef2, subCompilerState.scope, subCompilerState) + + return createCallAstFromValue(ctx, closure2, [], [new CompTimeObjAst(VoidType, ctx.location, consume), new CompTimeObjAst(VoidType, ctx.location, originalYieldFn)]) } const createIteratorSliceIterator = (token: Token, subCompilerState: SubCompilerState, selector: { node: ParseNode, start: ParseNode | null, end: ParseNode | null, step: ParseNode | null, setterIdentifier: ParseNode | null, indexIdentifier: ParseFreshIden | null }, iterator: Closure): Closure => { @@ -348,8 +502,7 @@ const createIteratorSliceIterator = (token: Token, subCompilerState: SubCompiler ]) return Task.of(stmts) }) - const loopClosure = createIteratorSliceLoop(token, ctx.compilerState, selector) - return createCallAstFromValue(fnctx1, loopClosure.closure, [], [new CompTimeObjAst(VoidType, ctx.location, fn1), new CompTimeObjAst(VoidType, ctx.location, originalYieldFn)]) + return createIteratorSliceLoop(fnctx1, token, ctx.compilerState, fn1, originalYieldFn, selector) }) ); @@ -477,8 +630,7 @@ const expandZipIterator = (state: ExpansionZipState): Task = }) const fnctx2: CompilerFunctionCallContext = { location, compilerState: state.compilerState, resultAst: undefined, typeCheckResult: undefined } - const callFsm = createCallAstFromValue(fnctx2, setter.closure, [], [new CompTimeObjAst(VoidType, location, finalProducer)]) - return callFsm + return createCallAstFromValue(fnctx2, setter.closure, [], [new CompTimeObjAst(VoidType, location, finalProducer)]) } else if (state.iteratorList.length === 0) { @@ -615,7 +767,7 @@ const compileExpansionToParseNode = (out: BytecodeWriter, expansion: ExpansionCo } const fnBody = new ParseStatements(createAnonymousToken(''), [expansion.loopBodyNode]) - const decl = createAnonymousParserFunctionDecl(`reduced${expansion.debugName}`, createAnonymousToken(''), fnParams, fnBody) + const decl = createAnonymousParserFunctionDecl(`${expansion.debugName}_reduced`, createAnonymousToken(''), fnParams, fnBody) const funcDef = insertFunctionDefinition(vm.context.subCompilerState.globalCompiler, decl) let resultClosure = new Closure(funcDef, vm.context.subCompilerState.scope, vm.context.subCompilerState) setOptimiseSimpleIterator(resultClosure) @@ -730,6 +882,84 @@ 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 + letAst: LetAst + constructor() {} +} + +const createDeferObject = new ExternalFunction('createDeferObject', CompileTimeObjectType, (ctx, values) => { + return new CompTimeObjAst(CompileTimeObjectType, ctx.location, new DeferredLetBinding()) +}) +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 }) + 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) => { + 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 bindingAst = new BindingAst(binding.type, ctx.location, binding) + return createStatements(ctx.location, [defer.letAst, stmts, bindingAst]) +}) + +const createFreshLet = new ExternalFunction('createFreshLet', VoidType, (ctx, values) => { + let [value] = values + compilerAssert(isAst(value), "Expected ast", { value }) + const binding = new Binding("defer", value.type) + return new LetAst(binding.type, ctx.location, binding, value) +}) + +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[]) => { compilerAssert(!out.state.expansion, "Already in expansion state") const node = args[0] @@ -739,16 +969,12 @@ 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)]) - - // TODO: Only supports numbers at the moment + const deferIden = new ParseFreshIden(node.token, new FreshBindingToken('defer')) + const break_ = new ParseBreak(node.token, expansion.breakIden, null) + const deferSet_ = deferredHelperSetter(node.token, deferIden, result) + expansion.loopBodyNode = new ParseStatements(node.token, [deferSet_, break_]) 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) + visitParseNode(out, deferredHelperResult(node.token, deferIden, 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 09f9e29..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[], @@ -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'; @@ -202,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' } | @@ -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' } @@ -860,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, @@ -930,6 +934,7 @@ export const createDefaultGlobalCompiler = () => { const globalCompiler: GlobalCompilerState = { compiledFunctions: new Map(), functionDefinitions: [], + functionDefinitionsByDeclaration: new Map(), classDefinitions: [], allWaitingEvents: [], globalLets: [], @@ -1014,7 +1019,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}`; }; diff --git a/tests/fixtures/iterator_expansion.rad b/tests/fixtures/iterator_expansion.rad index 994a3c0..ffc76d4 100644 --- a/tests/fixtures/iterator_expansion.rad +++ b/tests/fixtures/iterator_expansion.rad @@ -201,10 +201,16 @@ 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) + + people := [Person(10), Person(20), Person(30), Person(40), Person(50)] + p := @first(people[1:]) + + print(p.age) + # assert(x == 40) fn test_range(): rng := Range(10, 20) @@ -328,6 +334,16 @@ fn test_iterator_to_array(): assert(@sum(arr[:]) == 43545640) print(arr[:]) ... +fn test_slice(): + c :: @concat(factorial[2:8:2]) + assert(@sum(c[:]) == 746) + +fn test_array(): + x := 0.2 + arr := [ 3, 1, 1] + # assert(@sum(arr[:]) == 43545640) + print(arr[:]) ... + fn main(): test_fold() @@ -353,3 +369,5 @@ fn main(): test_flatmap() test_custom_iterator() test_iterator_to_array() + test_array() + test_slice()