diff --git a/projects/compiler/example.abra b/projects/compiler/example.abra index e2b85370..624a0819 100644 --- a/projects/compiler/example.abra +++ b/projects/compiler/example.abra @@ -1,79 +1,14 @@ -// // enum Foo { -// // Bar(a: Int, b: String = "default") -// // } +// val str: String? = None -// // val f = Foo.Bar(a: 123) -// // println(f) +// println(str?.isEmpty()) -// func foo(a: Int, b = "asdf", c = 123) { -// println(a, b, c) -// } +// println("a s d f".split(by: " ")) +// println("asdf".split()) -// foo(a: 1) -// foo(a: 1, c: 456) -// foo(a: 1, b: "456") -// foo(a: 1, b: "456", c: 456) +val arr = [1, 2, 3, 4] -// type Foo { -// func bar(self, a: Int, b = "asdf", c = 123) { -// println(a, b, c) -// } - -// func baz(a: Int, b = "asdf", c = 123) { -// println(a, b, c) -// } -// } - -// // Foo.baz(a: 1) -// // Foo.baz(a: 1, c: 456) -// // Foo.baz(a: 1, b: "456") -// // Foo.baz(a: 1, b: "456", c: 456) - -// val f = Foo() -// // f.bar(a: 1) -// // f.bar(a: 1, c: 456) -// // f.bar(a: 1, b: "456") -// // f.bar(a: 1, b: "456", c: 456) - - -// func callFn2(fn: (Int, String) => Unit) { -// fn(24, "foo") -// } - -// func callFn3(fn: (Int, String, Int, String) => Unit) { -// fn(24, "foo", 24, "foo") -// } - -// callFn1(foo) -// callFn2(foo) -// callFn3(foo) - -// callFn1(f.bar) -// callFn2(f.bar) -// callFn3(f.bar) - -// callFn1(Foo.baz) -// callFn2(Foo.baz) -// callFn3(Foo.baz) - -enum Color { - Red - Green - Blue - RGB(r: Int = 0, g: Int = 0, b: Int = 0) -} - -val black = Color.RGB() -println(black) -val white = Color.RGB(r: 255, g: 255, b: 255) -println(white) -val red = Color.RGB(r: 255) -println(red) -val green = Color.RGB(g: 255) -println(green) -val pink = Color.RGB(r: 255, b: 255) -println(pink) -val cyan = Color.RGB(g: 255, b: 255) -println(cyan) -val yellow = Color.RGB(r: 255, g: 255) -println(yellow) +/// Expect: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] +val arr2 = arr.flatMap(i => Array.fill(i, i)) +println(arr2) +println(arr2.length) +println(arr2) diff --git a/projects/compiler/src/compiler.abra b/projects/compiler/src/compiler.abra index 468dfd3e..3a4f7ff8 100644 --- a/projects/compiler/src/compiler.abra +++ b/projects/compiler/src/compiler.abra @@ -1199,11 +1199,7 @@ export type Compiler { self._currentFn.block.registerLabel(labelIsNone) val noneRes = if fn.returnType.kind != TypeKind.PrimitiveUnit { val optNoneVariant = if self._project.preludeOptionEnum.variants.find(v => v.label.name == "None") |v| v else unreachable("Option.None must exist") - match self._resolvedGenerics.addLayer("Option.None", { "V": innerTy }) { Ok => {}, Err(e) => return Err(CompileError(position: node.token.position, kind: CompileErrorKind.ResolvedGenericsError(context: "Option.None", message: e))) } - val noneVariantFn = try self._getOrCompileEnumVariantFn(self._project.preludeOptionEnum, optNoneVariant) - self._resolvedGenerics.popLayer() - // Do not track Option.None in callframes - val noneRes = try self._buildCall(None, Callable.Function(noneVariantFn), []) + val noneRes = try self._getOrCompileEnumVariantConst(self._project.preludeOptionEnum, optNoneVariant) Some(noneRes) } else None self._currentFn.block.buildJmp(labelCont) @@ -1270,12 +1266,7 @@ export type Compiler { } (Callable.Function(enumVariantFn), frameCtx) } - _ => { - val enumVariantFn = try self._getOrCompileEnumVariantFn(enum_, variant) - self._resolvedGenerics.popLayer() - - (Callable.Function(enumVariantFn), None) - } + _ => unreachable("cannot invoke constant enum variant ${enum_.label.name}.${variant.label.name}") } } TypedInvokee.Expr(expr) => { @@ -2108,7 +2099,7 @@ export type Compiler { // a closure's captures array, but a mutable variable needs an additional layer of indirection to handle possible reassignment. self._currentFn.block.addComment("move captured mutable '${variable.label.name}' to heap") val size = varTy.size() - val heapMem = try self._currentFn.block.buildCall(Callable.Function(self._malloc), [Value.Int(size)], Some("${variable.label.name}.mem")) else |e| return qbeError(e) + val heapMem = try self._callMalloc(Value.Int(size), Some("${variable.label.name}.mem")) self._currentFn.block.buildStore(varTy, v, heapMem) val slot = self._buildStackAllocForQbeType(QbeType.Pointer, Some(slotName)) @@ -2173,7 +2164,7 @@ export type Compiler { func _createClosureCaptures(self, fn: Function): Result { val size = QbeType.Pointer.size() * (fn.captures.length + fn.capturedClosures.length) - val capturesMem = try self._currentFn.block.buildCall(Callable.Function(self._malloc), [Value.Int(size)], Some("${fn.label.name}_captures.mem")) else |e| return qbeError(e) + val capturesMem = try self._callMalloc(Value.Int(size), Some("${fn.label.name}_captures.mem")) var cursor = capturesMem for variable in fn.captures { @@ -2499,11 +2490,7 @@ export type Compiler { _ => {} } - try self._addResolvedGenericsLayerForEnumVariant(ty, variant.label.name, label.position) - val enumVariantFn = try self._getOrCompileEnumVariantFn(enum_, variant) - - curVal = try self._buildCall(None, Callable.Function(enumVariantFn), []) - self._resolvedGenerics.popLayer() + curVal = try self._getOrCompileEnumVariantConst(enum_, variant) instTy = StructOrEnum.Enum(enum_) } @@ -2561,11 +2548,7 @@ export type Compiler { self._currentFn.block.registerLabel(labelIsNone) val optNoneVariant = if self._project.preludeOptionEnum.variants.find(v => v.label.name == "None") |v| v else unreachable("Option.None must exist") - match self._resolvedGenerics.addLayer("Option.None", { "V": innerTy }) { Ok => {}, Err(e) => return Err(CompileError(position: label.position, kind: CompileErrorKind.ResolvedGenericsError(context: "Option.None", message: e))) } - val noneVariantFn = try self._getOrCompileEnumVariantFn(self._project.preludeOptionEnum, optNoneVariant) - self._resolvedGenerics.popLayer() - // Do not track Option.None in callframes - val noneRes = try self._buildCall(None, Callable.Function(noneVariantFn), []) + val noneRes = try self._getOrCompileEnumVariantConst(self._project.preludeOptionEnum, optNoneVariant) self._currentFn.block.buildJmp(labelCont) self._currentFn.block.registerLabel(labelIsSome) @@ -2672,7 +2655,7 @@ export type Compiler { val baseFn = try self._getOrCompileStructInitializer(struct) try self._buildCall(None, Callable.Function(baseFn), argsForUnderlying) } else { - val memLocal = try fnVal.block.buildCall(Callable.Function(self._malloc), [Value.Int(size)], Some("struct.mem")) else |e| return qbeError(e) + val memLocal = try self._callMalloc(Value.Int(size), Some("struct.mem")) var offset = 0 for field in struct.fields { @@ -2697,6 +2680,17 @@ export type Compiler { Ok(fnVal) } + func _getOrCompileEnumVariantConst(self, enum_: Enum, variant: TypedEnumVariant): Result { + val base = self._enumTypeNameBase(enum_) + val variantDataName = "$base.${variant.label.name}" + if self._builder.getData(variantDataName) |v| return Ok(v) + + val variantIdx = if enum_.variants.findIndex(v => v.label.name == variant.label.name) |(_, idx)| idx else unreachable("variant '${variant.label.name}' must exist") + val (slot, _) = self._builder.addData(QbeData(name: variantDataName, kind: QbeDataKind.Constants([(QbeType.U64, Value.Int(variantIdx))]))) + + Ok(slot) + } + func _getOrCompileEnumVariantFn(self, enum_: Enum, variant: TypedEnumVariant, fieldsNeedingDefaultValue: Bool[] = []): Result { val defaultValuesFlag = fieldsNeedingDefaultValue.reduce(0, (acc, f) => (acc << 1) || (if f 1 else 0)) var variantFnName = try self._enumVariantFnName(enum_, variant) @@ -2710,57 +2704,52 @@ export type Compiler { self._currentFn = fn fn.addComment(try self._enumVariantSignature(enum_, variant, fieldsNeedingDefaultValue)) + val fields = match variant.kind { + EnumVariantKind.Container(fields) => fields + _ => unreachable("constant enum variants are not backed by functions, see _getOrCompileEnumVariantConst") + } + val argsForUnderlying: Value[] = [] var anyFieldNeedsDefault = false var size = 0 size += QbeType.U64.size() // account for space for variant idx slot - match variant.kind { - EnumVariantKind.Container(fields) => { - for field, idx in fields { - val fieldTy = try self._getQbeTypeForTypeExpect(field.ty, "unacceptable type for field", Some(field.name.position)) - size += fieldTy.size() - - val fieldVal = if field.initializer |initializerNode| { - val fieldNeedsDefault = fieldsNeedingDefaultValue[idx] ?: false - if fieldNeedsDefault { - anyFieldNeedsDefault = true - try self._compileExpression(initializerNode, Some(field.name.name)) - } else { - fn.addParameter(field.name.name, fieldTy) - } - } else { - fn.addParameter(field.name.name, fieldTy) - } - argsForUnderlying.push(fieldVal) + for field, idx in fields { + val fieldTy = try self._getQbeTypeForTypeExpect(field.ty, "unacceptable type for field", Some(field.name.position)) + size += fieldTy.size() + + val fieldVal = if field.initializer |initializerNode| { + val fieldNeedsDefault = fieldsNeedingDefaultValue[idx] ?: false + if fieldNeedsDefault { + anyFieldNeedsDefault = true + try self._compileExpression(initializerNode, Some(field.name.name)) + } else { + fn.addParameter(field.name.name, fieldTy) } + } else { + fn.addParameter(field.name.name, fieldTy) } - _ => {} + argsForUnderlying.push(fieldVal) } val retVal = if anyFieldNeedsDefault { val baseFn = try self._getOrCompileEnumVariantFn(enum_, variant) try self._buildCall(None, Callable.Function(baseFn), argsForUnderlying) } else { - val memLocal = try fn.block.buildCall(Callable.Function(self._malloc), [Value.Int(size)], Some("enum_variant.mem")) else |e| return qbeError(e) + val memLocal = try self._callMalloc(Value.Int(size), Some("enum_variant.mem")) val variantIdx = if enum_.variants.findIndex(v => v.label.name == variant.label.name) |(_, idx)| idx else unreachable("variant '${variant.label.name}' must exist") fn.block.buildStoreL(Value.Int(variantIdx), memLocal) // Store variant idx at designated slot var offset = QbeType.U64.size() // begin inserting any fields after that variant idx slot - match variant.kind { - EnumVariantKind.Container(fields) => { - for field in fields { - val fieldTy = try self._getQbeTypeForTypeExpect(field.ty, "unacceptable type for field", Some(field.name.position)) - val param = Value.Ident(field.name.name, fieldTy) + for field in fields { + val fieldTy = try self._getQbeTypeForTypeExpect(field.ty, "unacceptable type for field", Some(field.name.position)) + val param = Value.Ident(field.name.name, fieldTy) - val localName = "mem_offset_${field.name.name}" - val memCursorLocal = try fn.block.buildAdd(Value.Int(offset), memLocal, Some(localName)) else |e| return qbeError(e) - fn.block.buildStore(fieldTy, param, memCursorLocal) + val localName = "mem_offset_${field.name.name}" + val memCursorLocal = try fn.block.buildAdd(Value.Int(offset), memLocal, Some(localName)) else |e| return qbeError(e) + fn.block.buildStore(fieldTy, param, memCursorLocal) - offset += fieldTy.size() - } - } - _ => {} + offset += fieldTy.size() } memLocal @@ -2919,7 +2908,7 @@ export type Compiler { val innerTySize = try self._pointerSize(innerTy) val sizeVal = try self._currentFn.block.buildMul(Value.Int(innerTySize), countVal) else |e| return qbeError(e) - val mem = try self._currentFn.block.buildCall(Callable.Function(self._malloc), [sizeVal], Some("ptr.mem")) else |e| return qbeError(e) + val mem = try self._callMalloc(sizeVal, Some("ptr.mem")) self._currentFn.block.addComment("...pointer_malloc end") @@ -2959,8 +2948,12 @@ export type Compiler { val innerTy = if self._resolvedGenerics.resolveGeneric("T") |ty| ty else unreachable("(pointer_store) could not resolve T for Pointer") val innerTySize = try self._pointerSize(innerTy) - val innerQbeType = try self._getQbeTypeForTypeExpect(innerTy, "unacceptable type", None) - self._currentFn.block.buildStore(innerQbeType, valueVal, ptrVal) + if innerTySize == 1 { + self._currentFn.block.buildStoreB(valueVal, ptrVal) + } else { + val innerQbeType = try self._getQbeTypeForTypeExpect(innerTy, "unacceptable type", None) + self._currentFn.block.buildStore(innerQbeType, valueVal, ptrVal) + } self._currentFn.block.addComment("...pointer_store end") @@ -2996,20 +2989,8 @@ export type Compiler { val innerTy = if self._resolvedGenerics.resolveGeneric("T") |ty| ty else unreachable("(pointer_load) could not resolve T for Pointer") - val isByte = match innerTy.kind { - TypeKind.Instance(structOrEnum, _) => { - val r = match structOrEnum { - StructOrEnum.Struct(struct) => struct.builtin == Some(BuiltinModule.Intrinsics) && struct.label.name == "Byte" - _ => false - } - r - } - _ => false - } - val innerQbeType = if isByte QbeType.U8 else { - val res = try self._getQbeTypeForTypeExpect(innerTy, "unacceptable type", None) - res - } + val innerTySize = try self._pointerSize(innerTy) + val innerQbeType = if innerTySize == 1 QbeType.U8 else try self._getQbeTypeForTypeExpect(innerTy, "unacceptable type", None) val v = self._currentFn.block.buildLoad(innerQbeType, ptrVal) @@ -3090,7 +3071,7 @@ export type Compiler { val sizeVal = try self._currentFn.block.buildCall(Callable.Function(self._snprintf), [Value.Int(0), Value.Int(0), intFmtPtr, argVal]) else |e| return qbeError(e) val mallocSizeVal = try self._currentFn.block.buildAdd(Value.Int(1), sizeVal) else |e| return qbeError(e) - val mem = try self._currentFn.block.buildCall(Callable.Function(self._malloc), [mallocSizeVal]) else |e| return qbeError(e) + val mem = try self._callMalloc(mallocSizeVal) try self._currentFn.block.buildCall(Callable.Function(self._snprintf), [mem, mallocSizeVal, intFmtPtr, argVal]) else |e| return qbeError(e) val str = try self._constructString(mem, sizeVal) @@ -3637,7 +3618,7 @@ export type Compiler { val sizeVal = try fnVal.block.buildCall(Callable.Function(self._snprintf), [Value.Int(0), Value.Int(0), intFmtPtr, selfParam]) else |e| return qbeError(e) val mallocSizeVal = try fnVal.block.buildAdd(Value.Int(1), sizeVal) else |e| return qbeError(e) - val mem = try fnVal.block.buildCall(Callable.Function(self._malloc), [mallocSizeVal]) else |e| return qbeError(e) + val mem = try self._callMalloc(mallocSizeVal) try fnVal.block.buildCall(Callable.Function(self._snprintf), [mem, mallocSizeVal, intFmtPtr, selfParam]) else |e| return qbeError(e) val str = try self._constructString(mem, sizeVal) @@ -3669,7 +3650,7 @@ export type Compiler { val sizeVal = try fnVal.block.buildCall(Callable.Function(self._snprintf), [Value.Int(0), Value.Int(0), floatFmtPtr, selfParam]) else |e| return qbeError(e) val mallocSizeVal = try fnVal.block.buildAdd(Value.Int(1), sizeVal) else |e| return qbeError(e) - val mem = try fnVal.block.buildCall(Callable.Function(self._malloc), [mallocSizeVal]) else |e| return qbeError(e) + val mem = try self._callMalloc(mallocSizeVal) try fnVal.block.buildCall(Callable.Function(self._snprintf), [mem, mallocSizeVal, floatFmtPtr, selfParam]) else |e| return qbeError(e) val str = try self._constructString(mem, sizeVal) @@ -4153,6 +4134,21 @@ export type Compiler { Ok(0) } + func _callMalloc(self, sizeVal: Value, localName: String? = None): Result { + val mem = try self._currentFn.block.buildCall(Callable.Function(self._malloc), [sizeVal], localName) else |e| return qbeError(e) + + // val labelIsZero = self._currentFn.block.addLabel("malloc_is_zero") + // val labelCont = self._currentFn.block.addLabel("malloc_is_nonzero") + // self._currentFn.block.buildJnz(mem, labelCont, labelIsZero) + + // self._currentFn.block.registerLabel(labelIsZero) + // self._currentFn.block.buildHalt() + + // self._currentFn.block.registerLabel(labelCont) + + Ok(mem) + } + func _buildStackAllocForQbeType(self, ty: QbeType, name: String? = None): Value { match ty { QbeType.U8 => self._currentFn.block.buildAlloc8(1, name) // 'b' @@ -4453,8 +4449,9 @@ export type Compiler { Ok("$structTypeName.init") } + func _enumTypeNameBase(self, enum_: Enum): String = ".${enum_.moduleId}.${enum_.label.name}" func _enumTypeName(self, enum_: Enum): Result { - val base = ".${enum_.moduleId}.${enum_.label.name}" + val base = self._enumTypeNameBase(enum_) val name = if !enum_.typeParams.isEmpty() { val parts: String[] = [] for name in enum_.typeParams { diff --git a/projects/compiler/src/qbe.abra b/projects/compiler/src/qbe.abra index 40234079..b3d0a53b 100644 --- a/projects/compiler/src/qbe.abra +++ b/projects/compiler/src/qbe.abra @@ -2,6 +2,7 @@ import File from "fs" export type ModuleBuilder { _data: QbeData[] = [] + _dataByName: Map = {} _globalStrs: Map = {} _functions: QbeFunction[] = [] _functionsByName: Map = {} @@ -43,10 +44,13 @@ export type ModuleBuilder { ptr } + func getData(self, name: String): Value? = self._dataByName[name] + func addData(self, data: QbeData): (Value, Int) { val idx = self._data.length self._data.push(data) val dataGlobal = Value.Global(data.name, QbeType.Pointer) + self._dataByName[data.name] = dataGlobal (dataGlobal, idx) } diff --git a/projects/compiler/test/compiler/arrays.abra b/projects/compiler/test/compiler/arrays.abra index c0ef612d..6cddb35e 100644 --- a/projects/compiler/test/compiler/arrays.abra +++ b/projects/compiler/test/compiler/arrays.abra @@ -295,14 +295,13 @@ func exclaim(i: Int, _: Int, x = "!"): String = i + x //"$i$x" println(arr.map((i, _, x = "!") => i + x)) })() -// TODO: there's something up with printing an array of this particular size -// // Array#flatMap -// (() => { -// val arr = [1, 2, 3, 4] +// Array#flatMap +(() => { + val arr = [1, 2, 3, 4] -// /// Expect: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] -// println(arr.flatMap(i => Array.fill(i, i))) -// })() + /// Expect: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4] + println(arr.flatMap(i => Array.fill(i, i))) +})() // Array#filter func isEven(i: Int): Bool = i % 2 == 0 diff --git a/projects/compiler/test/compiler/process_callstack.abra b/projects/compiler/test/compiler/process_callstack.abra index 70cc2bac..ba3f0f0d 100644 --- a/projects/compiler/test/compiler/process_callstack.abra +++ b/projects/compiler/test/compiler/process_callstack.abra @@ -26,7 +26,7 @@ val arr = [1].map((i, _) => { /// Expect: at baz (%TEST_DIR%/compiler/process_callstack.abra:10) /// Expect: at bar (%TEST_DIR%/compiler/process_callstack.abra:5) /// Expect: at foo (%TEST_DIR%/compiler/process_callstack.abra:19) -/// Expect: at (%STD_DIR%/prelude.abra:592) +/// Expect: at (%STD_DIR%/prelude.abra:596) /// Expect: at Array.map (%TEST_DIR%/compiler/process_callstack.abra:18) type OneTwoThreeIterator { diff --git a/projects/std/src/prelude.abra b/projects/std/src/prelude.abra index 10dd3027..f95d5cf8 100644 --- a/projects/std/src/prelude.abra +++ b/projects/std/src/prelude.abra @@ -492,7 +492,8 @@ type Array { val reprs: String[] = Array.withCapacity(self.length) var len = 2 // account for '[' and ']' - for i in range(0, self.length) { + var i = 0 + while i < self.length { val item = self._buffer.offset(i).load() val repr = item.toString() reprs.push(repr) @@ -500,15 +501,16 @@ type Array { if i != self.length - 1 { len += 2 // account for ", " unless last item } + i += 1 } - // we have an extra 2 bytes for the trailing ", " but we need an extra 1 for the null termination anyway val str = String.withLength(len) var offset = 0 str._buffer.offset(offset).store(Byte.fromInt(91)) // '[' offset += 1 - for i in range(0, self.length) { + i = 0 + while i < self.length { val repr = reprs._buffer.offset(i).load() str._buffer.offset(offset).copyFrom(repr._buffer, repr.length) offset += repr.length @@ -519,6 +521,8 @@ type Array { str._buffer.offset(offset).store(Byte.fromInt(32)) // ' ' offset += 1 } + + i += 1 } str._buffer.offset(offset).store(Byte.fromInt(93)) // ']'