From 8fa6a947b0d8a1e7510b8c394fc8be537ad2f119 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 31 Dec 2024 19:03:11 +0200 Subject: [PATCH 01/48] Create a plan for current branch --- packages/codegen/src/codegen | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 3386bc756..7d4400919 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -25,13 +25,6 @@ import typeShouldBeFreed from "./helpers" -// TODO: implement ObjectDeclaration statement -// TODO: implement ObjectDeclarationMethod statement -// TODO: implement ObjectDeclarationProperty statement -// TODO: implement object expression -// TODO: implement property access expression -// TODO: error object needs special generation rules to move message and stack properties upfront - // TODO: implement FunctionDeclaration statement // TODO: implement Return statement // TODO: implement closure expression From 54fcf554ae84fd71da78d63205bbf1a9b3fabf2c Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 1 Jan 2025 17:31:25 +0200 Subject: [PATCH 02/48] Import scripts sorting utils from codegen --- scripts/pre-process-codegen | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/pre-process-codegen b/scripts/pre-process-codegen index 9379268fd..55dcd123a 100644 --- a/scripts/pre-process-codegen +++ b/scripts/pre-process-codegen @@ -14,6 +14,8 @@ * limitations under the License. */ +import sortStrAsc from "./packages/codegen/src/utils" + enum CParserTokenType { Id, Replacement, From 189db8a07996a6497e00876476710de8f5864a22 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 1 Jan 2025 18:43:04 +0200 Subject: [PATCH 03/48] analyzer: Add more methods --- packages/analyzer/README.md | 39 +++++++ packages/analyzer/src/type | 26 +++++ packages/analyzer/test/type-test | 171 +++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+) diff --git a/packages/analyzer/README.md b/packages/analyzer/README.md index 319afec1b..4615820a2 100644 --- a/packages/analyzer/README.md +++ b/packages/analyzer/README.md @@ -497,6 +497,19 @@ Whether enum type has enumerator with specified name. result := type.hasEnumerator("Color") ``` +### `Type.hasSelfParam () bool` +Checks whether current type is a method and has a leading self param. + +**Return value** + +Whether current type is a method and has a leading self param. + +**Examples** + +```the +result := type.hasSelfParam() +``` + ### `Type.hasType (search: ref Type) bool` Checks whether union type has specified subtype (should be used only on union type). @@ -515,6 +528,19 @@ mut tm := TypeMap{} result := type.hasType(tm.get("int)) ``` +### `Type.isCustomObject () bool` +Checks whether current type is object and not primitive. + +**Return value** + +Whether current type is object and not primitive. + +**Examples** + +```the +result := type.isCustomObject() +``` + ### `Type.isErrorLike () bool` Checks whether type looks like error object (with first 'message' property of `str` type and second 'stack' property of `str` type). @@ -567,6 +593,19 @@ Whether type floating point or integer type. result := type.isNumber() ``` +### `Type.isPrimitive () bool` +Checks whether current type is a primitive type. + +**Return value** + +Whether current type is a primitive type. + +**Examples** + +```the +result := type.isPrimitive() +``` + ### `Type.isRequired () bool` Checks whether type is required to have an initializer. diff --git a/packages/analyzer/src/type b/packages/analyzer/src/type index 0112c7ab5..3cfb6128f 100644 --- a/packages/analyzer/src/type +++ b/packages/analyzer/src/type @@ -198,6 +198,15 @@ export obj Type { return t.members.contains(name) } + fn hasSelfParam (self: ref Self) bool { + if self.isMethod() { + t := self.asMethod() + return t.withSelf + } + + return false + } + fn hasType (self: ref Self, search: ref Type) bool { if search.isUnion() { searchUnion := search.asUnion() @@ -222,6 +231,10 @@ export obj Type { return false } + fn isCustomObject (self: ref Self) bool { + return self.isObject() && !self.isPrimitive() + } + fn isErrorLike (self: ref Self) bool { if !self.isObject() || !self.has(0) || !self.has(1) { return false @@ -252,6 +265,19 @@ export obj Type { return self.isInt() || self.isFloat() } + fn isPrimitive (self: ref Self) bool { + return self.isNumber() || [ + "any", + "bool", + "byte", + "char", + "never", + "rune", + "str", + "void", + ].contains(self.name) + } + fn isRequired (self: ref Self) bool { if self.isAlias() { t := self.asAlias() diff --git a/packages/analyzer/test/type-test b/packages/analyzer/test/type-test index 2667518e8..b0230d582 100644 --- a/packages/analyzer/test/type-test +++ b/packages/analyzer/test/type-test @@ -397,6 +397,19 @@ export fn TEST_Type_hasEnumerator () { EXPECT_FALSE(t.hasEnumerator("Purple")) } +export fn TEST_Type_hasSelfParam () { + mut tm := TypeMap{} + tm.init() + + t1 := tm.createArray(tm.get("int")) + t2 := tm.createMethod(false, [], tm.get("int"), false, false, "", tm.get("int")) + t3 := tm.createMethod(false, [], tm.get("int"), true, false, "self", tm.get("int")) + + EXPECT_FALSE(t1.hasSelfParam()) + EXPECT_FALSE(t2.hasSelfParam()) + EXPECT_TRUE(t3.hasSelfParam()) +} + export fn TEST_Type_hasType () { mut tm := TypeMap{} tm.init() @@ -420,6 +433,85 @@ export fn TEST_Type_hasType () { EXPECT_FALSE(union3.hasType(tm.get("rune"))) } +export fn TEST_Type_isCustomObject () { + mut tm := TypeMap{} + tm.init() + + t1 := tm.createAlias("TestAlias", tm.get("int")) + t2 := tm.createAlias("TestAlias", tm.createObject("AliasObject")) + t3 := tm.createArray(tm.get("int")) + t4 := tm.createEnum("TestEnum", []) + t5 := tm.createFunction(false, [], tm.get("int")) + t6 := tm.createFunction(false, [], tm.get("str")) + t7 := tm.createMap(tm.get("int"), tm.get("int")) + t8 := tm.createMethod(false, [], tm.get("int"), false, false, "", tm.get("int")) + t9 := tm.createNamespace("TestNamespace", []) + t10 := tm.createObject("TestObject") + t11 := tm.createOptional(tm.get("int")) + t12 := tm.createReference(tm.get("str")) + t13 := tm.createReference(tm.get("int")) + t14 := tm.createUnion([tm.get("int"), tm.get("str")]) + t15 := tm.get("any") + t16 := tm.get("bool") + t17 := tm.get("byte") + t18 := tm.get("char") + t19 := tm.get("float") + t20 := tm.get("f32") + t21 := tm.get("f64") + t22 := tm.get("int") + t23 := tm.get("i8") + t24 := tm.get("i16") + t25 := tm.get("i32") + t26 := tm.get("i64") + t27 := tm.get("isize") + t28 := tm.get("never") + t29 := tm.get("rune") + t30 := tm.get("u8") + t31 := tm.get("u16") + t32 := tm.get("u32") + t33 := tm.get("u64") + t34 := tm.get("usize") + t35 := tm.get("str") + t36 := tm.get("void") + + EXPECT_FALSE(t1.isCustomObject()) + EXPECT_FALSE(t2.isCustomObject()) + EXPECT_FALSE(t3.isCustomObject()) + EXPECT_FALSE(t4.isCustomObject()) + EXPECT_FALSE(t5.isCustomObject()) + EXPECT_FALSE(t6.isCustomObject()) + EXPECT_FALSE(t7.isCustomObject()) + EXPECT_FALSE(t8.isCustomObject()) + EXPECT_FALSE(t9.isCustomObject()) + EXPECT_TRUE(t10.isCustomObject()) + EXPECT_FALSE(t11.isCustomObject()) + EXPECT_FALSE(t12.isCustomObject()) + EXPECT_FALSE(t13.isCustomObject()) + EXPECT_FALSE(t14.isCustomObject()) + EXPECT_FALSE(t15.isCustomObject()) + EXPECT_FALSE(t16.isCustomObject()) + EXPECT_FALSE(t17.isCustomObject()) + EXPECT_FALSE(t18.isCustomObject()) + EXPECT_FALSE(t19.isCustomObject()) + EXPECT_FALSE(t20.isCustomObject()) + EXPECT_FALSE(t21.isCustomObject()) + EXPECT_FALSE(t22.isCustomObject()) + EXPECT_FALSE(t23.isCustomObject()) + EXPECT_FALSE(t24.isCustomObject()) + EXPECT_FALSE(t25.isCustomObject()) + EXPECT_FALSE(t26.isCustomObject()) + EXPECT_FALSE(t27.isCustomObject()) + EXPECT_FALSE(t28.isCustomObject()) + EXPECT_FALSE(t29.isCustomObject()) + EXPECT_FALSE(t30.isCustomObject()) + EXPECT_FALSE(t31.isCustomObject()) + EXPECT_FALSE(t32.isCustomObject()) + EXPECT_FALSE(t33.isCustomObject()) + EXPECT_FALSE(t34.isCustomObject()) + EXPECT_FALSE(t35.isCustomObject()) + EXPECT_FALSE(t36.isCustomObject()) +} + export fn TEST_Type_isErrorLike () { mut tm := TypeMap{} tm.init() @@ -510,6 +602,85 @@ export fn TEST_Type_isNumber () { EXPECT_FALSE(v11.isNumber()) } +export fn TEST_Type_isPrimitive () { + mut tm := TypeMap{} + tm.init() + + t1 := tm.createAlias("TestAlias", tm.get("int")) + t2 := tm.createAlias("TestAlias", tm.createObject("AliasObject")) + t3 := tm.createArray(tm.get("int")) + t4 := tm.createEnum("TestEnum", []) + t5 := tm.createFunction(false, [], tm.get("int")) + t6 := tm.createFunction(false, [], tm.get("str")) + t7 := tm.createMap(tm.get("int"), tm.get("int")) + t8 := tm.createMethod(false, [], tm.get("int"), false, false, "", tm.get("int")) + t9 := tm.createNamespace("TestNamespace", []) + t10 := tm.createObject("TestObject") + t11 := tm.createOptional(tm.get("int")) + t12 := tm.createReference(tm.get("str")) + t13 := tm.createReference(tm.get("int")) + t14 := tm.createUnion([tm.get("int"), tm.get("str")]) + t15 := tm.get("any") + t16 := tm.get("bool") + t17 := tm.get("byte") + t18 := tm.get("char") + t19 := tm.get("float") + t20 := tm.get("f32") + t21 := tm.get("f64") + t22 := tm.get("int") + t23 := tm.get("i8") + t24 := tm.get("i16") + t25 := tm.get("i32") + t26 := tm.get("i64") + t27 := tm.get("isize") + t28 := tm.get("never") + t29 := tm.get("rune") + t30 := tm.get("u8") + t31 := tm.get("u16") + t32 := tm.get("u32") + t33 := tm.get("u64") + t34 := tm.get("usize") + t35 := tm.get("str") + t36 := tm.get("void") + + EXPECT_FALSE(t1.isPrimitive()) + EXPECT_FALSE(t2.isPrimitive()) + EXPECT_FALSE(t3.isPrimitive()) + EXPECT_FALSE(t4.isPrimitive()) + EXPECT_FALSE(t5.isPrimitive()) + EXPECT_FALSE(t6.isPrimitive()) + EXPECT_FALSE(t7.isPrimitive()) + EXPECT_FALSE(t8.isPrimitive()) + EXPECT_FALSE(t9.isPrimitive()) + EXPECT_FALSE(t10.isPrimitive()) + EXPECT_FALSE(t11.isPrimitive()) + EXPECT_FALSE(t12.isPrimitive()) + EXPECT_FALSE(t13.isPrimitive()) + EXPECT_FALSE(t14.isPrimitive()) + EXPECT_TRUE(t15.isPrimitive()) + EXPECT_TRUE(t16.isPrimitive()) + EXPECT_TRUE(t17.isPrimitive()) + EXPECT_TRUE(t18.isPrimitive()) + EXPECT_TRUE(t19.isPrimitive()) + EXPECT_TRUE(t20.isPrimitive()) + EXPECT_TRUE(t21.isPrimitive()) + EXPECT_TRUE(t22.isPrimitive()) + EXPECT_TRUE(t23.isPrimitive()) + EXPECT_TRUE(t24.isPrimitive()) + EXPECT_TRUE(t25.isPrimitive()) + EXPECT_TRUE(t26.isPrimitive()) + EXPECT_TRUE(t27.isPrimitive()) + EXPECT_TRUE(t28.isPrimitive()) + EXPECT_TRUE(t29.isPrimitive()) + EXPECT_TRUE(t30.isPrimitive()) + EXPECT_TRUE(t31.isPrimitive()) + EXPECT_TRUE(t32.isPrimitive()) + EXPECT_TRUE(t33.isPrimitive()) + EXPECT_TRUE(t34.isPrimitive()) + EXPECT_TRUE(t35.isPrimitive()) + EXPECT_TRUE(t36.isPrimitive()) +} + export fn TEST_Type_isRequired () { mut tm := TypeMap{} tm.init() From bf9063517930815b22034736050bdd8ccfe40e0d Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 1 Jan 2025 18:43:35 +0200 Subject: [PATCH 04/48] analyzer: Remove Buffer type --- packages/analyzer/src/type-map | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/packages/analyzer/src/type-map b/packages/analyzer/src/type-map index b330436ea..47855e32f 100644 --- a/packages/analyzer/src/type-map +++ b/packages/analyzer/src/type-map @@ -20,8 +20,6 @@ export obj TypeMap { fn init (mut self: ref Self) { self.mode = .Regular - self.items.push(Type.Type{name: "Buffer", body: Type.ObjectType{}}) - mut bufferType := self.items.last() self.items.push(Type.Type{name: "any", body: Type.ObjectType{}}) mut anyType := self.items.last() self.items.push(Type.Type{name: "bool", body: Type.ObjectType{}}) @@ -67,19 +65,6 @@ export obj TypeMap { self.items.push(Type.Type{name: "void", body: Type.ObjectType{}}) mut voidType := self.items.last() - bufferType.properties.push(Type.TypeProperty{name: "empty", t: self.get("bool"), mutable: false, builtin: true}) - bufferType.properties.push(Type.TypeProperty{name: "len", t: self.get("int"), mutable: false, builtin: true}) - bufferType.properties.push(Type.TypeProperty{name: "concat", t: self.createMethod(false, [ - Type.TypeParameter{name: "other", t: self.get("Buffer"), mutable: false, required: true, variadic: false} - ], self.get("Buffer"), true, false, "self", self.createReference(self.get("Buffer"))), mutable: false, builtin: true}) - bufferType.properties.push(Type.TypeProperty{name: "merge", t: self.createMethod(false, [ - Type.TypeParameter{name: "other", t: self.get("Buffer"), mutable: false, required: true, variadic: false} - ], self.createReference(self.get("Buffer")), true, true, "self", self.createReference(self.get("Buffer"))), mutable: false, builtin: true}) - bufferType.properties.push(Type.TypeProperty{name: "pop", t: self.createMethod(false, [], self.get("byte"), true, true, "self", self.createReference(self.get("Buffer"))), mutable: false, builtin: true}) - bufferType.properties.push(Type.TypeProperty{name: "push", t: self.createMethod(false, [ - Type.TypeParameter{name: "elements", t: self.createArray(self.get("byte")), mutable: false, required: false, variadic: true} - ], self.get("void"), true, true, "self", self.createReference(self.get("Buffer"))), mutable: false, builtin: true}) - bufferType.properties.push(Type.TypeProperty{name: "str", t: self.createMethod(false, [], self.get("str"), true, false, "self", self.createReference(self.get("Buffer"))), mutable: false, builtin: true}) anyType.properties.push(Type.TypeProperty{name: "str", t: self.createMethod(false, [], self.get("str"), true, false, "self", self.createReference(self.get("any"))), mutable: false, builtin: true}) boolType.properties.push(Type.TypeProperty{name: "str", t: self.createMethod(false, [], self.get("str"), true, false, "self", self.createReference(self.get("bool"))), mutable: false, builtin: true}) byteType.properties.push(Type.TypeProperty{name: "str", t: self.createMethod(false, [], self.get("str"), true, false, "self", self.createReference(self.get("byte"))), mutable: false, builtin: true}) @@ -150,7 +135,6 @@ export obj TypeMap { strType.properties.push(Type.TypeProperty{name: "split", t: self.createMethod(false, [ Type.TypeParameter{name: "delimiter", t: self.get("str"), mutable: false, required: false, variadic: false} ], self.createArray(self.get("str")), true, false, "self", self.createReference(self.get("str"))), mutable: false, builtin: true}) - strType.properties.push(Type.TypeProperty{name: "toBuffer", t: self.createMethod(false, [], self.get("Buffer"), true, false, "self", self.createReference(self.get("str"))), mutable: false, builtin: true}) strType.properties.push(Type.TypeProperty{name: "toFloat", t: self.createMethod(false, [], self.get("float"), true, false, "self", self.createReference(self.get("str"))), mutable: false, builtin: true}) strType.properties.push(Type.TypeProperty{name: "toF32", t: self.createMethod(false, [], self.get("f32"), true, false, "self", self.createReference(self.get("str"))), mutable: false, builtin: true}) strType.properties.push(Type.TypeProperty{name: "toF64", t: self.createMethod(false, [], self.get("f64"), true, false, "self", self.createReference(self.get("str"))), mutable: false, builtin: true}) @@ -261,12 +245,7 @@ export obj TypeMap { return result } - fn createFunction ( - mut self: ref Self, - asynchronous: bool, - parameters: Type.TypeParameter[], - returnType: ref Type.Type - ) ref Type.Type { + fn createFunction (mut self: ref Self, asynchronous: bool, parameters: Type.TypeParameter[], returnType: ref Type.Type) ref Type.Type { self.items.push(Type.Type{ body: Type.FunctionType{ asynchronous: asynchronous, @@ -328,7 +307,7 @@ export obj TypeMap { withSelf: bool, selfMutable: bool, selfName: str, - selfType: ref Type.Type + selfType: ref Type.Type, ) ref Type.Type { self.items.push(Type.Type{ body: Type.MethodType{ From 77bcd0d18a989c1ae3934b32d62dd77a54602f9b Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 1 Jan 2025 18:44:10 +0200 Subject: [PATCH 05/48] analyzer: Add ObjectDeclaration context --- packages/analyzer/src/analyzer | 4 ++++ packages/analyzer/src/context | 4 ++++ packages/analyzer/src/main | 2 ++ 3 files changed, 10 insertions(+) diff --git a/packages/analyzer/src/analyzer b/packages/analyzer/src/analyzer index 0dc6c0181..8bdcda22f 100644 --- a/packages/analyzer/src/analyzer +++ b/packages/analyzer/src/analyzer @@ -2462,6 +2462,10 @@ export obj AnalyzerFile { initialStateSelf := self.state.selfType t := self.tm.get(statement.name.name) + it.context = Context.ObjectDeclarationContext{ + selfType: t, + } + self.state.selfType = Type.opt(t) self.tm.setSelf(Type.opt(t)) self.analyze(ref statement.body) diff --git a/packages/analyzer/src/context b/packages/analyzer/src/context index e6d3a9184..a121a46ed 100644 --- a/packages/analyzer/src/context +++ b/packages/analyzer/src/context @@ -32,6 +32,10 @@ export obj IsExpressionContextExtra { mut t: ref Type.Type } +export obj ObjectDeclarationContext { + mut selfType: ref Type.Type +} + export obj VariableDeclarationContext { mut varType: ref Type.Type } diff --git a/packages/analyzer/src/main b/packages/analyzer/src/main index e0ec74e40..8e2662c9d 100644 --- a/packages/analyzer/src/main +++ b/packages/analyzer/src/main @@ -10,6 +10,7 @@ import CallExpressionContextArgument, CallExpressionContextExtra, IsExpressionContextExtra, + ObjectDeclarationContext, VariableDeclarationContext, initial as contextInitial, set as contextSet, @@ -60,6 +61,7 @@ export AsExpressionContextExtra export CallExpressionContextArgument export CallExpressionContextExtra export IsExpressionContextExtra +export ObjectDeclarationContext export VariableDeclarationContext export contextInitial export contextSet From e44f5e5acf839026ee9b2202b72dc12a297a5428 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 1 Jan 2025 19:35:57 +0200 Subject: [PATCH 06/48] c: Add ability to specify that type is a constant --- packages/c/src/ast | 17 +++++++++++------ packages/c/test/ast-test | 6 ++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/c/src/ast b/packages/c/src/ast index 3cf34cc50..d31b5d281 100644 --- a/packages/c/src/ast +++ b/packages/c/src/ast @@ -42,16 +42,21 @@ export type CStatementBody = CWhileStatement export obj CType { + mut constant: bool mut value: str fn stringify (self: ref Self, declaration := false, definition := false) str { + if !declaration && !definition { + throw error_NewError("Unable to stringify specified type") + } + + prefix := self.constant ? "const " : "" + if declaration { lastChar := self.value.slice(-1) - return self.value + (lastChar == " " || lastChar == "*" ? "" : " ") - } elif definition { - return self.value.trimEnd() + return prefix + self.value + (lastChar == " " || lastChar == "*" ? "" : " ") } else { - throw error_NewError("Unable to stringify specified type") + return prefix + self.value.trimEnd() } } } @@ -869,6 +874,6 @@ export fn createStatement (body: CStatementBody, terminated := true) CStatement return CStatement{body: body, terminated: terminated} } -export fn createType (value: str) CType { - return CType{value: value} +export fn createType (value: str, constant := false) CType { + return CType{constant: constant, value: value} } diff --git a/packages/c/test/ast-test b/packages/c/test/ast-test index 407a08d02..41ca0b38d 100644 --- a/packages/c/test/ast-test +++ b/packages/c/test/ast-test @@ -9,6 +9,8 @@ export fn TEST_AST_type () { type1 := AST.createType("int") type2 := AST.createType("int *") type3 := AST.createType("int[]") + type5 := AST.createType("int", constant: true) + type6 := AST.createType("int *", constant: true) EXPECT_EQ(type1.stringify(declaration: true), "int ") EXPECT_EQ(type1.stringify(definition: true), "int") @@ -16,6 +18,10 @@ export fn TEST_AST_type () { EXPECT_EQ(type2.stringify(definition: true), "int *") EXPECT_EQ(type3.stringify(declaration: true), "int[] ") EXPECT_EQ(type3.stringify(definition: true), "int[]") + EXPECT_EQ(type5.stringify(declaration: true), "const int ") + EXPECT_EQ(type5.stringify(definition: true), "const int") + EXPECT_EQ(type6.stringify(declaration: true), "const int *") + EXPECT_EQ(type6.stringify(definition: true), "const int *") EXPECT_THROW_WITH_MESSAGE(() -> void { type4 := AST.createType("int") From a788c3e4177bdef46f99027b4dabc0a2815ece35 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 1 Jan 2025 20:00:58 +0200 Subject: [PATCH 07/48] c: Ability to create VariableDeclaration w/o semi --- packages/c/src/ast | 10 +++++----- packages/c/test/ast-test | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/c/src/ast b/packages/c/src/ast index d31b5d281..aad9aab46 100644 --- a/packages/c/src/ast +++ b/packages/c/src/ast @@ -276,7 +276,7 @@ export obj CStatement { result = statement.stringify() } elif self.isVariableDeclaration() { statement := self.asVariableDeclaration() - result = statement.stringify() + result = statement.stringify(terminated: self.terminated) } elif self.isWhileStatement() { statement := self.asWhileStatement() result = statement.stringify(indent) @@ -687,12 +687,12 @@ export obj CVariableDeclaration { mut name: str mut initializer: CExpression? - fn stringify (self: ref Self) str { + fn stringify (self: ref Self, terminated: bool) str { mut result := self.t.stringify(declaration: true) + self.name if self.initializer != nil { result += " = " + self.initializer.stringify() } - return result + ";" + return result + (terminated ? ";" : "") } } @@ -858,8 +858,8 @@ export fn createTypedefDeclaration (t: CType, name: str) CStatement { return createStatement(CTypedefDeclaration{t: t, name: name}) } -export fn createVariableDeclaration (t: CType, name: str, initializer: CExpression? = nil) CStatement { - return createStatement(CVariableDeclaration{t: t, name: name, initializer: initializer}) +export fn createVariableDeclaration (t: CType, name: str, initializer: CExpression? = nil, terminated := true) CStatement { + return createStatement(CVariableDeclaration{t: t, name: name, initializer: initializer}, terminated: terminated) } export fn createWhileStatement (condition: CExpression, body: CStatement) CStatement { diff --git a/packages/c/test/ast-test b/packages/c/test/ast-test index 41ca0b38d..b4910ad25 100644 --- a/packages/c/test/ast-test +++ b/packages/c/test/ast-test @@ -87,7 +87,17 @@ export fn TEST_AST_statementDefinition () { export fn TEST_AST_statementTerminated () { statement1 := AST.createExpressionStatement(AST.createMacroInvocation("TEST", []), terminated: false) + + statement2 := AST.createVariableDeclaration(AST.createType("int"), "test", terminated: false) + statement3 := AST.createVariableDeclaration(AST.createType("int"), "test", AST.createLiteral("1"), terminated: false) + EXPECT_EQ(statement1.stringify(), "TEST()" + os_EOL) + EXPECT_EQ(statement1.stringify(2), " TEST()" + os_EOL) + + EXPECT_EQ(statement2.stringify(), "int test" + os_EOL) + EXPECT_EQ(statement2.stringify(2), " int test" + os_EOL) + EXPECT_EQ(statement3.stringify(), "int test = 1" + os_EOL) + EXPECT_EQ(statement3.stringify(2), " int test = 1" + os_EOL) } export fn TEST_AST_identifier () { From a22e2d6e6c83195ec23984f851849677b3372a7c Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 2 Jan 2025 08:41:38 +0200 Subject: [PATCH 08/48] codegen: ObjectDeclaration implementation --- packages/codegen/src/codegen | 308 ++++++++++++++++++++++++++++++----- packages/codegen/src/helpers | 33 ++-- packages/codegen/src/utils | 22 +++ 3 files changed, 302 insertions(+), 61 deletions(-) create mode 100644 packages/codegen/src/utils diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 7d4400919..8b354c5c0 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -16,9 +16,8 @@ import statementHasPrecedingNonVarDecl, statementIsMacroInvocation, statementIsMacroInvocationDeclare, - typeHasSelfParam, + typeIsSimple, typeParameterId, - typePrimitive, typeSafeForTernaryAlternate, typeSafeForVaArg, typeShouldBeAllocated, @@ -469,7 +468,14 @@ export obj Codegen { } statement := item.asObjectDeclaration() - // TODO: implement + + if !(item.context is Analyzer.ObjectDeclarationContext) { + throw error_NewError("Expected ObjectDeclaration context") + } + + context := item.context as Analyzer.ObjectDeclarationContext + self._generateObjectType(context.selfType) + return true } @@ -498,18 +504,18 @@ export obj Codegen { return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), [ AST.createLiteral("0"), ]) - // TODO: implement - // } elif it.isObject() { - // typeName := self._typeName(it) - // mut args: AST.CExpression[] - // loop i := 0; i < it.properties.len; i++ { - // property := it.properties[i] - // if property.builtin || property.t.isMethod() { - // continue - // } - // args.push(self._defaultInitializerExpression(property.t)) - // } - // return AST.createCallExpression(AST.createIdentifier(self._(typeName + "_alloc")), args) + } elif it.isCustomObject() { + // TODO: test + typeName := self._typeName(it) + mut args: (AST.CExpression | AST.CType)[] + loop i := 0; i < it.properties.len; i++ { + property := it.properties[i] + if property.builtin || property.t.isMethod() { + continue + } + args.push(self._defaultInitializerExpression(property.t)) + } + return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), args) } elif it.isOptional() { return AST.createIdentifier(self._("NULL")) } elif it.isUnion() { @@ -548,18 +554,18 @@ export obj Codegen { return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), [ AST.createLiteral("0"), ]) - // TODO: implement - // } elif it.isObject() { - // typeName := self._typeName(it) - // mut args : AST.CExpression[] - // loop i := 0; i < it.properties.len; i++ { - // property := it.properties[i] - // if property.builtin || property.t.isMethod() { - // continue - // } - // args.push(self._defaultInitializerExpression(property.t)) - // } - // return AST.createCallExpression(AST.createIdentifier(self._(typeName + "_alloc")), args) + } elif it.isCustomObject() { + // TODO: test + typeName := self._typeName(it) + mut args : (AST.CExpression | AST.CType)[] + loop i := 0; i < it.properties.len; i++ { + property := it.properties[i] + if property.builtin || property.t.isMethod() { + continue + } + args.push(self._defaultInitializerExpression(property.t)) + } + return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), args) } elif it.isOptional() { return AST.createIdentifier(self._("NULL")) } elif it.isUnion() { @@ -586,7 +592,8 @@ export obj Codegen { it.isArray() || it.isFunction() || it.isMap() || - // TODO: it.isObject() || + // TODO: test + it.isCustomObject() || it.isOptional() || it.isUnion() ) { @@ -610,7 +617,8 @@ export obj Codegen { } elif ( it.isArray() || it.isMap() || - // TODO: it.isObject() || + // TODO: test + it.isCustomObject() || it.isOptional() || it.isUnion() ) { @@ -635,7 +643,8 @@ export obj Codegen { it.isArray() || it.isFunction() || it.isMap() || - // TODO: it.isObject() || + // TODO: test + it.isCustomObject() || it.isOptional() || it.isUnion() ) { @@ -666,7 +675,8 @@ export obj Codegen { it.isArray() || it.isFunction() || it.isMap() || - // TODO: it.isObject() || + // TODO: test + it.isCustomObject() || it.isOptional() || it.isUnion() ) { @@ -679,7 +689,7 @@ export obj Codegen { ) } - throw error_NewError("Tried re-allocating unknown type") + throw error_NewError("Tried re-allocating unknown type '" + it.toString() + "'") } fn _functionStr (mut self: ref Self, it: ref Analyzer.Type, expression: AST.CExpression, quote := false) AST.CExpression { @@ -697,10 +707,11 @@ export obj Codegen { } elif it.isReference() { t := it.asReference() return self._functionStr(t.t, AST.createUnaryExpression("*", expression), quote: quote) - } else { - typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_str")), [expression]) } + + // TODO: test custom object + typeName := self._typeName(it) + return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_str")), [expression]) } fn _generateBlock (mut self: ref Self, items: ref Parser.Statement[]) AST.CStatement[] { @@ -974,7 +985,7 @@ export obj Codegen { AST.createCallExpression(AST.createIdentifier(self._("d4_str_concat")), [left, right]), ), ) - } elif !typePrimitive(leftType) || !typePrimitive(rightType) { + } elif !typeIsSimple(leftType) || !typeIsSimple(rightType) { return self._functionRealloc(leftType, left, right) } elif expression.operator.t == .OpAmpAmp || expression.operator.t == .OpPipePipe { right = AST.createBinaryExpression(left, expression.operator.val, right) @@ -1008,7 +1019,7 @@ export obj Codegen { if ( (expression.operator.t == .OpEqEq || expression.operator.t == .OpExclEq) && - (!typePrimitive(leftType) || !typePrimitive(rightType)) + (!typeIsSimple(leftType) || !typeIsSimple(rightType)) ) { return self._functionEq(leftType, left, right, reverse: expression.operator.t == .OpExclEq) } elif leftType.name == "str" && rightType.name == "str" && ( @@ -1059,7 +1070,7 @@ export obj Codegen { calleeExpression := self._generateExpression(ref expression.callee) mut paramsArgs: AST.CExpression[] - if typeHasSelfParam(calleeTargetType) { + if calleeTargetType.hasSelfParam() { paramsArgs.push(calleeExpression) } @@ -2098,6 +2109,219 @@ export obj Codegen { return name } + fn _generateObjectType (mut self: ref Codegen, item: ref Analyzer.Type) str { + typeName := "d4_" + self._typeName(item) + name := typeName + "_t" + + if self._hasEntity(name) { + return name + } + + self.entities.push(CodegenEntity{ + name: name, + codeName: name, + context: item, + generate: (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + it := entity.context as ref Analyzer.Type + + mut extraDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] + mut structureBlock: AST.CStatement[] + mut allocBlock: AST.CStatement[] + mut copyBlock: AST.CStatement[] + mut eqExpression := AST.createIdentifier(self._("true")) + mut freeBlock: AST.CStatement[] + mut strBlock: AST.CStatement[] + mut propertyIdx := 0 + + loop i := 0; i < it.properties.len; i++ { + property := it.properties[i] + + if property.builtin || property.t.isMethod() { + continue + } + + cPropertyType := self._type(property.t) + cSelfProperty := AST.createPropertyAccessExpression(AST.createIdentifier("self"), property.name) + + extraDeclarationArguments.push( + AST.createVariableDeclaration( + AST.createType(cPropertyType, constant: true), + property.name, + terminated: false, + ) + ) + + structureBlock.push( + AST.createVariableDeclaration(AST.createType(cPropertyType), property.name) + ) + + allocBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + cSelfProperty, + "=", + self._functionCopy(property.t, AST.createIdentifier(property.name)), + ), + ), + ) + + copyBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("result"), property.name), + "=", + self._functionCopy(property.t, cSelfProperty), + ), + ), + ) + + eqExpressionPiece := self._functionEq( + property.t, + cSelfProperty, + AST.createPropertyAccessExpression(AST.createIdentifier("rhs"), property.name), + ) + + eqExpression = propertyIdx == 0 + ? eqExpressionPiece + : AST.createBinaryExpression(eqExpression, "&&", eqExpressionPiece) + + if typeShouldBeFreed(property.t) { + freeBlock.push( + AST.createExpressionStatement( + self._functionFree(property.t, cSelfProperty), + ), + ) + } + + strBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createIdentifier("result"), + "=", + AST.createCallExpression(AST.createIdentifier(self._("d4_obj_str_append")), [ + AST.createIdentifier("result"), + AST.createCallExpression(AST.createIdentifier(self._("d4_str_alloc")), [ + AST.createLiteral("L\"" + property.name + "\""), + ]), + self._functionStr(property.t, cSelfProperty, quote: true), + ]), + ), + ), + ) + + propertyIdx++ + } + + if freeBlock.empty { + freeBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + } + + mut eqBlock: AST.CStatement[] + + if propertyIdx == 0 { + extraDeclarationArguments.push(AST.createType("void")) + + structureBlock.push( + AST.createVariableDeclaration(AST.createType(self._("bool")), "_") + ) + + allocBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), + "=", + AST.createIdentifier(self._("false")), + ), + ), + ) + + copyBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("result"), "_"), + "=", + AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), + ), + ), + ) + + eqBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + + eqBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("rhs")), + ), + ) + + strBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + } + + eqBlock.push(AST.createReturnStatement(eqExpression)) + + mut declarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + AST.createIdentifier(it.name), + AST.createCompoundStatement(structureBlock), + ] + + mut definitionArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + AST.createIdentifier(it.name), + AST.createIdentifier(it.name), + AST.createCompoundStatement(allocBlock), + AST.createCompoundStatement(copyBlock), + AST.createCompoundStatement(eqBlock), + AST.createCompoundStatement(freeBlock), + AST.createCompoundStatement(strBlock), + ] + + declarationArguments.merge(extraDeclarationArguments) + definitionArguments.merge(extraDeclarationArguments) + + return [ + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_DECLARE"), declarationArguments), + terminated: false, + ), + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_DEFINE"), definitionArguments), + terminated: false, + ), + ] + } + }) + + methods := ["alloc", "copy", "eq", "free", "realloc", "str"] + + entityCallee := (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + self._(entity.context as str) + return AST.createEmptyStatement() + } + + loop i := 0; i < methods.len; i++ { + methodName := methods[i] as str + + self.entities.push(CodegenEntity{ + name: typeName + "_" + methodName, + codeName: typeName + "_" + methodName, + context: name, + generate: entityCallee, + }) + } + + return name + } + fn _generateOptionalType (mut self: ref Codegen, item: ref Analyzer.Type) str { typeName := "d4_" + self._typeName(item) name := typeName + "_t" @@ -2473,13 +2697,17 @@ export obj Codegen { it.isEnum() || it.isFunction() || it.isMap() || - // TODO: it.isObject() || + // TODO: test + it.isCustomObject() || it.isOptional() || it.isReference() || it.isUnion() ) { if it.isArray() { self._generateArrayType(it) + } elif it.isCustomObject() { + // TODO: test + self._generateObjectType(it) } elif it.isFunction() { self._generateFunctionType(it) } elif it.isMap() { @@ -2592,6 +2820,8 @@ export obj Codegen { } elif it.isArray() { t := it.asArray() return "arr_" + self._typeName(t.elementType) + } elif it.isCustomObject() { + return "obj_" + it.name } elif it.isEnum() || it.isObject() { return it.name } elif it.isFunction() { diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 9f75c21cb..f91a3fe0e 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -119,39 +119,28 @@ export fn statementIsMacroInvocationDeclare (item: ref AST.CStatement) bool { return macroInvocation.callee.find("DECLARE") != -1 } -// TODO: move to analyzer -export fn typeHasSelfParam (it: ref Analyzer.Type) bool { - if it.isMethod() { - t := it.asMethod() - return t.withSelf - } - - return false -} - -export fn typeParameterId (parameter: ref Analyzer.TypeParameter) str { - if parameter.mutable && parameter.required { return "FP5" } - elif parameter.mutable && parameter.variadic { return "FP6" } - elif parameter.mutable { return "FP2" } - elif parameter.required { return "FP3" } - elif parameter.variadic { return "FP4" } - else { return "FP1" } -} - -// TODO: move to analyzer -export fn typePrimitive (it: ref Analyzer.Type) bool { +export fn typeIsSimple (it: ref Analyzer.Type) bool { return !( it.name == "any" || it.name == "str" || it.isArray() || it.isFunction() || it.isMap() || - // TODO: it.isObject() || + it.isCustomObject() || it.isOptional() || it.isUnion() ) } +export fn typeParameterId (parameter: ref Analyzer.TypeParameter) str { + if parameter.mutable && parameter.required { return "FP5" } + elif parameter.mutable && parameter.variadic { return "FP6" } + elif parameter.mutable { return "FP2" } + elif parameter.required { return "FP3" } + elif parameter.variadic { return "FP4" } + else { return "FP1" } +} + export fn typeSafeForTernaryAlternate (it: ref Analyzer.Type) bool { return !( it.name == "any" || diff --git a/packages/codegen/src/utils b/packages/codegen/src/utils new file mode 100644 index 000000000..3b0607ac4 --- /dev/null +++ b/packages/codegen/src/utils @@ -0,0 +1,22 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +export fn sortStrAsc (a: str, b: str) int { + if a.len == 0 && b.len == 0 { + return 0 + } elif a.len == 0 || b.len == 0 { + return a.len == 0 ? -1 : 1 + } + + len := a.len > b.len ? b.len : a.len + + loop i := 0; i < len; i++ { + if a[i] != b[i] { + return a[i].byte > b[i].byte ? 1 : -1 + } + } + + return a.len == b.len ? 0 : a.len > b.len ? 1 : -1 +} From 2f38590df7534f31aab1bb7287b884b1e4010fd1 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 2 Jan 2025 08:52:02 +0200 Subject: [PATCH 09/48] codegen: Sort union subtypes --- packages/codegen/src/codegen | 10 ++--- .../test/codegen/is-expression-root.txt | 44 +++++++++---------- .../codegen/test/codegen/is-expression.txt | 44 +++++++++---------- packages/codegen/test/codegen/union-type.txt | 6 +-- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 8b354c5c0..e35597b2f 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -23,6 +23,7 @@ import typeShouldBeAllocated, typeShouldBeFreed from "./helpers" +import sortStrAsc from "./utils" // TODO: implement FunctionDeclaration statement // TODO: implement Return statement @@ -2858,17 +2859,16 @@ export obj Codegen { t := it.asReference() return "ref_" + self._typeName(t.t) } elif it.isUnion() { - // TODO: account for different order of sub types t := it.asUnion() - mut name := "" + mut names: str[] loop i := 0; i < t.types.len; i++ { subType := t.types[i] as ref Analyzer.Type - name += i == 0 ? "" : "US" - name += self._typeName(subType) + names.push(self._typeName(subType)) } - return "union_" + name + "UE" + namesSorted := names.sort(sortStrAsc) + return "union_" + namesSorted.join("US") + "UE" } throw error_NewError("Failed to generate type name for '" + it.name + "'") diff --git a/packages/codegen/test/codegen/is-expression-root.txt b/packages/codegen/test/codegen/is-expression-root.txt index b51f6823d..4d4c10272 100644 --- a/packages/codegen/test/codegen/is-expression-root.txt +++ b/packages/codegen/test/codegen/is-expression-root.txt @@ -55,14 +55,14 @@ main { #define TYPE_float 2 #define TYPE_str 3 D4_ANY_DECLARE(str, d4_str_t) -D4_UNION_DECLARE(intUSfloat, { +D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) D4_ANY_DECLARE(float, double) D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) -D4_UNION_DEFINE(intUSfloat, { +D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); if (self.type == TYPE_float) self.data.v2 = va_arg(args, double); }, { @@ -81,17 +81,17 @@ D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4 D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { d4_any_t b_0; - d4_union_intUSfloatUE_t c_0; - d4_union_intUSfloatUE_t d_0; + d4_union_floatUSintUE_t c_0; + d4_union_floatUSintUE_t d_0; d4_any_t e_0; d4_any_t __THE_9 = {-1, NULL, NULL, NULL, NULL, NULL}; d4_any_t y_0; d4_any_t f_0; d4_any_t __THE_13 = {-1, NULL, NULL, NULL, NULL, NULL}; - d4_union_intUSfloatUE_t g_0; - d4_union_intUSfloatUE_t __THE_16 = d4_union_intUSfloatUE_alloc(-1); - d4_union_intUSfloatUE_t h_0; - d4_union_intUSfloatUE_t __THE_19 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t g_0; + d4_union_floatUSintUE_t __THE_16 = d4_union_floatUSintUE_alloc(-1); + d4_union_floatUSintUE_t h_0; + d4_union_floatUSintUE_t __THE_19 = d4_union_floatUSintUE_alloc(-1); d4_str_t __THE_22 = d4_str_empty_val; d4_str_t __THE_23 = d4_str_empty_val; d4_str_t __THE_24 = d4_str_empty_val; @@ -106,10 +106,10 @@ int main (void) { d4_any_t __THE_33 = {-1, NULL, NULL, NULL, NULL, NULL}; d4_str_t __THE_34 = d4_str_empty_val; d4_str_t __THE_35 = d4_str_empty_val; - d4_union_intUSfloatUE_t __THE_36 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t __THE_36 = d4_union_floatUSintUE_alloc(-1); d4_str_t __THE_37 = d4_str_empty_val; d4_str_t __THE_38 = d4_str_empty_val; - d4_union_intUSfloatUE_t __THE_39 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t __THE_39 = d4_union_floatUSintUE_alloc(-1); d4_str_t __THE_40 = d4_str_empty_val; d4_str_t __THE_41 = d4_str_empty_val; d4_any_t a_0 = d4_any_int_alloc(1); @@ -128,7 +128,7 @@ int main (void) { d4_str_free(__THE_4); d4_str_free(__THE_3); } - c_0 = d4_union_intUSfloatUE_alloc(TYPE_int, 1); + c_0 = d4_union_floatUSintUE_alloc(TYPE_int, 1); if ((c_0.data.v1, true)) { d4_str_t __THE_5 = d4_str_empty_val; d4_str_t __THE_6 = d4_str_empty_val; @@ -139,7 +139,7 @@ int main (void) { else { d4_str_free(d4_str_alloc(L"never")); } - d_0 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14); + d_0 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14); if ((d_0.data.v2, true)) { d4_str_t __THE_7 = d4_str_empty_val; d4_str_t __THE_8 = d4_str_empty_val; @@ -178,8 +178,8 @@ int main (void) { d4_str_free(__THE_15); d4_str_free(__THE_14); } - g_0 = d4_union_intUSfloatUE_alloc(TYPE_int, 1); - if (((g_0 = d4_union_intUSfloatUE_realloc(g_0, __THE_16 = d4_union_intUSfloatUE_alloc(TYPE_int, 1))), true)) { + g_0 = d4_union_floatUSintUE_alloc(TYPE_int, 1); + if (((g_0 = d4_union_floatUSintUE_realloc(g_0, __THE_16 = d4_union_floatUSintUE_alloc(TYPE_int, 1))), true)) { d4_str_t __THE_17 = d4_str_empty_val; d4_str_t __THE_18 = d4_str_empty_val; d4_str_free(d4_str_concat(__THE_17 = d4_str_alloc(L"int "), __THE_18 = d4_int_str(g_0.data.v1))); @@ -189,8 +189,8 @@ int main (void) { else { d4_str_free(d4_str_alloc(L"never")); } - h_0 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14); - if (((h_0 = d4_union_intUSfloatUE_realloc(h_0, __THE_19 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14))), true)) { + h_0 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14); + if (((h_0 = d4_union_floatUSintUE_realloc(h_0, __THE_19 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14))), true)) { d4_str_t __THE_20 = d4_str_empty_val; d4_str_t __THE_21 = d4_str_empty_val; d4_str_free(d4_str_concat(__THE_20 = d4_str_alloc(L"float "), __THE_21 = d4_float_str(h_0.data.v2))); @@ -206,14 +206,14 @@ int main (void) { d4_str_free((d_0.data.v2, true) ? d4_str_concat(__THE_28 = d4_str_alloc(L"float "), __THE_29 = d4_float_str(d_0.data.v2)) : d4_str_alloc(L"never")); d4_str_free(((e_0 = d4_any_realloc(e_0, __THE_30 = d4_any_int_alloc(1))), true) ? d4_str_concat(__THE_31 = d4_str_alloc(L"int "), __THE_32 = d4_int_str(*((d4_any_int_t) e_0.ctx))) : d4_str_alloc(L"never")); d4_str_free(((f_0 = d4_any_realloc(f_0, __THE_33 = d4_any_float_alloc(3.14))), true) ? d4_str_concat(__THE_34 = d4_str_alloc(L"float "), __THE_35 = d4_float_str(*((d4_any_float_t) f_0.ctx))) : d4_str_alloc(L"never")); - d4_str_free(((g_0 = d4_union_intUSfloatUE_realloc(g_0, __THE_36 = d4_union_intUSfloatUE_alloc(TYPE_int, 1))), true) ? d4_str_concat(__THE_37 = d4_str_alloc(L"int "), __THE_38 = d4_int_str(g_0.data.v1)) : d4_str_alloc(L"never")); - d4_str_free(((h_0 = d4_union_intUSfloatUE_realloc(h_0, __THE_39 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14))), true) ? d4_str_concat(__THE_40 = d4_str_alloc(L"float "), __THE_41 = d4_float_str(h_0.data.v2)) : d4_str_alloc(L"never")); + d4_str_free(((g_0 = d4_union_floatUSintUE_realloc(g_0, __THE_36 = d4_union_floatUSintUE_alloc(TYPE_int, 1))), true) ? d4_str_concat(__THE_37 = d4_str_alloc(L"int "), __THE_38 = d4_int_str(g_0.data.v1)) : d4_str_alloc(L"never")); + d4_str_free(((h_0 = d4_union_floatUSintUE_realloc(h_0, __THE_39 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14))), true) ? d4_str_concat(__THE_40 = d4_str_alloc(L"float "), __THE_41 = d4_float_str(h_0.data.v2)) : d4_str_alloc(L"never")); d4_str_free(__THE_41); d4_str_free(__THE_40); - d4_union_intUSfloatUE_free(__THE_39); + d4_union_floatUSintUE_free(__THE_39); d4_str_free(__THE_38); d4_str_free(__THE_37); - d4_union_intUSfloatUE_free(__THE_36); + d4_union_floatUSintUE_free(__THE_36); d4_str_free(__THE_35); d4_str_free(__THE_34); d4_any_free(__THE_33); @@ -228,8 +228,8 @@ int main (void) { d4_str_free(__THE_24); d4_str_free(__THE_23); d4_str_free(__THE_22); - d4_union_intUSfloatUE_free(__THE_19); - d4_union_intUSfloatUE_free(__THE_16); + d4_union_floatUSintUE_free(__THE_19); + d4_union_floatUSintUE_free(__THE_16); d4_any_free(__THE_13); d4_any_free(f_0); d4_any_free(y_0); diff --git a/packages/codegen/test/codegen/is-expression.txt b/packages/codegen/test/codegen/is-expression.txt index 63324592c..38a7db67e 100644 --- a/packages/codegen/test/codegen/is-expression.txt +++ b/packages/codegen/test/codegen/is-expression.txt @@ -59,14 +59,14 @@ main { #define TYPE_int 1 #define TYPE_float 2 #define TYPE_str 3 -D4_UNION_DECLARE(intUSfloat, { +D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) D4_ANY_DECLARE(float, double) D4_ANY_DECLARE(str, d4_str_t) D4_ANY_DECLARE(int, int32_t) -D4_UNION_DEFINE(intUSfloat, { +D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); if (self.type == TYPE_float) self.data.v2 = va_arg(args, double); }, { @@ -86,17 +86,17 @@ D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_ D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { d4_any_t b_0; - d4_union_intUSfloatUE_t c_0; - d4_union_intUSfloatUE_t d_0; + d4_union_floatUSintUE_t c_0; + d4_union_floatUSintUE_t d_0; d4_any_t e_0; d4_any_t __THE_25 = {-1, NULL, NULL, NULL, NULL, NULL}; d4_any_t y_0; d4_any_t f_0; d4_any_t __THE_37 = {-1, NULL, NULL, NULL, NULL, NULL}; - d4_union_intUSfloatUE_t g_0; - d4_union_intUSfloatUE_t __THE_44 = d4_union_intUSfloatUE_alloc(-1); - d4_union_intUSfloatUE_t h_0; - d4_union_intUSfloatUE_t __THE_51 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t g_0; + d4_union_floatUSintUE_t __THE_44 = d4_union_floatUSintUE_alloc(-1); + d4_union_floatUSintUE_t h_0; + d4_union_floatUSintUE_t __THE_51 = d4_union_floatUSintUE_alloc(-1); d4_str_t __THE_58 = d4_str_empty_val; d4_str_t __THE_59 = d4_str_empty_val; d4_str_t __THE_60 = d4_str_empty_val; @@ -123,12 +123,12 @@ int main (void) { d4_str_t __THE_81 = d4_str_empty_val; d4_str_t __THE_82 = d4_str_empty_val; d4_any_t __THE_83 = {-1, NULL, NULL, NULL, NULL, NULL}; - d4_union_intUSfloatUE_t __THE_84 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t __THE_84 = d4_union_floatUSintUE_alloc(-1); d4_str_t __THE_85 = d4_str_empty_val; d4_str_t __THE_86 = d4_str_empty_val; d4_str_t __THE_87 = d4_str_empty_val; d4_any_t __THE_88 = {-1, NULL, NULL, NULL, NULL, NULL}; - d4_union_intUSfloatUE_t __THE_89 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t __THE_89 = d4_union_floatUSintUE_alloc(-1); d4_str_t __THE_90 = d4_str_empty_val; d4_str_t __THE_91 = d4_str_empty_val; d4_str_t __THE_92 = d4_str_empty_val; @@ -171,7 +171,7 @@ int main (void) { d4_any_free(__THE_12); d4_str_free(__THE_11); } - c_0 = d4_union_intUSfloatUE_alloc(TYPE_int, 1); + c_0 = d4_union_floatUSintUE_alloc(TYPE_int, 1); if ((c_0.data.v1, true)) { d4_str_t __THE_13 = d4_str_empty_val; d4_str_t __THE_14 = d4_str_empty_val; @@ -190,7 +190,7 @@ int main (void) { d4_any_free(__THE_18); d4_str_free(__THE_17); } - d_0 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14); + d_0 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14); if ((d_0.data.v2, true)) { d4_str_t __THE_19 = d4_str_empty_val; d4_str_t __THE_20 = d4_str_empty_val; @@ -264,8 +264,8 @@ int main (void) { d4_any_free(__THE_43); d4_str_free(__THE_42); } - g_0 = d4_union_intUSfloatUE_alloc(TYPE_int, 1); - if (((g_0 = d4_union_intUSfloatUE_realloc(g_0, __THE_44 = d4_union_intUSfloatUE_alloc(TYPE_int, 1))), true)) { + g_0 = d4_union_floatUSintUE_alloc(TYPE_int, 1); + if (((g_0 = d4_union_floatUSintUE_realloc(g_0, __THE_44 = d4_union_floatUSintUE_alloc(TYPE_int, 1))), true)) { d4_str_t __THE_45 = d4_str_empty_val; d4_str_t __THE_46 = d4_str_empty_val; d4_str_t __THE_47 = d4_str_empty_val; @@ -283,8 +283,8 @@ int main (void) { d4_any_free(__THE_50); d4_str_free(__THE_49); } - h_0 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14); - if (((h_0 = d4_union_intUSfloatUE_realloc(h_0, __THE_51 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14))), true)) { + h_0 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14); + if (((h_0 = d4_union_floatUSintUE_realloc(h_0, __THE_51 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14))), true)) { d4_str_t __THE_52 = d4_str_empty_val; d4_str_t __THE_53 = d4_str_empty_val; d4_str_t __THE_54 = d4_str_empty_val; @@ -308,18 +308,18 @@ int main (void) { d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_73 = d4_any_str_alloc(__THE_72 = (d_0.data.v2, true) ? d4_str_concat(__THE_70 = d4_str_alloc(L"float "), __THE_71 = d4_float_str(d_0.data.v2)) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_78 = d4_any_str_alloc(__THE_77 = ((e_0 = d4_any_realloc(e_0, __THE_74 = d4_any_int_alloc(1))), true) ? d4_str_concat(__THE_75 = d4_str_alloc(L"int "), __THE_76 = d4_int_str(*((d4_any_int_t) e_0.ctx))) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_83 = d4_any_str_alloc(__THE_82 = ((f_0 = d4_any_realloc(f_0, __THE_79 = d4_any_float_alloc(3.14))), true) ? d4_str_concat(__THE_80 = d4_str_alloc(L"float "), __THE_81 = d4_float_str(*((d4_any_float_t) f_0.ctx))) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); - d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_88 = d4_any_str_alloc(__THE_87 = ((g_0 = d4_union_intUSfloatUE_realloc(g_0, __THE_84 = d4_union_intUSfloatUE_alloc(TYPE_int, 1))), true) ? d4_str_concat(__THE_85 = d4_str_alloc(L"int "), __THE_86 = d4_int_str(g_0.data.v1)) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); - d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_93 = d4_any_str_alloc(__THE_92 = ((h_0 = d4_union_intUSfloatUE_realloc(h_0, __THE_89 = d4_union_intUSfloatUE_alloc(TYPE_float, 3.14))), true) ? d4_str_concat(__THE_90 = d4_str_alloc(L"float "), __THE_91 = d4_float_str(h_0.data.v2)) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_88 = d4_any_str_alloc(__THE_87 = ((g_0 = d4_union_floatUSintUE_realloc(g_0, __THE_84 = d4_union_floatUSintUE_alloc(TYPE_int, 1))), true) ? d4_str_concat(__THE_85 = d4_str_alloc(L"int "), __THE_86 = d4_int_str(g_0.data.v1)) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_93 = d4_any_str_alloc(__THE_92 = ((h_0 = d4_union_floatUSintUE_realloc(h_0, __THE_89 = d4_union_floatUSintUE_alloc(TYPE_float, 3.14))), true) ? d4_str_concat(__THE_90 = d4_str_alloc(L"float "), __THE_91 = d4_float_str(h_0.data.v2)) : d4_str_alloc(L"never"))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); d4_any_free(__THE_93); d4_str_free(__THE_92); d4_str_free(__THE_91); d4_str_free(__THE_90); - d4_union_intUSfloatUE_free(__THE_89); + d4_union_floatUSintUE_free(__THE_89); d4_any_free(__THE_88); d4_str_free(__THE_87); d4_str_free(__THE_86); d4_str_free(__THE_85); - d4_union_intUSfloatUE_free(__THE_84); + d4_union_floatUSintUE_free(__THE_84); d4_any_free(__THE_83); d4_str_free(__THE_82); d4_str_free(__THE_81); @@ -346,8 +346,8 @@ int main (void) { d4_str_free(__THE_60); d4_str_free(__THE_59); d4_str_free(__THE_58); - d4_union_intUSfloatUE_free(__THE_51); - d4_union_intUSfloatUE_free(__THE_44); + d4_union_floatUSintUE_free(__THE_51); + d4_union_floatUSintUE_free(__THE_44); d4_any_free(__THE_37); d4_any_free(f_0); d4_any_free(y_0); diff --git a/packages/codegen/test/codegen/union-type.txt b/packages/codegen/test/codegen/union-type.txt index c7e1c6abb..e36dca036 100644 --- a/packages/codegen/test/codegen/union-type.txt +++ b/packages/codegen/test/codegen/union-type.txt @@ -18,7 +18,7 @@ D4_UNION_DECLARE(runeUSstr, { wchar_t v3; d4_str_t v4; }) -D4_UNION_DECLARE(intUSfloat, { +D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) @@ -37,7 +37,7 @@ D4_UNION_DEFINE(runeUSstr, { if (self.type == TYPE_rune) return d4_rune_str(self.data.v3); if (self.type == TYPE_str) return d4_str_copy(self.data.v4); }) -D4_UNION_DEFINE(intUSfloat, { +D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); if (self.type == TYPE_float) self.data.v2 = va_arg(args, double); }, { @@ -53,7 +53,7 @@ D4_UNION_DEFINE(intUSfloat, { if (self.type == TYPE_float) return d4_float_str(self.data.v2); }) int main (void) { - d4_union_intUSfloatUE_t var1_0 = d4_union_intUSfloatUE_alloc(-1); + d4_union_floatUSintUE_t var1_0 = d4_union_floatUSintUE_alloc(-1); d4_union_runeUSstrUE_t var2_0 = d4_union_runeUSstrUE_alloc(-1); d4_union_runeUSstrUE_free(var2_0); } From 92123790de78fcb01fe9914a7b853a933b2665f9 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 2 Jan 2025 11:01:47 +0200 Subject: [PATCH 10/48] codegen: Add update-tests script --- packages/codegen/package.yml | 3 + packages/codegen/scripts/update-codegen-tests | 59 +++++++++ packages/codegen/test/codegen-test | 116 +---------------- packages/codegen/test/utils | 119 ++++++++++++++++++ 4 files changed, 182 insertions(+), 115 deletions(-) create mode 100644 packages/codegen/scripts/update-codegen-tests create mode 100644 packages/codegen/test/utils diff --git a/packages/codegen/package.yml b/packages/codegen/package.yml index 5c5b3b6fc..dee8a01eb 100644 --- a/packages/codegen/package.yml +++ b/packages/codegen/package.yml @@ -9,3 +9,6 @@ packages: the/c: 0.1.0 the/parser: 0.4.16 the/testing: 0.4.0 + +scripts: + update-codegen-tests: the run scripts/update-codegen-tests diff --git a/packages/codegen/scripts/update-codegen-tests b/packages/codegen/scripts/update-codegen-tests new file mode 100644 index 000000000..4c8bdbb03 --- /dev/null +++ b/packages/codegen/scripts/update-codegen-tests @@ -0,0 +1,59 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +import * as Analyzer from "the/analyzer" +import * as Parser from "the/parser" +import Codegen from "../src/codegen" +import retrieveFileSections from "../test/utils" + +const CODE_DELIMITER := os_EOL + "===== code =====" + os_EOL +const OUTPUT_DELIMITER := os_EOL + "===== output =====" + os_EOL +const TESTS_DIR := "./test/codegen" + +main { + files := fs_scandirSync(TESTS_DIR) + + loop i := 0; i < files.len; i++ { + name := files[i].slice(0, -4) + print("updating:", name) + + filePath := TESTS_DIR + "/" + name + ".txt" + fileSections := retrieveFileSections(filePath) + + mut analyzer := Analyzer.Analyzer{} + + analyzer.files.push(Analyzer.AnalyzerFile{ + cwd: process_cwd(), + analyzer: ref analyzer, + f: Parser.parse(fileSections.input), + }) + + mut file := analyzer.files.last() + Parser.interconnect(ref file.f) + file.reader = Parser.Reader_init(file.f.content) + file.init() + file.analyze(ref file.f.program.body) + + if !analyzer.errors.empty { + print(analyzer.errors.join(os_EOL), to: "stderr") + continue + } + + mut generator := Codegen{analyzer: ref analyzer} + generator.init() + generator.generate() + + inputTrim := fileSections.input.trim() + outputTrim := fileSections.output.trim() + generatedCode := generator.stringify().trim() + + newContent := inputTrim + + CODE_DELIMITER + generatedCode + + OUTPUT_DELIMITER + outputTrim + + (outputTrim.empty ? "" : os_EOL) + + fs_writeFileSync(filePath, newContent.toBuffer()) + } +} diff --git a/packages/codegen/test/codegen-test b/packages/codegen/test/codegen-test index 6fa539dc0..90247c81a 100644 --- a/packages/codegen/test/codegen-test +++ b/packages/codegen/test/codegen-test @@ -6,124 +6,10 @@ import * as Analyzer from "the/analyzer" import * as Parser from "the/parser" import Codegen from "../src/codegen" +import applyRegexTemplate, matchRegex, retrieveFileSections from "./utils" import Tests, EXPECT_EQ from "the/testing" // TODO: delete when done testing -obj TestFileSections { - mut input: str - mut code: str - mut output: str -} - -fn matchRegex (input: str, regex: str) int { - regexLen := regex.len - mut regexArr: char[] - - loop i := 0; i < regexLen; i++ { - regexArr.push(regex[i]) - } - - inputLen := input.len - mut result := 0 - - if input.empty { - return 0 - } - - loop i := 0; i < regexLen && result < inputLen; i++ { - if regexArr[i] == '\\' && i != regexLen - 1 && i != inputLen - 1 { - nextTk := regexArr[i + 1] - - if nextTk == 's' { - if input[result].isWhitespace { - result += 1 - i += 1 - } else { - break - } - } else { - throw error_NewError("Invalid regex '" + regex + "'") - } - } else { - throw error_NewError("Invalid regex '" + regex + "'") - } - } - - return result -} - -fn applyRegexTemplate (pattern: str, source: str) str { - sourceLen := source.len - mut templatePos := 0 - mut sourcePos := 0 - mut result := "" - - loop { - patternSlice := pattern.slice(templatePos) - templateStart := patternSlice.find("{{") - - if templateStart == -1 { - result += patternSlice - break - } - - templateSlice := patternSlice.slice(templateStart) - templateEnd := templateSlice.find("}}") - - if templateEnd == -1 { - result += patternSlice - break - } - - beforeTemplate := patternSlice.slice(0, templateStart) - template := templateSlice.slice(2, templateEnd).trim() - result += beforeTemplate - templatePos += templateStart + templateEnd + 2 - sourcePos += templateStart - - matchLength := matchRegex(source.slice(sourcePos), template) - result += source.slice(sourcePos, sourcePos + matchLength) - sourcePos += matchLength - } - - return result -} - -fn retrieveFileSections (path: str) TestFileSections { - content := fs_readFileSync(path).str() - lines := content.lines(keepLineBreaks: true) - l := lines.len - - mut result := TestFileSections{} - mut propertyId := 0 - - loop i := 0; i < l; i++ { - line := lines[i] - - if "=====" == line.slice(0, 5) { - propertyName := line.trim().slice(6, -6) - - if "code" == propertyName { - propertyId = 1 - } elif "output" == propertyName { - propertyId = 2 - } - - continue - } - - if 0 == propertyId { - result.input += line - } elif 1 == propertyId { - result.code += line - } elif 2 == propertyId { - result.output += line - } - } - - return result -} - fn TEST_EXPECT_EQ (test: str, a: str, b: str) { EXPECT_EQ(test + os_EOL + a, test + os_EOL + b) } diff --git a/packages/codegen/test/utils b/packages/codegen/test/utils new file mode 100644 index 000000000..d8986b308 --- /dev/null +++ b/packages/codegen/test/utils @@ -0,0 +1,119 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +export obj TestFileSections { + mut input: str + mut code: str + mut output: str +} + +export fn applyRegexTemplate (pattern: str, source: str) str { + sourceLen := source.len + mut templatePos := 0 + mut sourcePos := 0 + mut result := "" + + loop { + patternSlice := pattern.slice(templatePos) + templateStart := patternSlice.find("{{") + + if templateStart == -1 { + result += patternSlice + break + } + + templateSlice := patternSlice.slice(templateStart) + templateEnd := templateSlice.find("}}") + + if templateEnd == -1 { + result += patternSlice + break + } + + beforeTemplate := patternSlice.slice(0, templateStart) + template := templateSlice.slice(2, templateEnd).trim() + result += beforeTemplate + templatePos += templateStart + templateEnd + 2 + sourcePos += templateStart + + matchLength := matchRegex(source.slice(sourcePos), template) + result += source.slice(sourcePos, sourcePos + matchLength) + sourcePos += matchLength + } + + return result +} + +export fn matchRegex (input: str, regex: str) int { + regexLen := regex.len + mut regexArr: char[] + + loop i := 0; i < regexLen; i++ { + regexArr.push(regex[i]) + } + + inputLen := input.len + mut result := 0 + + if input.empty { + return 0 + } + + loop i := 0; i < regexLen && result < inputLen; i++ { + if regexArr[i] == '\\' && i != regexLen - 1 && i != inputLen - 1 { + nextTk := regexArr[i + 1] + + if nextTk == 's' { + if input[result].isWhitespace { + result += 1 + i += 1 + } else { + break + } + } else { + throw error_NewError("Invalid regex '" + regex + "'") + } + } else { + throw error_NewError("Invalid regex '" + regex + "'") + } + } + + return result +} + +export fn retrieveFileSections (path: str) TestFileSections { + content := fs_readFileSync(path).str() + lines := content.lines(keepLineBreaks: true) + l := lines.len + + mut result := TestFileSections{} + mut propertyId := 0 + + loop i := 0; i < l; i++ { + line := lines[i] + + if "=====" == line.slice(0, 5) { + propertyName := line.trim().slice(6, -6) + + if "code" == propertyName { + propertyId = 1 + } elif "output" == propertyName { + propertyId = 2 + } + + continue + } + + if 0 == propertyId { + result.input += line + } elif 1 == propertyId { + result.code += line + } elif 2 == propertyId { + result.output += line + } + } + + return result +} From ebf2fad4ee3ba5dae9280f626adb3c9f3c4fb3c4 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 2 Jan 2025 11:59:31 +0200 Subject: [PATCH 11/48] codegen: Separately assemble `any` macro invocations --- packages/codegen/src/codegen | 9 +++++++-- packages/codegen/src/helpers | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index e35597b2f..c5f46c87b 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -16,6 +16,7 @@ import statementHasPrecedingNonVarDecl, statementIsMacroInvocation, statementIsMacroInvocationDeclare, + statementMacroInvocationCallee, typeIsSimple, typeParameterId, typeSafeForTernaryAlternate, @@ -280,6 +281,7 @@ export obj Codegen { mut defineStatements: AST.CStatement[] mut macroInvocationDeclareStatements: AST.CStatement[] + mut anyMacroInvocationDeclareStatements: AST.CStatement[] mut macroInvocationDefineStatements: AST.CStatement[] mut statements: AST.CStatement[] @@ -299,7 +301,9 @@ export obj Codegen { if item.isDefineDirective() { defineStatements.push(item) } elif statementIsMacroInvocation(item) { - if statementIsMacroInvocationDeclare(item) { + if statementMacroInvocationCallee(item) == "D4_ANY_DECLARE" { + anyMacroInvocationDeclareStatements.push(item) + } elif statementIsMacroInvocationDeclare(item) { macroInvocationDeclareStatements.push(item) } else { macroInvocationDefineStatements.push(item) @@ -339,6 +343,7 @@ export obj Codegen { self.statements.merge(defineStatements) self.statements.merge(macroInvocationDeclareStatements.reverse()) + self.statements.merge(anyMacroInvocationDeclareStatements.reverse()) self.statements.merge(macroInvocationDefineStatements.reverse()) self.statements.merge(statements) @@ -1804,7 +1809,7 @@ export obj Codegen { } }) - methods := ["alloc"] + methods := ["alloc", "copy", "eq", "free", "str"] entityCallee := (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { self._(entity.context as str) diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index f91a3fe0e..78e624c8f 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -114,9 +114,13 @@ export fn statementIsMacroInvocation (item: ref AST.CStatement) bool { } export fn statementIsMacroInvocationDeclare (item: ref AST.CStatement) bool { + return statementMacroInvocationCallee(item).find("DECLARE") != -1 +} + +export fn statementMacroInvocationCallee (item: ref AST.CStatement) str { expression := item.asExpressionStatement() macroInvocation := expression.expression.asMacroInvocation() - return macroInvocation.callee.find("DECLARE") != -1 + return macroInvocation.callee } export fn typeIsSimple (it: ref Analyzer.Type) bool { From 7e6675051721ca5c804c7871d7aa904150fc1560 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Fri, 3 Jan 2025 06:39:38 +0200 Subject: [PATCH 12/48] codegen: ObjectExpression implementation --- packages/codegen/src/codegen | 40 +++++++++++++++++++ .../test/codegen/is-expression-root.txt | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index c5f46c87b..daae8a414 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -44,6 +44,7 @@ import sortStrAsc from "./utils" // TODO: implement Throw statement // TODO: implement Try statement +// TODO: error object needs to move message and stack properties upfront // TODO: implement ImportDeclaration statement // TODO: implement ExportDeclaration statement @@ -838,6 +839,7 @@ export obj Codegen { (result = self._generateElementAccessExpression(item)) != nil || (result = self._generateIsExpression(item)) != nil || (result = self._generateMapExpression(item)) != nil || + (result = self._generateObjectExpression(item)) != nil || (result = self._generatePropertyAccessExpression(item)) != nil || (result = self._generateReferenceExpression(item)) != nil || (result = self._generateUnaryExpression(item)) != nil @@ -1221,6 +1223,44 @@ export obj Codegen { return AST.createCallExpression(AST.createIdentifier(self._type(targetType, "_alloc")), arguments) } + fn _generateObjectExpression (mut self: ref Codegen, item: ref Parser.Expression) AST.CExpression? { + if !item.isObject() { + return nil + } + + expression := item.asObject() + targetType := Analyzer.contextTarget(item) + + mut fields: (AST.CExpression | AST.CType)[] + + loop i := 0; i < targetType.properties.len; i++ { + property := targetType.properties[i] + + if property.builtin || property.t.isMethod() { + continue + } + + mut propertyValue: (ref Parser.Expression)? + + loop j := 0; j < expression.properties.len; j++ { + expressionProperty := expression.properties[j] + + if expressionProperty.name.name == property.name { + propertyValue = ref expressionProperty.value + break + } + } + + fields.push( + propertyValue == nil + ? self._defaultInitializerExpression(property.t) + : self._generateExpression(propertyValue) + ) + } + + return AST.createCallExpression(AST.createIdentifier(self._type(targetType, "_alloc")), fields) + } + fn _generateParenthesizedExpression (mut self: ref Codegen, item: ref Parser.Expression) AST.CExpression? { if !item.isParenthesized() { return nil diff --git a/packages/codegen/test/codegen/is-expression-root.txt b/packages/codegen/test/codegen/is-expression-root.txt index 4d4c10272..9681ec194 100644 --- a/packages/codegen/test/codegen/is-expression-root.txt +++ b/packages/codegen/test/codegen/is-expression-root.txt @@ -54,11 +54,11 @@ main { #define TYPE_int 1 #define TYPE_float 2 #define TYPE_str 3 -D4_ANY_DECLARE(str, d4_str_t) D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) +D4_ANY_DECLARE(str, d4_str_t) D4_ANY_DECLARE(float, double) D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) From c6212d7fa404e5836c19ccfc53d443d43fbd0a47 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Fri, 3 Jan 2025 13:04:01 +0200 Subject: [PATCH 13/48] codegen: Add pre/post declaration steps --- packages/codegen/src/builtin-entities | 8 +- packages/codegen/src/codegen | 624 ++++++++---------- packages/codegen/src/helpers | 13 +- .../test/codegen/is-expression-root.txt | 2 +- .../test/codegen/object-declaration-empty.txt | 8 + .../object-declaration-field-types.txt | 91 +++ ...object-declaration-forward-declaration.txt | 37 ++ .../codegen/object-declaration-mutable.txt | 25 + .../test/codegen/object-declaration-str.txt | 16 + .../test/codegen/object-declaration.txt | 18 +- .../test/codegen/object-expression-empty.txt | 25 + .../test/codegen/object-expression-nested.txt | 42 ++ .../test/codegen/object-expression-root.txt | 11 + .../test/codegen/object-expression.txt | 22 + 14 files changed, 581 insertions(+), 361 deletions(-) create mode 100644 packages/codegen/test/codegen/object-declaration-empty.txt create mode 100644 packages/codegen/test/codegen/object-declaration-field-types.txt create mode 100644 packages/codegen/test/codegen/object-declaration-forward-declaration.txt create mode 100644 packages/codegen/test/codegen/object-declaration-mutable.txt create mode 100644 packages/codegen/test/codegen/object-declaration-str.txt create mode 100644 packages/codegen/test/codegen/object-expression-empty.txt create mode 100644 packages/codegen/test/codegen/object-expression-nested.txt create mode 100644 packages/codegen/test/codegen/object-expression-root.txt create mode 100644 packages/codegen/test/codegen/object-expression.txt diff --git a/packages/codegen/src/builtin-entities b/packages/codegen/src/builtin-entities index 18e152e19..b5d193b24 100644 --- a/packages/codegen/src/builtin-entities +++ b/packages/codegen/src/builtin-entities @@ -173,7 +173,13 @@ export const BUILTIN_ENTITIES := { "d4_usize_str", ], - "d4Object": ["D4_OBJECT_DECLARE", "D4_OBJECT_DEFINE", "d4_obj_str_append"], + "d4Object": [ + "D4_OBJECT_FORWARD_DECLARE", + "D4_OBJECT_DECLARE", + "D4_OBJECT_DEFINE", + "d4_obj_str_append", + ], + "d4Optional": ["D4_OPTIONAL_DECLARE", "D4_OPTIONAL_DEFINE"], "d4Reference": ["D4_REFERENCE_DECLARE"], diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index daae8a414..dbb44993a 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -16,6 +16,7 @@ import statementHasPrecedingNonVarDecl, statementIsMacroInvocation, statementIsMacroInvocationDeclare, + statementIsMacroInvocationForwardDeclare, statementMacroInvocationCallee, typeIsSimple, typeParameterId, @@ -282,7 +283,7 @@ export obj Codegen { mut defineStatements: AST.CStatement[] mut macroInvocationDeclareStatements: AST.CStatement[] - mut anyMacroInvocationDeclareStatements: AST.CStatement[] + mut macroInvocationForwardDeclareStatements: AST.CStatement[] mut macroInvocationDefineStatements: AST.CStatement[] mut statements: AST.CStatement[] @@ -302,8 +303,8 @@ export obj Codegen { if item.isDefineDirective() { defineStatements.push(item) } elif statementIsMacroInvocation(item) { - if statementMacroInvocationCallee(item) == "D4_ANY_DECLARE" { - anyMacroInvocationDeclareStatements.push(item) + if statementIsMacroInvocationForwardDeclare(item) { + macroInvocationForwardDeclareStatements.push(item) } elif statementIsMacroInvocationDeclare(item) { macroInvocationDeclareStatements.push(item) } else { @@ -343,8 +344,8 @@ export obj Codegen { if self.builtin.wchar { self.statements.push(AST.createIncludeDirective("wchar.h")) } self.statements.merge(defineStatements) + self.statements.merge(macroInvocationForwardDeclareStatements.reverse()) self.statements.merge(macroInvocationDeclareStatements.reverse()) - self.statements.merge(anyMacroInvocationDeclareStatements.reverse()) self.statements.merge(macroInvocationDefineStatements.reverse()) self.statements.merge(statements) @@ -365,115 +366,80 @@ export obj Codegen { ) } - fn _declareStatement (mut self: ref Self, item: ref Parser.Statement) bool { - if !( - self._declareEnum(item) || - self._declareExport(item) || - self._declareFunction(item) || - self._declareMain(item) || - self._declareObject(item) - ) { - throw error_NewError("Tried declaring unknown statement '" + Parser.stringifyStatement(item) + "'") + fn _preDeclareStatement (mut self: ref Self, item: ref Parser.Statement) bool { + if item.isObjectDeclaration() { + self._preDeclareObject(item) } } - fn _declareEnum (mut self: ref Codegen, item: ref Parser.Statement) bool { - if !item.isEnumDeclaration() { - return false + fn _declareStatement (mut self: ref Self, item: ref Parser.Statement) bool { + if item.isMainDeclaration() { + self._declareMain(item) + } elif item.isObjectDeclaration() { + self._declareObject(item) } + } - statement := item.asEnumDeclaration() - // TODO: implement + fn _postDeclareStatement (mut self: ref Self, item: ref Parser.Statement) bool { + } - // auto nodeEnumDecl = std::get(*node.body); - // auto members = nodeEnumDecl.members; - // auto typeName = Codegen::typeName(nodeEnumDecl.type->codeName); - // auto enumType = std::get(nodeEnumDecl.type->body); - // auto cDecl = CodegenASTStmtCompound::create(); - // this->_apiEntity(typeName, CODEGEN_ENTITY_ENUM, [&] (auto &decl, [[maybe_unused]] auto &def) { - // auto membersCode = std::string(); - // for (auto i = static_cast(0); i < enumType.members.size(); i++) { - // auto typeMember = enumType.members[i]; - // auto member = std::find_if(members.begin(), members.end(), [&] (const auto &it) -> bool { - // return it.id == typeMember->name; - // }); - // if (member != members.end()) { - // membersCode += " " + Codegen::name(typeMember->codeName); - // if (member->init != std::nullopt) { - // auto cInit = this->_nodeExpr(*member->init, member->init->type, node, &cDecl, true); - // membersCode += " = " + cInit->str(); - // } - // membersCode += i == enumType.members.size() - 1 ? EOL : "," EOL; - // } - // } - // decl += "enum " + typeName + " {" EOL; - // decl += membersCode; - // decl += "};"; - // return 0; - // }); - // this->_apiEntity(typeName + "_rawValue", CODEGEN_ENTITY_FN, [&] (auto &decl, auto &def) { - // auto typeInfo = this->_typeInfo(nodeEnumDecl.type); - // decl += "_{struct str} " + typeName + "_rawValue (" + typeInfo.typeCodeTrimmed + ");"; - // def += "_{struct str} " + typeName + "_rawValue (" + typeInfo.typeCode + "n) {" EOL; - // for (const auto &member : enumType.members) { - // def += " if (n == " + Codegen::name(member->codeName) + ")" R"( return _{str_alloc}(")" + member->name + R"(");)" EOL; - // } - // def += "}"; - // return 0; - // }); + fn _declareMain (mut self: ref Codegen, item: ref Parser.Statement) void { + statement := item.asMainDeclaration() + statementBody := statement.body.asBlock() - return true + self.blockData.increase() + self.mainStatements = self._generateBlockBody(ref statementBody.body) + dataContextBlock := self.blockData.decrease() + self.blockData.currentMerge(dataContextBlock.setup, dataContextBlock.teardown) } - fn _declareExport (mut self: ref Codegen, item: ref Parser.Statement) bool { - if !item.isExportDeclaration() { - return false - } + fn _preDeclareObject (mut self: ref Codegen, item: ref Parser.Statement) void { + statement := item.asObjectDeclaration() - statement := item.asExportDeclaration() - // TODO: implement + if !(item.context is Analyzer.ObjectDeclarationContext) { + throw error_NewError("Expected ObjectDeclaration context") + } - // if ( - // nodeExportDecl.declaration != std::nullopt && - // !std::holds_alternative(*nodeExportDecl.declaration->body) - // ) { - // return this->_node(c, *nodeExportDecl.declaration); - // } + context := item.context as Analyzer.ObjectDeclarationContext + selfType := context.selfType - return true - } + typeName := "d4_" + self._typeName(selfType) + name := typeName + "_t" - fn _declareFunction (mut self: ref Codegen, item: ref Parser.Statement) bool { - if !item.isFunctionDeclaration() { - return false + if self._hasEntity(name) { + throw error_NewError("Object entity with name '" + name + "' already exists") } - statement := item.asFunctionDeclaration() - // TODO: implement - return true - } - - fn _declareMain (mut self: ref Codegen, item: ref Parser.Statement) bool { - if !item.isMainDeclaration() { - return false - } + self.entities.push(CodegenEntity{ + name: name, + codeName: name, + context: selfType, + generate: (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + return AST.createEmptyStatement() + } + }) - statement := item.asMainDeclaration() - statementBody := statement.body.asBlock() + methods := ["alloc", "copy", "eq", "free", "realloc", "str"] - self.blockData.increase() - self.mainStatements = self._generateBlockBody(ref statementBody.body) - dataContextBlock := self.blockData.decrease() - self.blockData.currentMerge(dataContextBlock.setup, dataContextBlock.teardown) + entityCallee := (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + self._(entity.context as str) + return AST.createEmptyStatement() + } - return true - } + loop i := 0; i < methods.len; i++ { + methodName := methods[i] as str - fn _declareObject (mut self: ref Codegen, item: ref Parser.Statement) bool { - if !item.isObjectDeclaration() { - return false + self.entities.push(CodegenEntity{ + name: typeName + "_" + methodName, + codeName: typeName + "_" + methodName, + context: name, + generate: entityCallee, + }) } + } + // TODO: rename params + fn _declareObject (mut self2: ref Codegen, item: ref Parser.Statement) void { statement := item.asObjectDeclaration() if !(item.context is Analyzer.ObjectDeclarationContext) { @@ -481,9 +447,197 @@ export obj Codegen { } context := item.context as Analyzer.ObjectDeclarationContext - self._generateObjectType(context.selfType) + name := self2._generateObjectType(context.selfType) + mut entity := self2._getEntity(name) + + entity.generate = (mut self: ref Codegen, entity2: ref CodegenEntity) -> GenerateReturnType { + it := entity2.context as ref Analyzer.Type + + mut extraDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] + mut structureBlock: AST.CStatement[] + mut allocBlock: AST.CStatement[] + mut copyBlock: AST.CStatement[] + mut eqExpression := AST.createIdentifier(self._("true")) + mut freeBlock: AST.CStatement[] + mut strBlock: AST.CStatement[] + mut propertyIdx := 0 + + loop i := 0; i < it.properties.len; i++ { + property := it.properties[i] + + if property.builtin || property.t.isMethod() { + continue + } + + cPropertyType := self._type(property.t) + cSelfProperty := AST.createPropertyAccessExpression(AST.createIdentifier("self"), property.name) + + extraDeclarationArguments.push( + AST.createVariableDeclaration( + AST.createType(cPropertyType, constant: true), + property.name, + terminated: false, + ) + ) + + structureBlock.push( + AST.createVariableDeclaration(AST.createType(cPropertyType), property.name) + ) + + allocBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + cSelfProperty, + "=", + self._functionCopy(property.t, AST.createIdentifier(property.name)), + ), + ), + ) + + copyBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("result"), property.name), + "=", + self._functionCopy(property.t, cSelfProperty), + ), + ), + ) + + eqExpressionPiece := self._functionEq( + property.t, + cSelfProperty, + AST.createPropertyAccessExpression(AST.createIdentifier("rhs"), property.name), + ) + + eqExpression = propertyIdx == 0 + ? eqExpressionPiece + : AST.createBinaryExpression(eqExpression, "&&", eqExpressionPiece) + + if typeShouldBeFreed(property.t) { + freeBlock.push( + AST.createExpressionStatement( + self._functionFree(property.t, cSelfProperty), + ), + ) + } - return true + strBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createIdentifier("result"), + "=", + AST.createCallExpression(AST.createIdentifier(self._("d4_obj_str_append")), [ + AST.createIdentifier("result"), + AST.createCallExpression(AST.createIdentifier(self._("d4_str_alloc")), [ + AST.createLiteral("L\"" + property.name + "\""), + ]), + self._functionStr(property.t, cSelfProperty, quote: true), + ]), + ), + ), + ) + + propertyIdx++ + } + + if freeBlock.empty { + freeBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + } + + mut eqBlock: AST.CStatement[] + + if propertyIdx == 0 { + extraDeclarationArguments.push(AST.createType("void")) + + structureBlock.push( + AST.createVariableDeclaration(AST.createType(self._("bool")), "_") + ) + + allocBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), + "=", + AST.createIdentifier(self._("false")), + ), + ), + ) + + copyBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("result"), "_"), + "=", + AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), + ), + ), + ) + + eqBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + + eqBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("rhs")), + ), + ) + + strBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + } + + eqBlock.push(AST.createReturnStatement(eqExpression)) + + cNameIdentifier := AST.createIdentifier(it.name) + + forwardDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + cNameIdentifier, + ] + + mut declarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + cNameIdentifier, + AST.createCompoundStatement(structureBlock), + ] + + mut definitionArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + cNameIdentifier, + cNameIdentifier, + AST.createCompoundStatement(allocBlock), + AST.createCompoundStatement(copyBlock), + AST.createCompoundStatement(eqBlock), + AST.createCompoundStatement(freeBlock), + AST.createCompoundStatement(strBlock), + ] + + declarationArguments.merge(extraDeclarationArguments) + definitionArguments.merge(extraDeclarationArguments) + + return [ + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_FORWARD_DECLARE"), forwardDeclarationArguments), + terminated: false, + ), + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_DECLARE"), declarationArguments), + terminated: false, + ), + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_DEFINE"), definitionArguments), + terminated: false, + ), + ] + } } fn _defaultInitializerExpression (mut self: ref Self, it: ref Analyzer.Type) AST.CExpression { @@ -499,42 +653,9 @@ export obj Codegen { AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), ])) - } elif it.name == "bool" { - return AST.createIdentifier(self._("false")) - } elif it.name == "char" { - return AST.createLiteral("'\\0'") - } elif it.name == "str" { - return AST.createIdentifier(self._("d4_str_empty_val")) - } elif it.isArray() || it.isMap() { - typeName := self._typeName(it) - - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), [ - AST.createLiteral("0"), - ]) - } elif it.isCustomObject() { - // TODO: test - typeName := self._typeName(it) - mut args: (AST.CExpression | AST.CType)[] - loop i := 0; i < it.properties.len; i++ { - property := it.properties[i] - if property.builtin || property.t.isMethod() { - continue - } - args.push(self._defaultInitializerExpression(property.t)) - } - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), args) - } elif it.isOptional() { - return AST.createIdentifier(self._("NULL")) - } elif it.isUnion() { - typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), [ - AST.createLiteral("-1"), - ]) - } elif !it.isFunction() && !it.isReference() { - return AST.createLiteral("0") } - throw error_NewError("Tried default object expressions declaration on unknown type '" + it.toString() + "'") + return self._defaultInitializerDeclaration(it) } fn _defaultInitializerDeclaration (mut self: ref Self, it: ref Analyzer.Type) AST.CExpression { @@ -558,29 +679,29 @@ export obj Codegen { return AST.createIdentifier(self._("d4_str_empty_val")) } elif it.isArray() || it.isMap() { typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), [ + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_alloc")), [ AST.createLiteral("0"), ]) } elif it.isCustomObject() { // TODO: test typeName := self._typeName(it) - mut args : (AST.CExpression | AST.CType)[] + mut args: (AST.CExpression | AST.CType)[] loop i := 0; i < it.properties.len; i++ { property := it.properties[i] if property.builtin || property.t.isMethod() { continue } - args.push(self._defaultInitializerExpression(property.t)) + args.push(self._defaultInitializerDeclaration(property.t)) } - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), args) - } elif it.isOptional() { + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_alloc")), args) + } elif it.isOptional() || it.isReference() { return AST.createIdentifier(self._("NULL")) } elif it.isUnion() { typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_alloc")), [ + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_alloc")), [ AST.createLiteral("-1"), ]) - } elif !it.isFunction() && !it.isReference() { + } elif it.isNumber() { return AST.createLiteral("0") } @@ -737,12 +858,34 @@ export obj Codegen { mut result: AST.CStatement[] l := items.len + loop i := 0; i < l; i++ { + item := items[i] + + if item.isDeclaration() { + self._preDeclareStatement(item) + } + } + loop i := 0; i < l; i++ { item := items[i] if item.isDeclaration() { self._declareStatement(item) - } else { + } + } + + loop i := 0; i < l; i++ { + item := items[i] + + if item.isDeclaration() { + self._postDeclareStatement(item) + } + } + + loop i := 0; i < l; i++ { + item := items[i] + + if !item.isDeclaration() { result.push(self._generateStatement(item)) } @@ -2156,213 +2299,10 @@ export obj Codegen { } fn _generateObjectType (mut self: ref Codegen, item: ref Analyzer.Type) str { - typeName := "d4_" + self._typeName(item) - name := typeName + "_t" - - if self._hasEntity(name) { - return name - } + name := "d4_" + self._typeName(item) + "_t" - self.entities.push(CodegenEntity{ - name: name, - codeName: name, - context: item, - generate: (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { - it := entity.context as ref Analyzer.Type - - mut extraDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] - mut structureBlock: AST.CStatement[] - mut allocBlock: AST.CStatement[] - mut copyBlock: AST.CStatement[] - mut eqExpression := AST.createIdentifier(self._("true")) - mut freeBlock: AST.CStatement[] - mut strBlock: AST.CStatement[] - mut propertyIdx := 0 - - loop i := 0; i < it.properties.len; i++ { - property := it.properties[i] - - if property.builtin || property.t.isMethod() { - continue - } - - cPropertyType := self._type(property.t) - cSelfProperty := AST.createPropertyAccessExpression(AST.createIdentifier("self"), property.name) - - extraDeclarationArguments.push( - AST.createVariableDeclaration( - AST.createType(cPropertyType, constant: true), - property.name, - terminated: false, - ) - ) - - structureBlock.push( - AST.createVariableDeclaration(AST.createType(cPropertyType), property.name) - ) - - allocBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - cSelfProperty, - "=", - self._functionCopy(property.t, AST.createIdentifier(property.name)), - ), - ), - ) - - copyBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createPropertyAccessExpression(AST.createIdentifier("result"), property.name), - "=", - self._functionCopy(property.t, cSelfProperty), - ), - ), - ) - - eqExpressionPiece := self._functionEq( - property.t, - cSelfProperty, - AST.createPropertyAccessExpression(AST.createIdentifier("rhs"), property.name), - ) - - eqExpression = propertyIdx == 0 - ? eqExpressionPiece - : AST.createBinaryExpression(eqExpression, "&&", eqExpressionPiece) - - if typeShouldBeFreed(property.t) { - freeBlock.push( - AST.createExpressionStatement( - self._functionFree(property.t, cSelfProperty), - ), - ) - } - - strBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createIdentifier("result"), - "=", - AST.createCallExpression(AST.createIdentifier(self._("d4_obj_str_append")), [ - AST.createIdentifier("result"), - AST.createCallExpression(AST.createIdentifier(self._("d4_str_alloc")), [ - AST.createLiteral("L\"" + property.name + "\""), - ]), - self._functionStr(property.t, cSelfProperty, quote: true), - ]), - ), - ), - ) - - propertyIdx++ - } - - if freeBlock.empty { - freeBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), - ), - ) - } - - mut eqBlock: AST.CStatement[] - - if propertyIdx == 0 { - extraDeclarationArguments.push(AST.createType("void")) - - structureBlock.push( - AST.createVariableDeclaration(AST.createType(self._("bool")), "_") - ) - - allocBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), - "=", - AST.createIdentifier(self._("false")), - ), - ), - ) - - copyBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createPropertyAccessExpression(AST.createIdentifier("result"), "_"), - "=", - AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), - ), - ), - ) - - eqBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), - ), - ) - - eqBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("rhs")), - ), - ) - - strBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), - ), - ) - } - - eqBlock.push(AST.createReturnStatement(eqExpression)) - - mut declarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ - AST.createIdentifier(it.name), - AST.createCompoundStatement(structureBlock), - ] - - mut definitionArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ - AST.createIdentifier(it.name), - AST.createIdentifier(it.name), - AST.createCompoundStatement(allocBlock), - AST.createCompoundStatement(copyBlock), - AST.createCompoundStatement(eqBlock), - AST.createCompoundStatement(freeBlock), - AST.createCompoundStatement(strBlock), - ] - - declarationArguments.merge(extraDeclarationArguments) - definitionArguments.merge(extraDeclarationArguments) - - return [ - AST.createExpressionStatement( - AST.createMacroInvocation(self._("D4_OBJECT_DECLARE"), declarationArguments), - terminated: false, - ), - AST.createExpressionStatement( - AST.createMacroInvocation(self._("D4_OBJECT_DEFINE"), definitionArguments), - terminated: false, - ), - ] - } - }) - - methods := ["alloc", "copy", "eq", "free", "realloc", "str"] - - entityCallee := (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { - self._(entity.context as str) - return AST.createEmptyStatement() - } - - loop i := 0; i < methods.len; i++ { - methodName := methods[i] as str - - self.entities.push(CodegenEntity{ - name: typeName + "_" + methodName, - codeName: typeName + "_" + methodName, - context: name, - generate: entityCallee, - }) + if !self._hasEntity(name) { + throw error_NewError("Object entity with name '" + name + "' doesn't exists") } return name diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 78e624c8f..e792d86a5 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -117,6 +117,10 @@ export fn statementIsMacroInvocationDeclare (item: ref AST.CStatement) bool { return statementMacroInvocationCallee(item).find("DECLARE") != -1 } +export fn statementIsMacroInvocationForwardDeclare (item: ref AST.CStatement) bool { + return statementMacroInvocationCallee(item).find("FORWARD_DECLARE") != -1 +} + export fn statementMacroInvocationCallee (item: ref AST.CStatement) str { expression := item.asExpressionStatement() macroInvocation := expression.expression.asMacroInvocation() @@ -152,7 +156,8 @@ export fn typeSafeForTernaryAlternate (it: ref Analyzer.Type) bool { it.isArray() || it.isFunction() || it.isMap() || - // TODO: it.isObject() || + // TODO: test + it.isCustomObject() || it.isOptional() || it.isUnion() ) @@ -186,12 +191,13 @@ export fn typeShouldBeAllocated (self: ref Analyzer.Type) bool { return typeShouldBeAllocated(t.t) } - // TODO: object return ( self.name == "any" || self.name == "str" || self.isArray() || self.isMap() || + // TODO: test + self.isCustomObject() || self.isOptional() || self.isUnion() ) @@ -215,11 +221,12 @@ export fn typeShouldBeFreed (self: ref Analyzer.Type) bool { return false } - // TODO: object return self.name == "any" || self.name == "str" || self.isArray() || + // TODO: test + self.isCustomObject() || self.isFunction() || self.isMap() || self.isOptional() diff --git a/packages/codegen/test/codegen/is-expression-root.txt b/packages/codegen/test/codegen/is-expression-root.txt index 9681ec194..4d4c10272 100644 --- a/packages/codegen/test/codegen/is-expression-root.txt +++ b/packages/codegen/test/codegen/is-expression-root.txt @@ -54,11 +54,11 @@ main { #define TYPE_int 1 #define TYPE_float 2 #define TYPE_str 3 +D4_ANY_DECLARE(str, d4_str_t) D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) -D4_ANY_DECLARE(str, d4_str_t) D4_ANY_DECLARE(float, double) D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) diff --git a/packages/codegen/test/codegen/object-declaration-empty.txt b/packages/codegen/test/codegen/object-declaration-empty.txt new file mode 100644 index 000000000..7e88f554e --- /dev/null +++ b/packages/codegen/test/codegen/object-declaration-empty.txt @@ -0,0 +1,8 @@ +obj EmptyObject { +} +main { + o := EmptyObject{} + print(o) +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-declaration-field-types.txt b/packages/codegen/test/codegen/object-declaration-field-types.txt new file mode 100644 index 000000000..afe86e65b --- /dev/null +++ b/packages/codegen/test/codegen/object-declaration-field-types.txt @@ -0,0 +1,91 @@ +type Alias = str +obj Test1 { + a: Alias +} +obj Test2 { + a: any +} +obj Test3 { + a: int[] + b: str[] +} +obj Test4 { + a: int[str] + b: str[str] +} +obj Test5 { + mut a: Test1 +} +obj Test6 { + a: int? +} +obj Test7 { + a: ref int +} +obj Test8 { + a: Self? +} +obj Test9 { + a: str +} +obj Test10 { + a: int | str +} + +main { + i1 := 1 + i2 := 2 + + t1 := Test1{a: ""} + mut t2 := t1 + t2 = Test1{a: "test"} + + t3 := Test2{a: 1} + mut t4 := t3 + t4 = Test2{a: "test2"} + + t5 := Test3{a: [1, 2, 3], b: ["string1", "string2"]} + mut t6 := t5 + t6 = Test3{a: [4, 5], b: ["string3", "string4"]} + + t7 := Test4{ + a: { "key1": 1, "key2": 2, "key3": 3 }, + b: { "key1": "string1", "key2": "string2" }, + } + + mut t8 := t7 + + t8 = Test4{ + a: { "key1": 4, "key2": 5 }, + b: { "key1": "string3", "key2": "string4" }, + } + + t9 := Test5{a: Test1{a: "string1"}} + mut t10 := t9 + t10 = Test5{a: Test1{a: "string2"}} + t10.a = t9.a + t10.a = Test1{a: "string3"} + + t11 := Test6{a: nil} + mut t12 := t11 + t12 = Test6{a: 3} + + t13 := Test7{a: ref i1} + mut t14 := t13 + t14 = Test7{a: ref i2} + + t15 := Test8{} + t16 := Test8{a: t15} + mut t17 := t16 + t17 = Test8{a: t16} + + t18 := Test9{a: "test1"} + mut t19 := t18 + t19 = Test9{a: "test2"} + + t20 := Test10{a: 1} + mut t21 := t20 + t21 = Test10{a: "test"} +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-declaration-forward-declaration.txt b/packages/codegen/test/codegen/object-declaration-forward-declaration.txt new file mode 100644 index 000000000..ab504ea11 --- /dev/null +++ b/packages/codegen/test/codegen/object-declaration-forward-declaration.txt @@ -0,0 +1,37 @@ +obj Person { + mut name: str; + mut age: int; + mut pets: (ref Pet)[]; +} +obj Pet { + mut name: str + mut age: int + mut owner: ref Person +} + +obj Test1 { + t3: Test3 +} +obj Test2 { + a: int +} +obj Test3 { + a: int +} +obj Test4 { + t2: Test2 +} + +main { + mut person := Person{name: "Daniel", age: 28} + dog := Pet{name: "Eric", age: 4, owner: ref person} + cat := Pet{name: "Sam", age: 8, owner: ref person} + + person.pets.push(ref dog) + person.pets.push(ref cat) + + t1 := Test1{t3: Test3{a: 2}} + t2 := Test4{t2: Test2{a: 1}} +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-declaration-mutable.txt b/packages/codegen/test/codegen/object-declaration-mutable.txt new file mode 100644 index 000000000..c95624c46 --- /dev/null +++ b/packages/codegen/test/codegen/object-declaration-mutable.txt @@ -0,0 +1,25 @@ +obj Animal { + name: str + mut age: int +} +obj Human { + name: str + mut pets: Animal[] +} +main { + mut person1 := Human{name: "Peter"} + mut person2 := Human{name: "Brian"} + + person1.pets.push(Animal{name: "Cleo", age: 1}) + person1.pets.push(Animal{name: "Bella", age: 1}) + + person1.pets[0].age++ + + person2.pets.push(Animal{name: "Luxor", age: 10}) + + person3 := person1 + person1 = person2 + person2 = person3 +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-declaration-str.txt b/packages/codegen/test/codegen/object-declaration-str.txt new file mode 100644 index 000000000..dbb0fb9bc --- /dev/null +++ b/packages/codegen/test/codegen/object-declaration-str.txt @@ -0,0 +1,16 @@ +obj Person { + name: str; +} +obj PersonWithAge { + name: str; + age: int; +} +main { + p1 := Person{name: "Jessica"} + p2 := PersonWithAge{name: "Leonard", age: 15} + + print(p1.str()) + print(p2.str()) +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-declaration.txt b/packages/codegen/test/codegen/object-declaration.txt index 784256850..118cf98e4 100644 --- a/packages/codegen/test/codegen/object-declaration.txt +++ b/packages/codegen/test/codegen/object-declaration.txt @@ -1,20 +1,10 @@ obj Person { - mut name: str; - mut age: int; - mut pets: (ref Pet)[] -} -obj Pet { - mut name: str; - mut age: int; - mut owner: ref Person + name: str; + age: int; } main { - mut person := Person{name: "Daniel", age: 28} - dog := Pet{name: "Eric", age: 4, owner: ref person} - cat := Pet{name: "Sam", age: 8, owner: ref person} - - person.pets.push(ref dog) - person.pets.push(ref cat) + person := Person{name: "Lauren", age: 61} + print(person) } ===== code ===== ===== output ===== diff --git a/packages/codegen/test/codegen/object-expression-empty.txt b/packages/codegen/test/codegen/object-expression-empty.txt new file mode 100644 index 000000000..9b8af565a --- /dev/null +++ b/packages/codegen/test/codegen/object-expression-empty.txt @@ -0,0 +1,25 @@ +obj Point { + mut x: float + mut y: float +} + +obj Test { + a: int +} + +obj Human { + mut name: str + age: int +} + +obj Empty { +} + +main { + point := Point{} + test := Test{} + human := Human{} + empty := Empty{} +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-expression-nested.txt b/packages/codegen/test/codegen/object-expression-nested.txt new file mode 100644 index 000000000..6d6e52446 --- /dev/null +++ b/packages/codegen/test/codegen/object-expression-nested.txt @@ -0,0 +1,42 @@ +obj Test1 { + mut t1: Test2 + t2: int +} +obj Test2 { + t1: Test3 + t2: rune +} +obj Test3 { + t1: bool + t2: float + mut t3: int +} +main { + t1 := Test3{t1: false, t2: 3.14, t3: -1} + t2 := Test2{t1: t1, t2: 'a'} + t3 := Test1{t1: t2, t2: 10} + + mut t4 := Test1{ + t1: Test2{ + t1: Test3{ + t1: false, + t2: 3.14, + t3: -1, + }, + t2: 'a', + }, + t2: 2, + } + + t4.t1 = Test2{ + t1: Test3{t1: true, t2: 2.16, t3: 1}, + t2: 100, + } + + t4.t1.t1 = Test3{t1: false, t2: 1.23, t3: 5} + t4.t1.t1.t3 = 0 + + t5 := Test1{} +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-expression-root.txt b/packages/codegen/test/codegen/object-expression-root.txt new file mode 100644 index 000000000..bffa754bd --- /dev/null +++ b/packages/codegen/test/codegen/object-expression-root.txt @@ -0,0 +1,11 @@ +obj Test { + a: int + b: int +} + +main { + Test{b: 1} + Test{a: 0, b: 1} +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/object-expression.txt b/packages/codegen/test/codegen/object-expression.txt new file mode 100644 index 000000000..b1c5444a6 --- /dev/null +++ b/packages/codegen/test/codegen/object-expression.txt @@ -0,0 +1,22 @@ +obj Point { + mut x: float + mut y: float +} +obj Human { + mut name: str + age: int +} +main { + mut a1 := Point{x: 37.7681251, y: -122.5138329} + mut a2 := Point{y: -122.5138329, x: 37.7681251} + + a1.x += 2 + a2.y /= 0.2 + + tina := Human{name: "Tina", age: 45} + mut nina := Human{name: "Nina", age: 33} + + nina.name += "Test" +} +===== code ===== +===== output ===== From 9bb6f4908d91a8e1418a1d8295a9f12fc7de2974 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 4 Jan 2025 10:20:35 +0200 Subject: [PATCH 14/48] analyzer: Add ElementAccess expression extra context --- packages/analyzer/src/analyzer | 2 + packages/analyzer/src/analyzer-print | 32 ++++++++++++++- packages/analyzer/src/context | 4 ++ packages/analyzer/src/main | 2 + .../analyzer/test/print/await-expression.txt | 2 +- .../analyzer/test/print/call-expression.txt | 28 ++++++------- .../test/print/closure-expression.txt | 2 +- .../test/print/element-access-expression.txt | 2 +- .../test/print/function-declaration.txt | 2 +- packages/analyzer/test/print/if-statement.txt | 40 +++++++++---------- .../analyzer/test/print/loop-statement.txt | 4 +- .../test/print/object-declaration.txt | 6 +-- .../analyzer/test/print/object-expression.txt | 2 +- .../test/print/property-access-expression.txt | 2 +- .../analyzer/test/print/throw-statement.txt | 4 +- .../analyzer/test/print/try-statement.txt | 18 ++++----- .../type-casting/assignment-expression.txt | 8 ++-- .../test/type-casting/await-expression.txt | 4 +- .../test/type-casting/call-expression.txt | 4 +- .../element-access-expression.txt | 2 +- .../test/type-casting/is-expression.txt | 2 +- .../test/type-casting/object-expression.txt | 2 +- .../property-access-expression.txt | 2 +- 23 files changed, 107 insertions(+), 69 deletions(-) diff --git a/packages/analyzer/src/analyzer b/packages/analyzer/src/analyzer index 8bdcda22f..8ee34d473 100644 --- a/packages/analyzer/src/analyzer +++ b/packages/analyzer/src/analyzer @@ -1702,6 +1702,8 @@ export obj AnalyzerFile { return true } + Context.setExtra(it, Context.ElementAccessExpressionContextExtra{selfType: expressionType}) + if targetType != nil { if Type.match(targetType, Context.initial(it)) { return true diff --git a/packages/analyzer/src/analyzer-print b/packages/analyzer/src/analyzer-print index 7b00b6ad1..5da16b905 100644 --- a/packages/analyzer/src/analyzer-print +++ b/packages/analyzer/src/analyzer-print @@ -187,7 +187,16 @@ export fn analyzerPrintStatement(statement: ref Parser.Statement, indent: int) s result += analyzerPrintStatement(ref mainDeclaration.body, indent + 2) } elif statement.isObjectDeclaration() { objectDeclaration := statement.asObjectDeclaration() - result += ' '.repeat(indent) + "ObjectDeclaration(name=" + objectDeclaration.name.name + ")\n" + mut attributes := "name=" + objectDeclaration.name.name + + statementContext := statement.context + + if statement.context is Context.ObjectDeclarationContext { + context := statementContext as Context.ObjectDeclarationContext + attributes += " context:selfType=" + context.selfType.toString() + } + + result += ' '.repeat(indent) + "ObjectDeclaration(" + attributes + ")\n" loop i := 0; i < objectDeclaration.body.len; i++ { bodyStatement := objectDeclaration.body[i] @@ -363,6 +372,12 @@ export fn analyzerPrintExpression(expression: ref Parser.Expression, indent: int } elif expression.isCall() { call := expression.asCall() name = "CallExpression" + expressionContextExtra := expression.contextExtra + if expression.contextExtra is Context.CallExpressionContextExtra { + contextExtra := expressionContextExtra as Context.CallExpressionContextExtra + attributes += " extra:asynchronous=" + contextExtra.asynchronous.str() + attributes += " extra:returnType=" + contextExtra.returnType.toString() + } result += analyzerPrintExpression(ref call.callee, indent + 2) loop i := 0; i < call.arguments.len; i++ { argument := call.arguments[i] @@ -373,6 +388,16 @@ export fn analyzerPrintExpression(expression: ref Parser.Expression, indent: int argumentAttributes += "name=" + argumentName.name } + if expression.contextExtra is Context.CallExpressionContextExtra { + contextExtra := expressionContextExtra as Context.CallExpressionContextExtra + + if contextExtra.arguments.len > i { + contextExtraArgument := contextExtra.arguments[i] + argumentAttributes += argumentAttributes.empty ? "" : " " + argumentAttributes += "extra:parameterIdx=" + contextExtraArgument.parameterIdx.str() + } + } + result += ' '.repeat(indent + 2) + "CallExpressionArgument(" + argumentAttributes + ")\n" result += analyzerPrintExpression(ref argument.expression, indent + 4) } @@ -390,6 +415,11 @@ export fn analyzerPrintExpression(expression: ref Parser.Expression, indent: int } elif expression.isElementAccess() { elementAccess := expression.asElementAccess() name = "ElementAccessExpression" + expressionContextExtra := expression.contextExtra + if expression.contextExtra is Context.ElementAccessExpressionContextExtra { + contextExtra := expressionContextExtra as Context.ElementAccessExpressionContextExtra + attributes += " extra:selfType=" + contextExtra.selfType.toString() + } result += analyzerPrintExpression(ref elementAccess.expression, indent + 2) result += analyzerPrintExpression(ref elementAccess.argument, indent + 2) } elif expression.isIs() { diff --git a/packages/analyzer/src/context b/packages/analyzer/src/context index a121a46ed..0da42e7a5 100644 --- a/packages/analyzer/src/context +++ b/packages/analyzer/src/context @@ -28,6 +28,10 @@ export obj CallExpressionContextExtra { mut returnType: ref Type.Type } +export obj ElementAccessExpressionContextExtra { + mut selfType: ref Type.Type +} + export obj IsExpressionContextExtra { mut t: ref Type.Type } diff --git a/packages/analyzer/src/main b/packages/analyzer/src/main index 8e2662c9d..e2d486491 100644 --- a/packages/analyzer/src/main +++ b/packages/analyzer/src/main @@ -9,6 +9,7 @@ import AsExpressionContextExtra, CallExpressionContextArgument, CallExpressionContextExtra, + ElementAccessExpressionContextExtra, IsExpressionContextExtra, ObjectDeclarationContext, VariableDeclarationContext, @@ -60,6 +61,7 @@ export Context export AsExpressionContextExtra export CallExpressionContextArgument export CallExpressionContextExtra +export ElementAccessExpressionContextExtra export IsExpressionContextExtra export ObjectDeclarationContext export VariableDeclarationContext diff --git a/packages/analyzer/test/print/await-expression.txt b/packages/analyzer/test/print/await-expression.txt index 6f05c4943..779184942 100644 --- a/packages/analyzer/test/print/await-expression.txt +++ b/packages/analyzer/test/print/await-expression.txt @@ -9,5 +9,5 @@ FunctionDeclaration(asynchronous=true name=test return=int) MainDeclaration() VariableDeclaration(name=a mutable=false constant=false context:varType=int) AwaitExpression(initial=int) - CallExpression(initial=async () -> int) + CallExpression(initial=async () -> int extra:asynchronous=true extra:returnType=int) Identifier(initial=async () -> int name=test) diff --git a/packages/analyzer/test/print/call-expression.txt b/packages/analyzer/test/print/call-expression.txt index 65b4dffea..a90fbb829 100644 --- a/packages/analyzer/test/print/call-expression.txt +++ b/packages/analyzer/test/print/call-expression.txt @@ -17,36 +17,36 @@ FunctionDeclaration(asynchronous=false name=test return=int) ReturnStatement() IntegerLiteral(initial=int value=10) VariableDeclaration(name=a mutable=false constant=true context:varType=int) - CallExpression(initial=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) Identifier(initial=(a: int..., i: int, j: int, o: int) -> int name=test) VariableDeclaration(name=b mutable=false constant=true context:varType=int) - CallExpression(initial=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) Identifier(initial=(a: int..., i: int, j: int, o: int) -> int name=test) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int value=1) VariableDeclaration(name=c mutable=false constant=true context:varType=int) - CallExpression(initial=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) Identifier(initial=(a: int..., i: int, j: int, o: int) -> int name=test) - CallExpressionArgument(name=o) + CallExpressionArgument(name=o extra:parameterIdx=3) IntegerLiteral(initial=int value=1) VariableDeclaration(name=d mutable=false constant=true context:varType=int) - CallExpression(initial=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) Identifier(initial=(a: int..., i: int, j: int, o: int) -> int name=test) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int value=1) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int value=2) VariableDeclaration(name=e mutable=false constant=true context:varType=int) - CallExpression(initial=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) Identifier(initial=(a: int..., i: int, j: int, o: int) -> int name=test) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int value=1) - CallExpressionArgument(name=i) + CallExpressionArgument(name=i extra:parameterIdx=1) IntegerLiteral(initial=int value=2) VariableDeclaration(name=f mutable=false constant=true context:varType=int) - CallExpression(initial=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) Identifier(initial=(a: int..., i: int, j: int, o: int) -> int name=test) - CallExpressionArgument(name=i) + CallExpressionArgument(name=i extra:parameterIdx=1) IntegerLiteral(initial=int value=1) - CallExpressionArgument(name=j) + CallExpressionArgument(name=j extra:parameterIdx=2) IntegerLiteral(initial=int value=2) diff --git a/packages/analyzer/test/print/closure-expression.txt b/packages/analyzer/test/print/closure-expression.txt index 5690dbf18..0d37bb232 100644 --- a/packages/analyzer/test/print/closure-expression.txt +++ b/packages/analyzer/test/print/closure-expression.txt @@ -23,7 +23,7 @@ VariableDeclaration(name=c mutable=false constant=true context:varType=async (mu VariableDeclaration(name=k mutable=false constant=false context:varType=int) BinaryExpression(initial=int operator=-) Identifier(initial=int name=i) - ElementAccessExpression(initial=ref int target=int) + ElementAccessExpression(initial=ref int target=int extra:selfType=int[]) Identifier(initial=int[] name=j) IntegerLiteral(initial=int value=0) ReturnStatement() diff --git a/packages/analyzer/test/print/element-access-expression.txt b/packages/analyzer/test/print/element-access-expression.txt index eb4cb3a44..bf73fd943 100644 --- a/packages/analyzer/test/print/element-access-expression.txt +++ b/packages/analyzer/test/print/element-access-expression.txt @@ -5,6 +5,6 @@ VariableDeclaration(name=a mutable=false constant=true context:varType=int[]) ArrayExpression(initial=int[]) IntegerLiteral(initial=int value=10) VariableDeclaration(name=b mutable=false constant=true context:varType=ref int) - ElementAccessExpression(initial=ref int) + ElementAccessExpression(initial=ref int extra:selfType=int[]) Identifier(initial=int[] name=a) IntegerLiteral(initial=int value=0) diff --git a/packages/analyzer/test/print/function-declaration.txt b/packages/analyzer/test/print/function-declaration.txt index ec957de58..273e5785f 100644 --- a/packages/analyzer/test/print/function-declaration.txt +++ b/packages/analyzer/test/print/function-declaration.txt @@ -34,7 +34,7 @@ FunctionDeclaration(asynchronous=true name=test6 return=int) VariableDeclaration(name=k mutable=false constant=false context:varType=int) BinaryExpression(initial=int operator=-) Identifier(initial=int name=i) - ElementAccessExpression(initial=ref int target=int) + ElementAccessExpression(initial=ref int target=int extra:selfType=int[]) Identifier(initial=int[] name=j) IntegerLiteral(initial=int value=0) ReturnStatement() diff --git a/packages/analyzer/test/print/if-statement.txt b/packages/analyzer/test/print/if-statement.txt index c14c9fe94..3224f0a89 100644 --- a/packages/analyzer/test/print/if-statement.txt +++ b/packages/analyzer/test/print/if-statement.txt @@ -31,58 +31,58 @@ MainDeclaration() IfStatement() ParenthesizedExpression(initial=bool) BooleanLiteral(initial=bool value=true) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=1) IfStatement() BooleanLiteral(initial=bool value=true) IfStatement() BooleanLiteral(initial=bool value=true) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=2) IfStatement() BooleanLiteral(initial=bool value=false) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=3) IfStatement() BooleanLiteral(initial=bool value=true) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=4) IfStatement() BooleanLiteral(initial=bool value=false) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=5) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=6) IfStatement() BooleanLiteral(initial=bool value=true) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=7) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=8) IfStatement() ParenthesizedExpression(initial=bool) BooleanLiteral(initial=bool value=true) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=9) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) IntegerLiteral(initial=int target=any value=10) diff --git a/packages/analyzer/test/print/loop-statement.txt b/packages/analyzer/test/print/loop-statement.txt index d53eeb3bc..013511e77 100644 --- a/packages/analyzer/test/print/loop-statement.txt +++ b/packages/analyzer/test/print/loop-statement.txt @@ -65,7 +65,7 @@ MainDeclaration() BinaryExpression(initial=int operator=+) Identifier(initial=int name=i) Identifier(initial=int name=a) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) Identifier(initial=int target=any name=b) diff --git a/packages/analyzer/test/print/object-declaration.txt b/packages/analyzer/test/print/object-declaration.txt index e92d5437f..0a15afa43 100644 --- a/packages/analyzer/test/print/object-declaration.txt +++ b/packages/analyzer/test/print/object-declaration.txt @@ -25,7 +25,7 @@ obj Test { } } ===== out ===== -ObjectDeclaration(name=Test) +ObjectDeclaration(name=Test context:selfType=Test) ObjectDeclarationProperty(name=a mutable=false constant=false type=int) ObjectDeclarationProperty(name=b mutable=true constant=false type=str) ObjectDeclarationMethod(asynchronous=false name=test1 return=void) @@ -66,7 +66,7 @@ ObjectDeclaration(name=Test) VariableDeclaration(name=k mutable=false constant=false context:varType=int) BinaryExpression(initial=int operator=-) Identifier(initial=int name=i) - ElementAccessExpression(initial=ref int target=int) + ElementAccessExpression(initial=ref int target=int extra:selfType=int[]) Identifier(initial=int[] name=j) IntegerLiteral(initial=int value=0) ReturnStatement() @@ -89,7 +89,7 @@ ObjectDeclaration(name=Test) VariableDeclaration(name=k mutable=false constant=false context:varType=int) BinaryExpression(initial=int operator=-) Identifier(initial=int name=i) - ElementAccessExpression(initial=ref int target=int) + ElementAccessExpression(initial=ref int target=int extra:selfType=int[]) Identifier(initial=int[] name=j) IntegerLiteral(initial=int value=0) ReturnStatement() diff --git a/packages/analyzer/test/print/object-expression.txt b/packages/analyzer/test/print/object-expression.txt index 8a306870c..b1a1721bc 100644 --- a/packages/analyzer/test/print/object-expression.txt +++ b/packages/analyzer/test/print/object-expression.txt @@ -6,7 +6,7 @@ const a := Test{} const b := Test{a: 10} const c := Test{a: 10, b: "str"} ===== out ===== -ObjectDeclaration(name=Test) +ObjectDeclaration(name=Test context:selfType=Test) ObjectDeclarationProperty(name=a mutable=false constant=false type=int) ObjectDeclarationProperty(name=b mutable=false constant=false type=str) VariableDeclaration(name=a mutable=false constant=true context:varType=Test) diff --git a/packages/analyzer/test/print/property-access-expression.txt b/packages/analyzer/test/print/property-access-expression.txt index 4edebba97..dfefed697 100644 --- a/packages/analyzer/test/print/property-access-expression.txt +++ b/packages/analyzer/test/print/property-access-expression.txt @@ -4,7 +4,7 @@ obj Test { const b := Test{a: 10} const c := b.a ===== out ===== -ObjectDeclaration(name=Test) +ObjectDeclaration(name=Test context:selfType=Test) ObjectDeclarationProperty(name=a mutable=false constant=false type=int) VariableDeclaration(name=b mutable=false constant=true context:varType=Test) ObjectExpression(initial=Test id=Test) diff --git a/packages/analyzer/test/print/throw-statement.txt b/packages/analyzer/test/print/throw-statement.txt index 1fa7ec1b4..b796906cb 100644 --- a/packages/analyzer/test/print/throw-statement.txt +++ b/packages/analyzer/test/print/throw-statement.txt @@ -4,7 +4,7 @@ main { ===== out ===== MainDeclaration() ThrowStatement() - CallExpression(initial=Error) + CallExpression(initial=Error extra:asynchronous=false extra:returnType=Error) Identifier(initial=(message: str) -> Error name=NewError) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) StringLiteral(initial=str value=Test) diff --git a/packages/analyzer/test/print/try-statement.txt b/packages/analyzer/test/print/try-statement.txt index 3cdf7fc45..6b1fcd6f3 100644 --- a/packages/analyzer/test/print/try-statement.txt +++ b/packages/analyzer/test/print/try-statement.txt @@ -25,7 +25,7 @@ main { } } ===== out ===== -ObjectDeclaration(name=MyError) +ObjectDeclaration(name=MyError context:selfType=MyError) ObjectDeclarationProperty(name=message mutable=true constant=false type=str) ObjectDeclarationProperty(name=stack mutable=true constant=false type=str) FunctionDeclaration(asynchronous=false name=parse) @@ -38,29 +38,29 @@ MainDeclaration() TryStatementHandler() VariableDeclaration(name=err mutable=false constant=false type=Error context:varType=Error) TryStatement() - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=() -> void name=parse) TryStatementHandler() VariableDeclaration(name=err mutable=false constant=false type=Error context:varType=Error) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) PropertyAccessExpression(initial=str target=any name=message) Identifier(initial=Error name=err) TryStatement() - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=() -> void name=parse) TryStatementHandler() VariableDeclaration(name=err mutable=false constant=false type=Error context:varType=Error) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) PropertyAccessExpression(initial=str target=any name=message) Identifier(initial=Error name=err) TryStatementHandler() VariableDeclaration(name=err mutable=false constant=false type=MyError context:varType=MyError) - CallExpression(initial=void) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) PropertyAccessExpression(initial=str target=any name=message) Identifier(initial=MyError name=err) diff --git a/packages/analyzer/test/type-casting/assignment-expression.txt b/packages/analyzer/test/type-casting/assignment-expression.txt index d46481024..ea316a6cc 100644 --- a/packages/analyzer/test/type-casting/assignment-expression.txt +++ b/packages/analyzer/test/type-casting/assignment-expression.txt @@ -18,10 +18,10 @@ MainDeclaration() ParenthesizedExpression(initial=bool target=bool | int) IsExpression(initial=bool target=bool | int is=bool extra:type=bool) Identifier(initial=bool | int name=b) - CallExpression(initial=str) + CallExpression(initial=str extra:asynchronous=false extra:returnType=str) PropertyAccessExpression(initial=(self: ref bool) -> str name=str) Identifier(initial=bool | int target=bool name=a) - CallExpression(initial=str) + CallExpression(initial=str extra:asynchronous=false extra:returnType=str) PropertyAccessExpression(initial=(self: ref int) -> str name=str) Identifier(initial=bool | int target=int name=b) VariableDeclaration(name=d mutable=false constant=false context:varType=str) @@ -33,10 +33,10 @@ MainDeclaration() ParenthesizedExpression(initial=bool) IsExpression(initial=bool is=bool extra:type=bool) Identifier(initial=bool | int name=b) - CallExpression(initial=str) + CallExpression(initial=str extra:asynchronous=false extra:returnType=str) PropertyAccessExpression(initial=(self: ref int) -> str name=str) Identifier(initial=bool | int target=int name=b) - CallExpression(initial=str) + CallExpression(initial=str extra:asynchronous=false extra:returnType=str) PropertyAccessExpression(initial=(self: ref bool) -> str name=str) Identifier(initial=bool | int target=bool name=a) VariableDeclaration(name=e mutable=false constant=false context:varType=bool) diff --git a/packages/analyzer/test/type-casting/await-expression.txt b/packages/analyzer/test/type-casting/await-expression.txt index b2369b967..7ec2c109f 100644 --- a/packages/analyzer/test/type-casting/await-expression.txt +++ b/packages/analyzer/test/type-casting/await-expression.txt @@ -18,9 +18,9 @@ MainDeclaration() VariableDeclaration(name=b mutable=false constant=false context:varType=int) ConditionalExpression(initial=int) AwaitExpression(initial=bool) - CallExpression(initial=async (n: int) -> bool) + CallExpression(initial=async (n: int) -> bool extra:asynchronous=true extra:returnType=bool) Identifier(initial=async (n: int) -> bool name=cond) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) AssignmentExpression(initial=int operator==) Identifier(initial=bool | int name=a) IntegerLiteral(initial=int target=bool | int value=10) diff --git a/packages/analyzer/test/type-casting/call-expression.txt b/packages/analyzer/test/type-casting/call-expression.txt index f747e9489..4a4e94561 100644 --- a/packages/analyzer/test/type-casting/call-expression.txt +++ b/packages/analyzer/test/type-casting/call-expression.txt @@ -17,9 +17,9 @@ MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) VariableDeclaration(name=b mutable=false constant=false context:varType=int) ConditionalExpression(initial=int) - CallExpression(initial=bool) + CallExpression(initial=bool extra:asynchronous=false extra:returnType=bool) Identifier(initial=(n: int) -> bool name=cond) - CallExpressionArgument() + CallExpressionArgument(extra:parameterIdx=0) AssignmentExpression(initial=int operator==) Identifier(initial=bool | int name=a) IntegerLiteral(initial=int target=bool | int value=10) diff --git a/packages/analyzer/test/type-casting/element-access-expression.txt b/packages/analyzer/test/type-casting/element-access-expression.txt index f7ae4e79f..c491d75c2 100644 --- a/packages/analyzer/test/type-casting/element-access-expression.txt +++ b/packages/analyzer/test/type-casting/element-access-expression.txt @@ -9,7 +9,7 @@ MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) VariableDeclaration(name=b mutable=false constant=false context:varType=int) ConditionalExpression(initial=int) - ElementAccessExpression(initial=ref bool) + ElementAccessExpression(initial=ref bool extra:selfType=bool[]) Identifier(initial=bool[] name=arr) AssignmentExpression(initial=int operator==) Identifier(initial=bool | int name=a) diff --git a/packages/analyzer/test/type-casting/is-expression.txt b/packages/analyzer/test/type-casting/is-expression.txt index d3cdd2095..d893e013e 100644 --- a/packages/analyzer/test/type-casting/is-expression.txt +++ b/packages/analyzer/test/type-casting/is-expression.txt @@ -97,6 +97,6 @@ MainDeclaration() Identifier(initial=any target=int name=n) BinaryExpression(initial=str operator=+) StringLiteral(initial=str value=int) - CallExpression(initial=str) + CallExpression(initial=str extra:asynchronous=false extra:returnType=str) PropertyAccessExpression(initial=(self: ref int) -> str name=str) Identifier(initial=any target=int name=n) diff --git a/packages/analyzer/test/type-casting/object-expression.txt b/packages/analyzer/test/type-casting/object-expression.txt index 5fc1d139b..09fa05afb 100644 --- a/packages/analyzer/test/type-casting/object-expression.txt +++ b/packages/analyzer/test/type-casting/object-expression.txt @@ -8,7 +8,7 @@ main { b := Cond{a: a = true}.empty ? a : false } ===== out ===== -ObjectDeclaration(name=Cond) +ObjectDeclaration(name=Cond context:selfType=Cond) ObjectDeclarationProperty(name=a mutable=false constant=false type=bool) ObjectDeclarationProperty(name=empty mutable=false constant=false type=bool) MainDeclaration() diff --git a/packages/analyzer/test/type-casting/property-access-expression.txt b/packages/analyzer/test/type-casting/property-access-expression.txt index 331ff7fdd..c73280496 100644 --- a/packages/analyzer/test/type-casting/property-access-expression.txt +++ b/packages/analyzer/test/type-casting/property-access-expression.txt @@ -7,7 +7,7 @@ main { b := Cond{a: a = true}.a ? a : false } ===== out ===== -ObjectDeclaration(name=Cond) +ObjectDeclaration(name=Cond context:selfType=Cond) ObjectDeclarationProperty(name=a mutable=false constant=false type=bool) MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) From afe66d19a6abee10de1de41401abf782316d0075 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 4 Jan 2025 10:20:55 +0200 Subject: [PATCH 15/48] analyzer: Add missing license banners --- packages/analyzer/scripts/stringify-type | 5 +++++ packages/analyzer/src/api | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/analyzer/scripts/stringify-type b/packages/analyzer/scripts/stringify-type index 4e57364c5..136d09587 100644 --- a/packages/analyzer/scripts/stringify-type +++ b/packages/analyzer/scripts/stringify-type @@ -1,3 +1,8 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + import Type, TypeParameter, TypeProperty from "../src/type" export fn stringifyTypeParameter (it: ref TypeParameter, indent: int, selfName: str) str { diff --git a/packages/analyzer/src/api b/packages/analyzer/src/api index 6d02960d3..6b7291cf3 100644 --- a/packages/analyzer/src/api +++ b/packages/analyzer/src/api @@ -1,3 +1,8 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + import * as Type from "./type" import TypeMap from "./type-map" import VarMap from "./var-map" From bd0da3dafca69424a82a2ad3a1c71fc33f9c44d9 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 4 Jan 2025 10:24:54 +0200 Subject: [PATCH 16/48] analyzer: Add update tests scripts --- packages/analyzer/package.yml | 6 ++- packages/analyzer/scripts/update-print-tests | 47 +++++++++++++++++ .../scripts/update-type-casting-tests | 50 +++++++++++++++++++ packages/analyzer/test/print-test | 15 +----- packages/analyzer/test/type-casting-test | 18 +------ packages/analyzer/test/utils | 18 +++++++ 6 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 packages/analyzer/scripts/update-print-tests create mode 100644 packages/analyzer/scripts/update-type-casting-tests create mode 100644 packages/analyzer/test/utils diff --git a/packages/analyzer/package.yml b/packages/analyzer/package.yml index 5abb566b5..9851f68ea 100644 --- a/packages/analyzer/package.yml +++ b/packages/analyzer/package.yml @@ -10,5 +10,7 @@ packages: the/testing: 0.4.0 scripts: - update-parser-tests: the run scripts/precompile-api - update-tokenizer-tests: the run scripts/precompile-builtin + precompile-api: the run scripts/precompile-api + precompile-builtin: the run scripts/precompile-builtin + update-print-tests: the run scripts/update-print-tests + update-type-casting-tests: the run scripts/update-type-casting-tests diff --git a/packages/analyzer/scripts/update-print-tests b/packages/analyzer/scripts/update-print-tests new file mode 100644 index 000000000..5440a0f04 --- /dev/null +++ b/packages/analyzer/scripts/update-print-tests @@ -0,0 +1,47 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +import * as Parser from "the/parser" +import Analyzer, AnalyzerFile from "../src/analyzer" +import analyzerPrint from "../src/analyzer-print" +import parseTestFile from "../test/utils" + +const OUT_DELIMITER := os_EOL + "===== out =====" + os_EOL +const TESTS_DIR := "./test/print/" + +main { + files := fs_scandirSync("./test/print") + + loop i := 0; i < files.len; i++ { + name := files[i] as str + if name.slice(-4) != ".txt" { continue } + filePath := TESTS_DIR + name + print("updating:", name) + + result := parseTestFile(filePath) + mut f := Parser.parse(result.in) + Parser.interconnect(ref f) + mut analyzer := Analyzer{} + analyzer.files.push(AnalyzerFile{cwd: process_cwd(), analyzer: ref analyzer, f: f}) + mut file := analyzer.files.last() + file.reader = Parser.Reader_init(file.f.content) + file.init() + file.analyze(ref file.f.program.body) + + if !analyzer.errors.empty { + print(analyzer.errors.join(os_EOL), to: "stderr") + continue + } + + out := analyzerPrint(ref file.f.program.body) + outputTrim := out.trim() + + newContent := result.in.trim() + + OUT_DELIMITER + outputTrim + + (outputTrim.empty ? "" : os_EOL) + + fs_writeFileSync(filePath, newContent.toBuffer()) + } +} diff --git a/packages/analyzer/scripts/update-type-casting-tests b/packages/analyzer/scripts/update-type-casting-tests new file mode 100644 index 000000000..7c607269a --- /dev/null +++ b/packages/analyzer/scripts/update-type-casting-tests @@ -0,0 +1,50 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +import * as Parser from "the/parser" +import Analyzer, AnalyzerFile from "../src/analyzer" +import * as Type from "../src/type" +import TypeMap from "../src/type-map" +import analyzerPrint from "../src/analyzer-print" +import stringify as stringifyTC from "../src/type-cast" +import parseTestFile from "../test/utils" + +const OUT_DELIMITER := os_EOL + "===== out =====" + os_EOL +const TESTS_DIR := "./test/type-casting/" + +main { + files := fs_scandirSync(TESTS_DIR) + + loop i := 0; i < files.len; i++ { + name := files[i] as str + if name.slice(-4) != ".txt" { continue } + filePath := TESTS_DIR + name + print("updating:", name) + + result := parseTestFile(filePath) + mut f := Parser.parse(result.in) + Parser.interconnect(ref f) + mut analyzer := Analyzer{} + analyzer.files.push(AnalyzerFile{cwd: process_cwd(), analyzer: ref analyzer, f: f}) + mut file := analyzer.files.last() + file.reader = Parser.Reader_init(file.f.content) + file.init() + file.analyze(ref file.f.program.body) + + if !analyzer.errors.empty { + print(analyzer.errors.join(os_EOL), to: "stderr") + continue + } + + out := analyzerPrint(ref file.f.program.body) + outputTrim := out.trim() + + newContent := result.in.trim() + + OUT_DELIMITER + outputTrim + + (outputTrim.empty ? "" : os_EOL) + + fs_writeFileSync(filePath, newContent.toBuffer()) + } +} diff --git a/packages/analyzer/test/print-test b/packages/analyzer/test/print-test index 18afab4d7..1e01f0039 100644 --- a/packages/analyzer/test/print-test +++ b/packages/analyzer/test/print-test @@ -6,20 +6,7 @@ import * as Parser from "the/parser" import Analyzer, AnalyzerFile from "../src/analyzer" import analyzerPrint from "../src/analyzer-print" - -export obj PrintTestFileResult { - in: str - out: str -} - -export fn parseTestFile (path: str) PrintTestFileResult { - content := fs_readFileSync(path).str() - outLoc := content.find("===== out =====") - inContent := outLoc == -1 ? content : content.slice(0, outLoc) - outContent := outLoc == -1 ? "" : content.slice(outLoc + 15 + os_EOL.len) - - return PrintTestFileResult{in: inContent, out: outContent} -} +import parseTestFile from "./utils" export fn TEST_Print () { files := fs_scandirSync("./test/print") diff --git a/packages/analyzer/test/type-casting-test b/packages/analyzer/test/type-casting-test index 92bb9edee..2ff26109b 100644 --- a/packages/analyzer/test/type-casting-test +++ b/packages/analyzer/test/type-casting-test @@ -9,23 +9,7 @@ import * as Type from "../src/type" import TypeMap from "../src/type-map" import analyzerPrint from "../src/analyzer-print" import stringify as stringifyTC from "../src/type-cast" - -export obj TypeCastingFileResult { - in: str - out: str -} - -export fn parseTestFile (path: str) TypeCastingFileResult { - content := fs_readFileSync(path).str() - outLoc := content.find("===== out =====") - inContent := outLoc == -1 ? content : content.slice(0, outLoc) - outContent := outLoc == -1 ? "" : content.slice(outLoc + 15 + os_EOL.len) - - return TypeCastingFileResult{ - in: inContent, - out: outContent, - } -} +import parseTestFile from "./utils" export fn TEST_TypeCasting () { files := fs_scandirSync("./test/type-casting") diff --git a/packages/analyzer/test/utils b/packages/analyzer/test/utils new file mode 100644 index 000000000..177218428 --- /dev/null +++ b/packages/analyzer/test/utils @@ -0,0 +1,18 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +export obj TestFileResult { + in: str + out: str +} + +export fn parseTestFile (path: str) TestFileResult { + content := fs_readFileSync(path).str() + outLoc := content.find("===== out =====") + inContent := outLoc == -1 ? content : content.slice(0, outLoc) + outContent := outLoc == -1 ? "" : content.slice(outLoc + 15 + os_EOL.len) + + return TestFileResult{in: inContent, out: outContent} +} From d105db8aca2303f8fd1f28a1321f02e0b9e1e0a3 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 4 Jan 2025 10:39:57 +0200 Subject: [PATCH 17/48] analyzer: Add ability to convert method into a function --- packages/analyzer/README.md | 21 +++++++++++ packages/analyzer/src/type-map | 19 ++++++++++ packages/analyzer/test/type-map-test | 54 ++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/packages/analyzer/README.md b/packages/analyzer/README.md index 4615820a2..68875069f 100644 --- a/packages/analyzer/README.md +++ b/packages/analyzer/README.md @@ -802,6 +802,27 @@ tm.createMethod(false, [], tm.get("void"), false, false, "", selfType) tm.createMethod(false, [], tm.get("void"), true, true, "self", tm.createReference(selfType)) ``` +### `TypeMap.convertMethod (t: ref Type) ref Type` +Converts a method type into a function type. + +**Parameters** + +- `t` - method type to convert + +**Return value** + +Function type representation of the method type provided. + +**Examples** + +```the +mut tm := TypeMap{} +mut selfType := tm.createObject("Animal") +method := tm.createMethod(false, [], tm.get("void"), false, false, "", selfType) + +function := tm.convertMethod(method) +``` + ### `TypeMap.createNamespace (name: str, members: NamespaceMember[]) ref Type` Creates namespace type and puts it inside type map. diff --git a/packages/analyzer/src/type-map b/packages/analyzer/src/type-map index 47855e32f..2f531ae44 100644 --- a/packages/analyzer/src/type-map +++ b/packages/analyzer/src/type-map @@ -329,6 +329,25 @@ export obj TypeMap { return result } + fn convertMethod (mut self: ref Self, t: ref Type.Type) ref Type.Type { + method := t.asMethod() + mut parameters: Type.TypeParameter[] + + if method.withSelf { + selfParameter := Type.TypeParameter{ + name: method.selfName, + t: method.selfType, + mutable: method.selfMutable, + required: true, + } + + parameters.push(selfParameter) + } + + parameters.merge(method.parameters) + return self.createFunction(method.asynchronous, parameters, method.returnType) + } + fn createNamespace (mut self: ref Self, name: str, members: Type.NamespaceMember[]) ref Type.Type { self.items.push(Type.Type{name: name, body: Type.NamespaceType{members: members}, scope: self.scope}) return self.items.last() diff --git a/packages/analyzer/test/type-map-test b/packages/analyzer/test/type-map-test index 2f9224ae8..89c698e2e 100644 --- a/packages/analyzer/test/type-map-test +++ b/packages/analyzer/test/type-map-test @@ -6,6 +6,60 @@ import * as Type from "../src/type" import TypeMap from "../src/type-map" +export fn TEST_TypeMap_convertMethod () { + mut tm := TypeMap{} + tm.init() + + m1 := tm.createMethod(true, [], tm.get("int"), false, false, "", tm.get("int")) + m2 := tm.createMethod(false, [ + Type.TypeParameter{t: tm.get("int")}, + ], tm.get("int"), false, false, "", tm.get("int")) + m3 := tm.createMethod(false, [ + Type.TypeParameter{name: "a", t: tm.get("int"), mutable: true}, + Type.TypeParameter{name: "b", t: tm.createArray(tm.get("int")), variadic: true}, + ], tm.get("int"), false, false, "", tm.get("int")) + + m4 := tm.createMethod(true, [], tm.get("int"), true, false, "self1", tm.createReference(tm.get("int"))) + m5 := tm.createMethod(false, [ + Type.TypeParameter{t: tm.get("int")}, + ], tm.get("int"), true, true, "self2", tm.get("int")) + m6 := tm.createMethod(false, [ + Type.TypeParameter{name: "a", t: tm.get("int"), mutable: true}, + Type.TypeParameter{name: "b", t: tm.createArray(tm.get("int")), variadic: true}, + ], tm.get("int"), true, false, "self3", tm.get("int")) + + f1 := tm.createFunction(true, [], tm.get("int")) + f2 := tm.createFunction(false, [ + Type.TypeParameter{t: tm.get("int")}, + ], tm.get("int")) + f3 := tm.createFunction(false, [ + Type.TypeParameter{name: "a", t: tm.get("int"), mutable: true}, + Type.TypeParameter{name: "b", t: tm.createArray(tm.get("int")), variadic: true}, + ], tm.get("int")) + + f4 := tm.createFunction(true, [ + Type.TypeParameter{name: "self1", t: tm.createReference(tm.get("int")), mutable: false, required: true}, + ], tm.get("int")) + + f5 := tm.createFunction(false, [ + Type.TypeParameter{name: "self2", t: tm.get("int"), mutable: true, required: true}, + Type.TypeParameter{t: tm.get("int")}, + ], tm.get("int")) + + f6 := tm.createFunction(false, [ + Type.TypeParameter{name: "self3", t: tm.get("int"), mutable: false, required: true}, + Type.TypeParameter{name: "a", t: tm.get("int"), mutable: true}, + Type.TypeParameter{name: "b", t: tm.createArray(tm.get("int")), variadic: true}, + ], tm.get("int")) + + EXPECT_TRUE(Type.match(tm.convertMethod(m1), f1)) + EXPECT_TRUE(Type.match(tm.convertMethod(m2), f2)) + EXPECT_TRUE(Type.match(tm.convertMethod(m3), f3)) + EXPECT_TRUE(Type.match(tm.convertMethod(m4), f4)) + EXPECT_TRUE(Type.match(tm.convertMethod(m5), f5)) + EXPECT_TRUE(Type.match(tm.convertMethod(m6), f6)) +} + export fn TEST_TypeMap_unionAdd () { mut tm := TypeMap{} tm.init() From a0ae5f07f5165d2a5c3ee6b344a7f23036db4ff9 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 4 Jan 2025 10:40:35 +0200 Subject: [PATCH 18/48] codegen: Add PropertyAccess expression tests --- .../codegen/property-access-expression-builtin-root.txt | 9 +++++++++ .../test/codegen/property-access-expression-builtin.txt | 9 +++++++++ .../test/codegen/property-access-expression-root.txt | 9 +++++++++ .../codegen/test/codegen/property-access-expression.txt | 9 +++++++++ 4 files changed, 36 insertions(+) create mode 100644 packages/codegen/test/codegen/property-access-expression-builtin-root.txt create mode 100644 packages/codegen/test/codegen/property-access-expression-builtin.txt create mode 100644 packages/codegen/test/codegen/property-access-expression-root.txt create mode 100644 packages/codegen/test/codegen/property-access-expression.txt diff --git a/packages/codegen/test/codegen/property-access-expression-builtin-root.txt b/packages/codegen/test/codegen/property-access-expression-builtin-root.txt new file mode 100644 index 000000000..d891d6f0a --- /dev/null +++ b/packages/codegen/test/codegen/property-access-expression-builtin-root.txt @@ -0,0 +1,9 @@ +obj Test { + a: int +} +main { + test := Test{a: 10} + test.str() +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/property-access-expression-builtin.txt b/packages/codegen/test/codegen/property-access-expression-builtin.txt new file mode 100644 index 000000000..64c6092a5 --- /dev/null +++ b/packages/codegen/test/codegen/property-access-expression-builtin.txt @@ -0,0 +1,9 @@ +obj Test { + a: int +} +main { + test := Test{a: 10} + v := test.str() +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/property-access-expression-root.txt b/packages/codegen/test/codegen/property-access-expression-root.txt new file mode 100644 index 000000000..59cbb423f --- /dev/null +++ b/packages/codegen/test/codegen/property-access-expression-root.txt @@ -0,0 +1,9 @@ +obj Test { + c: str +} +main { + test := Test{c: "test"} + test.c +} +===== code ===== +===== output ===== diff --git a/packages/codegen/test/codegen/property-access-expression.txt b/packages/codegen/test/codegen/property-access-expression.txt new file mode 100644 index 000000000..e41c86d82 --- /dev/null +++ b/packages/codegen/test/codegen/property-access-expression.txt @@ -0,0 +1,9 @@ +obj Test { + c: str +} +main { + test := Test{c: "test"} + v := test.c +} +===== code ===== +===== output ===== From 9089e469aa22e2534ddc27b3c41c25ae126229d4 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 4 Jan 2025 21:40:31 +0200 Subject: [PATCH 19/48] Fix element access assignment in c++ compiler --- src/AST.cpp | 19 ++ test/CodegenTest.cpp | 1 + test/ast-test/expr-access-elem.txt | 4 +- test/codegen-test/expr-access-elem-assign.txt | 260 ++++++++++++++++++ 4 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 test/codegen-test/expr-access-elem-assign.txt diff --git a/src/AST.cpp b/src/AST.cpp index f268c5b92..3b3cc55a1 100644 --- a/src/AST.cpp +++ b/src/AST.cpp @@ -37,6 +37,15 @@ bool isExprAccessLvalue (const ParserStmtExpr &stmtExpr) { return false; } +bool isElementAccessExpression (const ParserStmtExpr &stmtExpr) { + if (std::holds_alternative(*stmtExpr.body)) { + auto exprAccess = std::get(*stmtExpr.body); + return exprAccess.elem != std::nullopt; + } + + return false; +} + std::string stringifyExprAccess (const ParserStmtExpr &stmtExpr) { if (std::holds_alternative(*stmtExpr.body)) { auto exprAccess = std::get(*stmtExpr.body); @@ -1706,6 +1715,15 @@ Type *AST::_nodeExprType (const ParserStmtExpr &stmtExpr, Type *targetType) { auto leftType = this->_nodeExprType(exprAssign.left, nullptr); auto rightType = this->_nodeExprType(exprAssign.right, leftType); + if ( + std::holds_alternative(*exprAssign.left.body) && + isElementAccessExpression(exprAssign.left) && + leftType->isRef() && + rightType->isRef() + ) { + return this->_wrapNodeExprType(stmtExpr, targetType, Type::real(leftType)); + } + return this->_wrapNodeExprType(stmtExpr, targetType, leftType->isRef() && !rightType->isRef() ? Type::real(leftType) : leftType); } else if (std::holds_alternative(*stmtExpr.body)) { auto exprAwait = std::get(*stmtExpr.body); @@ -1889,6 +1907,7 @@ Type *AST::_nodeExprType (const ParserStmtExpr &stmtExpr, Type *targetType) { targetType == nullptr || (!this->typeMap.get("i64")->matchNice(targetType) && !this->typeMap.get("u64")->matchNice(targetType)) || Type::actual(targetType)->isAny() || + Type::actual(targetType)->isRef() || Type::actual(targetType)->isUnion() ) { return this->_wrapNodeExprType(stmtExpr, targetType, this->typeMap.get("int")); diff --git a/test/CodegenTest.cpp b/test/CodegenTest.cpp index 8ef4db490..a7c063a80 100644 --- a/test/CodegenTest.cpp +++ b/test/CodegenTest.cpp @@ -999,6 +999,7 @@ INSTANTIATE_TEST_SUITE_P(ExprAccess, CodegenPassTest, testing::Values( "expr-access", "expr-access-prop", "expr-access-elem", + "expr-access-elem-assign", "expr-access-member", "expr-access-alias", "expr-access-any", diff --git a/test/ast-test/expr-access-elem.txt b/test/ast-test/expr-access-elem.txt index 9184fc228..2ffdb12e4 100644 --- a/test/ast-test/expr-access-elem.txt +++ b/test/ast-test/expr-access-elem.txt @@ -100,9 +100,7 @@ main { - - - + diff --git a/test/codegen-test/expr-access-elem-assign.txt b/test/codegen-test/expr-access-elem-assign.txt new file mode 100644 index 000000000..ed1a2d045 --- /dev/null +++ b/test/codegen-test/expr-access-elem-assign.txt @@ -0,0 +1,260 @@ +======= stdin ======= +main { + mut test: byte[] + mut test2: byte[] + test.push(0) + test2.push(0) + mut pos := 0 + test[pos++] = 10 + test[0] = test2[0] +} +======= code ======= +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) + #define THE_OS_WINDOWS + #define THE_EOL "\r\n" + #define THE_PATH_SEP "\\" +#else + #if defined(__APPLE__) + #define THE_OS_MACOS + #elif defined(__linux__) + #define THE_OS_LINUX + #endif + #define THE_EOL "\n" + #define THE_PATH_SEP "/" +#endif + +#include +#include +#include +#include +#include +#include + +#define TYPE_error_Error 1 + +typedef struct err_buf { + jmp_buf buf; + struct err_buf *next; + struct err_buf *prev; +} err_buf_t; +typedef struct err_stack { + const char *file; + const char *name; + int line; + int col; + struct err_stack *next; + struct err_stack *prev; +} err_stack_t; +typedef struct { + int id; + void *ctx; + err_buf_t *buf_first; + err_buf_t *buf_last; + err_stack_t *stack_first; + err_stack_t *stack_last; + void (*_free) (void *); +} err_state_t; +struct str { + char *d; + size_t l; +}; + +struct error_Error; +struct __THE_1_array_byte; + +struct error_Error { + const struct str __THE_0_message; + const struct str __THE_0_stack; +}; +struct __THE_1_array_byte { + unsigned char *d; + size_t l; +}; + +err_state_t err_state = {-1, NULL, NULL, NULL, NULL, NULL, NULL}; + +void *alloc (size_t); +void error_alloc (err_state_t *, size_t); +void error_assign (err_state_t *, int, void *, void (*f) (void *), int, int); +void error_buf_decrease (err_state_t *); +err_buf_t *error_buf_increase (err_state_t *); +void error_stack_pop (err_state_t *); +void error_stack_pos (err_state_t *, int, int); +void error_stack_push (err_state_t *, const char *, const char *, int, int); +void error_stack_str (err_state_t *); +void *re_alloc (void *, size_t); +void str_free (struct str); +struct error_Error *error_Error_alloc (struct str, struct str); +void error_Error_free (struct error_Error *); +struct __THE_1_array_byte __THE_1_array_byte_alloc (size_t, ...); +unsigned char *__THE_1_array_byte_at (err_state_t *, int, int, struct __THE_1_array_byte, int32_t); +void __THE_1_array_byte_free (struct __THE_1_array_byte); +void __THE_1_array_byte_push (struct __THE_1_array_byte *, struct __THE_1_array_byte); + +void *alloc (size_t n1) { + void *r = malloc(n1); + if (r == NULL) error_alloc(&err_state, n1); + return r; +} +void error_alloc (err_state_t *fn_err_state, size_t n1) { + char d[4096]; + size_t l = 0; + for (err_stack_t *it = fn_err_state->stack_last; it != NULL; it = it->prev) { + const char *fmt = THE_EOL " at %s (%s)"; + size_t z = snprintf(NULL, 0, fmt, it->name, it->file); + if (l + z >= 4096) break; + sprintf(&d[l], fmt, it->name, it->file); + l += z; + } + fprintf(stderr, "Allocation Error: failed to allocate %zu bytes%s" THE_EOL, n1, d); + exit(EXIT_FAILURE); +} +void error_assign (err_state_t *fn_err_state, int id, void *ctx, void (*f) (void *), int line, int col) { + fn_err_state->id = id; + fn_err_state->ctx = ctx; + fn_err_state->_free = f; + error_stack_pos(fn_err_state, line, col); + error_stack_str(fn_err_state); +} +void error_buf_decrease (err_state_t *fn_err_state) { + err_buf_t *buf = fn_err_state->buf_last; + fn_err_state->buf_last = buf->prev; + free(buf); +} +err_buf_t *error_buf_increase (err_state_t *fn_err_state) { + err_buf_t *buf = alloc(sizeof(err_buf_t)); + buf->next = NULL; + buf->prev = fn_err_state->buf_last; + if (fn_err_state->buf_first == NULL) fn_err_state->buf_first = buf; + if (fn_err_state->buf_last != NULL) fn_err_state->buf_last->next = buf; + fn_err_state->buf_last = buf; + return fn_err_state->buf_last; +} +void error_stack_pop (err_state_t *fn_err_state) { + err_stack_t *stack = fn_err_state->stack_last; + fn_err_state->stack_last = stack->prev; + free(stack); +} +void error_stack_pos (err_state_t *fn_err_state, int line, int col) { + if (line != 0) fn_err_state->stack_last->line = line; + if (col != 0) fn_err_state->stack_last->col = col; +} +void error_stack_push (err_state_t *fn_err_state, const char *file, const char *name, int line, int col) { + error_stack_pos(fn_err_state, line, col); + err_stack_t *stack = alloc(sizeof(err_stack_t)); + stack->file = file; + stack->name = name; + stack->next = NULL; + stack->prev = fn_err_state->stack_last; + if (fn_err_state->stack_first == NULL) fn_err_state->stack_first = stack; + if (fn_err_state->stack_last != NULL) fn_err_state->stack_last->next = stack; + fn_err_state->stack_last = stack; +} +void error_stack_str (err_state_t *fn_err_state) { + struct str *stack = (struct str *) &((struct error_Error *) fn_err_state->ctx)->__THE_0_stack; + struct str message = ((struct error_Error *) fn_err_state->ctx)->__THE_0_message; + stack->l = message.l; + stack->d = re_alloc(stack->d, stack->l); + memcpy(stack->d, message.d, stack->l); + for (err_stack_t *it = fn_err_state->stack_last; it != NULL; it = it->prev) { + size_t z; + char *fmt; + if (it->col == 0 && it->line == 0) { + fmt = THE_EOL " at %s (%s)"; + z = snprintf(NULL, 0, fmt, it->name, it->file); + } else if (it->col == 0) { + fmt = THE_EOL " at %s (%s:%d)"; + z = snprintf(NULL, 0, fmt, it->name, it->file, it->line); + } else { + fmt = THE_EOL " at %s (%s:%d:%d)"; + z = snprintf(NULL, 0, fmt, it->name, it->file, it->line, it->col); + } + stack->d = re_alloc(stack->d, stack->l + z + 1); + if (it->col == 0 && it->line == 0) { + sprintf(&stack->d[stack->l], fmt, it->name, it->file); + } else if (it->col == 0) { + sprintf(&stack->d[stack->l], fmt, it->name, it->file, it->line); + } else { + sprintf(&stack->d[stack->l], fmt, it->name, it->file, it->line, it->col); + } + stack->l += z; + } +} +void *re_alloc (void *n1, size_t n2) { + void *r = realloc(n1, n2); + if (r == NULL) error_alloc(&err_state, n2); + return r; +} +void str_free (struct str s) { + free(s.d); +} +struct error_Error *error_Error_alloc (struct str __THE_0_message, struct str __THE_0_stack) { + struct error_Error *r = alloc(sizeof(struct error_Error)); + struct error_Error s = {__THE_0_message, __THE_0_stack}; + memcpy(r, &s, sizeof(struct error_Error)); + return r; +} +void error_Error_free (struct error_Error *n) { + str_free((struct str) n->__THE_0_message); + str_free((struct str) n->__THE_0_stack); + free(n); +} +struct __THE_1_array_byte __THE_1_array_byte_alloc (size_t x, ...) { + if (x == 0) return (struct __THE_1_array_byte) {NULL, 0}; + unsigned char *d = alloc(x * sizeof(unsigned char)); + va_list args; + va_start(args, x); + for (size_t i = 0; i < x; i++) d[i] = va_arg(args, int); + va_end(args); + return (struct __THE_1_array_byte) {d, x}; +} +unsigned char *__THE_1_array_byte_at (err_state_t *fn_err_state, int line, int col, struct __THE_1_array_byte n, int32_t i) { + if ((i >= 0 && i >= n.l) || (i < 0 && i < -((int32_t) n.l))) { + const char *fmt = "index %" PRId32 " out of array bounds"; + size_t z = snprintf(NULL, 0, fmt, i); + char *d = alloc(z + 1); + sprintf(d, fmt, i); + error_assign(fn_err_state, TYPE_error_Error, (void *) error_Error_alloc((struct str) {d, z}, (struct str) {NULL, 0}), (void (*) (void *)) &error_Error_free, line, col); + longjmp(fn_err_state->buf_last->buf, fn_err_state->id); + } + return i < 0 ? &n.d[n.l + i] : &n.d[i]; +} +void __THE_1_array_byte_free (struct __THE_1_array_byte n) { + free(n.d); +} +void __THE_1_array_byte_push (struct __THE_1_array_byte *n, struct __THE_1_array_byte m) { + if (m.l == 0) return; + n->l += m.l; + n->d = re_alloc(n->d, n->l * sizeof(unsigned char)); + size_t k = 0; + for (size_t i = n->l - m.l; i < n->l; i++) n->d[i] = m.d[k++]; + free(m.d); +} + +int main () { + error_stack_push(&err_state, "/test", "main", 0, 0); + struct __THE_1_array_byte __THE_0_test_0 = __THE_1_array_byte_alloc(0); + struct __THE_1_array_byte __THE_0_test2_0 = __THE_1_array_byte_alloc(0); + if (setjmp(error_buf_increase(&err_state)->buf) != 0) goto L1; + __THE_1_array_byte_push(&__THE_0_test_0, __THE_1_array_byte_alloc(1, 0)); + if (setjmp(err_state.buf_last->buf) != 0) goto L1; + __THE_1_array_byte_push(&__THE_0_test2_0, __THE_1_array_byte_alloc(1, 0)); + int32_t __THE_0_pos_0 = 0; + if (setjmp(err_state.buf_last->buf) != 0) goto L1; + *__THE_1_array_byte_at(&err_state, 7, 3, __THE_0_test_0, __THE_0_pos_0++) = 10; + if (setjmp(err_state.buf_last->buf) != 0) goto L1; + *__THE_1_array_byte_at(&err_state, 8, 3, __THE_0_test_0, 0) = *__THE_1_array_byte_at(&err_state, 8, 13, __THE_0_test2_0, 0); +L1: + error_buf_decrease(&err_state); + __THE_1_array_byte_free((struct __THE_1_array_byte) __THE_0_test2_0); + __THE_1_array_byte_free((struct __THE_1_array_byte) __THE_0_test_0); + error_stack_pop(&err_state); + if (err_state.id != -1) { + struct error_Error *err = err_state.ctx; + fprintf(stderr, "Uncaught Error: %.*s" THE_EOL, (int) err->__THE_0_stack.l, err->__THE_0_stack.d); + err_state._free(err_state.ctx); + exit(EXIT_FAILURE); + } +} +======= flags ======= +======= stdout ======= From 100e2f65abdaec350dbefd3d7856b2d7132ad1ed Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sun, 5 Jan 2025 06:44:49 +0200 Subject: [PATCH 20/48] algorithm: Move lorem ipsum text to utilities --- packages/algorithm/README.md | 2 +- packages/algorithm/test/base64-test | 7 ++++--- packages/algorithm/test/utils | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 packages/algorithm/test/utils diff --git a/packages/algorithm/README.md b/packages/algorithm/README.md index a93c88499..386d279ed 100644 --- a/packages/algorithm/README.md +++ b/packages/algorithm/README.md @@ -62,7 +62,7 @@ Constructed `IQRCode` object. **Exceptions** -- `Base64Error` - thrown if string to be decoded is not correctly encoded +- `QRCodeError` - thrown if string to be decoded is not correctly encoded **Examples** diff --git a/packages/algorithm/test/base64-test b/packages/algorithm/test/base64-test index c4e7007ce..add881701 100644 --- a/packages/algorithm/test/base64-test +++ b/packages/algorithm/test/base64-test @@ -4,6 +4,7 @@ */ import * as base64 from "../src/base64" +import LOREM_IPSUM from "./utils" export fn TEST_base64_encode () { EXPECT_EQ(base64.encode("0".toBuffer()), "MA==") @@ -14,8 +15,8 @@ export fn TEST_base64_encode () { EXPECT_EQ(base64.encode("Hello, World!".toBuffer()), "SGVsbG8sIFdvcmxkIQ==") EXPECT_EQ( - base64.encode("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tortor quam, tincidunt id nisl ac, vulputate sagittis nibh. Pellentesque nec diam orci. Proin malesuada nisl aliquam lacus viverra, sit amet pharetra nulla blandit. Phasellus porttitor, elit at gravida efficitur, lacus leo accumsan tellus, nec ornare augue magna quis mauris. Aliquam commodo eleifend lorem quis rhoncus. Aliquam rutrum tempus malesuada. Morbi mollis massa sit amet condimentum lacinia. Duis eu maximus ipsum. Proin placerat, ligula vel elementum consequat, purus lectus tempor magna, quis commodo nulla nisl ut ante. Duis viverra finibus lacus, a sollicitudin justo elementum id. Quisque venenatis mattis placerat. Suspendisse potenti.".toBuffer()), - "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gTWFlY2VuYXMgdG9ydG9yIHF1YW0sIHRpbmNpZHVudCBpZCBuaXNsIGFjLCB2dWxwdXRhdGUgc2FnaXR0aXMgbmliaC4gUGVsbGVudGVzcXVlIG5lYyBkaWFtIG9yY2kuIFByb2luIG1hbGVzdWFkYSBuaXNsIGFsaXF1YW0gbGFjdXMgdml2ZXJyYSwgc2l0IGFtZXQgcGhhcmV0cmEgbnVsbGEgYmxhbmRpdC4gUGhhc2VsbHVzIHBvcnR0aXRvciwgZWxpdCBhdCBncmF2aWRhIGVmZmljaXR1ciwgbGFjdXMgbGVvIGFjY3Vtc2FuIHRlbGx1cywgbmVjIG9ybmFyZSBhdWd1ZSBtYWduYSBxdWlzIG1hdXJpcy4gQWxpcXVhbSBjb21tb2RvIGVsZWlmZW5kIGxvcmVtIHF1aXMgcmhvbmN1cy4gQWxpcXVhbSBydXRydW0gdGVtcHVzIG1hbGVzdWFkYS4gTW9yYmkgbW9sbGlzIG1hc3NhIHNpdCBhbWV0IGNvbmRpbWVudHVtIGxhY2luaWEuIER1aXMgZXUgbWF4aW11cyBpcHN1bS4gUHJvaW4gcGxhY2VyYXQsIGxpZ3VsYSB2ZWwgZWxlbWVudHVtIGNvbnNlcXVhdCwgcHVydXMgbGVjdHVzIHRlbXBvciBtYWduYSwgcXVpcyBjb21tb2RvIG51bGxhIG5pc2wgdXQgYW50ZS4gRHVpcyB2aXZlcnJhIGZpbmlidXMgbGFjdXMsIGEgc29sbGljaXR1ZGluIGp1c3RvIGVsZW1lbnR1bSBpZC4gUXVpc3F1ZSB2ZW5lbmF0aXMgbWF0dGlzIHBsYWNlcmF0LiBTdXNwZW5kaXNzZSBwb3RlbnRpLg==" + base64.encode(LOREM_IPSUM.toBuffer()), + "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gTWFlY2VuYXMgdG9ydG9yIHF1YW0sIHRpbmNpZHVudCBpZCBuaXNsIGFjLCB2dWxwdXRhdGUgc2FnaXR0aXMgbmliaC4gUGVsbGVudGVzcXVlIG5lYyBkaWFtIG9yY2kuIFByb2luIG1hbGVzdWFkYSBuaXNsIGFsaXF1YW0gbGFjdXMgdml2ZXJyYSwgc2l0IGFtZXQgcGhhcmV0cmEgbnVsbGEgYmxhbmRpdC4gUGhhc2VsbHVzIHBvcnR0aXRvciwgZWxpdCBhdCBncmF2aWRhIGVmZmljaXR1ciwgbGFjdXMgbGVvIGFjY3Vtc2FuIHRlbGx1cywgbmVjIG9ybmFyZSBhdWd1ZSBtYWduYSBxdWlzIG1hdXJpcy4gQWxpcXVhbSBjb21tb2RvIGVsZWlmZW5kIGxvcmVtIHF1aXMgcmhvbmN1cy4gQWxpcXVhbSBydXRydW0gdGVtcHVzIG1hbGVzdWFkYS4gTW9yYmkgbW9sbGlzIG1hc3NhIHNpdCBhbWV0IGNvbmRpbWVudHVtIGxhY2luaWEuIER1aXMgZXUgbWF4aW11cyBpcHN1bS4gUHJvaW4gcGxhY2VyYXQsIGxpZ3VsYSB2ZWwgZWxlbWVudHVtIGNvbnNlcXVhdCwgcHVydXMgbGVjdHVzIHRlbXBvciBtYWduYSwgcXVpcyBjb21tb2RvIG51bGxhIG5pc2wgdXQgYW50ZS4gRHVpcyB2aXZlcnJhIGZpbmlidXMgbGFjdXMsIGEgc29sbGljaXR1ZGluIGp1c3RvIGVsZW1lbnR1bSBpZC4gUXVpc3F1ZSB2ZW5lbmF0aXMgbWF0dGlzIHBsYWNlcmF0LiBTdXNwZW5kaXNzZSBwb3RlbnRpLg==", ) } @@ -29,6 +30,6 @@ export fn TEST_base64_decode () { EXPECT_EQ( base64.decode("TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gTWFlY2VuYXMgdG9ydG9yIHF1YW0sIHRpbmNpZHVudCBpZCBuaXNsIGFjLCB2dWxwdXRhdGUgc2FnaXR0aXMgbmliaC4gUGVsbGVudGVzcXVlIG5lYyBkaWFtIG9yY2kuIFByb2luIG1hbGVzdWFkYSBuaXNsIGFsaXF1YW0gbGFjdXMgdml2ZXJyYSwgc2l0IGFtZXQgcGhhcmV0cmEgbnVsbGEgYmxhbmRpdC4gUGhhc2VsbHVzIHBvcnR0aXRvciwgZWxpdCBhdCBncmF2aWRhIGVmZmljaXR1ciwgbGFjdXMgbGVvIGFjY3Vtc2FuIHRlbGx1cywgbmVjIG9ybmFyZSBhdWd1ZSBtYWduYSBxdWlzIG1hdXJpcy4gQWxpcXVhbSBjb21tb2RvIGVsZWlmZW5kIGxvcmVtIHF1aXMgcmhvbmN1cy4gQWxpcXVhbSBydXRydW0gdGVtcHVzIG1hbGVzdWFkYS4gTW9yYmkgbW9sbGlzIG1hc3NhIHNpdCBhbWV0IGNvbmRpbWVudHVtIGxhY2luaWEuIER1aXMgZXUgbWF4aW11cyBpcHN1bS4gUHJvaW4gcGxhY2VyYXQsIGxpZ3VsYSB2ZWwgZWxlbWVudHVtIGNvbnNlcXVhdCwgcHVydXMgbGVjdHVzIHRlbXBvciBtYWduYSwgcXVpcyBjb21tb2RvIG51bGxhIG5pc2wgdXQgYW50ZS4gRHVpcyB2aXZlcnJhIGZpbmlidXMgbGFjdXMsIGEgc29sbGljaXR1ZGluIGp1c3RvIGVsZW1lbnR1bSBpZC4gUXVpc3F1ZSB2ZW5lbmF0aXMgbWF0dGlzIHBsYWNlcmF0LiBTdXNwZW5kaXNzZSBwb3RlbnRpLg==").str(), - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tortor quam, tincidunt id nisl ac, vulputate sagittis nibh. Pellentesque nec diam orci. Proin malesuada nisl aliquam lacus viverra, sit amet pharetra nulla blandit. Phasellus porttitor, elit at gravida efficitur, lacus leo accumsan tellus, nec ornare augue magna quis mauris. Aliquam commodo eleifend lorem quis rhoncus. Aliquam rutrum tempus malesuada. Morbi mollis massa sit amet condimentum lacinia. Duis eu maximus ipsum. Proin placerat, ligula vel elementum consequat, purus lectus tempor magna, quis commodo nulla nisl ut ante. Duis viverra finibus lacus, a sollicitudin justo elementum id. Quisque venenatis mattis placerat. Suspendisse potenti." + LOREM_IPSUM, ) } diff --git a/packages/algorithm/test/utils b/packages/algorithm/test/utils new file mode 100644 index 000000000..598e120e0 --- /dev/null +++ b/packages/algorithm/test/utils @@ -0,0 +1,14 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +export const LOREM_IPSUM := + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tortor quam, tincidunt id " + + "nisl ac, vulputate sagittis nibh. Pellentesque nec diam orci. Proin malesuada nisl aliquam " + + "lacus viverra, sit amet pharetra nulla blandit. Phasellus porttitor, elit at gravida " + + "efficitur, lacus leo accumsan tellus, nec ornare augue magna quis mauris. Aliquam commodo " + + "eleifend lorem quis rhoncus. Aliquam rutrum tempus malesuada. Morbi mollis massa sit amet " + + "condimentum lacinia. Duis eu maximus ipsum. Proin placerat, ligula vel elementum consequat, " + + "purus lectus tempor magna, quis commodo nulla nisl ut ante. Duis viverra finibus lacus, a " + + "sollicitudin justo elementum id. Quisque venenatis mattis placerat. Suspendisse potenti." From 353f0f7cbac502782a98878949b093b9b7ee60a6 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sun, 5 Jan 2025 07:11:41 +0200 Subject: [PATCH 21/48] algorithm: Ability to convert byte array from/to str --- packages/algorithm/README.md | 34 +++++++++++++++++++++++++ packages/algorithm/src/byte-array | 21 +++++++++++++++ packages/algorithm/src/main | 2 ++ packages/algorithm/test/byte-array-test | 28 ++++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 packages/algorithm/src/byte-array create mode 100644 packages/algorithm/test/byte-array-test diff --git a/packages/algorithm/README.md b/packages/algorithm/README.md index 386d279ed..6b77609eb 100644 --- a/packages/algorithm/README.md +++ b/packages/algorithm/README.md @@ -49,6 +49,40 @@ A string in ASCII format containing decoded data from `data`. base64.decode("QWFyb24=") ``` +### `byteArray.fromStr (in: str) byte[]` +Converts a string into a byte array. + +**Parameters** + +- `in` - string to convert + +**Return value** + +A byte array representing input string. + +**Examples** + +```the +byteArray.fromStr("Aaron") +``` + +### `byteArray.toStr (in: byte[]) str` +Converts a byte array into a string. + +**Parameters** + +- `in` - byte array to convert + +**Return value** + +A string representing input byte array. + +**Examples** + +```the +byteArray.toStr([0x41, 0x61, 0x72, 0x6F, 0x6E]) +``` + ### `qrcode.QRCode (input: Buffer) IQRCode` Constructs `IQRCode` object. diff --git a/packages/algorithm/src/byte-array b/packages/algorithm/src/byte-array new file mode 100644 index 000000000..6b0317100 --- /dev/null +++ b/packages/algorithm/src/byte-array @@ -0,0 +1,21 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +export fn fromStr (in: str) byte[] { + mut result: byte[] + loop i := 0; i < in.len; i++ { + result.push(in[i].byte) + } + return result +} + +export fn toStr (in: byte[]) str { + mut result := "" + loop i := 0; i < in.len; i++ { + ch: char = in[i] as byte + result += ch.str() + } + return result +} diff --git a/packages/algorithm/src/main b/packages/algorithm/src/main index 25930fc7e..2af324fda 100644 --- a/packages/algorithm/src/main +++ b/packages/algorithm/src/main @@ -4,7 +4,9 @@ */ import * as base64 from "./base64" +import * as byteArray from "./byte-array" import * as qrcode from "./qrcode" export base64 +export byteArray export qrcode diff --git a/packages/algorithm/test/byte-array-test b/packages/algorithm/test/byte-array-test new file mode 100644 index 000000000..ac68ca20a --- /dev/null +++ b/packages/algorithm/test/byte-array-test @@ -0,0 +1,28 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +import * as byteArray from "../src/byte-array" + +const S1: byte[] = [] +const S2: byte[] = [0x30] +const S3: byte[] = [0x41] +const S4: byte[] = [0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x20, 0x57, 0x4F, 0x52, 0x4C, 0x44] +const S5: byte[] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21] + +export fn TEST_byteArray_fromStr () { + EXPECT_EQ(byteArray.fromStr(""), S1) + EXPECT_EQ(byteArray.fromStr("0"), S2) + EXPECT_EQ(byteArray.fromStr("A"), S3) + EXPECT_EQ(byteArray.fromStr("HELLO WORLD"), S4) + EXPECT_EQ(byteArray.fromStr("Hello, World!"), S5) +} + +export fn TEST_byteArray_toStr () { + EXPECT_EQ(byteArray.toStr(S1), "") + EXPECT_EQ(byteArray.toStr(S2), "0") + EXPECT_EQ(byteArray.toStr(S3), "A") + EXPECT_EQ(byteArray.toStr(S4), "HELLO WORLD") + EXPECT_EQ(byteArray.toStr(S5), "Hello, World!") +} From 32fcd757561150c59030bfbf83b188adee79c4fe Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sun, 5 Jan 2025 08:43:09 +0200 Subject: [PATCH 22/48] algorithm: Ability to convert byte array into hex str --- packages/algorithm/README.md | 17 +++++++++++++++++ packages/algorithm/src/byte-array | 14 ++++++++++++++ packages/algorithm/test/byte-array-test | 8 ++++++++ 3 files changed, 39 insertions(+) diff --git a/packages/algorithm/README.md b/packages/algorithm/README.md index 6b77609eb..61ae8c64d 100644 --- a/packages/algorithm/README.md +++ b/packages/algorithm/README.md @@ -66,6 +66,23 @@ A byte array representing input string. byteArray.fromStr("Aaron") ``` +### `byteArray.toHexStr (in: byte[]) str` +Converts a byte array into a hex string. + +**Parameters** + +- `in` - byte array to convert + +**Return value** + +A hex string representing input byte array. + +**Examples** + +```the +byteArray.toHexStr([0x41, 0x61, 0x72, 0x6F, 0x6E]) +``` + ### `byteArray.toStr (in: byte[]) str` Converts a byte array into a string. diff --git a/packages/algorithm/src/byte-array b/packages/algorithm/src/byte-array index 6b0317100..fcde4eecb 100644 --- a/packages/algorithm/src/byte-array +++ b/packages/algorithm/src/byte-array @@ -3,6 +3,8 @@ * Licensed under the MIT License */ +const HEX_CHARS := ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] + export fn fromStr (in: str) byte[] { mut result: byte[] loop i := 0; i < in.len; i++ { @@ -11,6 +13,18 @@ export fn fromStr (in: str) byte[] { return result } +export fn toHexStr (in: byte[]) str { + mut result := "" + loop i := 0; i < in.len; i++ { + b := in[i] as byte + i: int = b >> 4 + j: int = b & 0x0F + result += HEX_CHARS[i] + result += HEX_CHARS[j] + } + return result +} + export fn toStr (in: byte[]) str { mut result := "" loop i := 0; i < in.len; i++ { diff --git a/packages/algorithm/test/byte-array-test b/packages/algorithm/test/byte-array-test index ac68ca20a..a689e1c09 100644 --- a/packages/algorithm/test/byte-array-test +++ b/packages/algorithm/test/byte-array-test @@ -19,6 +19,14 @@ export fn TEST_byteArray_fromStr () { EXPECT_EQ(byteArray.fromStr("Hello, World!"), S5) } +export fn TEST_byteArray_toHexStr () { + EXPECT_EQ(byteArray.toHexStr(S1), "") + EXPECT_EQ(byteArray.toHexStr(S2), "30") + EXPECT_EQ(byteArray.toHexStr(S3), "41") + EXPECT_EQ(byteArray.toHexStr(S4), "48454c4c4f20574f524c44") + EXPECT_EQ(byteArray.toHexStr(S5), "48656c6c6f2c20576f726c6421") +} + export fn TEST_byteArray_toStr () { EXPECT_EQ(byteArray.toStr(S1), "") EXPECT_EQ(byteArray.toStr(S2), "0") From 54825f1106a32f376c3a3a3d35fa2b1028fbcb1f Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sun, 5 Jan 2025 10:23:18 +0200 Subject: [PATCH 23/48] algorithm: Implement md5 hash function --- packages/algorithm/README.md | 18 ++ packages/algorithm/src/main | 2 + packages/algorithm/src/md5 | 307 +++++++++++++++++++++++++++++++ packages/algorithm/test/md5-test | 71 +++++++ 4 files changed, 398 insertions(+) create mode 100644 packages/algorithm/src/md5 create mode 100644 packages/algorithm/test/md5-test diff --git a/packages/algorithm/README.md b/packages/algorithm/README.md index 61ae8c64d..a54853d58 100644 --- a/packages/algorithm/README.md +++ b/packages/algorithm/README.md @@ -164,3 +164,21 @@ mut qr := qrcode.QRCode("HELLO, WORLD!") qr.encode(ecc: .M, mask: 3) qrcode.toTerminal(qr) ``` + +### `md5 (data: byte[]) byte[]` +Encodes byte array with md5 algorithm. + +**Parameters** + +- `data` - byte array to encode + +**Return value** + +A byte array containing md5 representation of the `data`. + +**Examples** + +```the +import byteArray, md5 from "the/algorithm" +result := md5(byteArray.fromStr("Aaron")) +``` diff --git a/packages/algorithm/src/main b/packages/algorithm/src/main index 2af324fda..07ef33f43 100644 --- a/packages/algorithm/src/main +++ b/packages/algorithm/src/main @@ -6,7 +6,9 @@ import * as base64 from "./base64" import * as byteArray from "./byte-array" import * as qrcode from "./qrcode" +import md5 from "./md5" export base64 export byteArray export qrcode +export md5 diff --git a/packages/algorithm/src/md5 b/packages/algorithm/src/md5 new file mode 100644 index 000000000..274ab2275 --- /dev/null +++ b/packages/algorithm/src/md5 @@ -0,0 +1,307 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +// Implementation of: https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + +fn F (x: u32, y: u32, z: u32) u32 { + return z ^ (x & (y ^ z)) +} + +fn G (x: u32, y: u32, z: u32) u32 { + return y ^ (z & (x ^ y)) +} + +fn H (x: u32, y: u32, z: u32) u32 { + return (x ^ y) ^ z +} + +fn H2 (x: u32, y: u32, z: u32) u32 { + return x ^ (y ^ z) +} + +fn I (x: u32, y: u32, z: u32) u32 { + return y ^ (x | ~z) +} + +fn step (f: (u32, u32, u32) -> u32, a: u32, b: u32, c: u32, d: u32, x: u32, t: u32, s: u32) u32 { + r := a + f(b, c, d) + x + t + return ((r << s) | ((r & 0xffffffff) >> (32 - s))) + b +} + +export obj MD5 { + mut buffer: byte[] + mut block: u32[] + + mut low: u32 + mut high: u32 + + mut a: u32 + mut b: u32 + mut c: u32 + mut d: u32 + + fn init (mut self: ref Self) { + self._normalize() + self.block = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + + self.low = 0 + self.high = 0 + + self.a = 0x67452301 + self.b = 0xefcdab89 + self.c = 0x98badcfe + self.d = 0x10325476 + } + + fn update (mut self: ref Self, data: byte[]) { + mut d := data + mut l := d.len + + savedLow := self.low + self.low = (savedLow + l) & 0x1fffffff + + if self.low < savedLow { + self.high++ + } + + self.high += l >> 29 + used: int = savedLow & 0x3f + + if used { + available := 64 - used + + if l < available { + self._copy(used, d) + return + } + + self._copy(used, d) + d = d.slice(available) + l -= available + self.buffer = self._body(self.buffer, 64) + self._normalize() + } + + if l >= 64 { + d = self._body(d, l & ~0x3f) + l &= 0x3f + } + + self._copy(0, d) + } + + fn value (mut self: ref Self) byte[] { + mut used: int = self.low & 0x3f + self.buffer[used++] = 0x80 + mut available := 64 - used + + if available < 8 { + self._fill(used, available) + self.buffer = self._body(self.buffer, 64) + self._normalize() + used = 0 + available = 64 + } + + self._fill(used, available - 8) + self.low <<= 3 + + self.buffer[56] = self.low + self.buffer[57] = self.low >> 8 + self.buffer[58] = self.low >> 16 + self.buffer[59] = self.low >> 24 + self.buffer[60] = self.high + self.buffer[61] = self.high >> 8 + self.buffer[62] = self.high >> 16 + self.buffer[63] = self.high >> 24 + + self.buffer = self._body(self.buffer, 64) + + result: byte[] = [ + self.a, + self.a >> 8, + self.a >> 16, + self.a >> 24, + self.b, + self.b >> 8, + self.b >> 16, + self.b >> 24, + self.c, + self.c >> 8, + self.c >> 16, + self.c >> 24, + self.d, + self.d >> 8, + self.d >> 16, + self.d >> 24, + ] + + self._reset() + return result + } + + fn _body (mut self: ref Self, data: byte[], len: int) byte[] { + mut d := data + mut l := len + + loop { + savedA := self.a + savedB := self.b + savedC := self.c + savedD := self.d + + self._updateBlock(d) + + self.a = step(F, self.a, self.b, self.c, self.d, self.block[0], 0xd76aa478, 7) + self.d = step(F, self.d, self.a, self.b, self.c, self.block[1], 0xe8c7b756, 12) + self.c = step(F, self.c, self.d, self.a, self.b, self.block[2], 0x242070db, 17) + self.b = step(F, self.b, self.c, self.d, self.a, self.block[3], 0xc1bdceee, 22) + self.a = step(F, self.a, self.b, self.c, self.d, self.block[4], 0xf57c0faf, 7) + self.d = step(F, self.d, self.a, self.b, self.c, self.block[5], 0x4787c62a, 12) + self.c = step(F, self.c, self.d, self.a, self.b, self.block[6], 0xa8304613, 17) + self.b = step(F, self.b, self.c, self.d, self.a, self.block[7], 0xfd469501, 22) + self.a = step(F, self.a, self.b, self.c, self.d, self.block[8], 0x698098d8, 7) + self.d = step(F, self.d, self.a, self.b, self.c, self.block[9], 0x8b44f7af, 12) + self.c = step(F, self.c, self.d, self.a, self.b, self.block[10], 0xffff5bb1, 17) + self.b = step(F, self.b, self.c, self.d, self.a, self.block[11], 0x895cd7be, 22) + self.a = step(F, self.a, self.b, self.c, self.d, self.block[12], 0x6b901122, 7) + self.d = step(F, self.d, self.a, self.b, self.c, self.block[13], 0xfd987193, 12) + self.c = step(F, self.c, self.d, self.a, self.b, self.block[14], 0xa679438e, 17) + self.b = step(F, self.b, self.c, self.d, self.a, self.block[15], 0x49b40821, 22) + + self.a = step(G, self.a, self.b, self.c, self.d, self.block[1], 0xf61e2562, 5) + self.d = step(G, self.d, self.a, self.b, self.c, self.block[6], 0xc040b340, 9) + self.c = step(G, self.c, self.d, self.a, self.b, self.block[11], 0x265e5a51, 14) + self.b = step(G, self.b, self.c, self.d, self.a, self.block[0], 0xe9b6c7aa, 20) + self.a = step(G, self.a, self.b, self.c, self.d, self.block[5], 0xd62f105d, 5) + self.d = step(G, self.d, self.a, self.b, self.c, self.block[10], 0x02441453, 9) + self.c = step(G, self.c, self.d, self.a, self.b, self.block[15], 0xd8a1e681, 14) + self.b = step(G, self.b, self.c, self.d, self.a, self.block[4], 0xe7d3fbc8, 20) + self.a = step(G, self.a, self.b, self.c, self.d, self.block[9], 0x21e1cde6, 5) + self.d = step(G, self.d, self.a, self.b, self.c, self.block[14], 0xc33707d6, 9) + self.c = step(G, self.c, self.d, self.a, self.b, self.block[3], 0xf4d50d87, 14) + self.b = step(G, self.b, self.c, self.d, self.a, self.block[8], 0x455a14ed, 20) + self.a = step(G, self.a, self.b, self.c, self.d, self.block[13], 0xa9e3e905, 5) + self.d = step(G, self.d, self.a, self.b, self.c, self.block[2], 0xfcefa3f8, 9) + self.c = step(G, self.c, self.d, self.a, self.b, self.block[7], 0x676f02d9, 14) + self.b = step(G, self.b, self.c, self.d, self.a, self.block[12], 0x8d2a4c8a, 20) + + self.a = step(H, self.a, self.b, self.c, self.d, self.block[5], 0xfffa3942, 4) + self.d = step(H2, self.d, self.a, self.b, self.c, self.block[8], 0x8771f681, 11) + self.c = step(H, self.c, self.d, self.a, self.b, self.block[11], 0x6d9d6122, 16) + self.b = step(H2, self.b, self.c, self.d, self.a, self.block[14], 0xfde5380c, 23) + self.a = step(H, self.a, self.b, self.c, self.d, self.block[1], 0xa4beea44, 4) + self.d = step(H2, self.d, self.a, self.b, self.c, self.block[4], 0x4bdecfa9, 11) + self.c = step(H, self.c, self.d, self.a, self.b, self.block[7], 0xf6bb4b60, 16) + self.b = step(H2, self.b, self.c, self.d, self.a, self.block[10], 0xbebfbc70, 23) + self.a = step(H, self.a, self.b, self.c, self.d, self.block[13], 0x289b7ec6, 4) + self.d = step(H2, self.d, self.a, self.b, self.c, self.block[0], 0xeaa127fa, 11) + self.c = step(H, self.c, self.d, self.a, self.b, self.block[3], 0xd4ef3085, 16) + self.b = step(H2, self.b, self.c, self.d, self.a, self.block[6], 0x04881d05, 23) + self.a = step(H, self.a, self.b, self.c, self.d, self.block[9], 0xd9d4d039, 4) + self.d = step(H2, self.d, self.a, self.b, self.c, self.block[12], 0xe6db99e5, 11) + self.c = step(H, self.c, self.d, self.a, self.b, self.block[15], 0x1fa27cf8, 16) + self.b = step(H2, self.b, self.c, self.d, self.a, self.block[2], 0xc4ac5665, 23) + + self.a = step(I, self.a, self.b, self.c, self.d, self.block[0], 0xf4292244, 6) + self.d = step(I, self.d, self.a, self.b, self.c, self.block[7], 0x432aff97, 10) + self.c = step(I, self.c, self.d, self.a, self.b, self.block[14], 0xab9423a7, 15) + self.b = step(I, self.b, self.c, self.d, self.a, self.block[5], 0xfc93a039, 21) + self.a = step(I, self.a, self.b, self.c, self.d, self.block[12], 0x655b59c3, 6) + self.d = step(I, self.d, self.a, self.b, self.c, self.block[3], 0x8f0ccc92, 10) + self.c = step(I, self.c, self.d, self.a, self.b, self.block[10], 0xffeff47d, 15) + self.b = step(I, self.b, self.c, self.d, self.a, self.block[1], 0x85845dd1, 21) + self.a = step(I, self.a, self.b, self.c, self.d, self.block[8], 0x6fa87e4f, 6) + self.d = step(I, self.d, self.a, self.b, self.c, self.block[15], 0xfe2ce6e0, 10) + self.c = step(I, self.c, self.d, self.a, self.b, self.block[6], 0xa3014314, 15) + self.b = step(I, self.b, self.c, self.d, self.a, self.block[13], 0x4e0811a1, 21) + self.a = step(I, self.a, self.b, self.c, self.d, self.block[4], 0xf7537e82, 6) + self.d = step(I, self.d, self.a, self.b, self.c, self.block[11], 0xbd3af235, 10) + self.c = step(I, self.c, self.d, self.a, self.b, self.block[2], 0x2ad7d2bb, 15) + self.b = step(I, self.b, self.c, self.d, self.a, self.block[9], 0xeb86d391, 21) + + self.a += savedA + self.b += savedB + self.c += savedC + self.d += savedD + + d = d.slice(64) + l -= 64 + + if l <= 0 { + break + } + } + + return d + } + + fn _copy (mut self: ref Self, position: int, data: byte[]) { + bl := self.buffer.len + dl := data.len + l := position + dl > bl ? bl - position : dl + + loop i := 0; i < l; i++ { + self.buffer[position + i] = data[i] + } + } + + fn _fill (mut self: ref Self, start: int, end: int) { + loop i := start; i < end; i++ { + self.buffer[i] = 0 + } + } + + fn _normalize (mut self: ref Self) { + l := self.buffer.len + loop i := 0; i < 64 - l; i++ { + self.buffer.push(0) + } + } + + fn _reset (mut self: ref Self) { + self.buffer = [] + self.block = [] + + self.low = 0 + self.high = 0 + + self.a = 0 + self.b = 0 + self.c = 0 + self.d = 0 + } + + fn _updateBlock (mut self: ref Self, data: byte[]) { + self._updateBlockItem(data, 0) + self._updateBlockItem(data, 1) + self._updateBlockItem(data, 2) + self._updateBlockItem(data, 3) + self._updateBlockItem(data, 4) + self._updateBlockItem(data, 5) + self._updateBlockItem(data, 6) + self._updateBlockItem(data, 7) + self._updateBlockItem(data, 8) + self._updateBlockItem(data, 9) + self._updateBlockItem(data, 10) + self._updateBlockItem(data, 11) + self._updateBlockItem(data, 12) + self._updateBlockItem(data, 13) + self._updateBlockItem(data, 14) + self._updateBlockItem(data, 15) + } + + fn _updateBlockItem (mut self: ref Self, data: byte[], n: int) { + self.block[n] = + data[n * 4] | (data[n * 4 + 1] << 8) | (data[n * 4 + 2] << 16) | (data[n * 4 + 3] << 24) + } +} + +export fn md5 (data: byte[]) byte[] { + mut algorithm := MD5{} + algorithm.init() + algorithm.update(data) + return algorithm.value() +} diff --git a/packages/algorithm/test/md5-test b/packages/algorithm/test/md5-test new file mode 100644 index 000000000..c60be2dc0 --- /dev/null +++ b/packages/algorithm/test/md5-test @@ -0,0 +1,71 @@ +/*! + * Copyright (c) Aaron Delasy + * Licensed under the MIT License + */ + +import MD5, md5 from "../src/md5" +import * as byteArray from "../src/byte-array" +import LOREM_IPSUM from "./utils" + +export fn TEST_md5 () { + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr(""))), + "d41d8cd98f00b204e9800998ecf8427e", + ) + + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr("0"))), + "cfcd208495d565ef66e7dff9f98764da", + ) + + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr("A"))), + "7fc56270e7a70fa81a5935b72eacbe29", + ) + + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr("HELLO WORLD"))), + "361fadf1c712e812d198c4cab5712a79", + ) + + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr("Hello, World!"))), + "65a8e27d8879283831b664bd8b7f0ad4", + ) + + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr(LOREM_IPSUM))), + "5ca7d2e24fb2a7658b2721a151433a8c", + ) + + veryLongStr := LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM + + EXPECT_EQ( + byteArray.toHexStr(md5(byteArray.fromStr(veryLongStr))), + "011ffe33420a2ff9d20232475188e3f7", + ) + + mut algorithm1 := MD5{} + + algorithm1.init() + algorithm1.update(byteArray.fromStr(LOREM_IPSUM + "\n")) + algorithm1.update(byteArray.fromStr(LOREM_IPSUM + "\n")) + + EXPECT_EQ( + byteArray.toHexStr(algorithm1.value()), + "a7396aa24ca986eb42265b24a726bc25", + ) + + mut algorithm2 := MD5{} + + algorithm2.init() + algorithm2.update(byteArray.fromStr(LOREM_IPSUM + "\n")) + algorithm2.update(byteArray.fromStr(LOREM_IPSUM + "\n")) + algorithm2.update(byteArray.fromStr(LOREM_IPSUM + "\n")) + algorithm2.update(byteArray.fromStr("test")) + + EXPECT_EQ( + byteArray.toHexStr(algorithm2.value()), + "d7fd595ebabcfdf5bb12b5f47f559491", + ) +} From bf9c8d7ca973cbc1416c34041fef6565dbb3dab3 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sun, 5 Jan 2025 11:30:14 +0200 Subject: [PATCH 24/48] codegen: Support for method/function type initializer --- packages/codegen/src/codegen | 87 ++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index dbb44993a..f8fec240d 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -28,6 +28,7 @@ from "./helpers" import sortStrAsc from "./utils" // TODO: implement FunctionDeclaration statement +// TODO: implement ObjectDeclarationMethod statement // TODO: implement Return statement // TODO: implement closure expression // TODO: test Function type @@ -678,13 +679,20 @@ export obj Codegen { } elif it.name == "str" { return AST.createIdentifier(self._("d4_str_empty_val")) } elif it.isArray() || it.isMap() { - typeName := self._typeName(it) return AST.createCallExpression(AST.createIdentifier(self._type(it, "_alloc")), [ AST.createLiteral("0"), ]) + } elif it.isFunction() || it.isMethod() { + // TODO: test + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_alloc")), [ + AST.createIdentifier(self._("d4_str_empty_val")), + AST.createIdentifier(self._("NULL")), + AST.createIdentifier(self._("NULL")), + AST.createIdentifier(self._("NULL")), + AST.createIdentifier(self._("NULL")), + ]) } elif it.isCustomObject() { // TODO: test - typeName := self._typeName(it) mut args: (AST.CExpression | AST.CType)[] loop i := 0; i < it.properties.len; i++ { property := it.properties[i] @@ -697,7 +705,6 @@ export obj Codegen { } elif it.isOptional() || it.isReference() { return AST.createIdentifier(self._("NULL")) } elif it.isUnion() { - typeName := self._typeName(it) return AST.createCallExpression(AST.createIdentifier(self._type(it, "_alloc")), [ AST.createLiteral("-1"), ]) @@ -721,12 +728,13 @@ export obj Codegen { it.isFunction() || it.isMap() || // TODO: test + it.isMethod() || + // TODO: test it.isCustomObject() || it.isOptional() || it.isUnion() ) { - typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_copy")), [expression]) + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_copy")), [expression]) } return expression @@ -744,14 +752,17 @@ export obj Codegen { result = AST.createCallExpression(AST.createIdentifier(self._("d4_str_eq")), [left, right]) } elif ( it.isArray() || + // TODO: test + it.isFunction() || it.isMap() || // TODO: test + it.isMethod() || + // TODO: test it.isCustomObject() || it.isOptional() || it.isUnion() ) { - typeName := self._typeName(it) - result = AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_eq")), [left, right]) + result = AST.createCallExpression(AST.createIdentifier(self._type(it, "_eq")), [left, right]) } else { return result } @@ -772,12 +783,13 @@ export obj Codegen { it.isFunction() || it.isMap() || // TODO: test + it.isMethod() || + // TODO: test it.isCustomObject() || it.isOptional() || it.isUnion() ) { - typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_free")), [expression]) + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_free")), [expression]) } return AST.createCastExpression(AST.createType("void"), expression) @@ -804,16 +816,16 @@ export obj Codegen { it.isFunction() || it.isMap() || // TODO: test + it.isMethod() || + // TODO: test it.isCustomObject() || it.isOptional() || it.isUnion() ) { - typeName := self._typeName(it) - return AST.createAssignmentExpression( left, "=", - AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_realloc")), [left, right]), + AST.createCallExpression(AST.createIdentifier(self._type(it, "_realloc")), [left, right]), ) } @@ -835,11 +847,33 @@ export obj Codegen { } elif it.isReference() { t := it.asReference() return self._functionStr(t.t, AST.createUnaryExpression("*", expression), quote: quote) + } elif ( + it.name == "bool" || + it.name == "byte" || + it.name == "char" || + it.name == "rune" || + it.isNumber() + ) { + typeName := self._typeName(it) + return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_str")), [expression]) + } elif ( + it.isArray() || + // TODO: test + it.isEnum() || + // TODO: test + it.isFunction() || + it.isMap() || + // TODO: test + it.isMethod() || + // TODO: test + it.isCustomObject() || + it.isOptional() || + it.isUnion() + ) { + return AST.createCallExpression(AST.createIdentifier(self._type(it, "_str")), [expression]) } - // TODO: test custom object - typeName := self._typeName(it) - return AST.createCallExpression(AST.createIdentifier(self._("d4_" + typeName + "_str")), [expression]) + throw error_NewError("Tried stringifying unknown type '" + it.toString() + "'") } fn _generateBlock (mut self: ref Self, items: ref Parser.Statement[]) AST.CStatement[] { @@ -1925,7 +1959,6 @@ export obj Codegen { } context := item.context as Analyzer.VariableDeclarationContext - typeName := self._typeName(context.varType) t := AST.createType(self._type(context.varType)) shouldSplitDeclaration := statementHasPrecedingNonVarDecl(item) @@ -2684,6 +2717,8 @@ export obj Codegen { it.isFunction() || it.isMap() || // TODO: test + it.isMethod() || + // TODO: test it.isCustomObject() || it.isOptional() || it.isReference() || @@ -2698,6 +2733,10 @@ export obj Codegen { self._generateFunctionType(it) } elif it.isMap() { self._generateMapType(it) + } elif it.isMethod() { + // TODO: test + tm := self._tm() + self._generateFunctionType(tm.convertMethod(it)) } elif it.isOptional() { self._generateOptionalType(it) } elif it.isReference() { @@ -2824,19 +2863,9 @@ export obj Codegen { t := it.asMap() return "map_" + self._typeName(t.keyType) + "MS" + self._typeName(t.valueType) + "ME" } elif it.isMethod() { - t := it.asMethod() - mut name := "fn_" + (t.asynchronous ? "a" : "s") - - if t.withSelf { - name += "FS" + self._typeName(t.selfType) - } - - loop i := 0; i < t.parameters.len; i++ { - parameter := t.parameters[i] - name += typeParameterId(parameter) + self._typeName(parameter.t) - } - - return name + "FR" + self._typeName(t.returnType) + "FE" + // TODO: test + tm := self._tm() + return self._typeName(tm.convertMethod(it)) } elif it.isOptional() { t := it.asOptional() return "opt_" + self._typeName(t.t) From bd06ee76408a67328701e054f169250a1df37926 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sun, 5 Jan 2025 13:45:47 +0200 Subject: [PATCH 25/48] codegen: Fix generation of element access expression --- packages/codegen/src/codegen | 9 +++++++-- .../codegen/test/codegen/object-declaration-mutable.txt | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index f8fec240d..9e788eb63 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -1349,9 +1349,14 @@ export obj Codegen { } expression := item.asElementAccess() - targetType := Analyzer.contextTarget(item) - return AST.createCallExpression(AST.createIdentifier(self._type(targetType, "_at")), [ + if !(item.contextExtra is Analyzer.ElementAccessExpressionContextExtra) { + throw error_NewError("Expected ElementAccessExpression context extra") + } + + contextExtra := item.contextExtra as Analyzer.ElementAccessExpressionContextExtra + + return AST.createCallExpression(AST.createIdentifier(self._type(contextExtra.selfType, "_at")), [ self._generateExpression(ref expression.expression), self._generateExpression(ref expression.argument), ]) diff --git a/packages/codegen/test/codegen/object-declaration-mutable.txt b/packages/codegen/test/codegen/object-declaration-mutable.txt index c95624c46..1261cf57f 100644 --- a/packages/codegen/test/codegen/object-declaration-mutable.txt +++ b/packages/codegen/test/codegen/object-declaration-mutable.txt @@ -20,6 +20,10 @@ main { person3 := person1 person1 = person2 person2 = person3 + + print(person1) + print(person2) + print(person3) } ===== code ===== ===== output ===== From f0acda011aca1f14f40eab3d6c20ddc7d58c8171 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 07:32:16 +0200 Subject: [PATCH 26/48] Support for native new line in c++ compiler --- src/codegen/expr-lit.cpp | 4 +- test/CodegenTest.cpp | 3 +- test/codegen-test/expr-lit-str-nl.txt | 95 +++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 test/codegen-test/expr-lit-str-nl.txt diff --git a/src/codegen/expr-lit.cpp b/src/codegen/expr-lit.cpp index 0b569464d..17a9e1afc 100644 --- a/src/codegen/expr-lit.cpp +++ b/src/codegen/expr-lit.cpp @@ -37,7 +37,9 @@ std::shared_ptr Codegen::_exprLit (const ASTNodeExpr &nodeExpr, } else if (exprLit.type == AST_EXPR_LIT_NIL) { expr = CodegenASTExprAccess::create(this->_("NULL")); } else if (!root && exprLit.type == AST_EXPR_LIT_STR) { - expr = CodegenASTExprCall::create(CodegenASTExprAccess::create(this->_("str_alloc")), {expr}); + expr = CodegenASTExprCall::create(CodegenASTExprAccess::create(this->_("str_alloc")), { + CodegenASTExprLiteral::create(Token::escape(exprLit.body)) + }); } return this->_wrapNodeExpr(nodeExpr, targetType, root, expr); diff --git a/test/CodegenTest.cpp b/test/CodegenTest.cpp index a7c063a80..45543e98e 100644 --- a/test/CodegenTest.cpp +++ b/test/CodegenTest.cpp @@ -1155,7 +1155,8 @@ INSTANTIATE_TEST_SUITE_P(ExprLit, CodegenPassTest, testing::Values( "expr-lit-int-oct", "expr-lit-nil", "expr-lit-str", - "expr-lit-str-esc" + "expr-lit-str-esc", + "expr-lit-str-nl" )); INSTANTIATE_TEST_SUITE_P(ExprMap, CodegenPassTest, testing::Values( diff --git a/test/codegen-test/expr-lit-str-nl.txt b/test/codegen-test/expr-lit-str-nl.txt new file mode 100644 index 000000000..de9b4684d --- /dev/null +++ b/test/codegen-test/expr-lit-str-nl.txt @@ -0,0 +1,95 @@ +======= stdin ======= +main { + a := " +first line +second line +" +} +======= code ======= +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) + #define THE_OS_WINDOWS + #define THE_EOL "\r\n" + #define THE_PATH_SEP "\\" +#else + #if defined(__APPLE__) + #define THE_OS_MACOS + #elif defined(__linux__) + #define THE_OS_LINUX + #endif + #define THE_EOL "\n" + #define THE_PATH_SEP "/" +#endif + +#include +#include +#include +#include + +typedef struct err_buf { + jmp_buf buf; + struct err_buf *next; + struct err_buf *prev; +} err_buf_t; +typedef struct err_stack { + const char *file; + const char *name; + int line; + int col; + struct err_stack *next; + struct err_stack *prev; +} err_stack_t; +typedef struct { + int id; + void *ctx; + err_buf_t *buf_first; + err_buf_t *buf_last; + err_stack_t *stack_first; + err_stack_t *stack_last; + void (*_free) (void *); +} err_state_t; +struct str { + char *d; + size_t l; +}; + +err_state_t err_state = {-1, NULL, NULL, NULL, NULL, NULL, NULL}; + +void *alloc (size_t); +void error_alloc (err_state_t *, size_t); +struct str str_alloc (const char *); +void str_free (struct str); + +void *alloc (size_t n1) { + void *r = malloc(n1); + if (r == NULL) error_alloc(&err_state, n1); + return r; +} +void error_alloc (err_state_t *fn_err_state, size_t n1) { + char d[4096]; + size_t l = 0; + for (err_stack_t *it = fn_err_state->stack_last; it != NULL; it = it->prev) { + const char *fmt = THE_EOL " at %s (%s)"; + size_t z = snprintf(NULL, 0, fmt, it->name, it->file); + if (l + z >= 4096) break; + sprintf(&d[l], fmt, it->name, it->file); + l += z; + } + fprintf(stderr, "Allocation Error: failed to allocate %zu bytes%s" THE_EOL, n1, d); + exit(EXIT_FAILURE); +} +struct str str_alloc (const char *r) { + size_t l = strlen(r); + char *d = alloc(l); + memcpy(d, r, l); + return (struct str) {d, l}; +} +void str_free (struct str s) { + free(s.d); +} + +int main () { + const struct str __THE_0_a_0 = str_alloc("\nfirst line\nsecond line\n"); + str_free((struct str) __THE_0_a_0); +} +======= flags ======= +======= stdout ======= From 4ec865afdfea78fe8b2df4e44830a0a3bf60b7c9 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 09:42:38 +0200 Subject: [PATCH 27/48] analyzer: Add dependsOn method --- packages/analyzer/README.md | 21 ++++++ packages/analyzer/src/type | 95 +++++++++++++++++++++++ packages/analyzer/test/type-test | 126 +++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+) diff --git a/packages/analyzer/README.md b/packages/analyzer/README.md index 68875069f..e5e6f1794 100644 --- a/packages/analyzer/README.md +++ b/packages/analyzer/README.md @@ -440,6 +440,27 @@ mut tm := TypeMap{} tm.get("u8").canPromoteTo(tm.get("int")) ``` +### `Type.dependsOn (on: ref Type, stack: (ref Type)[] = []) bool` +Checks whether current type depends on specified type (using strict matching). + +**Parameters** + +- `on` - type to check whether current type depends on +- `stack` - stack of already visited types + +**Return value** + +Whether current type depends on specified type. + +**Examples** + +```the +mut tm := TypeMap{} +arr := tm.createArray(tm.get("int")) + +arr.dependsOn(tm.get("int")) +``` + ### `Type.get (nameOrIndex: int | str) TypeProperty` Finds type's property with specified name or index. diff --git a/packages/analyzer/src/type b/packages/analyzer/src/type index 3cfb6128f..40bfd8eaa 100644 --- a/packages/analyzer/src/type +++ b/packages/analyzer/src/type @@ -137,6 +137,101 @@ export obj Type { (a.isNumber() && b.isNumber() && numberMatch(b.name, a.name)) } + fn dependsOn (self: ref Self, on: ref Type, stack: (ref Type)[] = []) bool { + a := unwrap(self, withReference: false) + b := unwrap(on, withReference: false) + + if stack.contains(a) { + return false + } + + stack.push(a) + + if a.isArray() { + t := a.asArray() + return t.elementType.dependsOn(b, stack) + } elif a.isFunction() { + t := a.asFunction() + + if t.returnType.dependsOn(b, stack) { + return true + } + + l := t.parameters.len + + loop i := 0; i < l; i++ { + parameter := t.parameters[i] + + if parameter.t.dependsOn(b, stack) { + return true + } + } + } elif a.isMap() { + t := a.asMap() + return t.keyType.dependsOn(b, stack) || t.valueType.dependsOn(b, stack) + } elif a.isMethod() { + t := a.asMethod() + + if t.returnType.dependsOn(b, stack) || (t.withSelf && t.selfType.dependsOn(b, stack)) { + return true + } + + l := t.parameters.len + + loop i := 0; i < l; i++ { + parameter := t.parameters[i] + + if parameter.t.dependsOn(b, stack) { + return true + } + } + } elif a.isNamespace() { + t := a.asNamespace() + l := t.members.len + + loop i := 0; i < l; i++ { + member := t.members[i] + + if member.t != nil { + memberType := member.t + + if memberType.dependsOn(b, stack) { + return true + } + } + } + } elif a.isCustomObject() { + l := a.properties.len + + loop i := 0; i < l; i++ { + property := a.properties[i] + + if property.t.dependsOn(b, stack) { + return true + } + } + } elif a.isOptional() { + t := a.asOptional() + return t.t.dependsOn(b, stack) + } elif a.isReference() { + t := a.asReference() + return t.t.dependsOn(b, stack) + } elif a.isUnion() { + t := a.asUnion() + l := t.types.len + + loop i := 0; i < l; i++ { + subtype := t.types[i] as ref Type + + if subtype.dependsOn(b, stack) { + return true + } + } + } + + return stack.len > 1 && match(a, b) + } + fn get (self: ref Self, nameOrIndex: int | str) TypeProperty { t := unwrap(self) propertiesLen := t.properties.len diff --git a/packages/analyzer/test/type-test b/packages/analyzer/test/type-test index b0230d582..d478bfa2e 100644 --- a/packages/analyzer/test/type-test +++ b/packages/analyzer/test/type-test @@ -326,6 +326,132 @@ export fn TEST_Type_canPromoteTo () { EXPECT_FALSE(t24.canPromoteTo(t26)) } +export fn TEST_Type_dependsOn () { + mut tm := TypeMap{} + tm.init() + + t1 := tm.get("any") + t2 := tm.get("bool") + t3 := tm.get("byte") + t4 := tm.get("char") + t5 := tm.get("float") + t6 := tm.get("f32") + t7 := tm.get("f64") + t8 := tm.get("int") + t9 := tm.get("i8") + t10 := tm.get("i16") + t11 := tm.get("i32") + t12 := tm.get("i64") + t13 := tm.get("isize") + t14 := tm.get("never") + t15 := tm.get("rune") + t16 := tm.get("u8") + t17 := tm.get("u16") + t18 := tm.get("u32") + t19 := tm.get("u64") + t20 := tm.get("usize") + t21 := tm.get("str") + t22 := tm.get("void") + + t23 := tm.createObject("TestObject", [ + Type.TypeProperty{name: "a", t: t3}, + ]) + t24 := tm.createAlias("TestAlias", t1) + t25 := tm.createAlias("TestAlias2", t23) + t26 := tm.createArray(t23) + t27 := tm.createEnum("TestEnum", []) + t28 := tm.createFunction(false, [], t15) + t29 := tm.createFunction(false, [ + Type.TypeParameter{t: t23}, + ], t15) + t30 := tm.createMap(t15, t23) + t31 := tm.createNamespace("TestNamespace", [ + Type.NamespaceMember{name: "a"}, + Type.NamespaceMember{name: "b", t: t23}, + ]) + t32 := tm.createMethod(false, [], t15, false, false, "", t23) + t33 := tm.createMethod(false, [ + Type.TypeParameter{t: t20}, + ], t15, true, false, "self", t23) + t34 := tm.createOptional(t23) + t35 := tm.createReference(t23) + t36 := tm.createReference(t23) + t37 := tm.createUnion([t3, t15]) + + t38 := tm.createObject("TestObject2") + t39 := tm.createObject("TestObject3") + + t38.properties.push(Type.TypeProperty{name: "a", t: t39}) + t38.properties.push(Type.TypeProperty{name: "b", t: t12}) + t39.properties.push(Type.TypeProperty{name: "a", t: t38}) + t39.properties.push(Type.TypeProperty{name: "b", t: t19}) + + EXPECT_FALSE(t1.dependsOn(t1)) + EXPECT_FALSE(t2.dependsOn(t2)) + EXPECT_FALSE(t3.dependsOn(t3)) + EXPECT_FALSE(t4.dependsOn(t4)) + EXPECT_FALSE(t5.dependsOn(t5)) + EXPECT_FALSE(t6.dependsOn(t6)) + EXPECT_FALSE(t7.dependsOn(t7)) + EXPECT_FALSE(t8.dependsOn(t8)) + EXPECT_FALSE(t9.dependsOn(t9)) + EXPECT_FALSE(t10.dependsOn(t10)) + EXPECT_FALSE(t11.dependsOn(t11)) + EXPECT_FALSE(t12.dependsOn(t12)) + EXPECT_FALSE(t13.dependsOn(t13)) + EXPECT_FALSE(t14.dependsOn(t14)) + EXPECT_FALSE(t15.dependsOn(t15)) + EXPECT_FALSE(t16.dependsOn(t16)) + EXPECT_FALSE(t17.dependsOn(t17)) + EXPECT_FALSE(t18.dependsOn(t18)) + EXPECT_FALSE(t19.dependsOn(t19)) + EXPECT_FALSE(t20.dependsOn(t20)) + EXPECT_FALSE(t21.dependsOn(t21)) + EXPECT_FALSE(t22.dependsOn(t22)) + + EXPECT_TRUE(t23.dependsOn(t3)) + EXPECT_FALSE(t23.dependsOn(t15)) + EXPECT_FALSE(t24.dependsOn(t1)) + EXPECT_TRUE(t25.dependsOn(t3)) + EXPECT_FALSE(t25.dependsOn(t9)) + EXPECT_TRUE(t26.dependsOn(t23)) + EXPECT_FALSE(t26.dependsOn(t15)) + EXPECT_FALSE(t27.dependsOn(t15)) + EXPECT_TRUE(t28.dependsOn(t15)) + EXPECT_FALSE(t28.dependsOn(t6)) + EXPECT_TRUE(t29.dependsOn(t15)) + EXPECT_TRUE(t29.dependsOn(t23)) + EXPECT_FALSE(t29.dependsOn(t6)) + EXPECT_TRUE(t30.dependsOn(t15)) + EXPECT_TRUE(t30.dependsOn(t23)) + EXPECT_FALSE(t30.dependsOn(t6)) + EXPECT_TRUE(t31.dependsOn(t23)) + EXPECT_FALSE(t31.dependsOn(t6)) + EXPECT_TRUE(t32.dependsOn(t15)) + EXPECT_FALSE(t32.dependsOn(t23)) + EXPECT_FALSE(t32.dependsOn(t6)) + EXPECT_TRUE(t33.dependsOn(t20)) + EXPECT_TRUE(t33.dependsOn(t15)) + EXPECT_TRUE(t33.dependsOn(t23)) + EXPECT_FALSE(t33.dependsOn(t6)) + EXPECT_TRUE(t34.dependsOn(t23)) + EXPECT_FALSE(t34.dependsOn(t6)) + EXPECT_TRUE(t35.dependsOn(t23)) + EXPECT_FALSE(t35.dependsOn(t6)) + EXPECT_TRUE(t36.dependsOn(t23)) + EXPECT_FALSE(t36.dependsOn(t6)) + EXPECT_TRUE(t37.dependsOn(t3)) + EXPECT_TRUE(t37.dependsOn(t15)) + EXPECT_FALSE(t37.dependsOn(t6)) + + EXPECT_TRUE(t38.dependsOn(t39)) + EXPECT_TRUE(t38.dependsOn(t12)) + EXPECT_FALSE(t38.dependsOn(t1)) + EXPECT_TRUE(t39.dependsOn(t38)) + EXPECT_TRUE(t39.dependsOn(t19)) + EXPECT_FALSE(t39.dependsOn(t1)) +} + export fn TEST_Type_get () { mut tm := TypeMap{} tm.init() From 1327dc4640e943734b57fa3b30f86c4061924ebf Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 09:48:31 +0200 Subject: [PATCH 28/48] codegen: Combine pre/post declaration steps --- packages/codegen/src/codegen | 472 +++++++++++++++++------------------ 1 file changed, 228 insertions(+), 244 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 9e788eb63..d1fcb6d20 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -368,9 +368,6 @@ export obj Codegen { } fn _preDeclareStatement (mut self: ref Self, item: ref Parser.Statement) bool { - if item.isObjectDeclaration() { - self._preDeclareObject(item) - } } fn _declareStatement (mut self: ref Self, item: ref Parser.Statement) bool { @@ -394,53 +391,7 @@ export obj Codegen { self.blockData.currentMerge(dataContextBlock.setup, dataContextBlock.teardown) } - fn _preDeclareObject (mut self: ref Codegen, item: ref Parser.Statement) void { - statement := item.asObjectDeclaration() - - if !(item.context is Analyzer.ObjectDeclarationContext) { - throw error_NewError("Expected ObjectDeclaration context") - } - - context := item.context as Analyzer.ObjectDeclarationContext - selfType := context.selfType - - typeName := "d4_" + self._typeName(selfType) - name := typeName + "_t" - - if self._hasEntity(name) { - throw error_NewError("Object entity with name '" + name + "' already exists") - } - - self.entities.push(CodegenEntity{ - name: name, - codeName: name, - context: selfType, - generate: (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { - return AST.createEmptyStatement() - } - }) - - methods := ["alloc", "copy", "eq", "free", "realloc", "str"] - - entityCallee := (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { - self._(entity.context as str) - return AST.createEmptyStatement() - } - - loop i := 0; i < methods.len; i++ { - methodName := methods[i] as str - - self.entities.push(CodegenEntity{ - name: typeName + "_" + methodName, - codeName: typeName + "_" + methodName, - context: name, - generate: entityCallee, - }) - } - } - - // TODO: rename params - fn _declareObject (mut self2: ref Codegen, item: ref Parser.Statement) void { + fn _declareObject (mut self: ref Codegen, item: ref Parser.Statement) void { statement := item.asObjectDeclaration() if !(item.context is Analyzer.ObjectDeclarationContext) { @@ -448,197 +399,7 @@ export obj Codegen { } context := item.context as Analyzer.ObjectDeclarationContext - name := self2._generateObjectType(context.selfType) - mut entity := self2._getEntity(name) - - entity.generate = (mut self: ref Codegen, entity2: ref CodegenEntity) -> GenerateReturnType { - it := entity2.context as ref Analyzer.Type - - mut extraDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] - mut structureBlock: AST.CStatement[] - mut allocBlock: AST.CStatement[] - mut copyBlock: AST.CStatement[] - mut eqExpression := AST.createIdentifier(self._("true")) - mut freeBlock: AST.CStatement[] - mut strBlock: AST.CStatement[] - mut propertyIdx := 0 - - loop i := 0; i < it.properties.len; i++ { - property := it.properties[i] - - if property.builtin || property.t.isMethod() { - continue - } - - cPropertyType := self._type(property.t) - cSelfProperty := AST.createPropertyAccessExpression(AST.createIdentifier("self"), property.name) - - extraDeclarationArguments.push( - AST.createVariableDeclaration( - AST.createType(cPropertyType, constant: true), - property.name, - terminated: false, - ) - ) - - structureBlock.push( - AST.createVariableDeclaration(AST.createType(cPropertyType), property.name) - ) - - allocBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - cSelfProperty, - "=", - self._functionCopy(property.t, AST.createIdentifier(property.name)), - ), - ), - ) - - copyBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createPropertyAccessExpression(AST.createIdentifier("result"), property.name), - "=", - self._functionCopy(property.t, cSelfProperty), - ), - ), - ) - - eqExpressionPiece := self._functionEq( - property.t, - cSelfProperty, - AST.createPropertyAccessExpression(AST.createIdentifier("rhs"), property.name), - ) - - eqExpression = propertyIdx == 0 - ? eqExpressionPiece - : AST.createBinaryExpression(eqExpression, "&&", eqExpressionPiece) - - if typeShouldBeFreed(property.t) { - freeBlock.push( - AST.createExpressionStatement( - self._functionFree(property.t, cSelfProperty), - ), - ) - } - - strBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createIdentifier("result"), - "=", - AST.createCallExpression(AST.createIdentifier(self._("d4_obj_str_append")), [ - AST.createIdentifier("result"), - AST.createCallExpression(AST.createIdentifier(self._("d4_str_alloc")), [ - AST.createLiteral("L\"" + property.name + "\""), - ]), - self._functionStr(property.t, cSelfProperty, quote: true), - ]), - ), - ), - ) - - propertyIdx++ - } - - if freeBlock.empty { - freeBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), - ), - ) - } - - mut eqBlock: AST.CStatement[] - - if propertyIdx == 0 { - extraDeclarationArguments.push(AST.createType("void")) - - structureBlock.push( - AST.createVariableDeclaration(AST.createType(self._("bool")), "_") - ) - - allocBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), - "=", - AST.createIdentifier(self._("false")), - ), - ), - ) - - copyBlock.push( - AST.createExpressionStatement( - AST.createAssignmentExpression( - AST.createPropertyAccessExpression(AST.createIdentifier("result"), "_"), - "=", - AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), - ), - ), - ) - - eqBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), - ), - ) - - eqBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("rhs")), - ), - ) - - strBlock.push( - AST.createExpressionStatement( - AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), - ), - ) - } - - eqBlock.push(AST.createReturnStatement(eqExpression)) - - cNameIdentifier := AST.createIdentifier(it.name) - - forwardDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ - cNameIdentifier, - ] - - mut declarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ - cNameIdentifier, - AST.createCompoundStatement(structureBlock), - ] - - mut definitionArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ - cNameIdentifier, - cNameIdentifier, - AST.createCompoundStatement(allocBlock), - AST.createCompoundStatement(copyBlock), - AST.createCompoundStatement(eqBlock), - AST.createCompoundStatement(freeBlock), - AST.createCompoundStatement(strBlock), - ] - - declarationArguments.merge(extraDeclarationArguments) - definitionArguments.merge(extraDeclarationArguments) - - return [ - AST.createExpressionStatement( - AST.createMacroInvocation(self._("D4_OBJECT_FORWARD_DECLARE"), forwardDeclarationArguments), - terminated: false, - ), - AST.createExpressionStatement( - AST.createMacroInvocation(self._("D4_OBJECT_DECLARE"), declarationArguments), - terminated: false, - ), - AST.createExpressionStatement( - AST.createMacroInvocation(self._("D4_OBJECT_DEFINE"), definitionArguments), - terminated: false, - ), - ] - } + self._generateObjectType(context.selfType) } fn _defaultInitializerExpression (mut self: ref Self, it: ref Analyzer.Type) AST.CExpression { @@ -2337,10 +2098,233 @@ export obj Codegen { } fn _generateObjectType (mut self: ref Codegen, item: ref Analyzer.Type) str { - name := "d4_" + self._typeName(item) + "_t" + typeName := "d4_" + self._typeName(item) + name := typeName + "_t" + + if self._hasEntity(name) { + return name + } + + loop i := 0; i < item.properties.len; i++ { + property := item.properties[i] + + if property.builtin || property.t.isMethod() { + continue + } + + self._typeGen(property.t) + } + + self.entities.push(CodegenEntity{ + name: name, + codeName: name, + context: item, + generate: (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + it := entity.context as ref Analyzer.Type + + mut extraDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] + mut structureBlock: AST.CStatement[] + mut allocBlock: AST.CStatement[] + mut copyBlock: AST.CStatement[] + mut eqExpression := AST.createIdentifier(self._("true")) + mut freeBlock: AST.CStatement[] + mut strBlock: AST.CStatement[] + mut propertyIdx := 0 + + loop i := 0; i < it.properties.len; i++ { + property := it.properties[i] + + if property.builtin || property.t.isMethod() { + continue + } + + cPropertyType := self._type(property.t) + cSelfProperty := AST.createPropertyAccessExpression(AST.createIdentifier("self"), property.name) + + extraDeclarationArguments.push( + AST.createVariableDeclaration( + AST.createType(cPropertyType, constant: true), + property.name, + terminated: false, + ) + ) + + structureBlock.push( + AST.createVariableDeclaration(AST.createType(cPropertyType), property.name) + ) + + allocBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + cSelfProperty, + "=", + self._functionCopy(property.t, AST.createIdentifier(property.name)), + ), + ), + ) + + copyBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("result"), property.name), + "=", + self._functionCopy(property.t, cSelfProperty), + ), + ), + ) + + eqExpressionPiece := self._functionEq( + property.t, + cSelfProperty, + AST.createPropertyAccessExpression(AST.createIdentifier("rhs"), property.name), + ) + + eqExpression = propertyIdx == 0 + ? eqExpressionPiece + : AST.createBinaryExpression(eqExpression, "&&", eqExpressionPiece) + + if typeShouldBeFreed(property.t) { + freeBlock.push( + AST.createExpressionStatement( + self._functionFree(property.t, cSelfProperty), + ), + ) + } + + strBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createIdentifier("result"), + "=", + AST.createCallExpression(AST.createIdentifier(self._("d4_obj_str_append")), [ + AST.createIdentifier("result"), + AST.createCallExpression(AST.createIdentifier(self._("d4_str_alloc")), [ + AST.createLiteral("L\"" + property.name + "\""), + ]), + self._functionStr(property.t, cSelfProperty, quote: true), + ]), + ), + ), + ) + + propertyIdx++ + } + + if freeBlock.empty { + freeBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + } + + mut eqBlock: AST.CStatement[] + + if propertyIdx == 0 { + extraDeclarationArguments.push(AST.createType("void")) + + structureBlock.push( + AST.createVariableDeclaration(AST.createType(self._("bool")), "_") + ) + + allocBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), + "=", + AST.createIdentifier(self._("false")), + ), + ), + ) + + copyBlock.push( + AST.createExpressionStatement( + AST.createAssignmentExpression( + AST.createPropertyAccessExpression(AST.createIdentifier("result"), "_"), + "=", + AST.createPropertyAccessExpression(AST.createIdentifier("self"), "_"), + ), + ), + ) + + eqBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + + eqBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("rhs")), + ), + ) + + strBlock.push( + AST.createExpressionStatement( + AST.createCastExpression(AST.createType("void"), AST.createIdentifier("self")), + ), + ) + } + + eqBlock.push(AST.createReturnStatement(eqExpression)) + + cNameIdentifier := AST.createIdentifier(it.name) + + forwardDeclarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + cNameIdentifier, + ] - if !self._hasEntity(name) { - throw error_NewError("Object entity with name '" + name + "' doesn't exists") + mut declarationArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + cNameIdentifier, + AST.createCompoundStatement(structureBlock), + ] + + mut definitionArguments: (AST.CExpression | AST.CStatement | AST.CType)[] = [ + cNameIdentifier, + cNameIdentifier, + AST.createCompoundStatement(allocBlock), + AST.createCompoundStatement(copyBlock), + AST.createCompoundStatement(eqBlock), + AST.createCompoundStatement(freeBlock), + AST.createCompoundStatement(strBlock), + ] + + declarationArguments.merge(extraDeclarationArguments) + definitionArguments.merge(extraDeclarationArguments) + + return [ + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_FORWARD_DECLARE"), forwardDeclarationArguments), + terminated: false, + ), + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_DECLARE"), declarationArguments), + terminated: false, + ), + AST.createExpressionStatement( + AST.createMacroInvocation(self._("D4_OBJECT_DEFINE"), definitionArguments), + terminated: false, + ), + ] + } + }) + + methods := ["alloc", "copy", "eq", "free", "realloc", "str"] + + entityCallee := (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + self._(entity.context as str) + return AST.createEmptyStatement() + } + + loop i := 0; i < methods.len; i++ { + methodName := methods[i] as str + + self.entities.push(CodegenEntity{ + name: typeName + "_" + methodName, + codeName: typeName + "_" + methodName, + context: name, + generate: entityCallee, + }) } return name From 4e864a33b9def863a7b3d5ab2dc9ce43dd041d56 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 09:49:03 +0200 Subject: [PATCH 29/48] codegen: Sort declarations before generating block --- packages/codegen/src/codegen | 92 +++++++++++++++++++++--------------- packages/codegen/src/helpers | 38 +++++++++++++++ 2 files changed, 92 insertions(+), 38 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index d1fcb6d20..f7ec650f8 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -9,6 +9,7 @@ import * as Parser from "the/parser" import BUILTIN_ENTITIES from "./builtin-entities" import BlockData, BlockDataContext from "./block-data" import + declarationByDependency, expressionIsBuiltinMethod, expressionResolve, expressionShouldBeAllocated, @@ -345,9 +346,9 @@ export obj Codegen { if self.builtin.wchar { self.statements.push(AST.createIncludeDirective("wchar.h")) } self.statements.merge(defineStatements) - self.statements.merge(macroInvocationForwardDeclareStatements.reverse()) - self.statements.merge(macroInvocationDeclareStatements.reverse()) - self.statements.merge(macroInvocationDefineStatements.reverse()) + self.statements.merge(macroInvocationForwardDeclareStatements) + self.statements.merge(macroInvocationDeclareStatements) + self.statements.merge(macroInvocationDefineStatements) self.statements.merge(statements) dataContextBlock := self.blockData.decrease() @@ -652,29 +653,22 @@ export obj Codegen { fn _generateBlockBody (mut self: ref Self, items: ref Parser.Statement[]) AST.CStatement[] { mut result: AST.CStatement[] l := items.len + mut declarations: (ref Parser.Statement)[] loop i := 0; i < l; i++ { item := items[i] if item.isDeclaration() { - self._preDeclareStatement(item) + declarations.push(item) } } - loop i := 0; i < l; i++ { - item := items[i] - - if item.isDeclaration() { - self._declareStatement(item) - } - } - - loop i := 0; i < l; i++ { - item := items[i] + declarations.sort(declarationByDependency) + dl := declarations.len - if item.isDeclaration() { - self._postDeclareStatement(item) - } + loop i := 0; i < dl; i++ { + declaration := declarations[i] as ref Parser.Statement + self._declareStatement(declaration) } loop i := 0; i < l; i++ { @@ -1763,6 +1757,8 @@ export obj Codegen { return name } + self._typeGen(item) + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -1820,6 +1816,9 @@ export obj Codegen { return name } + itemType := item.asArray() + self._typeGen(itemType.elementType) + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -1902,8 +1901,13 @@ export obj Codegen { itemType := item.asFunction() hasParams := !itemType.parameters.empty + self._typeGen(itemType.returnType) if hasParams { + loop i := 0; i < itemType.parameters.len; i++ { + self._typeGen(itemType.parameters[i].t) + } + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -2338,6 +2342,9 @@ export obj Codegen { return name } + itemType := item.asOptional() + self._typeGen(itemType.t) + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -2394,6 +2401,9 @@ export obj Codegen { return name } + itemType := item.asReference() + self._typeGen(itemType.t) + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -2713,33 +2723,39 @@ export obj Codegen { it.isReference() || it.isUnion() ) { - if it.isArray() { - self._generateArrayType(it) - } elif it.isCustomObject() { - // TODO: test - self._generateObjectType(it) - } elif it.isFunction() { - self._generateFunctionType(it) - } elif it.isMap() { - self._generateMapType(it) - } elif it.isMethod() { - // TODO: test - tm := self._tm() - self._generateFunctionType(tm.convertMethod(it)) - } elif it.isOptional() { - self._generateOptionalType(it) - } elif it.isReference() { - self._generateReferenceType(it) - } elif it.isUnion() { - self._generateUnionType(it) - } - + self._typeGen(it) return self._("d4_" + self._typeName(it) + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } throw error_NewError("Failed to generate type for '" + it.toString() + "'") } + fn _typeGen (mut self: ref Self, it: ref Analyzer.Type) { + if it.isAlias() { + t := it.asAlias() + self._typeGen(t.t) + } elif it.isArray() { + self._generateArrayType(it) + } elif it.isCustomObject() { + // TODO: test + self._generateObjectType(it) + } elif it.isFunction() { + self._generateFunctionType(it) + } elif it.isMap() { + self._generateMapType(it) + } elif it.isMethod() { + // TODO: test + tm := self._tm() + self._generateFunctionType(tm.convertMethod(it)) + } elif it.isOptional() { + self._generateOptionalType(it) + } elif it.isReference() { + self._generateReferenceType(it) + } elif it.isUnion() { + self._generateUnionType(it) + } + } + fn _wrap (mut self: ref Self, mut expression: AST.CExpression, item: ref Parser.Expression, t: ref Analyzer.Type, targetType: ref Analyzer.Type, transform := false) AST.CExpression { shouldFree := typeShouldBeFreed(targetType) && expressionShouldBeFreed(item) diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index e792d86a5..da54ba5ae 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -7,6 +7,44 @@ import * as Analyzer from "the/analyzer" import * as AST from "the/c" import * as Parser from "the/parser" +export fn declarationType (item: ref Parser.Statement) (ref Analyzer.Type)? { + itemContext := item.context + + if item.context is Analyzer.ObjectDeclarationContext { + context := itemContext as Analyzer.ObjectDeclarationContext + return context.selfType + } + + return nil +} + +export fn declarationByDependency (a: ref Parser.Statement, b: ref Parser.Statement) int { + aType := declarationType(a) + bType := declarationType(b) + + if aType == nil && bType == nil { + return 0 + } + + if aType == nil || bType == nil { + return aType == nil ? 1 : -1 + } + + aDefiniteType := aType as ref Analyzer.Type + bDefiniteType := bType as ref Analyzer.Type + + aDependsOnB := aDefiniteType.dependsOn(bDefiniteType) + bDependsOnA := bDefiniteType.dependsOn(aDefiniteType) + + if aDependsOnB && !bDependsOnA { + return 1 + } elif bDependsOnA && !aDependsOnB { + return -1 + } + + return 0 +} + export fn expressionIsBuiltinMethod (item: ref Parser.Expression) bool { if item.isPropertyAccess() { body := item.asPropertyAccess() From 6500e513bd86188033b6fbba66adc760fe2fd11f Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 12:41:47 +0200 Subject: [PATCH 30/48] parser: Add missing documentation --- packages/parser/README.md | 214 ++++++++++++++++++++++++++++++++++++- packages/parser/src/reader | 6 +- 2 files changed, 215 insertions(+), 5 deletions(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 4fa13a910..58a65f278 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -91,6 +91,50 @@ Program_traverse(ref f.program, (it: Statement) -> void { }) ``` +### `Reader_init (pathOrCode: str) Reader` +Initializes reader instance with either path or code provided. + +**Parameters** + +- `pathOrCode` - path or code to initialize reader with. + +**Return value** + +A new reader instance. + +**Examples** + +```the +reader := Reader_init("/path/to/file") +reader := Reader_init("const PI := 3.14159") +``` + +**Exceptions** + +- `ParserError` - thrown if path doesn't exists +- `ParserError` - thrown if path is not a file + +### `Reader_error (reader: ref Reader, errorType: str, message: str, start: int, end: int) str` +Generates an error of specified type with specified message at specified position range. + +**Parameters** + +- `reader` - reader instance to retrieve lines information from. +- `errorType` - error type that goes before message. +- `message` - error message that is present on the first line. +- `start` - beginning of error location range. +- `end` - ending of error location range. + +**Return value** + +Generated error string. + +**Examples** + +```the +error := Reader_error(ref reader, "TypeError", "Missing type", 1, 10) +``` + ### `Expression_toText (self: Expression, indent := 0) str` Generates string representation of the expression. @@ -186,6 +230,109 @@ Type_toText(type) Type_toText(type, indent: 2) ``` +### `Reader.eof () bool` +Checks whether reader reached end of file. + +**Return value** + +Whether reader reached end of file. + +**Examples** + +```the +reader.eof() +``` + +### `Reader.lookahead (check: char) bool` +Increases position if the next character in the stream matches specified character. + +**Parameters** + +- `check` - character to check for. + +**Return value** + +Whether next character in the stream matches specified character. + +**Examples** + +```the +reader.lookahead('a') +``` + +### `Reader.next () char` +Increase position and returns next character from the stream. + +**Return value** + +Next character from the stream. + +**Examples** + +```the +ch := reader.next() +``` + +**Exceptions** + +- `Error` - thrown when tried reading on end of file + +### `Reader.seek (pos: int) void` +Change reader position. + +**Parameters** + +- `pos` - new reader position. + +**Return value** + +none + +**Examples** + +```the +reader.seek(100) +``` + +### `Reader.slice (start: int, end: int) str` +Create a slice of the reader content and return it. + +**Parameters** + +- `start` - slice starting position. +- `end` - slice ending position. + +**Return value** + +Slice of the reader content. + +**Examples** + +```the +slice := reader.slice(1, 5) +``` + +### `Reader.walk (matcher: (char) -> bool) void` +Iterate over reader content with matcher function. + +**Parameters** + +- `matcher` - matcher function to iterate with. + +**Return value** + +none + +**Examples** + +```the +fn whitespaceWalker (ch: char) bool { + return ch.isWhitespace +} + +slice := reader.walk(whitespaceWalker) +``` + ### `Expression.asIdentifier () ref Identifier` ### `Expression.asBooleanLiteral () ref BooleanLiteral` ### `Expression.asCharacterLiteral () ref CharacterLiteral` @@ -640,12 +787,12 @@ Checks whether type's body contains corresponding type. type.isArray() ``` -### `expressionToType (expression: Expression) Type` +### `expressionToType (self: Expression) Type` Converts expression to type. **Parameters** -- `expression` - expression to convert to type. +- `self` - expression to convert to type. **Return value** @@ -661,6 +808,69 @@ expressionToType(expression) - `Error` - thrown if unable to transform +### `stringifyExpression (expression: Expression) str` +Converts expression to a code string. + +**Parameters** + +- `expression` - expression to convert to code string. + +**Return value** + +Code string representation of the expression. + +**Examples** + +```the +stringifyExpression(expression) +``` + +**Exceptions** + +- `Error` - thrown if unable to convert to a code string + +### `stringifyStatement (self: Statement) str` +Converts statement to a code string. + +**Parameters** + +- `self` - statement to convert to code string. + +**Return value** + +Code string representation of the statement. + +**Examples** + +```the +stringifyStatement(statement) +``` + +**Exceptions** + +- `Error` - thrown if unable to convert to a code string + +### `stringifyType (self: Type) str` +Converts type to a code string. + +**Parameters** + +- `self` - type to convert to code string. + +**Return value** + +Code string representation of the type. + +**Examples** + +```the +stringifyType(type) +``` + +**Exceptions** + +- `Error` - thrown if unable to convert to a code string + ### `validObjExprId (expression: Expression) bool` Checks whether expression is valid `ObjectExpression` identifier. diff --git a/packages/parser/src/reader b/packages/parser/src/reader index 8deab3ea9..2efb25d36 100644 --- a/packages/parser/src/reader +++ b/packages/parser/src/reader @@ -40,12 +40,12 @@ export obj Reader { return self.content.slice(start, end) } - fn walk (mut self: ref Self, match: (char) -> bool) { + fn walk (mut self: ref Self, matcher: (char) -> bool) { loop !self.eof() { pos := self.pos ch := self.next() - if !match(ch) { + if !matcher(ch) { self.seek(pos) break } @@ -79,7 +79,7 @@ export fn init (pathOrCode: str) Reader { return Reader{path: p, content: content, contentLen: content.len} } -export fn error (mut reader: ref Reader, errorType: str, message: str, start: int, end: int) str { +export fn error (reader: ref Reader, errorType: str, message: str, start: int, end: int) str { startLoc := locate(reader, start) endLoc := locate(reader, end) lines := reader.content.lines() From ce1cb4575a41e700b7daaecaa1ce915246f49b1f Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 12:44:39 +0200 Subject: [PATCH 31/48] parser: Re-export Reader_locate function --- packages/parser/README.md | 18 ++++++++++++++++++ packages/parser/src/main | 10 +++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 58a65f278..461a5b2dc 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -135,6 +135,24 @@ Generated error string. error := Reader_error(ref reader, "TypeError", "Missing type", 1, 10) ``` +### `Reader_locate (reader: ref Reader, pos: int) Location` +Converts file position into location object. + +**Parameters** + +- `reader` - reader instance to locate position from. +- `pos` - position to locate. + +**Return value** + +File position converted into location object. + +**Examples** + +```the +loc := Reader_locate(ref reader, 10) +``` + ### `Expression_toText (self: Expression, indent := 0) str` Generates string representation of the expression. diff --git a/packages/parser/src/main b/packages/parser/src/main index dd46d0024..96ced114b 100644 --- a/packages/parser/src/main +++ b/packages/parser/src/main @@ -70,6 +70,7 @@ import TokenType, Type, File, + Location, Program from "./types" import @@ -80,7 +81,12 @@ import Type_toText from "./text" import Program_traverse from "./program" -import Reader, error as Reader_error, init as Reader_init from "./reader" +import + Reader, + error as Reader_error, + init as Reader_init, + locate as Reader_locate +from "./reader" import toType as expressionToType, typeToExpression, validObjExprId from "./expression" import interconnect, parse, report from "./parser" import expression as stringifyExpression, statement as stringifyStatement, typeT as stringifyType from "./string" @@ -151,6 +157,7 @@ export Token export TokenType export Type export File +export Location export Program export Expression_toText export FunctionParameter_toText @@ -161,6 +168,7 @@ export Program_traverse export Reader export Reader_error export Reader_init +export Reader_locate export expressionToType export typeToExpression export validObjExprId From 6dc6fbca99d1ad406916a1337432403faba58a3c Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 13:00:29 +0200 Subject: [PATCH 32/48] analyzer: Extra context for PropertyAccess expression --- packages/analyzer/src/analyzer | 1 + packages/analyzer/src/analyzer-print | 12 ++++++++++++ packages/analyzer/src/context | 4 ++++ packages/analyzer/src/main | 2 ++ .../test/print/property-access-expression.txt | 2 +- packages/analyzer/test/print/try-statement.txt | 6 +++--- .../analyzer/test/type-casting/array-expression.txt | 2 +- .../test/type-casting/assignment-expression.txt | 8 ++++---- .../analyzer/test/type-casting/is-expression.txt | 2 +- .../analyzer/test/type-casting/map-expression.txt | 2 +- .../analyzer/test/type-casting/object-expression.txt | 2 +- .../test/type-casting/property-access-expression.txt | 2 +- 12 files changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/analyzer/src/analyzer b/packages/analyzer/src/analyzer index 8ee34d473..4aadc3274 100644 --- a/packages/analyzer/src/analyzer +++ b/packages/analyzer/src/analyzer @@ -1922,6 +1922,7 @@ export obj AnalyzerFile { } Context.set(it, property.t) + Context.setExtra(it, Context.PropertyAccessExpressionContextExtra{property: property}) } elif t.isEnum() { Context.set(it, t) diff --git a/packages/analyzer/src/analyzer-print b/packages/analyzer/src/analyzer-print index 5da16b905..6b64067af 100644 --- a/packages/analyzer/src/analyzer-print +++ b/packages/analyzer/src/analyzer-print @@ -462,6 +462,18 @@ export fn analyzerPrintExpression(expression: ref Parser.Expression, indent: int propertyAccess := expression.asPropertyAccess() name = "PropertyAccessExpression" attributes += " name=" + propertyAccess.name.name + expressionContextExtra := expression.contextExtra + if expression.contextExtra is Context.PropertyAccessExpressionContextExtra { + contextExtra := expressionContextExtra as Context.PropertyAccessExpressionContextExtra + attributes += " extra:propertyName=" + contextExtra.property.name + attributes += " extra:propertyType=" + contextExtra.property.t.toString() + if contextExtra.property.mutable { + attributes += " extra:propertyMutable" + } + if contextExtra.property.builtin { + attributes += " extra:propertyBuiltin" + } + } result += analyzerPrintExpression(ref propertyAccess.expression, indent + 2) } elif expression.isReference() { reference := expression.asReference() diff --git a/packages/analyzer/src/context b/packages/analyzer/src/context index 0da42e7a5..2ab98c6bf 100644 --- a/packages/analyzer/src/context +++ b/packages/analyzer/src/context @@ -40,6 +40,10 @@ export obj ObjectDeclarationContext { mut selfType: ref Type.Type } +export obj PropertyAccessExpressionContextExtra { + mut property: Type.TypeProperty +} + export obj VariableDeclarationContext { mut varType: ref Type.Type } diff --git a/packages/analyzer/src/main b/packages/analyzer/src/main index e2d486491..ca1284c25 100644 --- a/packages/analyzer/src/main +++ b/packages/analyzer/src/main @@ -12,6 +12,7 @@ import ElementAccessExpressionContextExtra, IsExpressionContextExtra, ObjectDeclarationContext, + PropertyAccessExpressionContextExtra, VariableDeclarationContext, initial as contextInitial, set as contextSet, @@ -64,6 +65,7 @@ export CallExpressionContextExtra export ElementAccessExpressionContextExtra export IsExpressionContextExtra export ObjectDeclarationContext +export PropertyAccessExpressionContextExtra export VariableDeclarationContext export contextInitial export contextSet diff --git a/packages/analyzer/test/print/property-access-expression.txt b/packages/analyzer/test/print/property-access-expression.txt index dfefed697..37e9c869b 100644 --- a/packages/analyzer/test/print/property-access-expression.txt +++ b/packages/analyzer/test/print/property-access-expression.txt @@ -11,5 +11,5 @@ VariableDeclaration(name=b mutable=false constant=true context:varType=Test) ObjectExpressionProperty(name=a) IntegerLiteral(initial=int value=10) VariableDeclaration(name=c mutable=false constant=true context:varType=int) - PropertyAccessExpression(initial=int name=a) + PropertyAccessExpression(initial=int name=a extra:propertyName=a extra:propertyType=int) Identifier(initial=Test name=b) diff --git a/packages/analyzer/test/print/try-statement.txt b/packages/analyzer/test/print/try-statement.txt index 6b1fcd6f3..540f14b7d 100644 --- a/packages/analyzer/test/print/try-statement.txt +++ b/packages/analyzer/test/print/try-statement.txt @@ -45,7 +45,7 @@ MainDeclaration() CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) CallExpressionArgument(extra:parameterIdx=0) - PropertyAccessExpression(initial=str target=any name=message) + PropertyAccessExpression(initial=str target=any name=message extra:propertyName=message extra:propertyType=str) Identifier(initial=Error name=err) TryStatement() CallExpression(initial=void extra:asynchronous=false extra:returnType=void) @@ -55,12 +55,12 @@ MainDeclaration() CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) CallExpressionArgument(extra:parameterIdx=0) - PropertyAccessExpression(initial=str target=any name=message) + PropertyAccessExpression(initial=str target=any name=message extra:propertyName=message extra:propertyType=str) Identifier(initial=Error name=err) TryStatementHandler() VariableDeclaration(name=err mutable=false constant=false type=MyError context:varType=MyError) CallExpression(initial=void extra:asynchronous=false extra:returnType=void) Identifier(initial=(items: any..., separator: str, terminator: str, to: str) -> void name=print) CallExpressionArgument(extra:parameterIdx=0) - PropertyAccessExpression(initial=str target=any name=message) + PropertyAccessExpression(initial=str target=any name=message extra:propertyName=message extra:propertyType=str extra:propertyMutable) Identifier(initial=MyError name=err) diff --git a/packages/analyzer/test/type-casting/array-expression.txt b/packages/analyzer/test/type-casting/array-expression.txt index 954591e3d..c6843bf4e 100644 --- a/packages/analyzer/test/type-casting/array-expression.txt +++ b/packages/analyzer/test/type-casting/array-expression.txt @@ -7,7 +7,7 @@ MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) VariableDeclaration(name=b mutable=false constant=false context:varType=bool) ConditionalExpression(initial=bool) - PropertyAccessExpression(initial=bool name=empty) + PropertyAccessExpression(initial=bool name=empty extra:propertyName=empty extra:propertyType=bool extra:propertyBuiltin) ArrayExpression(initial=bool[]) AssignmentExpression(initial=bool operator==) Identifier(initial=bool | int name=a) diff --git a/packages/analyzer/test/type-casting/assignment-expression.txt b/packages/analyzer/test/type-casting/assignment-expression.txt index ea316a6cc..ff6afb4cb 100644 --- a/packages/analyzer/test/type-casting/assignment-expression.txt +++ b/packages/analyzer/test/type-casting/assignment-expression.txt @@ -19,10 +19,10 @@ MainDeclaration() IsExpression(initial=bool target=bool | int is=bool extra:type=bool) Identifier(initial=bool | int name=b) CallExpression(initial=str extra:asynchronous=false extra:returnType=str) - PropertyAccessExpression(initial=(self: ref bool) -> str name=str) + PropertyAccessExpression(initial=(self: ref bool) -> str name=str extra:propertyName=str extra:propertyType=(self: ref bool) -> str extra:propertyBuiltin) Identifier(initial=bool | int target=bool name=a) CallExpression(initial=str extra:asynchronous=false extra:returnType=str) - PropertyAccessExpression(initial=(self: ref int) -> str name=str) + PropertyAccessExpression(initial=(self: ref int) -> str name=str extra:propertyName=str extra:propertyType=(self: ref int) -> str extra:propertyBuiltin) Identifier(initial=bool | int target=int name=b) VariableDeclaration(name=d mutable=false constant=false context:varType=str) ConditionalExpression(initial=str) @@ -34,10 +34,10 @@ MainDeclaration() IsExpression(initial=bool is=bool extra:type=bool) Identifier(initial=bool | int name=b) CallExpression(initial=str extra:asynchronous=false extra:returnType=str) - PropertyAccessExpression(initial=(self: ref int) -> str name=str) + PropertyAccessExpression(initial=(self: ref int) -> str name=str extra:propertyName=str extra:propertyType=(self: ref int) -> str extra:propertyBuiltin) Identifier(initial=bool | int target=int name=b) CallExpression(initial=str extra:asynchronous=false extra:returnType=str) - PropertyAccessExpression(initial=(self: ref bool) -> str name=str) + PropertyAccessExpression(initial=(self: ref bool) -> str name=str extra:propertyName=str extra:propertyType=(self: ref bool) -> str extra:propertyBuiltin) Identifier(initial=bool | int target=bool name=a) VariableDeclaration(name=e mutable=false constant=false context:varType=bool) ConditionalExpression(initial=bool) diff --git a/packages/analyzer/test/type-casting/is-expression.txt b/packages/analyzer/test/type-casting/is-expression.txt index d893e013e..30a522855 100644 --- a/packages/analyzer/test/type-casting/is-expression.txt +++ b/packages/analyzer/test/type-casting/is-expression.txt @@ -98,5 +98,5 @@ MainDeclaration() BinaryExpression(initial=str operator=+) StringLiteral(initial=str value=int) CallExpression(initial=str extra:asynchronous=false extra:returnType=str) - PropertyAccessExpression(initial=(self: ref int) -> str name=str) + PropertyAccessExpression(initial=(self: ref int) -> str name=str extra:propertyName=str extra:propertyType=(self: ref int) -> str extra:propertyBuiltin) Identifier(initial=any target=int name=n) diff --git a/packages/analyzer/test/type-casting/map-expression.txt b/packages/analyzer/test/type-casting/map-expression.txt index 7626d7c90..ed8218613 100644 --- a/packages/analyzer/test/type-casting/map-expression.txt +++ b/packages/analyzer/test/type-casting/map-expression.txt @@ -7,7 +7,7 @@ MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) VariableDeclaration(name=b mutable=false constant=false context:varType=bool) ConditionalExpression(initial=bool) - PropertyAccessExpression(initial=bool name=empty) + PropertyAccessExpression(initial=bool name=empty extra:propertyName=empty extra:propertyType=bool extra:propertyBuiltin) MapExpression(initial=bool[str]) MapExpressionElement() StringLiteral(initial=str value=key) diff --git a/packages/analyzer/test/type-casting/object-expression.txt b/packages/analyzer/test/type-casting/object-expression.txt index 09fa05afb..b3f2528a0 100644 --- a/packages/analyzer/test/type-casting/object-expression.txt +++ b/packages/analyzer/test/type-casting/object-expression.txt @@ -15,7 +15,7 @@ MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) VariableDeclaration(name=b mutable=false constant=false context:varType=bool) ConditionalExpression(initial=bool) - PropertyAccessExpression(initial=bool name=empty) + PropertyAccessExpression(initial=bool name=empty extra:propertyName=empty extra:propertyType=bool) ObjectExpression(initial=Cond id=Cond) ObjectExpressionProperty(name=a) AssignmentExpression(initial=bool operator==) diff --git a/packages/analyzer/test/type-casting/property-access-expression.txt b/packages/analyzer/test/type-casting/property-access-expression.txt index c73280496..02c36ccaa 100644 --- a/packages/analyzer/test/type-casting/property-access-expression.txt +++ b/packages/analyzer/test/type-casting/property-access-expression.txt @@ -13,7 +13,7 @@ MainDeclaration() VariableDeclaration(name=a mutable=true constant=false type=bool | int context:varType=bool | int) VariableDeclaration(name=b mutable=false constant=false context:varType=bool) ConditionalExpression(initial=bool) - PropertyAccessExpression(initial=bool name=a) + PropertyAccessExpression(initial=bool name=a extra:propertyName=a extra:propertyType=bool) ObjectExpression(initial=Cond id=Cond) ObjectExpressionProperty(name=a) AssignmentExpression(initial=bool operator==) From d9317f963e7221014d1611ace8710baed3a52085 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Tue, 7 Jan 2025 14:11:31 +0200 Subject: [PATCH 33/48] analyzer: Unwrap property access type --- packages/analyzer/src/analyzer | 9 +++++++- ...access-expression-with-property-access.txt | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 packages/analyzer/test/type-casting/element-access-expression-with-property-access.txt diff --git a/packages/analyzer/src/analyzer b/packages/analyzer/src/analyzer index 4aadc3274..33873fd90 100644 --- a/packages/analyzer/src/analyzer +++ b/packages/analyzer/src/analyzer @@ -1905,7 +1905,14 @@ export obj AnalyzerFile { } mut expression := it.asPropertyAccess() - expressionType := self.e(ref expression.expression, withMutable: withMutable || withMutableNext) + niceExpressionType := self.eNice(ref expression.expression) + + expressionType := self.e( + ref expression.expression, + Type.opt(Type.unwrap(niceExpressionType)), + withMutable: withMutable || withMutableNext, + ) + t := Type.unwrap(expressionType) if t.has(expression.name.name) { diff --git a/packages/analyzer/test/type-casting/element-access-expression-with-property-access.txt b/packages/analyzer/test/type-casting/element-access-expression-with-property-access.txt new file mode 100644 index 000000000..e4c2470f4 --- /dev/null +++ b/packages/analyzer/test/type-casting/element-access-expression-with-property-access.txt @@ -0,0 +1,21 @@ +obj Animal { + mut age: int +} +main { + mut pets := [Animal{age: 1}] + pets[0].age++ +} +===== out ===== +ObjectDeclaration(name=Animal context:selfType=Animal) + ObjectDeclarationProperty(name=age mutable=true constant=false type=int) +MainDeclaration() + VariableDeclaration(name=pets mutable=true constant=false context:varType=Animal[]) + ArrayExpression(initial=Animal[]) + ObjectExpression(initial=Animal id=Animal) + ObjectExpressionProperty(name=age) + IntegerLiteral(initial=int value=1) + UnaryExpression(initial=int operator=++ prefix=false) + PropertyAccessExpression(initial=int name=age extra:propertyName=age extra:propertyType=int extra:propertyMutable) + ElementAccessExpression(initial=ref Animal target=Animal extra:selfType=Animal[]) + Identifier(initial=Animal[] name=pets) + IntegerLiteral(initial=int value=0) From 24a04f9eb7d82389b97740571b9eb559a2eea9fd Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 8 Jan 2025 08:01:37 +0200 Subject: [PATCH 34/48] codegen: Generate ElementAccess error location --- packages/codegen/src/codegen | 9 +++++++++ packages/codegen/src/helpers | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index f7ec650f8..296ae4c16 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -1110,8 +1110,12 @@ export obj Codegen { } contextExtra := item.contextExtra as Analyzer.ElementAccessExpressionContextExtra + startLoc := Parser.Reader_locate(self._r(), item.start) return AST.createCallExpression(AST.createIdentifier(self._type(contextExtra.selfType, "_at")), [ + AST.createUnaryExpression("&", AST.createIdentifier(self._("d4_err_state"))), + AST.createLiteral(startLoc.line.str()), + AST.createLiteral(startLoc.col.str()), self._generateExpression(ref expression.expression), self._generateExpression(ref expression.argument), ]) @@ -2658,6 +2662,11 @@ export obj Codegen { return false } + fn _r (mut self: ref Self) ref Parser.Reader { + mut file := self.analyzer.files.last() + return ref file.reader + } + fn _tm (mut self: ref Self) ref Analyzer.TypeMap { mut file := self.analyzer.files.last() return ref file.tm diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index da54ba5ae..78a2a67d7 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -47,8 +47,12 @@ export fn declarationByDependency (a: ref Parser.Statement, b: ref Parser.Statem export fn expressionIsBuiltinMethod (item: ref Parser.Expression) bool { if item.isPropertyAccess() { - body := item.asPropertyAccess() - return ["str"].contains(body.name.name) + contextExtra := item.contextExtra + + if item.contextExtra is Analyzer.PropertyAccessExpressionContextExtra { + contextExtra := contextExtra as Analyzer.PropertyAccessExpressionContextExtra + return contextExtra.property.builtin + } } return false From ec0ccc30bc0f4a38cc5054708f4e0d608c20ce61 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 8 Jan 2025 08:02:45 +0200 Subject: [PATCH 35/48] codegen: Wrap dereferenced C expressions --- packages/codegen/src/codegen | 13 +++++++------ packages/codegen/src/helpers | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 296ae4c16..4a37553d5 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -9,6 +9,7 @@ import * as Parser from "the/parser" import BUILTIN_ENTITIES from "./builtin-entities" import BlockData, BlockDataContext from "./block-data" import + createASTPropertyAccess, declarationByDependency, expressionIsBuiltinMethod, expressionResolve, @@ -1058,7 +1059,7 @@ export obj Codegen { } mut args: (AST.CExpression | AST.CType)[] = [ - AST.createPropertyAccessExpression(calleeExpression, "ctx") + createASTPropertyAccess(calleeExpression, "ctx") ] if !paramsArgs.empty { @@ -1075,7 +1076,7 @@ export obj Codegen { ])) } - return AST.createCallExpression(AST.createPropertyAccessExpression(calleeExpression, "func"), args) + return AST.createCallExpression(createASTPropertyAccess(calleeExpression, "func"), args) } fn _generateConditionalExpression (mut self: ref Codegen, item: ref Parser.Expression) AST.CExpression? { @@ -1142,7 +1143,7 @@ export obj Codegen { return result.wrap() } - left = AST.createPropertyAccessExpression(left, "type") + left = createASTPropertyAccess(left, "type") return AST.createBinaryExpression(left, "==", right) } @@ -1219,7 +1220,7 @@ export obj Codegen { expression := item.asPropertyAccess() - return AST.createPropertyAccessExpression( + return createASTPropertyAccess( self._generateExpression(ref expression.expression), expression.name.name, ) @@ -2781,7 +2782,7 @@ export obj Codegen { // TODO: throw runtime error if not possible to cast innerExpression := AST.createCastExpression( AST.createType(self._(typeName)), - AST.createPropertyAccessExpression(expression, "ctx") + createASTPropertyAccess(expression, "ctx") ) expression = AST.createUnaryExpression("*", innerExpression.wrap()) @@ -2801,7 +2802,7 @@ export obj Codegen { // TODO: throw runtime error if not possible to cast expression = AST.createPropertyAccessExpression( - AST.createPropertyAccessExpression(expression, "data"), + createASTPropertyAccess(expression, "data"), "v" + typeDefIdx.str(), ) } elif !Analyzer.match(t, targetType) && ( diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 78a2a67d7..60df80d49 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -7,6 +7,32 @@ import * as Analyzer from "the/analyzer" import * as AST from "the/c" import * as Parser from "the/parser" +export fn createASTPropertyAccess (expression: AST.CExpression, property: str) AST.CExpression { + if expression.isAssignmentExpression() { + return createASTPropertyAccessWithAssignment(expression, property) + } elif expression.isUnaryExpression() { + return createASTPropertyAccessWithUnary(expression, property) + } + + return AST.createPropertyAccessExpression(expression, property) +} + +export fn createASTPropertyAccessWithAssignment (expression: AST.CExpression, property: str) AST.CExpression { + wrappedExpression := expression.wrap() + return AST.createPropertyAccessExpression(wrappedExpression, property) +} + +export fn createASTPropertyAccessWithUnary (expression: AST.CExpression, property: str) AST.CExpression { + cUnaryExpression := expression.asUnaryExpression() + + if cUnaryExpression.postfix || cUnaryExpression.operator != "*" { + return AST.createPropertyAccessExpression(expression, property) + } + + wrappedExpression := expression.wrap() + return AST.createPropertyAccessExpression(wrappedExpression, property) +} + export fn declarationType (item: ref Parser.Statement) (ref Analyzer.Type)? { itemContext := item.context From 2ed614a70219554973400d19ce7a9c9f4894038f Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Wed, 8 Jan 2025 10:35:35 +0200 Subject: [PATCH 36/48] codegen: Update object-related tests --- packages/codegen/src/codegen | 23 +++-- packages/codegen/src/helpers | 2 +- .../codegen/test/codegen/array-expression.txt | 4 +- .../codegen/test/codegen/as-expression.txt | 48 +++++------ .../break-statement-with-blockdata.txt | 4 +- .../codegen/test/codegen/break-statement.txt | 4 +- .../continue-statement-with-blockdata.txt | 4 +- .../test/codegen/continue-statement.txt | 4 +- .../test/codegen/is-expression-root.txt | 12 +-- .../codegen/test/codegen/is-expression.txt | 12 +-- .../test/codegen/object-declaration-empty.txt | 32 +++++++ .../test/codegen/object-declaration-str.txt | 69 +++++++++++++++ .../test/codegen/object-declaration.txt | 39 +++++++++ .../test/codegen/object-expression-empty.txt | 85 +++++++++++++++++++ .../test/codegen/object-expression-nested.txt | 4 +- .../test/codegen/object-expression-root.txt | 28 ++++++ .../codegen/test/codegen/optional-type.txt | 4 +- .../test/codegen/parenthesized-type.txt | 6 +- ...roperty-access-expression-builtin-root.txt | 25 ++++++ .../property-access-expression-builtin.txt | 26 ++++++ .../codegen/test/codegen/reference-type.txt | 2 +- packages/codegen/test/codegen/union-type.txt | 36 ++++---- 22 files changed, 393 insertions(+), 80 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 4a37553d5..57118712f 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -369,9 +369,6 @@ export obj Codegen { ) } - fn _preDeclareStatement (mut self: ref Self, item: ref Parser.Statement) bool { - } - fn _declareStatement (mut self: ref Self, item: ref Parser.Statement) bool { if item.isMainDeclaration() { self._declareMain(item) @@ -380,9 +377,6 @@ export obj Codegen { } } - fn _postDeclareStatement (mut self: ref Self, item: ref Parser.Statement) bool { - } - fn _declareMain (mut self: ref Codegen, item: ref Parser.Statement) void { statement := item.asMainDeclaration() statementBody := statement.body.asBlock() @@ -439,6 +433,8 @@ export obj Codegen { return AST.createIdentifier(self._("false")) } elif it.name == "char" { return AST.createLiteral("'\\0'") + } elif it.name == "rune" { + return AST.createLiteral("L'\\0'") } elif it.name == "str" { return AST.createIdentifier(self._("d4_str_empty_val")) } elif it.isArray() || it.isMap() { @@ -2021,6 +2017,10 @@ export obj Codegen { return name } + itemType := item.asMap() + self._typeGen(itemType.keyType) + self._typeGen(itemType.valueType) + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -2435,6 +2435,14 @@ export obj Codegen { return name } + itemType := item.asUnion() + subtypesLen := itemType.types.len + + loop i := 0; i < subtypesLen; i++ { + subtype := itemType.types[i] as ref Analyzer.Type + self._typeGen(subtype) + } + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -2767,9 +2775,10 @@ export obj Codegen { } fn _wrap (mut self: ref Self, mut expression: AST.CExpression, item: ref Parser.Expression, t: ref Analyzer.Type, targetType: ref Analyzer.Type, transform := false) AST.CExpression { + shouldAllocate := typeShouldBeAllocated(t) && expressionShouldBeAllocated(item) shouldFree := typeShouldBeFreed(targetType) && expressionShouldBeFreed(item) - if transform && !shouldFree && typeShouldBeAllocated(t) && expressionShouldBeAllocated(item) { + if transform && !shouldFree && shouldAllocate { expression = self._allocateExpression(t, expression) } diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 60df80d49..8c7e0318c 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -134,7 +134,7 @@ export fn expressionShouldBeAllocated (item: ref Parser.Expression) bool { if itUp.hasParent() { parent := itUp.parent() - if parent.isConditional() { + if parent.isConditional() || parent.isPropertyAccess() { return false } } diff --git a/packages/codegen/test/codegen/array-expression.txt b/packages/codegen/test/codegen/array-expression.txt index 69cdd0721..8a55d4fa8 100644 --- a/packages/codegen/test/codegen/array-expression.txt +++ b/packages/codegen/test/codegen/array-expression.txt @@ -8,10 +8,10 @@ main { #include #include #include -D4_ARRAY_DECLARE(byte, unsigned char) D4_ARRAY_DECLARE(int, int32_t) -D4_ARRAY_DEFINE(byte, unsigned char, int32_t, element, lhs_element == rhs_element, (void) element, d4_byte_str(element)) +D4_ARRAY_DECLARE(byte, unsigned char) D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) +D4_ARRAY_DEFINE(byte, unsigned char, int32_t, element, lhs_element == rhs_element, (void) element, d4_byte_str(element)) int main (void) { d4_arr_int_t a_0 = d4_arr_int_alloc(3, 1, 2, 3); d4_arr_byte_t b_0 = d4_arr_byte_alloc(3, 1, 2, 3); diff --git a/packages/codegen/test/codegen/as-expression.txt b/packages/codegen/test/codegen/as-expression.txt index 5f74453ca..a20e34a67 100644 --- a/packages/codegen/test/codegen/as-expression.txt +++ b/packages/codegen/test/codegen/as-expression.txt @@ -63,32 +63,32 @@ main { #define TYPE_u8 11 #define TYPE_char 12 #define TYPE_i8 13 -D4_ANY_DECLARE(i8, int8_t) -D4_ANY_DECLARE(char, char) -D4_ANY_DECLARE(u8, uint8_t) -D4_ANY_DECLARE(u64, uint64_t) -D4_ANY_DECLARE(u32, uint32_t) -D4_ANY_DECLARE(u16, uint16_t) -D4_ANY_DECLARE(f64, double) -D4_ANY_DECLARE(f32, float) -D4_ANY_DECLARE(float, double) -D4_ANY_DECLARE(i64, int64_t) -D4_ANY_DECLARE(int, int32_t) -D4_ANY_DECLARE(i32, int32_t) D4_ANY_DECLARE(i16, int16_t) -D4_ANY_DEFINE(TYPE_i8, i8, int8_t, val, lhs_val == rhs_val, (void) val, d4_i8_str(val)) -D4_ANY_DEFINE(TYPE_char, char, char, val, lhs_val == rhs_val, (void) val, d4_char_str(val)) -D4_ANY_DEFINE(TYPE_u8, u8, uint8_t, val, lhs_val == rhs_val, (void) val, d4_u8_str(val)) -D4_ANY_DEFINE(TYPE_u64, u64, uint64_t, val, lhs_val == rhs_val, (void) val, d4_u64_str(val)) -D4_ANY_DEFINE(TYPE_u32, u32, uint32_t, val, lhs_val == rhs_val, (void) val, d4_u32_str(val)) -D4_ANY_DEFINE(TYPE_u16, u16, uint16_t, val, lhs_val == rhs_val, (void) val, d4_u16_str(val)) -D4_ANY_DEFINE(TYPE_f64, f64, double, val, lhs_val == rhs_val, (void) val, d4_f64_str(val)) -D4_ANY_DEFINE(TYPE_f32, f32, float, val, lhs_val == rhs_val, (void) val, d4_f32_str(val)) -D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) -D4_ANY_DEFINE(TYPE_i64, i64, int64_t, val, lhs_val == rhs_val, (void) val, d4_i64_str(val)) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) -D4_ANY_DEFINE(TYPE_i32, i32, int32_t, val, lhs_val == rhs_val, (void) val, d4_i32_str(val)) +D4_ANY_DECLARE(i32, int32_t) +D4_ANY_DECLARE(int, int32_t) +D4_ANY_DECLARE(i64, int64_t) +D4_ANY_DECLARE(float, double) +D4_ANY_DECLARE(f32, float) +D4_ANY_DECLARE(f64, double) +D4_ANY_DECLARE(u16, uint16_t) +D4_ANY_DECLARE(u32, uint32_t) +D4_ANY_DECLARE(u64, uint64_t) +D4_ANY_DECLARE(u8, uint8_t) +D4_ANY_DECLARE(char, char) +D4_ANY_DECLARE(i8, int8_t) D4_ANY_DEFINE(TYPE_i16, i16, int16_t, val, lhs_val == rhs_val, (void) val, d4_i16_str(val)) +D4_ANY_DEFINE(TYPE_i32, i32, int32_t, val, lhs_val == rhs_val, (void) val, d4_i32_str(val)) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DEFINE(TYPE_i64, i64, int64_t, val, lhs_val == rhs_val, (void) val, d4_i64_str(val)) +D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) +D4_ANY_DEFINE(TYPE_f32, f32, float, val, lhs_val == rhs_val, (void) val, d4_f32_str(val)) +D4_ANY_DEFINE(TYPE_f64, f64, double, val, lhs_val == rhs_val, (void) val, d4_f64_str(val)) +D4_ANY_DEFINE(TYPE_u16, u16, uint16_t, val, lhs_val == rhs_val, (void) val, d4_u16_str(val)) +D4_ANY_DEFINE(TYPE_u32, u32, uint32_t, val, lhs_val == rhs_val, (void) val, d4_u32_str(val)) +D4_ANY_DEFINE(TYPE_u64, u64, uint64_t, val, lhs_val == rhs_val, (void) val, d4_u64_str(val)) +D4_ANY_DEFINE(TYPE_u8, u8, uint8_t, val, lhs_val == rhs_val, (void) val, d4_u8_str(val)) +D4_ANY_DEFINE(TYPE_char, char, char, val, lhs_val == rhs_val, (void) val, d4_char_str(val)) +D4_ANY_DEFINE(TYPE_i8, i8, int8_t, val, lhs_val == rhs_val, (void) val, d4_i8_str(val)) int main (void) { d4_any_t __THE_1 = {-1, NULL, NULL, NULL, NULL, NULL}; d4_any_t __THE_2 = {-1, NULL, NULL, NULL, NULL, NULL}; diff --git a/packages/codegen/test/codegen/break-statement-with-blockdata.txt b/packages/codegen/test/codegen/break-statement-with-blockdata.txt index 4d80cd7eb..8e20b8338 100644 --- a/packages/codegen/test/codegen/break-statement-with-blockdata.txt +++ b/packages/codegen/test/codegen/break-statement-with-blockdata.txt @@ -82,10 +82,10 @@ main { #include #define TYPE_str 1 #define TYPE_int 2 -D4_ANY_DECLARE(int, int32_t) D4_ANY_DECLARE(str, d4_str_t) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { d4_str_t v1_0 = d4_str_alloc(L"test1"); for (int32_t i_0 = 0; i_0 < 10; i_0++) { diff --git a/packages/codegen/test/codegen/break-statement.txt b/packages/codegen/test/codegen/break-statement.txt index 6135fe9fb..239e3eb8e 100644 --- a/packages/codegen/test/codegen/break-statement.txt +++ b/packages/codegen/test/codegen/break-statement.txt @@ -16,10 +16,10 @@ main { #include #define TYPE_str 1 #define TYPE_int 2 -D4_ANY_DECLARE(int, int32_t) D4_ANY_DECLARE(str, d4_str_t) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { for (int32_t i_0 = 0; i_0 < 10; i_0++) { unsigned char b1 = 0; diff --git a/packages/codegen/test/codegen/continue-statement-with-blockdata.txt b/packages/codegen/test/codegen/continue-statement-with-blockdata.txt index 12219ddeb..e02ed6bdc 100644 --- a/packages/codegen/test/codegen/continue-statement-with-blockdata.txt +++ b/packages/codegen/test/codegen/continue-statement-with-blockdata.txt @@ -84,10 +84,10 @@ main { #include #define TYPE_str 1 #define TYPE_int 2 -D4_ANY_DECLARE(int, int32_t) D4_ANY_DECLARE(str, d4_str_t) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { d4_str_t v1_0 = d4_str_alloc(L"test1"); for (int32_t i_0 = 0; i_0 < 10; i_0++) { diff --git a/packages/codegen/test/codegen/continue-statement.txt b/packages/codegen/test/codegen/continue-statement.txt index b7ad9d704..be560ea45 100644 --- a/packages/codegen/test/codegen/continue-statement.txt +++ b/packages/codegen/test/codegen/continue-statement.txt @@ -16,10 +16,10 @@ main { #include #define TYPE_str 1 #define TYPE_int 2 -D4_ANY_DECLARE(int, int32_t) D4_ANY_DECLARE(str, d4_str_t) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DECLARE(int, int32_t) D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { for (int32_t i_0 = 0; i_0 < 10; i_0++) { unsigned char c1 = 0; diff --git a/packages/codegen/test/codegen/is-expression-root.txt b/packages/codegen/test/codegen/is-expression-root.txt index 4d4c10272..3e7d180c1 100644 --- a/packages/codegen/test/codegen/is-expression-root.txt +++ b/packages/codegen/test/codegen/is-expression-root.txt @@ -54,14 +54,15 @@ main { #define TYPE_int 1 #define TYPE_float 2 #define TYPE_str 3 -D4_ANY_DECLARE(str, d4_str_t) +D4_ANY_DECLARE(int, int32_t) +D4_ANY_DECLARE(float, double) D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) -D4_ANY_DECLARE(float, double) -D4_ANY_DECLARE(int, int32_t) -D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_ANY_DECLARE(str, d4_str_t) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); if (self.type == TYPE_float) self.data.v2 = va_arg(args, double); @@ -77,8 +78,7 @@ D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) return d4_int_str(self.data.v1); if (self.type == TYPE_float) return d4_float_str(self.data.v2); }) -D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) int main (void) { d4_any_t b_0; d4_union_floatUSintUE_t c_0; diff --git a/packages/codegen/test/codegen/is-expression.txt b/packages/codegen/test/codegen/is-expression.txt index 38a7db67e..6d15dde50 100644 --- a/packages/codegen/test/codegen/is-expression.txt +++ b/packages/codegen/test/codegen/is-expression.txt @@ -59,13 +59,16 @@ main { #define TYPE_int 1 #define TYPE_float 2 #define TYPE_str 3 +D4_ANY_DECLARE(int, int32_t) +D4_ANY_DECLARE(str, d4_str_t) +D4_ANY_DECLARE(float, double) D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) -D4_ANY_DECLARE(float, double) -D4_ANY_DECLARE(str, d4_str_t) -D4_ANY_DECLARE(int, int32_t) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); if (self.type == TYPE_float) self.data.v2 = va_arg(args, double); @@ -81,9 +84,6 @@ D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) return d4_int_str(self.data.v1); if (self.type == TYPE_float) return d4_float_str(self.data.v2); }) -D4_ANY_DEFINE(TYPE_float, float, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) -D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) -D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { d4_any_t b_0; d4_union_floatUSintUE_t c_0; diff --git a/packages/codegen/test/codegen/object-declaration-empty.txt b/packages/codegen/test/codegen/object-declaration-empty.txt index 7e88f554e..c492d8bf1 100644 --- a/packages/codegen/test/codegen/object-declaration-empty.txt +++ b/packages/codegen/test/codegen/object-declaration-empty.txt @@ -5,4 +5,36 @@ main { print(o) } ===== code ===== +#include +#include +#include +#include +#include +#include +#define TYPE_obj_EmptyObject 1 +D4_OBJECT_FORWARD_DECLARE(EmptyObject) +D4_OBJECT_DECLARE(EmptyObject, { + bool _; +}, void) +D4_ANY_DECLARE(obj_EmptyObject, d4_obj_EmptyObject_t) +D4_OBJECT_DEFINE(EmptyObject, EmptyObject, { + self._ = false; +}, { + result._ = self._; +}, { + (void) self; + (void) rhs; + return true; +}, { + (void) self; +}, { + (void) self; +}, void) +D4_ANY_DEFINE(TYPE_obj_EmptyObject, obj_EmptyObject, d4_obj_EmptyObject_t, d4_obj_EmptyObject_copy(val), d4_obj_EmptyObject_eq(lhs_val, rhs_val), d4_obj_EmptyObject_free(val), d4_obj_EmptyObject_str(val)) +int main (void) { + d4_obj_EmptyObject_t o_0 = d4_obj_EmptyObject_alloc(); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, d4_any_obj_EmptyObject_alloc(o_0)), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_obj_EmptyObject_free(o_0); +} ===== output ===== +EmptyObject{} diff --git a/packages/codegen/test/codegen/object-declaration-str.txt b/packages/codegen/test/codegen/object-declaration-str.txt index dbb0fb9bc..2684a7891 100644 --- a/packages/codegen/test/codegen/object-declaration-str.txt +++ b/packages/codegen/test/codegen/object-declaration-str.txt @@ -13,4 +13,73 @@ main { print(p2.str()) } ===== code ===== +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TYPE_str 1 +D4_OBJECT_FORWARD_DECLARE(Person) +D4_OBJECT_FORWARD_DECLARE(PersonWithAge) +D4_OBJECT_DECLARE(Person, { + d4_str_t name; +}, const d4_str_t name) +D4_OBJECT_DECLARE(PersonWithAge, { + d4_str_t name; + int32_t age; +}, const d4_str_t name, const int32_t age) +D4_ANY_DECLARE(str, d4_str_t) +D4_OBJECT_DEFINE(Person, Person, { + self.name = d4_str_copy(name); +}, { + result.name = d4_str_copy(self.name); +}, { + return d4_str_eq(self.name, rhs.name); +}, { + d4_str_free(self.name); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); +}, const d4_str_t name) +D4_OBJECT_DEFINE(PersonWithAge, PersonWithAge, { + self.name = d4_str_copy(name); + self.age = age; +}, { + result.name = d4_str_copy(self.name); + result.age = self.age; +}, { + return d4_str_eq(self.name, rhs.name) && self.age == rhs.age; +}, { + d4_str_free(self.name); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); + result = d4_obj_str_append(result, d4_str_alloc(L"age"), d4_int_str(self.age)); +}, const d4_str_t name, const int32_t age) +D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_str_t __THE_2 = d4_str_empty_val; + d4_str_t __THE_3 = d4_str_empty_val; + d4_any_t __THE_4 = {-1, NULL, NULL, NULL, NULL, NULL}; + d4_str_t __THE_5 = d4_str_empty_val; + d4_any_t __THE_6 = {-1, NULL, NULL, NULL, NULL, NULL}; + d4_obj_Person_t p1_0 = d4_obj_Person_alloc(__THE_1 = d4_str_alloc(L"Jessica")); + d4_obj_PersonWithAge_t p2_0 = d4_obj_PersonWithAge_alloc(__THE_2 = d4_str_alloc(L"Leonard"), 15); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_4 = d4_any_str_alloc(__THE_3 = d4_obj_Person_str(p1_0))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, __THE_6 = d4_any_str_alloc(__THE_5 = d4_obj_PersonWithAge_str(p2_0))), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_any_free(__THE_6); + d4_str_free(__THE_5); + d4_any_free(__THE_4); + d4_str_free(__THE_3); + d4_obj_PersonWithAge_free(p2_0); + d4_str_free(__THE_2); + d4_obj_Person_free(p1_0); + d4_str_free(__THE_1); +} ===== output ===== +Person{name: "Jessica"} +PersonWithAge{name: "Leonard", age: 15} diff --git a/packages/codegen/test/codegen/object-declaration.txt b/packages/codegen/test/codegen/object-declaration.txt index 118cf98e4..41db21bda 100644 --- a/packages/codegen/test/codegen/object-declaration.txt +++ b/packages/codegen/test/codegen/object-declaration.txt @@ -7,4 +7,43 @@ main { print(person) } ===== code ===== +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TYPE_obj_Person 1 +D4_OBJECT_FORWARD_DECLARE(Person) +D4_OBJECT_DECLARE(Person, { + d4_str_t name; + int32_t age; +}, const d4_str_t name, const int32_t age) +D4_ANY_DECLARE(obj_Person, d4_obj_Person_t) +D4_OBJECT_DEFINE(Person, Person, { + self.name = d4_str_copy(name); + self.age = age; +}, { + result.name = d4_str_copy(self.name); + result.age = self.age; +}, { + return d4_str_eq(self.name, rhs.name) && self.age == rhs.age; +}, { + d4_str_free(self.name); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); + result = d4_obj_str_append(result, d4_str_alloc(L"age"), d4_int_str(self.age)); +}, const d4_str_t name, const int32_t age) +D4_ANY_DEFINE(TYPE_obj_Person, obj_Person, d4_obj_Person_t, d4_obj_Person_copy(val), d4_obj_Person_eq(lhs_val, rhs_val), d4_obj_Person_free(val), d4_obj_Person_str(val)) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_obj_Person_t person_0 = d4_obj_Person_alloc(__THE_1 = d4_str_alloc(L"Lauren"), 61); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, d4_any_obj_Person_alloc(person_0)), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_obj_Person_free(person_0); + d4_str_free(__THE_1); +} ===== output ===== +Person{name: "Lauren", age: 61} diff --git a/packages/codegen/test/codegen/object-expression-empty.txt b/packages/codegen/test/codegen/object-expression-empty.txt index 9b8af565a..a68e4ee86 100644 --- a/packages/codegen/test/codegen/object-expression-empty.txt +++ b/packages/codegen/test/codegen/object-expression-empty.txt @@ -22,4 +22,89 @@ main { empty := Empty{} } ===== code ===== +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Point) +D4_OBJECT_FORWARD_DECLARE(Test) +D4_OBJECT_FORWARD_DECLARE(Human) +D4_OBJECT_FORWARD_DECLARE(Empty) +D4_OBJECT_DECLARE(Point, { + double x; + double y; +}, const double x, const double y) +D4_OBJECT_DECLARE(Test, { + int32_t a; +}, const int32_t a) +D4_OBJECT_DECLARE(Human, { + d4_str_t name; + int32_t age; +}, const d4_str_t name, const int32_t age) +D4_OBJECT_DECLARE(Empty, { + bool _; +}, void) +D4_OBJECT_DEFINE(Point, Point, { + self.x = x; + self.y = y; +}, { + result.x = self.x; + result.y = self.y; +}, { + return self.x == rhs.x && self.y == rhs.y; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"x"), d4_float_str(self.x)); + result = d4_obj_str_append(result, d4_str_alloc(L"y"), d4_float_str(self.y)); +}, const double x, const double y) +D4_OBJECT_DEFINE(Test, Test, { + self.a = a; +}, { + result.a = self.a; +}, { + return self.a == rhs.a; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_int_str(self.a)); +}, const int32_t a) +D4_OBJECT_DEFINE(Human, Human, { + self.name = d4_str_copy(name); + self.age = age; +}, { + result.name = d4_str_copy(self.name); + result.age = self.age; +}, { + return d4_str_eq(self.name, rhs.name) && self.age == rhs.age; +}, { + d4_str_free(self.name); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); + result = d4_obj_str_append(result, d4_str_alloc(L"age"), d4_int_str(self.age)); +}, const d4_str_t name, const int32_t age) +D4_OBJECT_DEFINE(Empty, Empty, { + self._ = false; +}, { + result._ = self._; +}, { + (void) self; + (void) rhs; + return true; +}, { + (void) self; +}, { + (void) self; +}, void) +int main (void) { + d4_obj_Point_t point_0 = d4_obj_Point_alloc(0, 0); + d4_obj_Test_t test_0 = d4_obj_Test_alloc(0); + d4_obj_Human_t human_0 = d4_obj_Human_alloc(d4_str_empty_val, 0); + d4_obj_Empty_t empty_0 = d4_obj_Empty_alloc(); + d4_obj_Empty_free(empty_0); + d4_obj_Human_free(human_0); + d4_obj_Test_free(test_0); + d4_obj_Point_free(point_0); +} ===== output ===== diff --git a/packages/codegen/test/codegen/object-expression-nested.txt b/packages/codegen/test/codegen/object-expression-nested.txt index 6d6e52446..98620a2e2 100644 --- a/packages/codegen/test/codegen/object-expression-nested.txt +++ b/packages/codegen/test/codegen/object-expression-nested.txt @@ -3,7 +3,7 @@ obj Test1 { t2: int } obj Test2 { - t1: Test3 + mut t1: Test3 t2: rune } obj Test3 { @@ -30,7 +30,7 @@ main { t4.t1 = Test2{ t1: Test3{t1: true, t2: 2.16, t3: 1}, - t2: 100, + t2: 'y', } t4.t1.t1 = Test3{t1: false, t2: 1.23, t3: 5} diff --git a/packages/codegen/test/codegen/object-expression-root.txt b/packages/codegen/test/codegen/object-expression-root.txt index bffa754bd..fadda32df 100644 --- a/packages/codegen/test/codegen/object-expression-root.txt +++ b/packages/codegen/test/codegen/object-expression-root.txt @@ -8,4 +8,32 @@ main { Test{a: 0, b: 1} } ===== code ===== +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test) +D4_OBJECT_DECLARE(Test, { + int32_t a; + int32_t b; +}, const int32_t a, const int32_t b) +D4_OBJECT_DEFINE(Test, Test, { + self.a = a; + self.b = b; +}, { + result.a = self.a; + result.b = self.b; +}, { + return self.a == rhs.a && self.b == rhs.b; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_int_str(self.a)); + result = d4_obj_str_append(result, d4_str_alloc(L"b"), d4_int_str(self.b)); +}, const int32_t a, const int32_t b) +int main (void) { + d4_obj_Test_free(d4_obj_Test_alloc(0, 1)); + d4_obj_Test_free(d4_obj_Test_alloc(0, 1)); +} ===== output ===== diff --git a/packages/codegen/test/codegen/optional-type.txt b/packages/codegen/test/codegen/optional-type.txt index 91958b107..6dc780a26 100644 --- a/packages/codegen/test/codegen/optional-type.txt +++ b/packages/codegen/test/codegen/optional-type.txt @@ -8,10 +8,10 @@ main { #include #include #include -D4_OPTIONAL_DECLARE(str, d4_str_t) D4_OPTIONAL_DECLARE(int, int32_t) -D4_OPTIONAL_DEFINE(str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +D4_OPTIONAL_DECLARE(str, d4_str_t) D4_OPTIONAL_DEFINE(int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_OPTIONAL_DEFINE(str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) int main (void) { d4_opt_int_t var1_0 = NULL; d4_opt_str_t var2_0 = NULL; diff --git a/packages/codegen/test/codegen/parenthesized-type.txt b/packages/codegen/test/codegen/parenthesized-type.txt index 517c569fe..12f3d4eb1 100644 --- a/packages/codegen/test/codegen/parenthesized-type.txt +++ b/packages/codegen/test/codegen/parenthesized-type.txt @@ -9,12 +9,12 @@ main { #include #include #include -D4_REFERENCE_DECLARE(str, d4_str_t) D4_REFERENCE_DECLARE(int, int32_t) -D4_OPTIONAL_DECLARE(ref_str, d4_ref_str_t) D4_OPTIONAL_DECLARE(ref_int, d4_ref_int_t) -D4_OPTIONAL_DEFINE(ref_str, d4_ref_str_t, val, lhs_val == rhs_val, (void) val, d4_str_copy(*val)) +D4_REFERENCE_DECLARE(str, d4_str_t) +D4_OPTIONAL_DECLARE(ref_str, d4_ref_str_t) D4_OPTIONAL_DEFINE(ref_int, d4_ref_int_t, val, lhs_val == rhs_val, (void) val, d4_int_str(*val)) +D4_OPTIONAL_DEFINE(ref_str, d4_ref_str_t, val, lhs_val == rhs_val, (void) val, d4_str_copy(*val)) int main (void) { d4_opt_ref_int_t var1_0 = NULL; d4_opt_ref_str_t var2_0 = NULL; diff --git a/packages/codegen/test/codegen/property-access-expression-builtin-root.txt b/packages/codegen/test/codegen/property-access-expression-builtin-root.txt index d891d6f0a..b2e846fb5 100644 --- a/packages/codegen/test/codegen/property-access-expression-builtin-root.txt +++ b/packages/codegen/test/codegen/property-access-expression-builtin-root.txt @@ -6,4 +6,29 @@ main { test.str() } ===== code ===== +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test) +D4_OBJECT_DECLARE(Test, { + int32_t a; +}, const int32_t a) +D4_OBJECT_DEFINE(Test, Test, { + self.a = a; +}, { + result.a = self.a; +}, { + return self.a == rhs.a; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_int_str(self.a)); +}, const int32_t a) +int main (void) { + d4_obj_Test_t test_0 = d4_obj_Test_alloc(10); + d4_str_free(d4_obj_Test_str(test_0)); + d4_obj_Test_free(test_0); +} ===== output ===== diff --git a/packages/codegen/test/codegen/property-access-expression-builtin.txt b/packages/codegen/test/codegen/property-access-expression-builtin.txt index 64c6092a5..5b6812c9c 100644 --- a/packages/codegen/test/codegen/property-access-expression-builtin.txt +++ b/packages/codegen/test/codegen/property-access-expression-builtin.txt @@ -6,4 +6,30 @@ main { v := test.str() } ===== code ===== +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test) +D4_OBJECT_DECLARE(Test, { + int32_t a; +}, const int32_t a) +D4_OBJECT_DEFINE(Test, Test, { + self.a = a; +}, { + result.a = self.a; +}, { + return self.a == rhs.a; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_int_str(self.a)); +}, const int32_t a) +int main (void) { + d4_obj_Test_t test_0 = d4_obj_Test_alloc(10); + d4_str_t v_0 = d4_obj_Test_str(test_0); + d4_str_free(v_0); + d4_obj_Test_free(test_0); +} ===== output ===== diff --git a/packages/codegen/test/codegen/reference-type.txt b/packages/codegen/test/codegen/reference-type.txt index d0553f75c..d29afb914 100644 --- a/packages/codegen/test/codegen/reference-type.txt +++ b/packages/codegen/test/codegen/reference-type.txt @@ -9,8 +9,8 @@ main { #include #include #include -D4_REFERENCE_DECLARE(str, d4_str_t) D4_REFERENCE_DECLARE(int, int32_t) +D4_REFERENCE_DECLARE(str, d4_str_t) int main (void) { int32_t a_0 = 0; d4_str_t b_0 = d4_str_alloc(L"name"); diff --git a/packages/codegen/test/codegen/union-type.txt b/packages/codegen/test/codegen/union-type.txt index e36dca036..884a1dd97 100644 --- a/packages/codegen/test/codegen/union-type.txt +++ b/packages/codegen/test/codegen/union-type.txt @@ -14,28 +14,13 @@ main { #define TYPE_float 2 #define TYPE_rune 3 #define TYPE_str 4 -D4_UNION_DECLARE(runeUSstr, { - wchar_t v3; - d4_str_t v4; -}) D4_UNION_DECLARE(floatUSint, { int32_t v1; double v2; }) -D4_UNION_DEFINE(runeUSstr, { - if (self.type == TYPE_rune) self.data.v3 = va_arg(args, wchar_t); - if (self.type == TYPE_str) self.data.v4 = d4_str_copy(va_arg(args, d4_str_t)); -}, { - if (self.type == TYPE_rune) result.data.v3 = self.data.v3; - if (self.type == TYPE_str) result.data.v4 = d4_str_copy(self.data.v4); -}, { - if (self.type == TYPE_rune) return self.data.v3 == rhs.data.v3; - if (self.type == TYPE_str) return d4_str_eq(self.data.v4, rhs.data.v4); -}, { - if (self.type == TYPE_str) d4_str_free(self.data.v4); -}, { - if (self.type == TYPE_rune) return d4_rune_str(self.data.v3); - if (self.type == TYPE_str) return d4_str_copy(self.data.v4); +D4_UNION_DECLARE(runeUSstr, { + wchar_t v3; + d4_str_t v4; }) D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); @@ -52,6 +37,21 @@ D4_UNION_DEFINE(floatUSint, { if (self.type == TYPE_int) return d4_int_str(self.data.v1); if (self.type == TYPE_float) return d4_float_str(self.data.v2); }) +D4_UNION_DEFINE(runeUSstr, { + if (self.type == TYPE_rune) self.data.v3 = va_arg(args, wchar_t); + if (self.type == TYPE_str) self.data.v4 = d4_str_copy(va_arg(args, d4_str_t)); +}, { + if (self.type == TYPE_rune) result.data.v3 = self.data.v3; + if (self.type == TYPE_str) result.data.v4 = d4_str_copy(self.data.v4); +}, { + if (self.type == TYPE_rune) return self.data.v3 == rhs.data.v3; + if (self.type == TYPE_str) return d4_str_eq(self.data.v4, rhs.data.v4); +}, { + if (self.type == TYPE_str) d4_str_free(self.data.v4); +}, { + if (self.type == TYPE_rune) return d4_rune_str(self.data.v3); + if (self.type == TYPE_str) return d4_str_copy(self.data.v4); +}) int main (void) { d4_union_floatUSintUE_t var1_0 = d4_union_floatUSintUE_alloc(-1); d4_union_runeUSstrUE_t var2_0 = d4_union_runeUSstrUE_alloc(-1); From 3d141b452fcef3bc0ac9fa150a6af6c4f4bde961 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 9 Jan 2025 08:27:19 +0200 Subject: [PATCH 37/48] codegen: Fix map type generation --- packages/codegen/scripts/update-codegen-tests | 2 + packages/codegen/src/codegen | 4 + packages/codegen/src/helpers | 42 +++--- .../test/codegen/map-expression-root.txt | 24 ++-- .../codegen/test/codegen/map-expression.txt | 120 +++++++++--------- packages/codegen/test/codegen/map-type.txt | 4 +- 6 files changed, 99 insertions(+), 97 deletions(-) diff --git a/packages/codegen/scripts/update-codegen-tests b/packages/codegen/scripts/update-codegen-tests index 4c8bdbb03..19d4924d5 100644 --- a/packages/codegen/scripts/update-codegen-tests +++ b/packages/codegen/scripts/update-codegen-tests @@ -56,4 +56,6 @@ main { fs_writeFileSync(filePath, newContent.toBuffer()) } + + print("run successful") } diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 57118712f..c424a3d1f 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -2018,8 +2018,12 @@ export obj Codegen { } itemType := item.asMap() + tm := self._tm() + self._typeGen(itemType.keyType) self._typeGen(itemType.valueType) + self._typeGen(tm.createArray(itemType.keyType)) + self._typeGen(tm.createArray(itemType.valueType)) self.entities.push(CodegenEntity{ name: name, diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 8c7e0318c..271ff5771 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -254,29 +254,25 @@ export fn typeSafeForVaArg (mut tm: ref Analyzer.TypeMap, it: ref Analyzer.Type) } export fn typeShouldBeAllocated (self: ref Analyzer.Type) bool { - if self.isAlias() { - t := self.asAlias() - return typeShouldBeAllocated(t.t) - } + t := Analyzer.unwrap(self, withReference: false) return ( - self.name == "any" || - self.name == "str" || - self.isArray() || - self.isMap() || + t.name == "any" || + t.name == "str" || + t.isArray() || + t.isMap() || // TODO: test - self.isCustomObject() || - self.isOptional() || - self.isUnion() + t.isCustomObject() || + t.isOptional() || + t.isUnion() ) } export fn typeShouldBeFreed (self: ref Analyzer.Type) bool { - if self.isAlias() { - t := self.asAlias() - return typeShouldBeFreed(t.t) - } elif self.isUnion() { - t := self.asUnion() + t := Analyzer.unwrap(self, withReference: false) + + if t.isUnion() { + t := t.asUnion() loop i := 0; i < t.types.len; i++ { subType := t.types[i] @@ -290,12 +286,12 @@ export fn typeShouldBeFreed (self: ref Analyzer.Type) bool { } return - self.name == "any" || - self.name == "str" || - self.isArray() || + t.name == "any" || + t.name == "str" || + t.isArray() || // TODO: test - self.isCustomObject() || - self.isFunction() || - self.isMap() || - self.isOptional() + t.isCustomObject() || + t.isFunction() || + t.isMap() || + t.isOptional() } diff --git a/packages/codegen/test/codegen/map-expression-root.txt b/packages/codegen/test/codegen/map-expression-root.txt index e389e7403..c71e14e82 100644 --- a/packages/codegen/test/codegen/map-expression-root.txt +++ b/packages/codegen/test/codegen/map-expression-root.txt @@ -19,22 +19,22 @@ main { #include #include #include -D4_ARRAY_DECLARE(int, int32_t) -D4_ARRAY_DECLARE(float, double) -D4_ARRAY_DECLARE(rune, wchar_t) D4_ARRAY_DECLARE(bool, bool) -D4_MAP_DECLARE(str, d4_str_t, int, int32_t) -D4_MAP_DECLARE(str, d4_str_t, float, double) -D4_MAP_DECLARE(str, d4_str_t, rune, wchar_t) D4_MAP_DECLARE(str, d4_str_t, bool, bool) -D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) -D4_ARRAY_DEFINE(float, double, double, element, lhs_element == rhs_element, (void) element, d4_float_str(element)) -D4_ARRAY_DEFINE(rune, wchar_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_rune_str(element)) +D4_ARRAY_DECLARE(rune, wchar_t) +D4_MAP_DECLARE(str, d4_str_t, rune, wchar_t) +D4_ARRAY_DECLARE(float, double) +D4_MAP_DECLARE(str, d4_str_t, float, double) +D4_ARRAY_DECLARE(int, int32_t) +D4_MAP_DECLARE(str, d4_str_t, int, int32_t) D4_ARRAY_DEFINE(bool, bool, int32_t, element, lhs_element == rhs_element, (void) element, d4_bool_str(element)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), int, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), float, double, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), rune, wchar_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_rune_str(val)) D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), bool, bool, int32_t, val, lhs_val == rhs_val, (void) val, d4_bool_str(val)) +D4_ARRAY_DEFINE(rune, wchar_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_rune_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), rune, wchar_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_rune_str(val)) +D4_ARRAY_DEFINE(float, double, double, element, lhs_element == rhs_element, (void) element, d4_float_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), float, double, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) +D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), int, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) int main (void) { d4_str_t __THE_1 = d4_str_empty_val; d4_str_t __THE_2 = d4_str_empty_val; diff --git a/packages/codegen/test/codegen/map-expression.txt b/packages/codegen/test/codegen/map-expression.txt index c02aad6b6..e86b29a7c 100644 --- a/packages/codegen/test/codegen/map-expression.txt +++ b/packages/codegen/test/codegen/map-expression.txt @@ -76,70 +76,70 @@ main { #include #include #include -D4_ARRAY_DECLARE(rune, wchar_t) -D4_ARRAY_DECLARE(u64, uint64_t) -D4_ARRAY_DECLARE(u32, uint32_t) -D4_ARRAY_DECLARE(u16, uint16_t) -D4_ARRAY_DECLARE(u8, uint8_t) -D4_ARRAY_DECLARE(int, int32_t) -D4_ARRAY_DECLARE(i64, int64_t) -D4_ARRAY_DECLARE(i32, int32_t) -D4_ARRAY_DECLARE(i16, int16_t) -D4_ARRAY_DECLARE(i8, int8_t) -D4_ARRAY_DECLARE(float, double) -D4_ARRAY_DECLARE(f64, double) -D4_ARRAY_DECLARE(f32, float) -D4_ARRAY_DECLARE(char, char) -D4_ARRAY_DECLARE(byte, unsigned char) D4_ARRAY_DECLARE(bool, bool) -D4_MAP_DECLARE(str, d4_str_t, rune, wchar_t) -D4_MAP_DECLARE(str, d4_str_t, u64, uint64_t) -D4_MAP_DECLARE(str, d4_str_t, u32, uint32_t) -D4_MAP_DECLARE(str, d4_str_t, u16, uint16_t) -D4_MAP_DECLARE(str, d4_str_t, u8, uint8_t) -D4_MAP_DECLARE(str, d4_str_t, int, int32_t) -D4_MAP_DECLARE(str, d4_str_t, i64, int64_t) -D4_MAP_DECLARE(str, d4_str_t, i32, int32_t) -D4_MAP_DECLARE(str, d4_str_t, i16, int16_t) -D4_MAP_DECLARE(str, d4_str_t, i8, int8_t) -D4_MAP_DECLARE(str, d4_str_t, float, double) -D4_MAP_DECLARE(str, d4_str_t, f64, double) -D4_MAP_DECLARE(str, d4_str_t, f32, float) -D4_MAP_DECLARE(str, d4_str_t, char, char) -D4_MAP_DECLARE(str, d4_str_t, byte, unsigned char) D4_MAP_DECLARE(str, d4_str_t, bool, bool) -D4_ARRAY_DEFINE(rune, wchar_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_rune_str(element)) -D4_ARRAY_DEFINE(u64, uint64_t, uint64_t, element, lhs_element == rhs_element, (void) element, d4_u64_str(element)) -D4_ARRAY_DEFINE(u32, uint32_t, uint32_t, element, lhs_element == rhs_element, (void) element, d4_u32_str(element)) -D4_ARRAY_DEFINE(u16, uint16_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_u16_str(element)) -D4_ARRAY_DEFINE(u8, uint8_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_u8_str(element)) -D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) -D4_ARRAY_DEFINE(i64, int64_t, int64_t, element, lhs_element == rhs_element, (void) element, d4_i64_str(element)) -D4_ARRAY_DEFINE(i32, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_i32_str(element)) -D4_ARRAY_DEFINE(i16, int16_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_i16_str(element)) -D4_ARRAY_DEFINE(i8, int8_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_i8_str(element)) -D4_ARRAY_DEFINE(float, double, double, element, lhs_element == rhs_element, (void) element, d4_float_str(element)) -D4_ARRAY_DEFINE(f64, double, double, element, lhs_element == rhs_element, (void) element, d4_f64_str(element)) -D4_ARRAY_DEFINE(f32, float, double, element, lhs_element == rhs_element, (void) element, d4_f32_str(element)) -D4_ARRAY_DEFINE(char, char, int32_t, element, lhs_element == rhs_element, (void) element, d4_char_str(element)) -D4_ARRAY_DEFINE(byte, unsigned char, int32_t, element, lhs_element == rhs_element, (void) element, d4_byte_str(element)) +D4_ARRAY_DECLARE(byte, unsigned char) +D4_MAP_DECLARE(str, d4_str_t, byte, unsigned char) +D4_ARRAY_DECLARE(char, char) +D4_MAP_DECLARE(str, d4_str_t, char, char) +D4_ARRAY_DECLARE(f32, float) +D4_MAP_DECLARE(str, d4_str_t, f32, float) +D4_ARRAY_DECLARE(f64, double) +D4_MAP_DECLARE(str, d4_str_t, f64, double) +D4_ARRAY_DECLARE(float, double) +D4_MAP_DECLARE(str, d4_str_t, float, double) +D4_ARRAY_DECLARE(i8, int8_t) +D4_MAP_DECLARE(str, d4_str_t, i8, int8_t) +D4_ARRAY_DECLARE(i16, int16_t) +D4_MAP_DECLARE(str, d4_str_t, i16, int16_t) +D4_ARRAY_DECLARE(i32, int32_t) +D4_MAP_DECLARE(str, d4_str_t, i32, int32_t) +D4_ARRAY_DECLARE(i64, int64_t) +D4_MAP_DECLARE(str, d4_str_t, i64, int64_t) +D4_ARRAY_DECLARE(int, int32_t) +D4_MAP_DECLARE(str, d4_str_t, int, int32_t) +D4_ARRAY_DECLARE(u8, uint8_t) +D4_MAP_DECLARE(str, d4_str_t, u8, uint8_t) +D4_ARRAY_DECLARE(u16, uint16_t) +D4_MAP_DECLARE(str, d4_str_t, u16, uint16_t) +D4_ARRAY_DECLARE(u32, uint32_t) +D4_MAP_DECLARE(str, d4_str_t, u32, uint32_t) +D4_ARRAY_DECLARE(u64, uint64_t) +D4_MAP_DECLARE(str, d4_str_t, u64, uint64_t) +D4_ARRAY_DECLARE(rune, wchar_t) +D4_MAP_DECLARE(str, d4_str_t, rune, wchar_t) D4_ARRAY_DEFINE(bool, bool, int32_t, element, lhs_element == rhs_element, (void) element, d4_bool_str(element)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), rune, wchar_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_rune_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u64, uint64_t, uint64_t, val, lhs_val == rhs_val, (void) val, d4_u64_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u32, uint32_t, uint32_t, val, lhs_val == rhs_val, (void) val, d4_u32_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u16, uint16_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_u16_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u8, uint8_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_u8_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), int, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i64, int64_t, int64_t, val, lhs_val == rhs_val, (void) val, d4_i64_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i32, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_i32_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i16, int16_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_i16_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i8, int8_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_i8_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), float, double, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), f64, double, double, val, lhs_val == rhs_val, (void) val, d4_f64_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), f32, float, double, val, lhs_val == rhs_val, (void) val, d4_f32_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), char, char, int32_t, val, lhs_val == rhs_val, (void) val, d4_char_str(val)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), byte, unsigned char, int32_t, val, lhs_val == rhs_val, (void) val, d4_byte_str(val)) D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), bool, bool, int32_t, val, lhs_val == rhs_val, (void) val, d4_bool_str(val)) +D4_ARRAY_DEFINE(byte, unsigned char, int32_t, element, lhs_element == rhs_element, (void) element, d4_byte_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), byte, unsigned char, int32_t, val, lhs_val == rhs_val, (void) val, d4_byte_str(val)) +D4_ARRAY_DEFINE(char, char, int32_t, element, lhs_element == rhs_element, (void) element, d4_char_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), char, char, int32_t, val, lhs_val == rhs_val, (void) val, d4_char_str(val)) +D4_ARRAY_DEFINE(f32, float, double, element, lhs_element == rhs_element, (void) element, d4_f32_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), f32, float, double, val, lhs_val == rhs_val, (void) val, d4_f32_str(val)) +D4_ARRAY_DEFINE(f64, double, double, element, lhs_element == rhs_element, (void) element, d4_f64_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), f64, double, double, val, lhs_val == rhs_val, (void) val, d4_f64_str(val)) +D4_ARRAY_DEFINE(float, double, double, element, lhs_element == rhs_element, (void) element, d4_float_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), float, double, double, val, lhs_val == rhs_val, (void) val, d4_float_str(val)) +D4_ARRAY_DEFINE(i8, int8_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_i8_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i8, int8_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_i8_str(val)) +D4_ARRAY_DEFINE(i16, int16_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_i16_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i16, int16_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_i16_str(val)) +D4_ARRAY_DEFINE(i32, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_i32_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i32, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_i32_str(val)) +D4_ARRAY_DEFINE(i64, int64_t, int64_t, element, lhs_element == rhs_element, (void) element, d4_i64_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), i64, int64_t, int64_t, val, lhs_val == rhs_val, (void) val, d4_i64_str(val)) +D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), int, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ARRAY_DEFINE(u8, uint8_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_u8_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u8, uint8_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_u8_str(val)) +D4_ARRAY_DEFINE(u16, uint16_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_u16_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u16, uint16_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_u16_str(val)) +D4_ARRAY_DEFINE(u32, uint32_t, uint32_t, element, lhs_element == rhs_element, (void) element, d4_u32_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u32, uint32_t, uint32_t, val, lhs_val == rhs_val, (void) val, d4_u32_str(val)) +D4_ARRAY_DEFINE(u64, uint64_t, uint64_t, element, lhs_element == rhs_element, (void) element, d4_u64_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), u64, uint64_t, uint64_t, val, lhs_val == rhs_val, (void) val, d4_u64_str(val)) +D4_ARRAY_DEFINE(rune, wchar_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_rune_str(element)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), rune, wchar_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_rune_str(val)) int main (void) { d4_str_t __THE_1 = d4_str_empty_val; d4_str_t __THE_2 = d4_str_empty_val; diff --git a/packages/codegen/test/codegen/map-type.txt b/packages/codegen/test/codegen/map-type.txt index 22e114bc9..227e27312 100644 --- a/packages/codegen/test/codegen/map-type.txt +++ b/packages/codegen/test/codegen/map-type.txt @@ -9,11 +9,11 @@ main { #include #include D4_ARRAY_DECLARE(int, int32_t) -D4_MAP_DECLARE(str, d4_str_t, str, d4_str_t) D4_MAP_DECLARE(int, int32_t, int, int32_t) +D4_MAP_DECLARE(str, d4_str_t, str, d4_str_t) D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) -D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), str, d4_str_t, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_quoted_escape(val)) D4_MAP_DEFINE(int, int32_t, int32_t, key, lhs_key == rhs_key, (void) key, d4_int_str(key), d4_int_str(key), int, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), str, d4_str_t, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_quoted_escape(val)) int main (void) { d4_map_intMSintME_t var1_0 = d4_map_intMSintME_alloc(0); d4_map_strMSstrME_t var2_0 = d4_map_strMSstrME_alloc(0); From 8094ce8976880e8f6fee3d5acd1fbef9abcd660b Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 9 Jan 2025 09:45:43 +0200 Subject: [PATCH 38/48] codegen: Improve function* methods --- packages/codegen/src/codegen | 136 +++++++++++++++-------------------- 1 file changed, 58 insertions(+), 78 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index c424a3d1f..2afc5cc3b 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -475,120 +475,100 @@ export obj Codegen { } fn _functionCopy (mut self: ref Self, it: ref Analyzer.Type, expression: AST.CExpression) AST.CExpression { - if it.isAlias() { - t := it.asAlias() - return self._functionCopy(t.t, expression) - } elif it.name == "any" { - return AST.createCallExpression(AST.createIdentifier(self._("d4_any_copy")), [expression]) - } elif it.name == "str" { - return AST.createCallExpression(AST.createIdentifier(self._("d4_str_copy")), [expression]) - } elif ( - it.isArray() || - it.isFunction() || - it.isMap() || + t := Analyzer.unwrap(it, withReference: false) + + if ( + t.name == "any" || + t.name == "str" || + t.isArray() || + t.isFunction() || + t.isMap() || // TODO: test - it.isMethod() || + t.isMethod() || // TODO: test - it.isCustomObject() || - it.isOptional() || - it.isUnion() + t.isCustomObject() || + t.isOptional() || + t.isUnion() ) { - return AST.createCallExpression(AST.createIdentifier(self._type(it, "_copy")), [expression]) + return AST.createCallExpression(AST.createIdentifier(self._type(t, "_copy")), [expression]) } return expression } fn _functionEq (mut self: ref Self, it: ref Analyzer.Type, left: AST.CExpression, right: AST.CExpression, reverse := false) AST.CExpression { - mut result := AST.createAssignmentExpression(left, reverse ? "!=" : "==", right) + t := Analyzer.unwrap(it, withReference: false) - if it.isAlias() { - t := it.asAlias() - result = self._functionEq(t.t, left, right, reverse) - } elif it.name == "any" { - result = AST.createCallExpression(AST.createIdentifier(self._("d4_any_eq")), [left, right]) - } elif it.name == "str" { - result = AST.createCallExpression(AST.createIdentifier(self._("d4_str_eq")), [left, right]) - } elif ( - it.isArray() || + if ( + t.name == "any" || + t.name == "str" || + t.isArray() || // TODO: test - it.isFunction() || - it.isMap() || + t.isFunction() || + t.isMap() || // TODO: test - it.isMethod() || + t.isMethod() || // TODO: test - it.isCustomObject() || - it.isOptional() || - it.isUnion() + t.isCustomObject() || + t.isOptional() || + t.isUnion() ) { - result = AST.createCallExpression(AST.createIdentifier(self._type(it, "_eq")), [left, right]) - } else { - return result + result := AST.createCallExpression(AST.createIdentifier(self._type(t, "_eq")), [left, right]) + return reverse ? AST.createUnaryExpression("!", result) : result } - return reverse ? AST.createUnaryExpression("!", result) : result + return AST.createAssignmentExpression(left, reverse ? "!=" : "==", right) } fn _functionFree (mut self: ref Self, it: ref Analyzer.Type, expression: AST.CExpression) AST.CExpression { - if it.isAlias() { - t := it.asAlias() - return self._functionFree(t.t, expression) - } elif it.name == "any" { - return AST.createCallExpression(AST.createIdentifier(self._("d4_any_free")), [expression]) - } elif it.name == "str" { - return AST.createCallExpression(AST.createIdentifier(self._("d4_str_free")), [expression]) - } elif ( - it.isArray() || - it.isFunction() || - it.isMap() || + t := Analyzer.unwrap(it, withReference: false) + + if ( + t.name == "any" || + t.name == "str" || + t.isArray() || + t.isFunction() || + t.isMap() || // TODO: test - it.isMethod() || + t.isMethod() || // TODO: test - it.isCustomObject() || - it.isOptional() || - it.isUnion() + t.isCustomObject() || + t.isOptional() || + t.isUnion() ) { - return AST.createCallExpression(AST.createIdentifier(self._type(it, "_free")), [expression]) + return AST.createCallExpression(AST.createIdentifier(self._type(t, "_free")), [expression]) } return AST.createCastExpression(AST.createType("void"), expression) } fn _functionRealloc (mut self: ref Self, it: ref Analyzer.Type, left: AST.CExpression, right: AST.CExpression) AST.CExpression { - if it.isAlias() { - t := it.asAlias() - return self._functionRealloc(t.t, left, right) - } elif it.name == "any" { - return AST.createAssignmentExpression( - left, - "=", - AST.createCallExpression(AST.createIdentifier(self._("d4_any_realloc")), [left, right]), - ) - } elif it.name == "str" { - return AST.createAssignmentExpression( - left, - "=", - AST.createCallExpression(AST.createIdentifier(self._("d4_str_realloc")), [left, right]), - ) - } elif ( - it.isArray() || - it.isFunction() || - it.isMap() || + t := Analyzer.unwrap(it, withReference: false) + + if ( + t.name == "any" || + t.name == "str" || + t.isArray() || + t.isFunction() || + t.isMap() || // TODO: test - it.isMethod() || + t.isMethod() || // TODO: test - it.isCustomObject() || - it.isOptional() || - it.isUnion() + t.isCustomObject() || + t.isOptional() || + t.isUnion() ) { return AST.createAssignmentExpression( left, "=", - AST.createCallExpression(AST.createIdentifier(self._type(it, "_realloc")), [left, right]), + AST.createCallExpression( + AST.createIdentifier(self._type(t, "_realloc")), + [left, right] + ), ) } - throw error_NewError("Tried re-allocating unknown type '" + it.toString() + "'") + throw error_NewError("Tried re-allocating unknown type '" + t.toString() + "'") } fn _functionStr (mut self: ref Self, it: ref Analyzer.Type, expression: AST.CExpression, quote := false) AST.CExpression { @@ -2695,7 +2675,7 @@ export obj Codegen { t := it.asAlias() return self._type(t.t) } elif it.name == "any" { - return self._("d4_any_t") + " " + return self._("d4_any" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } elif it.name == "bool" { return self._("bool") + " " } elif it.name == "byte" { From 7d0b314030e22fc44269ab9b3f0fa3460db75c9d Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 9 Jan 2025 11:23:17 +0200 Subject: [PATCH 39/48] codegen: Simplify wrapping logic --- packages/codegen/src/codegen | 8 +++---- packages/codegen/src/helpers | 42 ++++++++++-------------------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 2afc5cc3b..b72042232 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -890,7 +890,7 @@ export obj Codegen { expression := item.asAssignment() leftType := Analyzer.unwrap(Analyzer.contextTarget(ref expression.left), withReference: false) rightType := Analyzer.unwrap(Analyzer.contextTarget(ref expression.right), withReference: false) - left := self._generateExpression(ref expression.left) + left := self._generateExpression(ref expression.left, transform: false) mut right := self._generateExpression(ref expression.right) if expression.operator.t == .OpPlusEq && (leftType.name == "str" || rightType.name == "str") { @@ -1062,7 +1062,7 @@ export obj Codegen { expression := item.asConditional() alternateType := Analyzer.unwrap(Analyzer.contextTarget(ref expression.alternate), withReference: false) - mut alternate := self._generateExpression(ref expression.alternate) + mut alternate := self._generateExpression(ref expression.alternate, transform: false) if alternate.isAssignmentExpression() && !typeSafeForTernaryAlternate(alternateType) { alternate = alternate.wrap() @@ -1070,7 +1070,7 @@ export obj Codegen { return AST.createConditionalExpression( self._generateExpression(ref expression.condition), - self._generateExpression(ref expression.consequent), + self._generateExpression(ref expression.consequent, transform: false), alternate, ) } @@ -1705,7 +1705,7 @@ export obj Codegen { initializer := statement.initializer == nil ? self._defaultInitializerDeclaration(context.varType) - : self._generateExpression(ref statement.initializer) + : self._generateExpression(ref statement.initializer, transform: false) variableName := self.blockData.setVariable(statement.name.name) diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 271ff5771..f2255cf03 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -120,45 +120,25 @@ export fn expressionResolve (item: ref Parser.Expression, up := false, down := f } export fn expressionShouldBeAllocated (item: ref Parser.Expression) bool { - itUp := expressionResolve(item, up: true) itDown := expressionResolve(item, down: true) - if !itUp.hasParent() && itUp.hasParentNode() { - parentNode := itUp.parentNode() - - if parentNode.isVariableDeclaration() { - return false - } - } - - if itUp.hasParent() { - parent := itUp.parent() - - if parent.isConditional() || parent.isPropertyAccess() { - return false - } - } - - return !itDown.isIdentifier() && !itDown.isNilLiteral() && !itDown.isAssignment() + return !( + itDown.isIdentifier() || + itDown.isNilLiteral() || + itDown.isAssignment() + ) } export fn expressionShouldBeFreed (item: ref Parser.Expression) bool { itUp := expressionResolve(item, up: true) itDown := expressionResolve(item, down: true) - if itUp.hasParent() { - return false - } - - if itUp.hasParentNode() { - parentNode := itUp.parentNode() - - if parentNode.isVariableDeclaration() { - return false - } - } - - return !itDown.isIdentifier() && !itDown.isNilLiteral() && !itDown.isAssignment() + return !( + itUp.hasParent() || + itDown.isIdentifier() || + itDown.isNilLiteral() || + itDown.isAssignment() + ) } export fn statementHasPrecedingNonVarDecl (item: ref Parser.Statement) bool { From 01747b421ff62d0e1b12ffa64b401d12d13a33ff Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Thu, 9 Jan 2025 14:58:07 +0200 Subject: [PATCH 40/48] codegen: Improve object property assignment --- packages/codegen/src/codegen | 16 +++- packages/codegen/src/helpers | 16 +++- .../test/codegen/object-expression-nested.txt | 95 +++++++++++++++++++ .../test/codegen/object-expression.txt | 67 +++++++++++++ .../property-access-expression-root.txt | 28 ++++++ .../codegen/property-access-expression.txt | 30 ++++++ 6 files changed, 246 insertions(+), 6 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index b72042232..4490de34c 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -14,6 +14,7 @@ import expressionIsBuiltinMethod, expressionResolve, expressionShouldBeAllocated, + expressionShouldBeCopied, expressionShouldBeFreed, statementHasPrecedingNonVarDecl, statementIsMacroInvocation, @@ -514,7 +515,7 @@ export obj Codegen { t.isUnion() ) { result := AST.createCallExpression(AST.createIdentifier(self._type(t, "_eq")), [left, right]) - return reverse ? AST.createUnaryExpression("!", result) : result + return reverse ? AST.createUnaryExpression("!", result) : result } return AST.createAssignmentExpression(left, reverse ? "!=" : "==", right) @@ -1703,12 +1704,20 @@ export obj Codegen { t := AST.createType(self._type(context.varType)) shouldSplitDeclaration := statementHasPrecedingNonVarDecl(item) - initializer := statement.initializer == nil + mut initializer := statement.initializer == nil ? self._defaultInitializerDeclaration(context.varType) : self._generateExpression(ref statement.initializer, transform: false) variableName := self.blockData.setVariable(statement.name.name) + if statement.initializer != nil { + statementInitializer := statement.initializer + + if typeShouldBeAllocated(context.varType) && expressionShouldBeCopied(ref statementInitializer) { + initializer = self._functionCopy(context.varType, initializer) + } + } + if typeShouldBeFreed(context.varType) { self.blockData.addTeardown( AST.createExpressionStatement( @@ -2759,10 +2768,9 @@ export obj Codegen { } fn _wrap (mut self: ref Self, mut expression: AST.CExpression, item: ref Parser.Expression, t: ref Analyzer.Type, targetType: ref Analyzer.Type, transform := false) AST.CExpression { - shouldAllocate := typeShouldBeAllocated(t) && expressionShouldBeAllocated(item) shouldFree := typeShouldBeFreed(targetType) && expressionShouldBeFreed(item) - if transform && !shouldFree && shouldAllocate { + if transform && !shouldFree && typeShouldBeAllocated(t) && expressionShouldBeAllocated(item) { expression = self._allocateExpression(t, expression) } diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index f2255cf03..74472230b 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -125,7 +125,18 @@ export fn expressionShouldBeAllocated (item: ref Parser.Expression) bool { return !( itDown.isIdentifier() || itDown.isNilLiteral() || - itDown.isAssignment() + itDown.isAssignment() || + itDown.isPropertyAccess() + ) +} + +export fn expressionShouldBeCopied (item: ref Parser.Expression) bool { + itDown := expressionResolve(item, down: true) + + return ( + itDown.isIdentifier() || + itDown.isAssignment() || + itDown.isPropertyAccess() ) } @@ -137,7 +148,8 @@ export fn expressionShouldBeFreed (item: ref Parser.Expression) bool { itUp.hasParent() || itDown.isIdentifier() || itDown.isNilLiteral() || - itDown.isAssignment() + itDown.isAssignment() || + itDown.isPropertyAccess() ) } diff --git a/packages/codegen/test/codegen/object-expression-nested.txt b/packages/codegen/test/codegen/object-expression-nested.txt index 98620a2e2..76f82bddc 100644 --- a/packages/codegen/test/codegen/object-expression-nested.txt +++ b/packages/codegen/test/codegen/object-expression-nested.txt @@ -39,4 +39,99 @@ main { t5 := Test1{} } ===== code ===== +#include +#include +#include +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test3) +D4_OBJECT_FORWARD_DECLARE(Test2) +D4_OBJECT_FORWARD_DECLARE(Test1) +D4_OBJECT_DECLARE(Test3, { + bool t1; + double t2; + int32_t t3; +}, const bool t1, const double t2, const int32_t t3) +D4_OBJECT_DECLARE(Test2, { + d4_obj_Test3_t t1; + wchar_t t2; +}, const d4_obj_Test3_t t1, const wchar_t t2) +D4_OBJECT_DECLARE(Test1, { + d4_obj_Test2_t t1; + int32_t t2; +}, const d4_obj_Test2_t t1, const int32_t t2) +D4_OBJECT_DEFINE(Test3, Test3, { + self.t1 = t1; + self.t2 = t2; + self.t3 = t3; +}, { + result.t1 = self.t1; + result.t2 = self.t2; + result.t3 = self.t3; +}, { + return self.t1 == rhs.t1 && self.t2 == rhs.t2 && self.t3 == rhs.t3; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"t1"), d4_bool_str(self.t1)); + result = d4_obj_str_append(result, d4_str_alloc(L"t2"), d4_float_str(self.t2)); + result = d4_obj_str_append(result, d4_str_alloc(L"t3"), d4_int_str(self.t3)); +}, const bool t1, const double t2, const int32_t t3) +D4_OBJECT_DEFINE(Test2, Test2, { + self.t1 = d4_obj_Test3_copy(t1); + self.t2 = t2; +}, { + result.t1 = d4_obj_Test3_copy(self.t1); + result.t2 = self.t2; +}, { + return d4_obj_Test3_eq(self.t1, rhs.t1) && self.t2 == rhs.t2; +}, { + d4_obj_Test3_free(self.t1); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"t1"), d4_obj_Test3_str(self.t1)); + result = d4_obj_str_append(result, d4_str_alloc(L"t2"), d4_rune_str(self.t2)); +}, const d4_obj_Test3_t t1, const wchar_t t2) +D4_OBJECT_DEFINE(Test1, Test1, { + self.t1 = d4_obj_Test2_copy(t1); + self.t2 = t2; +}, { + result.t1 = d4_obj_Test2_copy(self.t1); + result.t2 = self.t2; +}, { + return d4_obj_Test2_eq(self.t1, rhs.t1) && self.t2 == rhs.t2; +}, { + d4_obj_Test2_free(self.t1); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"t1"), d4_obj_Test2_str(self.t1)); + result = d4_obj_str_append(result, d4_str_alloc(L"t2"), d4_int_str(self.t2)); +}, const d4_obj_Test2_t t1, const int32_t t2) +int main (void) { + d4_obj_Test3_t __THE_1 = d4_obj_Test3_alloc(false, 0, 0); + d4_obj_Test2_t __THE_2 = d4_obj_Test2_alloc(d4_obj_Test3_alloc(false, 0, 0), L'\0'); + d4_obj_Test3_t __THE_3 = d4_obj_Test3_alloc(false, 0, 0); + d4_obj_Test2_t __THE_4 = d4_obj_Test2_alloc(d4_obj_Test3_alloc(false, 0, 0), L'\0'); + d4_obj_Test3_t __THE_5 = d4_obj_Test3_alloc(false, 0, 0); + d4_obj_Test1_t t5_0; + d4_obj_Test3_t t1_0 = d4_obj_Test3_alloc(false, 3.14, -1); + d4_obj_Test2_t t2_0 = d4_obj_Test2_alloc(t1_0, L'a'); + d4_obj_Test1_t t3_0 = d4_obj_Test1_alloc(t2_0, 10); + d4_obj_Test1_t t4_0 = d4_obj_Test1_alloc(__THE_2 = d4_obj_Test2_alloc(__THE_1 = d4_obj_Test3_alloc(false, 3.14, -1), L'a'), 2); + t4_0.t1 = d4_obj_Test2_realloc(t4_0.t1, __THE_4 = d4_obj_Test2_alloc(__THE_3 = d4_obj_Test3_alloc(true, 2.16, 1), L'y')); + t4_0.t1.t1 = d4_obj_Test3_realloc(t4_0.t1.t1, __THE_5 = d4_obj_Test3_alloc(false, 1.23, 5)); + t4_0.t1.t1.t3 = 0; + t5_0 = d4_obj_Test1_alloc(d4_obj_Test2_alloc(d4_obj_Test3_alloc(false, 0, 0), L'\0'), 0); + d4_obj_Test1_free(t5_0); + d4_obj_Test3_free(__THE_5); + d4_obj_Test2_free(__THE_4); + d4_obj_Test3_free(__THE_3); + d4_obj_Test1_free(t4_0); + d4_obj_Test2_free(__THE_2); + d4_obj_Test3_free(__THE_1); + d4_obj_Test1_free(t3_0); + d4_obj_Test2_free(t2_0); + d4_obj_Test3_free(t1_0); +} ===== output ===== diff --git a/packages/codegen/test/codegen/object-expression.txt b/packages/codegen/test/codegen/object-expression.txt index b1c5444a6..30ea7db96 100644 --- a/packages/codegen/test/codegen/object-expression.txt +++ b/packages/codegen/test/codegen/object-expression.txt @@ -19,4 +19,71 @@ main { nina.name += "Test" } ===== code ===== +#include +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Point) +D4_OBJECT_FORWARD_DECLARE(Human) +D4_OBJECT_DECLARE(Point, { + double x; + double y; +}, const double x, const double y) +D4_OBJECT_DECLARE(Human, { + d4_str_t name; + int32_t age; +}, const d4_str_t name, const int32_t age) +D4_OBJECT_DEFINE(Point, Point, { + self.x = x; + self.y = y; +}, { + result.x = self.x; + result.y = self.y; +}, { + return self.x == rhs.x && self.y == rhs.y; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"x"), d4_float_str(self.x)); + result = d4_obj_str_append(result, d4_str_alloc(L"y"), d4_float_str(self.y)); +}, const double x, const double y) +D4_OBJECT_DEFINE(Human, Human, { + self.name = d4_str_copy(name); + self.age = age; +}, { + result.name = d4_str_copy(self.name); + result.age = self.age; +}, { + return d4_str_eq(self.name, rhs.name) && self.age == rhs.age; +}, { + d4_str_free(self.name); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); + result = d4_obj_str_append(result, d4_str_alloc(L"age"), d4_int_str(self.age)); +}, const d4_str_t name, const int32_t age) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_obj_Human_t tina_0; + d4_str_t __THE_2 = d4_str_empty_val; + d4_obj_Human_t nina_0; + d4_str_t __THE_3 = d4_str_empty_val; + d4_str_t __THE_4 = d4_str_empty_val; + d4_obj_Point_t a1_0 = d4_obj_Point_alloc(37.7681251, -((bool) 122.5138329)); + d4_obj_Point_t a2_0 = d4_obj_Point_alloc(37.7681251, -((bool) 122.5138329)); + a1_0.x += 2; + a2_0.y /= 0.2; + tina_0 = d4_obj_Human_alloc(__THE_1 = d4_str_alloc(L"Tina"), 45); + nina_0 = d4_obj_Human_alloc(__THE_2 = d4_str_alloc(L"Nina"), 33); + nina_0.name = d4_str_realloc(nina_0.name, __THE_4 = d4_str_concat(nina_0.name, __THE_3 = d4_str_alloc(L"Test"))); + d4_str_free(__THE_4); + d4_str_free(__THE_3); + d4_obj_Human_free(nina_0); + d4_str_free(__THE_2); + d4_obj_Human_free(tina_0); + d4_str_free(__THE_1); + d4_obj_Point_free(a2_0); + d4_obj_Point_free(a1_0); +} ===== output ===== diff --git a/packages/codegen/test/codegen/property-access-expression-root.txt b/packages/codegen/test/codegen/property-access-expression-root.txt index 59cbb423f..18b2892b1 100644 --- a/packages/codegen/test/codegen/property-access-expression-root.txt +++ b/packages/codegen/test/codegen/property-access-expression-root.txt @@ -4,6 +4,34 @@ obj Test { main { test := Test{c: "test"} test.c + test.c } ===== code ===== +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test) +D4_OBJECT_DECLARE(Test, { + d4_str_t c; +}, const d4_str_t c) +D4_OBJECT_DEFINE(Test, Test, { + self.c = d4_str_copy(c); +}, { + result.c = d4_str_copy(self.c); +}, { + return d4_str_eq(self.c, rhs.c); +}, { + d4_str_free(self.c); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"c"), d4_str_quoted_escape(self.c)); +}, const d4_str_t c) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_obj_Test_t test_0 = d4_obj_Test_alloc(__THE_1 = d4_str_alloc(L"test")); + test_0.c; + test_0.c; + d4_obj_Test_free(test_0); + d4_str_free(__THE_1); +} ===== output ===== diff --git a/packages/codegen/test/codegen/property-access-expression.txt b/packages/codegen/test/codegen/property-access-expression.txt index e41c86d82..f1dc259c0 100644 --- a/packages/codegen/test/codegen/property-access-expression.txt +++ b/packages/codegen/test/codegen/property-access-expression.txt @@ -4,6 +4,36 @@ obj Test { main { test := Test{c: "test"} v := test.c + w := test.c } ===== code ===== +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test) +D4_OBJECT_DECLARE(Test, { + d4_str_t c; +}, const d4_str_t c) +D4_OBJECT_DEFINE(Test, Test, { + self.c = d4_str_copy(c); +}, { + result.c = d4_str_copy(self.c); +}, { + return d4_str_eq(self.c, rhs.c); +}, { + d4_str_free(self.c); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"c"), d4_str_quoted_escape(self.c)); +}, const d4_str_t c) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_obj_Test_t test_0 = d4_obj_Test_alloc(__THE_1 = d4_str_alloc(L"test")); + d4_str_t v_0 = d4_str_copy(test_0.c); + d4_str_t w_0 = d4_str_copy(test_0.c); + d4_str_free(w_0); + d4_str_free(v_0); + d4_obj_Test_free(test_0); + d4_str_free(__THE_1); +} ===== output ===== From ca6b35b75b81bd4b66c65b1363607f7b7f3cdddc Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Fri, 10 Jan 2025 16:57:10 +0200 Subject: [PATCH 41/48] codegen: CallExpression generation for builtin functions --- packages/codegen/src/codegen | 125 +++++++++++++++++- packages/codegen/src/helpers | 6 + .../codegen/object-declaration-mutable.txt | 92 +++++++++++++ 3 files changed, 217 insertions(+), 6 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 4490de34c..e53301b0e 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -974,6 +974,7 @@ export obj Codegen { } contextExtra := item.contextExtra as Analyzer.CallExpressionContextExtra + isBuiltinMethod := expressionIsBuiltinMethod(ref expression.callee) if expressionIsBuiltinMethod(ref expression.callee) { calleeBody := expression.callee.asPropertyAccess() @@ -988,8 +989,25 @@ export obj Codegen { calleeExpression := self._generateExpression(ref expression.callee) mut paramsArgs: AST.CExpression[] - if calleeTargetType.hasSelfParam() { + if !isBuiltinMethod && calleeTargetType.hasSelfParam() { paramsArgs.push(calleeExpression) + } elif calleeTargetType.hasSelfParam() { + methodType := calleeTargetType.asMethod() + calleeBody := expression.callee.asPropertyAccess() + calleeBodyTargetType := Analyzer.contextTarget(ref calleeBody.expression) + cCalleeBodyExpression := self._generateExpression(ref calleeBody.expression) + + cSelfExpression := methodType.selfType.isReference() + ? self._wrap( + cCalleeBodyExpression, + ref calleeBody.expression, + calleeBodyTargetType, + methodType.selfType, + transform: false, + ) + : cCalleeBodyExpression + + paramsArgs.push(cSelfExpression) } loop i := 0; i < contextExtra.parameters.len; i++ { @@ -1035,9 +1053,26 @@ export obj Codegen { } } - mut args: (AST.CExpression | AST.CType)[] = [ - createASTPropertyAccess(calleeExpression, "ctx") - ] + mut args: (AST.CExpression | AST.CType)[] = [] + + if isBuiltinMethod { + calleeBody := expression.callee.asPropertyAccess() + calleeBodyTargetType := Analyzer.contextTarget(ref calleeBody.expression) + + if !paramsArgs.empty { + loop i := 0; i < paramsArgs.len; i++ { + paramsArg := paramsArgs[i] as AST.CExpression + args.push(paramsArg) + } + } + + return AST.createCallExpression( + AST.createIdentifier(self._type(calleeBodyTargetType, "_" + calleeBody.name.name)), + args, + ) + } + + args.push(createASTPropertyAccess(calleeExpression, "ctx")) if !paramsArgs.empty { paramsName := self._type(calleeTargetType, "_params_t") @@ -2686,39 +2721,115 @@ export obj Codegen { } elif it.name == "any" { return self._("d4_any" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } elif it.name == "bool" { + if postfix == "_t" { return self._("bool") + " " + } else { + return self._("d4_bool" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "byte" { + if postfix == "_t" { return "unsigned char " + } else { + return self._("d4_byte" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "char" { + if postfix == "_t" { return "char " + } else { + return self._("d4_char" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "f32" { + if postfix == "_t" { return "float " - } elif it.name == "f64" || it.name == "float" { + } else { + return self._("d4_f32" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } + } elif it.name == "f64" { + if postfix == "_t" { return "double " + } else { + return self._("d4_f64" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } + } elif it.name == "float" { + if postfix == "_t" { + return "double " + } else { + return self._("d4_float" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "i8" { + if postfix == "_t" { return self._("int8_t") + " " + } else { + return self._("d4_i8" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "i16" { + if postfix == "_t" { return self._("int16_t") + " " - } elif it.name == "i32" || it.name == "int" { + } else { + return self._("d4_i16" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } + } elif it.name == "i32" { + if postfix == "_t" { return self._("int32_t") + " " + } else { + return self._("d4_i32" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } + } elif it.name == "int" { + if postfix == "_t" { + return self._("int32_t") + " " + } else { + return self._("d4_int" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "i64" { + if postfix == "_t" { return self._("int64_t") + " " + } else { + return self._("d4_i64" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "isize" { + if postfix == "_t" { return self._("ptrdiff_t") + " " + } else { + return self._("d4_isize" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "rune" { + if postfix == "_t" { return self._("wchar_t") + " " + } else { + return self._("d4_rune" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "str" { return self._("d4_str" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } elif it.name == "u8" { + if postfix == "_t" { return self._("uint8_t") + " " + } else { + return self._("d4_u8" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "u16" { + if postfix == "_t" { return self._("uint16_t") + " " + } else { + return self._("d4_u16" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "u32" { + if postfix == "_t" { return self._("uint32_t") + " " + } else { + return self._("d4_u32" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "u64" { + if postfix == "_t" { return self._("uint64_t") + " " + } else { + return self._("d4_u64" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "usize" { + if postfix == "_t" { return self._("size_t") + " " + } else { + return self._("d4_usize" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") + } } elif it.name == "void" { return "void " } elif ( @@ -2789,6 +2900,8 @@ export obj Codegen { expression = AST.createUnaryExpression("*", innerExpression.wrap()) } elif !targetType.isReference() && t.isReference() { expression = AST.createUnaryExpression("*", expression) + } elif targetType.isReference() && !t.isReference() { + expression = AST.createUnaryExpression("&", expression) } elif targetType.isUnion() && !t.isUnion() { allocTargetType := self._type(targetType, "_alloc") typeDef := self._typeDef(t) diff --git a/packages/codegen/src/helpers b/packages/codegen/src/helpers index 74472230b..16d8264cd 100644 --- a/packages/codegen/src/helpers +++ b/packages/codegen/src/helpers @@ -126,6 +126,8 @@ export fn expressionShouldBeAllocated (item: ref Parser.Expression) bool { itDown.isIdentifier() || itDown.isNilLiteral() || itDown.isAssignment() || + itDown.isElementAccess() || + itDown.isMemberAccess() || itDown.isPropertyAccess() ) } @@ -136,6 +138,8 @@ export fn expressionShouldBeCopied (item: ref Parser.Expression) bool { return ( itDown.isIdentifier() || itDown.isAssignment() || + itDown.isElementAccess() || + itDown.isMemberAccess() || itDown.isPropertyAccess() ) } @@ -149,6 +153,8 @@ export fn expressionShouldBeFreed (item: ref Parser.Expression) bool { itDown.isIdentifier() || itDown.isNilLiteral() || itDown.isAssignment() || + itDown.isElementAccess() || + itDown.isMemberAccess() || itDown.isPropertyAccess() ) } diff --git a/packages/codegen/test/codegen/object-declaration-mutable.txt b/packages/codegen/test/codegen/object-declaration-mutable.txt index 1261cf57f..a1f36fd7d 100644 --- a/packages/codegen/test/codegen/object-declaration-mutable.txt +++ b/packages/codegen/test/codegen/object-declaration-mutable.txt @@ -26,4 +26,96 @@ main { print(person3) } ===== code ===== +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TYPE_obj_Human 1 +D4_OBJECT_FORWARD_DECLARE(Animal) +D4_OBJECT_FORWARD_DECLARE(Human) +D4_OBJECT_DECLARE(Animal, { + d4_str_t name; + int32_t age; +}, const d4_str_t name, const int32_t age) +D4_ARRAY_DECLARE(obj_Animal, d4_obj_Animal_t) +D4_OBJECT_DECLARE(Human, { + d4_str_t name; + d4_arr_obj_Animal_t pets; +}, const d4_str_t name, const d4_arr_obj_Animal_t pets) +D4_ANY_DECLARE(obj_Human, d4_obj_Human_t) +D4_OBJECT_DEFINE(Animal, Animal, { + self.name = d4_str_copy(name); + self.age = age; +}, { + result.name = d4_str_copy(self.name); + result.age = self.age; +}, { + return d4_str_eq(self.name, rhs.name) && self.age == rhs.age; +}, { + d4_str_free(self.name); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); + result = d4_obj_str_append(result, d4_str_alloc(L"age"), d4_int_str(self.age)); +}, const d4_str_t name, const int32_t age) +D4_ARRAY_DEFINE(obj_Animal, d4_obj_Animal_t, d4_obj_Animal_t, d4_obj_Animal_copy(element), d4_obj_Animal_eq(lhs_element, rhs_element), d4_obj_Animal_free(element), d4_obj_Animal_str(element)) +D4_OBJECT_DEFINE(Human, Human, { + self.name = d4_str_copy(name); + self.pets = d4_arr_obj_Animal_copy(pets); +}, { + result.name = d4_str_copy(self.name); + result.pets = d4_arr_obj_Animal_copy(self.pets); +}, { + return d4_str_eq(self.name, rhs.name) && d4_arr_obj_Animal_eq(self.pets, rhs.pets); +}, { + d4_str_free(self.name); + d4_arr_obj_Animal_free(self.pets); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"name"), d4_str_quoted_escape(self.name)); + result = d4_obj_str_append(result, d4_str_alloc(L"pets"), d4_arr_obj_Animal_str(self.pets)); +}, const d4_str_t name, const d4_arr_obj_Animal_t pets) +D4_ANY_DEFINE(TYPE_obj_Human, obj_Human, d4_obj_Human_t, d4_obj_Human_copy(val), d4_obj_Human_eq(lhs_val, rhs_val), d4_obj_Human_free(val), d4_obj_Human_str(val)) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_str_t __THE_2 = d4_str_empty_val; + d4_str_t __THE_3 = d4_str_empty_val; + d4_obj_Animal_t __THE_4 = d4_obj_Animal_alloc(d4_str_empty_val, 0); + d4_str_t __THE_5 = d4_str_empty_val; + d4_obj_Animal_t __THE_6 = d4_obj_Animal_alloc(d4_str_empty_val, 0); + d4_str_t __THE_7 = d4_str_empty_val; + d4_obj_Animal_t __THE_8 = d4_obj_Animal_alloc(d4_str_empty_val, 0); + d4_obj_Human_t person3_0; + d4_obj_Human_t person1_0 = d4_obj_Human_alloc(__THE_1 = d4_str_alloc(L"Peter"), d4_arr_obj_Animal_alloc(0)); + d4_obj_Human_t person2_0 = d4_obj_Human_alloc(__THE_2 = d4_str_alloc(L"Brian"), d4_arr_obj_Animal_alloc(0)); + d4_arr_obj_Animal_push(&person1_0.pets, d4_arr_obj_Animal_alloc(1, __THE_4 = d4_obj_Animal_alloc(__THE_3 = d4_str_alloc(L"Cleo"), 1))); + d4_arr_obj_Animal_push(&person1_0.pets, d4_arr_obj_Animal_alloc(1, __THE_6 = d4_obj_Animal_alloc(__THE_5 = d4_str_alloc(L"Bella"), 1))); + (*d4_arr_obj_Animal_at(&d4_err_state, 16, 3, person1_0.pets, 0)).age++; + d4_arr_obj_Animal_push(&person2_0.pets, d4_arr_obj_Animal_alloc(1, __THE_8 = d4_obj_Animal_alloc(__THE_7 = d4_str_alloc(L"Luxor"), 10))); + person3_0 = d4_obj_Human_copy(person1_0); + person1_0 = d4_obj_Human_realloc(person1_0, person2_0); + person2_0 = d4_obj_Human_realloc(person2_0, person3_0); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, d4_any_obj_Human_alloc(person1_0)), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, d4_any_obj_Human_alloc(person2_0)), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_print.func(d4_print.ctx, d4_safe_calloc(&(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t) {d4_arr_any_alloc(1, d4_any_obj_Human_alloc(person3_0)), 0, d4_str_empty_val, 0, d4_str_empty_val, 0, d4_str_empty_val}, sizeof(d4_fn_sFP4arr_anyFP1strFP1strFP1strFRvoidFE_params_t))); + d4_obj_Human_free(person3_0); + d4_obj_Animal_free(__THE_8); + d4_str_free(__THE_7); + d4_obj_Animal_free(__THE_6); + d4_str_free(__THE_5); + d4_obj_Animal_free(__THE_4); + d4_str_free(__THE_3); + d4_obj_Human_free(person2_0); + d4_str_free(__THE_2); + d4_obj_Human_free(person1_0); + d4_str_free(__THE_1); +} ===== output ===== +Human{name: "Brian", pets: [Animal{name: "Luxor", age: 10}]} +Human{name: "Peter", pets: [Animal{name: "Cleo", age: 2}, Animal{name: "Bella", age: 1}]} +Human{name: "Peter", pets: [Animal{name: "Cleo", age: 2}, Animal{name: "Bella", age: 1}]} From dfcdda65ae7c93bbf925477f0fe153475c5732e4 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 11 Jan 2025 09:38:20 +0200 Subject: [PATCH 42/48] analyzer: Properly type cast nested ElementAccess --- packages/analyzer/src/analyzer | 11 +++- ...-access-expression-with-element-access.txt | 62 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt diff --git a/packages/analyzer/src/analyzer b/packages/analyzer/src/analyzer index 33873fd90..f97fdfde4 100644 --- a/packages/analyzer/src/analyzer +++ b/packages/analyzer/src/analyzer @@ -1684,7 +1684,16 @@ export obj AnalyzerFile { } mut expression := it.asElementAccess() - expressionType := Type.unwrap(self.e(ref expression.expression, withMutable: withMutable || withMutableNext)) + niceExpressionType := Type.unwrap(self.eNice(ref expression.expression)) + + expressionType := Type.unwrap( + self.e( + ref expression.expression, + Type.opt(niceExpressionType), + withMutable: withMutable || withMutableNext, + ), + ) + argumentType := Type.unwrap(self.e(ref expression.argument)) if !Type.similarTo(argumentType, self.tm.get("i32")) { diff --git a/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt b/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt new file mode 100644 index 000000000..06107fdec --- /dev/null +++ b/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt @@ -0,0 +1,62 @@ +obj Human { + name: str + mut pets: Animal[] +} +obj Animal { + name: str + mut age: int +} +main { + mut people: Human[][] + people.push([]) + people[0].push(Human{name: "Peter"}) + people[0][0].pets.push(Animal{name: "Cleo", age: 1}) + people[0][0].pets[0].age++ +} +===== out ===== +ObjectDeclaration(name=Human context:selfType=Human) + ObjectDeclarationProperty(name=name mutable=false constant=false type=str) + ObjectDeclarationProperty(name=pets mutable=true constant=false type=Animal[]) +ObjectDeclaration(name=Animal context:selfType=Animal) + ObjectDeclarationProperty(name=name mutable=false constant=false type=str) + ObjectDeclarationProperty(name=age mutable=true constant=false type=int) +MainDeclaration() + VariableDeclaration(name=people mutable=true constant=false type=Human[][] context:varType=Human[][]) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) + PropertyAccessExpression(initial=(mut self: ref Human[][], elements: Human[]...) -> void name=push extra:propertyName=push extra:propertyType=(mut self: ref Human[][], elements: Human[]...) -> void extra:propertyBuiltin) + Identifier(initial=Human[][] name=people) + CallExpressionArgument(extra:parameterIdx=0) + ArrayExpression(initial=Human[]) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) + PropertyAccessExpression(initial=(mut self: ref Human[], elements: Human...) -> void name=push extra:propertyName=push extra:propertyType=(mut self: ref Human[], elements: Human...) -> void extra:propertyBuiltin) + ElementAccessExpression(initial=ref Human[] target=Human[] extra:selfType=Human[][]) + Identifier(initial=Human[][] name=people) + IntegerLiteral(initial=int value=0) + CallExpressionArgument(extra:parameterIdx=0) + ObjectExpression(initial=Human id=Human) + ObjectExpressionProperty(name=name) + StringLiteral(initial=str value=Peter) + CallExpression(initial=void extra:asynchronous=false extra:returnType=void) + PropertyAccessExpression(initial=(mut self: ref Animal[], elements: Animal...) -> void name=push extra:propertyName=push extra:propertyType=(mut self: ref Animal[], elements: Animal...) -> void extra:propertyBuiltin) + PropertyAccessExpression(initial=Animal[] name=pets extra:propertyName=pets extra:propertyType=Animal[] extra:propertyMutable) + ElementAccessExpression(initial=ref Human target=Human extra:selfType=Human[]) + ElementAccessExpression(initial=ref Human[] target=Human[] extra:selfType=Human[][]) + Identifier(initial=Human[][] name=people) + IntegerLiteral(initial=int value=0) + IntegerLiteral(initial=int value=0) + CallExpressionArgument(extra:parameterIdx=0) + ObjectExpression(initial=Animal id=Animal) + ObjectExpressionProperty(name=name) + StringLiteral(initial=str value=Cleo) + ObjectExpressionProperty(name=age) + IntegerLiteral(initial=int value=1) + UnaryExpression(initial=int operator=++ prefix=false) + PropertyAccessExpression(initial=int name=age extra:propertyName=age extra:propertyType=int extra:propertyMutable) + ElementAccessExpression(initial=ref Animal target=Animal extra:selfType=Animal[]) + PropertyAccessExpression(initial=Animal[] name=pets extra:propertyName=pets extra:propertyType=Animal[] extra:propertyMutable) + ElementAccessExpression(initial=ref Human target=Human extra:selfType=Human[]) + ElementAccessExpression(initial=ref Human[] target=Human[] extra:selfType=Human[][]) + Identifier(initial=Human[][] name=people) + IntegerLiteral(initial=int value=0) + IntegerLiteral(initial=int value=0) + IntegerLiteral(initial=int value=0) From df15433dd7958569e29e769ada75e9deadad1d1a Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 11 Jan 2025 11:48:59 +0200 Subject: [PATCH 43/48] analyzer: Drop mutableNext logic --- packages/analyzer/src/analyzer | 98 +++++++++---------- .../call-expression-with-method-type.txt | 60 ++++++++++++ .../call-expression-with-reference-type.txt | 19 ++++ ...-access-expression-with-element-access.txt | 6 +- 4 files changed, 129 insertions(+), 54 deletions(-) create mode 100644 packages/analyzer/test/type-casting/call-expression-with-method-type.txt create mode 100644 packages/analyzer/test/type-casting/call-expression-with-reference-type.txt diff --git a/packages/analyzer/src/analyzer b/packages/analyzer/src/analyzer index f97fdfde4..1137e36f1 100644 --- a/packages/analyzer/src/analyzer +++ b/packages/analyzer/src/analyzer @@ -392,27 +392,26 @@ export obj AnalyzerFile { noTargetPromote := false, noTypeCast := false, withMutable := false, - withMutableNext := false, ) ref Type.Type { if ( self._eIdentifier(it, targetType, noTypeCast, withMutable) || self._eLiteral(it, targetType) || self._eArray(it, targetType) || - self._eAs(it, targetType, withMutable, withMutableNext) || + self._eAs(it, targetType, withMutable) || self._eAssignment(it, targetType) || self._eAwait(it, targetType) || self._eBinary(it, targetType) || self._eCall(it, targetType) || self._eClosure(it, targetType) || - self._eConditional(it, targetType, withMutable, withMutableNext) || - self._eElementAccess(it, targetType, noTypeCast, withMutable, withMutableNext) || - self._eIs(it, targetType, withMutable, withMutableNext) || + self._eConditional(it, targetType, withMutable) || + self._eElementAccess(it, targetType, noTypeCast, withMutable) || + self._eIs(it, targetType, withMutable) || self._eMap(it, targetType) || self._eMemberAccess(it, targetType) || self._eObject(it, targetType) || - self._eParenthesized(it, targetType, noTargetPromote, withMutable, withMutableNext) || - self._ePropertyAccess(it, targetType, noTypeCast, withMutable, withMutableNext) || - self._eReference(it, targetType, withMutable, withMutableNext) || + self._eParenthesized(it, targetType, noTargetPromote, withMutable) || + self._ePropertyAccess(it, targetType, noTypeCast, withMutable) || + self._eReference(it, targetType, withMutable) || self._eUnary(it, targetType) ) { if !noTargetPromote && targetType != nil { @@ -1276,7 +1275,7 @@ export obj AnalyzerFile { return true } - fn _eAs (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool, withMutableNext: bool) bool { + fn _eAs (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool) bool { if !it.isAs() { return false } @@ -1288,7 +1287,7 @@ export obj AnalyzerFile { ref expression.expression, Type.opt(t), noTargetPromote: true, - withMutable: withMutable || withMutableNext, + withMutable: withMutable, ) if !expressionType.canCastTo(t) { @@ -1455,6 +1454,7 @@ export obj AnalyzerFile { initialErrorsLen := self.analyzer.errors.len mut expression := it.asCall() calleeType := Type.unwrap(self.e(ref expression.callee)) + Context.setTarget(ref expression.callee, calleeType) mut asynchronous: bool mut parameters: Type.TypeParameter[] mut returnType := self.tm.get("void") @@ -1472,7 +1472,18 @@ export obj AnalyzerFile { if t.withSelf && t.selfMutable && t.selfType.isReference() { self.analyzer.errors = self.analyzer.errors.slice(0, initialErrorsLen) - self.e(ref expression.callee, withMutableNext: true) + + selfReferenceType := t.selfType.asReference() + innerExpressions := Parser.getInnerExpressions(ref expression.callee) + + loop i := 0; i < innerExpressions.len; i++ { + innerExpression := innerExpressions[i] as ref Parser.Expression + innerExpressionType := self.e(innerExpression, withMutable: true) + + if Type.match(selfReferenceType.t, innerExpressionType) { + Context.setTarget(innerExpression, t.selfType) + } + } } } else { Context.set(it, self.tm.get("void")) @@ -1628,7 +1639,7 @@ export obj AnalyzerFile { return true } - fn _eConditional (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool, withMutableNext: bool) bool { + fn _eConditional (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool) bool { if !it.isConditional() { return false } @@ -1647,14 +1658,14 @@ export obj AnalyzerFile { guessTypeResult := self.guessType((operandType: (ref Type.Type)?) -> ref Type.Type { self.tc.merge(typeCastResult.consequent) - result := self.e(ref expression.consequent, operandType, withMutable: withMutable || withMutableNext) + result := self.e(ref expression.consequent, operandType, withMutable: withMutable) consequentDiffTC = self.typeCastExclude(afterConditionTC) self.tc = afterConditionTC return result }, (operandType: (ref Type.Type)?) -> ref Type.Type { self.tc.merge(typeCastResult.alternate) - result := self.e(ref expression.alternate, operandType, withMutable: withMutable || withMutableNext) + result := self.e(ref expression.alternate, operandType, withMutable: withMutable) alternateDiffTC = self.typeCastExclude(afterConditionTC) self.tc = afterConditionTC @@ -1678,22 +1689,14 @@ export obj AnalyzerFile { return true } - fn _eElementAccess (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, noTypeCast: bool, withMutable: bool, withMutableNext: bool) bool { + fn _eElementAccess (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, noTypeCast: bool, withMutable: bool) bool { if !it.isElementAccess() { return false } mut expression := it.asElementAccess() - niceExpressionType := Type.unwrap(self.eNice(ref expression.expression)) - - expressionType := Type.unwrap( - self.e( - ref expression.expression, - Type.opt(niceExpressionType), - withMutable: withMutable || withMutableNext, - ), - ) - + expressionType := Type.unwrap(self.e(ref expression.expression, withMutable: withMutable)) + Context.setTarget(ref expression.expression, expressionType) argumentType := Type.unwrap(self.e(ref expression.argument)) if !Type.similarTo(argumentType, self.tm.get("i32")) { @@ -1732,13 +1735,13 @@ export obj AnalyzerFile { return true } - fn _eIs (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool, withMutableNext: bool) bool { + fn _eIs (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool) bool { if !it.isIs() { return false } mut expression := it.asIs() - expressionType := self.e(ref expression.expression, withMutable: withMutable || withMutableNext) + expressionType := self.e(ref expression.expression, withMutable: withMutable) t := self.t(ref expression.t) if !expressionType.canCastTo(t) { @@ -1888,7 +1891,7 @@ export obj AnalyzerFile { return true } - fn _eParenthesized (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, noTargetPromote: bool, withMutable: bool, withMutableNext: bool) bool { + fn _eParenthesized (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, noTargetPromote: bool, withMutable: bool) bool { if !it.isParenthesized() { return false } @@ -1899,7 +1902,7 @@ export obj AnalyzerFile { ref expression.expression, targetType, noTargetPromote: noTargetPromote, - withMutable: withMutable || withMutableNext, + withMutable: withMutable, ) Context.set(it, Context.initial(ref expression.expression)) @@ -1908,30 +1911,23 @@ export obj AnalyzerFile { return true } - fn _ePropertyAccess (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, noTypeCast: bool, withMutable: bool, withMutableNext: bool) bool { + fn _ePropertyAccess (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, noTypeCast: bool, withMutable: bool) bool { if !it.isPropertyAccess() { return false } mut expression := it.asPropertyAccess() - niceExpressionType := self.eNice(ref expression.expression) - - expressionType := self.e( - ref expression.expression, - Type.opt(Type.unwrap(niceExpressionType)), - withMutable: withMutable || withMutableNext, - ) - - t := Type.unwrap(expressionType) + expressionType := Type.unwrap(self.e(ref expression.expression, withMutable: withMutable)) + Context.setTarget(ref expression.expression, expressionType) - if t.has(expression.name.name) { - if !t.has(expression.name.name) { + if expressionType.has(expression.name.name) { + if !expressionType.has(expression.name.name) { Context.set(it, targetType == nil ? self.tm.get("any") : targetType) - self.raise(errors.E1002(expression.name.name, t.toString()), expression.name.start, expression.name.end) + self.raise(errors.E1002(expression.name.name, expressionType.toString()), expression.name.start, expression.name.end) return true } - property := t.get(expression.name.name) + property := expressionType.get(expression.name.name) if withMutable && !property.mutable { self.raise(errors.E1012(expression.name.name), it.start, it.end) @@ -1939,16 +1935,16 @@ export obj AnalyzerFile { Context.set(it, property.t) Context.setExtra(it, Context.PropertyAccessExpressionContextExtra{property: property}) - } elif t.isEnum() { - Context.set(it, t) + } elif expressionType.isEnum() { + Context.set(it, expressionType) - if !t.hasEnumerator(expression.name.name) { + if !expressionType.hasEnumerator(expression.name.name) { self.raise(errors.E1021(expression.name.name), expression.name.start, expression.name.end) } return true - } elif t.isNamespace() { - namespaceType := t.asNamespace() + } elif expressionType.isNamespace() { + namespaceType := expressionType.asNamespace() mut maybeExportVar: Type.Var? if AnalyzerExport.has(ref namespaceType.members, expression.name.name) { @@ -1980,7 +1976,7 @@ export obj AnalyzerFile { Context.set(it, exportVar.t) } else { Context.set(it, targetType == nil ? self.tm.get("any") : targetType) - self.raise(errors.E1002(expression.name.name, t.toString()), expression.name.start, expression.name.end) + self.raise(errors.E1002(expression.name.name, expressionType.toString()), expression.name.start, expression.name.end) return true } @@ -2003,13 +1999,13 @@ export obj AnalyzerFile { return true } - fn _eReference (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool, withMutableNext: bool) bool { + fn _eReference (mut self: ref Self, mut it: ref Parser.Expression, targetType: (ref Type.Type)?, withMutable: bool) bool { if !it.isReference() { return false } mut expression := it.asReference() - expressionType := self.e(ref expression.expression, withMutable: withMutable || withMutableNext) + expressionType := self.e(ref expression.expression, withMutable: withMutable) Context.set(it, self.tm.createReference(expressionType)) return true diff --git a/packages/analyzer/test/type-casting/call-expression-with-method-type.txt b/packages/analyzer/test/type-casting/call-expression-with-method-type.txt new file mode 100644 index 000000000..2d37fbba7 --- /dev/null +++ b/packages/analyzer/test/type-casting/call-expression-with-method-type.txt @@ -0,0 +1,60 @@ +obj Example { + mut a: int + fn test1 (self: Self) int { return self.a } + fn test2 (mut self: Self) int { return self.a } + fn test3 (self: ref Self) int { return self.a } + fn test4 (mut self: ref Self) int { return self.a } +} + +main { + mut example := Example{a: 10} + + b := example.test1() + c := example.test2() + d := example.test3() + e := example.test4() +} +===== out ===== +ObjectDeclaration(name=Example context:selfType=Example) + ObjectDeclarationProperty(name=a mutable=true constant=false type=int) + ObjectDeclarationMethod(asynchronous=false name=test1 return=int) + FunctionParameter(name=self type=Self mutable=false variadic=false) + ReturnStatement() + PropertyAccessExpression(initial=int name=a extra:propertyName=a extra:propertyType=int extra:propertyMutable) + Identifier(initial=Example name=self) + ObjectDeclarationMethod(asynchronous=false name=test2 return=int) + FunctionParameter(name=self type=Self mutable=true variadic=false) + ReturnStatement() + PropertyAccessExpression(initial=int name=a extra:propertyName=a extra:propertyType=int extra:propertyMutable) + Identifier(initial=Example name=self) + ObjectDeclarationMethod(asynchronous=false name=test3 return=int) + FunctionParameter(name=self type=ref Self mutable=false variadic=false) + ReturnStatement() + PropertyAccessExpression(initial=int name=a extra:propertyName=a extra:propertyType=int extra:propertyMutable) + Identifier(initial=ref Example target=Example name=self) + ObjectDeclarationMethod(asynchronous=false name=test4 return=int) + FunctionParameter(name=self type=ref Self mutable=true variadic=false) + ReturnStatement() + PropertyAccessExpression(initial=int name=a extra:propertyName=a extra:propertyType=int extra:propertyMutable) + Identifier(initial=ref Example target=Example name=self) +MainDeclaration() + VariableDeclaration(name=example mutable=true constant=false context:varType=Example) + ObjectExpression(initial=Example id=Example) + ObjectExpressionProperty(name=a) + IntegerLiteral(initial=int value=10) + VariableDeclaration(name=b mutable=false constant=false context:varType=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) + PropertyAccessExpression(initial=(self: Example) -> int name=test1 extra:propertyName=test1 extra:propertyType=(self: Example) -> int) + Identifier(initial=Example name=example) + VariableDeclaration(name=c mutable=false constant=false context:varType=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) + PropertyAccessExpression(initial=(mut self: Example) -> int name=test2 extra:propertyName=test2 extra:propertyType=(mut self: Example) -> int) + Identifier(initial=Example name=example) + VariableDeclaration(name=d mutable=false constant=false context:varType=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) + PropertyAccessExpression(initial=(self: ref Example) -> int name=test3 extra:propertyName=test3 extra:propertyType=(self: ref Example) -> int) + Identifier(initial=Example name=example) + VariableDeclaration(name=e mutable=false constant=false context:varType=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) + PropertyAccessExpression(initial=(mut self: ref Example) -> int name=test4 extra:propertyName=test4 extra:propertyType=(mut self: ref Example) -> int) + Identifier(initial=Example target=ref Example name=example) diff --git a/packages/analyzer/test/type-casting/call-expression-with-reference-type.txt b/packages/analyzer/test/type-casting/call-expression-with-reference-type.txt new file mode 100644 index 000000000..82982f0f1 --- /dev/null +++ b/packages/analyzer/test/type-casting/call-expression-with-reference-type.txt @@ -0,0 +1,19 @@ +fn example () int { + return 1 +} + +main { + d := ref example + b := d() +} +===== out ===== +FunctionDeclaration(asynchronous=false name=example return=int) + ReturnStatement() + IntegerLiteral(initial=int value=1) +MainDeclaration() + VariableDeclaration(name=d mutable=false constant=false context:varType=ref () -> int) + ReferenceExpression(initial=ref () -> int) + Identifier(initial=() -> int name=example) + VariableDeclaration(name=b mutable=false constant=false context:varType=int) + CallExpression(initial=int extra:asynchronous=false extra:returnType=int) + Identifier(initial=ref () -> int target=() -> int name=d) diff --git a/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt b/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt index 06107fdec..4aef306f8 100644 --- a/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt +++ b/packages/analyzer/test/type-casting/element-access-expression-with-element-access.txt @@ -24,12 +24,12 @@ MainDeclaration() VariableDeclaration(name=people mutable=true constant=false type=Human[][] context:varType=Human[][]) CallExpression(initial=void extra:asynchronous=false extra:returnType=void) PropertyAccessExpression(initial=(mut self: ref Human[][], elements: Human[]...) -> void name=push extra:propertyName=push extra:propertyType=(mut self: ref Human[][], elements: Human[]...) -> void extra:propertyBuiltin) - Identifier(initial=Human[][] name=people) + Identifier(initial=Human[][] target=ref Human[][] name=people) CallExpressionArgument(extra:parameterIdx=0) ArrayExpression(initial=Human[]) CallExpression(initial=void extra:asynchronous=false extra:returnType=void) PropertyAccessExpression(initial=(mut self: ref Human[], elements: Human...) -> void name=push extra:propertyName=push extra:propertyType=(mut self: ref Human[], elements: Human...) -> void extra:propertyBuiltin) - ElementAccessExpression(initial=ref Human[] target=Human[] extra:selfType=Human[][]) + ElementAccessExpression(initial=ref Human[] extra:selfType=Human[][]) Identifier(initial=Human[][] name=people) IntegerLiteral(initial=int value=0) CallExpressionArgument(extra:parameterIdx=0) @@ -38,7 +38,7 @@ MainDeclaration() StringLiteral(initial=str value=Peter) CallExpression(initial=void extra:asynchronous=false extra:returnType=void) PropertyAccessExpression(initial=(mut self: ref Animal[], elements: Animal...) -> void name=push extra:propertyName=push extra:propertyType=(mut self: ref Animal[], elements: Animal...) -> void extra:propertyBuiltin) - PropertyAccessExpression(initial=Animal[] name=pets extra:propertyName=pets extra:propertyType=Animal[] extra:propertyMutable) + PropertyAccessExpression(initial=Animal[] target=ref Animal[] name=pets extra:propertyName=pets extra:propertyType=Animal[] extra:propertyMutable) ElementAccessExpression(initial=ref Human target=Human extra:selfType=Human[]) ElementAccessExpression(initial=ref Human[] target=Human[] extra:selfType=Human[][]) Identifier(initial=Human[][] name=people) From d63fb30899dfbf30ff977f28bc288361bcb47338 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 11 Jan 2025 12:17:53 +0200 Subject: [PATCH 44/48] Parser: Ability to get inner expressions --- packages/parser/README.md | 21 +++++++ packages/parser/src/expression | 28 +++++++++ packages/parser/src/main | 8 ++- packages/parser/test/expression-test | 90 ++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 1 deletion(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 461a5b2dc..94ea76d4a 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -826,6 +826,27 @@ expressionToType(expression) - `Error` - thrown if unable to transform +### `getInnerExpressions (self: ref Expression) (ref Expression)[]` +Get first level inner expressions of a specified expression. + +**Parameters** + +- `self` - expression to get inner expression from. + +**Return value** + +First level inner expressions. + +**Examples** + +```the +result := getInnerExpression(ref expression) +``` + +**Exceptions** + +- `Error` - thrown if unable to get inner expressions + ### `stringifyExpression (expression: Expression) str` Converts expression to a code string. diff --git a/packages/parser/src/expression b/packages/parser/src/expression index d94b5ec63..c697e9320 100644 --- a/packages/parser/src/expression +++ b/packages/parser/src/expression @@ -3,6 +3,7 @@ * Licensed under the MIT License */ +import * as string from "./string" import * as t from "./types" export fn create (body: t.ExpressionBody, start: int, end: int) t.Expression { @@ -72,6 +73,33 @@ export fn extractLastObjExpr (self: t.Expression) t.ObjectExpression { throw error_NewError("Unable to extract last object expression") } +export fn getInner (self: ref t.Expression) (ref t.Expression)[] { + if self.isAs() { + mut body := self.asAs() + return [ref body.expression] + } elif self.isConditional() { + mut body := self.asConditional() + return [ref body.consequent, ref body.alternate] + } elif self.isElementAccess() { + mut body := self.asElementAccess() + return [ref body.expression] + } elif self.isIs() { + mut body := self.asIs() + return [ref body.expression] + } elif self.isParenthesized() { + mut body := self.asParenthesized() + return getInner(ref body.expression) + } elif self.isPropertyAccess() { + mut body := self.asPropertyAccess() + return [ref body.expression] + } elif self.isReference() { + mut body := self.asReference() + return [ref body.expression] + } + + throw error_NewError("Unable to get inner expressions of '" + string.expression(self) + "'") +} + export fn subtractLastObjExpr (self: t.Expression) t.Expression { if self.isAssignment() { selfBody := self.asAssignment() diff --git a/packages/parser/src/main b/packages/parser/src/main index 96ced114b..ebbcd34fc 100644 --- a/packages/parser/src/main +++ b/packages/parser/src/main @@ -87,7 +87,12 @@ import init as Reader_init, locate as Reader_locate from "./reader" -import toType as expressionToType, typeToExpression, validObjExprId from "./expression" +import + getInner as getInnerExpressions, + toType as expressionToType, + typeToExpression, + validObjExprId +from "./expression" import interconnect, parse, report from "./parser" import expression as stringifyExpression, statement as stringifyStatement, typeT as stringifyType from "./string" @@ -170,6 +175,7 @@ export Reader_error export Reader_init export Reader_locate export expressionToType +export getInnerExpressions export typeToExpression export validObjExprId export interconnect diff --git a/packages/parser/test/expression-test b/packages/parser/test/expression-test index b3786f936..1e315faca 100644 --- a/packages/parser/test/expression-test +++ b/packages/parser/test/expression-test @@ -53,6 +53,96 @@ export fn TEST_Expression_extractLastObjExpr () { }, "Unable to extract last object expression") } +export fn TEST_Expression_getInner () { + EXPECT_NO_THROW(() -> void { + e := parseExpression("t1.t2 as int") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("true ? t1.t2 : t3.t4") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 2) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + + r1 := r[1] as ref t.Expression + EXPECT_EQ(string.expression(r1), "t3.t4") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("t1.t2[0]") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("t1.t2 is int") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("(t1.t2.t3)") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("((t1.t2.t3))") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("t1.t2.t3") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_NO_THROW(() -> void { + e := parseExpression("ref t1.t2") + r := Expression.getInner(ref e) + + EXPECT_EQ(r.len, 1) + + r0 := r[0] as ref t.Expression + EXPECT_EQ(string.expression(r0), "t1.t2") + }) + + EXPECT_THROW_WITH_MESSAGE(() -> void { + e := parseExpression("10") + Expression.getInner(ref e) + }, "Unable to get inner expressions of '10'") +} + export fn TEST_Expression_subtractLastObjExpr () { EXPECT_EQ(string.expression(Expression.subtractLastObjExpr(parseExpression("a = Test{}"))), "a = Test") EXPECT_EQ(string.expression(Expression.subtractLastObjExpr(parseExpression("await Test{}"))), "await Test") From 3ae7f2827350779c1a0d811e6ba02f72b15d70db Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 11 Jan 2025 12:34:35 +0200 Subject: [PATCH 45/48] codegen: Improve CallExpression generation --- packages/codegen/src/codegen | 200 +++++++++++++++-------------------- 1 file changed, 88 insertions(+), 112 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index e53301b0e..549da5ea3 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -975,39 +975,15 @@ export obj Codegen { contextExtra := item.contextExtra as Analyzer.CallExpressionContextExtra isBuiltinMethod := expressionIsBuiltinMethod(ref expression.callee) - - if expressionIsBuiltinMethod(ref expression.callee) { - calleeBody := expression.callee.asPropertyAccess() - - if calleeBody.name.name == "str" { - calleeTargetType := Analyzer.contextTarget(ref calleeBody.expression) - return self._functionStr(calleeTargetType, self._generateExpression(ref calleeBody.expression)) - } - } - calleeTargetType := Analyzer.contextTarget(ref expression.callee) - calleeExpression := self._generateExpression(ref expression.callee) + cCalleeExpression := self._generateExpression(ref expression.callee) mut paramsArgs: AST.CExpression[] if !isBuiltinMethod && calleeTargetType.hasSelfParam() { - paramsArgs.push(calleeExpression) + paramsArgs.push(cCalleeExpression) } elif calleeTargetType.hasSelfParam() { - methodType := calleeTargetType.asMethod() calleeBody := expression.callee.asPropertyAccess() - calleeBodyTargetType := Analyzer.contextTarget(ref calleeBody.expression) - cCalleeBodyExpression := self._generateExpression(ref calleeBody.expression) - - cSelfExpression := methodType.selfType.isReference() - ? self._wrap( - cCalleeBodyExpression, - ref calleeBody.expression, - calleeBodyTargetType, - methodType.selfType, - transform: false, - ) - : cCalleeBodyExpression - - paramsArgs.push(cSelfExpression) + paramsArgs.push(self._generateExpression(ref calleeBody.expression)) } loop i := 0; i < contextExtra.parameters.len; i++ { @@ -1057,7 +1033,7 @@ export obj Codegen { if isBuiltinMethod { calleeBody := expression.callee.asPropertyAccess() - calleeBodyTargetType := Analyzer.contextTarget(ref calleeBody.expression) + calleeBodyTargetType := Analyzer.unwrap(Analyzer.contextTarget(ref calleeBody.expression)) if !paramsArgs.empty { loop i := 0; i < paramsArgs.len; i++ { @@ -1072,7 +1048,7 @@ export obj Codegen { ) } - args.push(createASTPropertyAccess(calleeExpression, "ctx")) + args.push(createASTPropertyAccess(cCalleeExpression, "ctx")) if !paramsArgs.empty { paramsName := self._type(calleeTargetType, "_params_t") @@ -1088,7 +1064,7 @@ export obj Codegen { ])) } - return AST.createCallExpression(createASTPropertyAccess(calleeExpression, "func"), args) + return AST.createCallExpression(createASTPropertyAccess(cCalleeExpression, "func"), args) } fn _generateConditionalExpression (mut self: ref Codegen, item: ref Parser.Expression) AST.CExpression? { @@ -2722,31 +2698,31 @@ export obj Codegen { return self._("d4_any" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } elif it.name == "bool" { if postfix == "_t" { - return self._("bool") + " " + return self._("bool") + " " } else { return self._("d4_bool" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "byte" { if postfix == "_t" { - return "unsigned char " + return "unsigned char " } else { return self._("d4_byte" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "char" { if postfix == "_t" { - return "char " + return "char " } else { return self._("d4_char" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "f32" { if postfix == "_t" { - return "float " + return "float " } else { return self._("d4_f32" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "f64" { if postfix == "_t" { - return "double " + return "double " } else { return self._("d4_f64" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } @@ -2758,19 +2734,19 @@ export obj Codegen { } } elif it.name == "i8" { if postfix == "_t" { - return self._("int8_t") + " " + return self._("int8_t") + " " } else { return self._("d4_i8" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "i16" { if postfix == "_t" { - return self._("int16_t") + " " + return self._("int16_t") + " " } else { return self._("d4_i16" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "i32" { if postfix == "_t" { - return self._("int32_t") + " " + return self._("int32_t") + " " } else { return self._("d4_i32" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } @@ -2782,19 +2758,19 @@ export obj Codegen { } } elif it.name == "i64" { if postfix == "_t" { - return self._("int64_t") + " " + return self._("int64_t") + " " } else { return self._("d4_i64" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "isize" { if postfix == "_t" { - return self._("ptrdiff_t") + " " + return self._("ptrdiff_t") + " " } else { return self._("d4_isize" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "rune" { if postfix == "_t" { - return self._("wchar_t") + " " + return self._("wchar_t") + " " } else { return self._("d4_rune" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } @@ -2802,31 +2778,31 @@ export obj Codegen { return self._("d4_str" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } elif it.name == "u8" { if postfix == "_t" { - return self._("uint8_t") + " " + return self._("uint8_t") + " " } else { return self._("d4_u8" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "u16" { if postfix == "_t" { - return self._("uint16_t") + " " + return self._("uint16_t") + " " } else { return self._("d4_u16" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "u32" { if postfix == "_t" { - return self._("uint32_t") + " " + return self._("uint32_t") + " " } else { return self._("d4_u32" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "u64" { if postfix == "_t" { - return self._("uint64_t") + " " + return self._("uint64_t") + " " } else { return self._("d4_u64" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } } elif it.name == "usize" { if postfix == "_t" { - return self._("size_t") + " " + return self._("size_t") + " " } else { return self._("d4_usize" + postfix) + (postfix.slice(-3) == "_t" ? " " : "") } @@ -2878,72 +2854,6 @@ export obj Codegen { } } - fn _wrap (mut self: ref Self, mut expression: AST.CExpression, item: ref Parser.Expression, t: ref Analyzer.Type, targetType: ref Analyzer.Type, transform := false) AST.CExpression { - shouldFree := typeShouldBeFreed(targetType) && expressionShouldBeFreed(item) - - if transform && !shouldFree && typeShouldBeAllocated(t) && expressionShouldBeAllocated(item) { - expression = self._allocateExpression(t, expression) - } - - if targetType.name == "any" && t.name != "any" { - allocAnyType := self._generateAnyType(t).slice(0, -2) + "_alloc" - expression = AST.createCallExpression(AST.createIdentifier(self._(allocAnyType)), [expression]) - } elif targetType.name != "any" && t.name == "any" { - typeName := self._generateAnyType(targetType) - - // TODO: throw runtime error if not possible to cast - innerExpression := AST.createCastExpression( - AST.createType(self._(typeName)), - createASTPropertyAccess(expression, "ctx") - ) - - expression = AST.createUnaryExpression("*", innerExpression.wrap()) - } elif !targetType.isReference() && t.isReference() { - expression = AST.createUnaryExpression("*", expression) - } elif targetType.isReference() && !t.isReference() { - expression = AST.createUnaryExpression("&", expression) - } elif targetType.isUnion() && !t.isUnion() { - allocTargetType := self._type(targetType, "_alloc") - typeDef := self._typeDef(t) - - expression = AST.createCallExpression(AST.createIdentifier(allocTargetType), [ - AST.createIdentifier(self._(typeDef)), - expression - ]) - } elif !targetType.isUnion() && t.isUnion() { - typeName := self._typeDef(targetType) - typeDefIdx := self.typeDefs.get(typeName) - - // TODO: throw runtime error if not possible to cast - expression = AST.createPropertyAccessExpression( - createASTPropertyAccess(expression, "data"), - "v" + typeDefIdx.str(), - ) - } elif !Analyzer.match(t, targetType) && ( - targetType.name == "bool" || - targetType.name == "byte" || - targetType.name == "char" || - targetType.name == "rune" || - targetType.isNumber() - ) { - expression = AST.createCastExpression(AST.createType(self._type(targetType)), expression) - } - - if !transform { - return expression - } - - typesMatch := Analyzer.match(t, targetType) - - if shouldFree { - expression = self._functionFree(targetType, expression) - } elif !typesMatch && typeShouldBeAllocated(targetType) && expressionShouldBeAllocated(item) { - expression = self._allocateExpression(targetType, expression) - } - - return expression - } - fn _typeDef (mut self: ref Self, it: ref Analyzer.Type) str { typeName := "TYPE_" + self._typeName(it) @@ -3016,6 +2926,72 @@ export obj Codegen { throw error_NewError("Failed to generate type name for '" + it.name + "'") } + + fn _wrap (mut self: ref Self, mut expression: AST.CExpression, item: ref Parser.Expression, t: ref Analyzer.Type, targetType: ref Analyzer.Type, transform := false) AST.CExpression { + shouldFree := typeShouldBeFreed(targetType) && expressionShouldBeFreed(item) + + if transform && !shouldFree && typeShouldBeAllocated(t) && expressionShouldBeAllocated(item) { + expression = self._allocateExpression(t, expression) + } + + if targetType.name == "any" && t.name != "any" { + allocAnyType := self._generateAnyType(t).slice(0, -2) + "_alloc" + expression = AST.createCallExpression(AST.createIdentifier(self._(allocAnyType)), [expression]) + } elif targetType.name != "any" && t.name == "any" { + typeName := self._generateAnyType(targetType) + + // TODO: throw runtime error if not possible to cast + innerExpression := AST.createCastExpression( + AST.createType(self._(typeName)), + createASTPropertyAccess(expression, "ctx") + ) + + expression = AST.createUnaryExpression("*", innerExpression.wrap()) + } elif !targetType.isReference() && t.isReference() { + expression = AST.createUnaryExpression("*", expression) + } elif targetType.isReference() && !t.isReference() { + expression = AST.createUnaryExpression("&", expression) + } elif targetType.isUnion() && !t.isUnion() { + allocTargetType := self._type(targetType, "_alloc") + typeDef := self._typeDef(t) + + expression = AST.createCallExpression(AST.createIdentifier(allocTargetType), [ + AST.createIdentifier(self._(typeDef)), + expression + ]) + } elif !targetType.isUnion() && t.isUnion() { + typeName := self._typeDef(targetType) + typeDefIdx := self.typeDefs.get(typeName) + + // TODO: throw runtime error if not possible to cast + expression = AST.createPropertyAccessExpression( + createASTPropertyAccess(expression, "data"), + "v" + typeDefIdx.str(), + ) + } elif !Analyzer.match(t, targetType) && ( + targetType.name == "bool" || + targetType.name == "byte" || + targetType.name == "char" || + targetType.name == "rune" || + targetType.isNumber() + ) { + expression = AST.createCastExpression(AST.createType(self._type(targetType)), expression) + } + + if !transform { + return expression + } + + typesMatch := Analyzer.match(t, targetType) + + if shouldFree { + expression = self._functionFree(targetType, expression) + } elif !typesMatch && typeShouldBeAllocated(targetType) && expressionShouldBeAllocated(item) { + expression = self._allocateExpression(targetType, expression) + } + + return expression + } } export fn compile (analyzer: ref Analyzer.Analyzer, executablePath: str) { From 4505fa5f9b3d12e86c36b2b44cfd1e340abec4dd Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Sat, 11 Jan 2025 15:35:07 +0200 Subject: [PATCH 46/48] codegen: Generation of self referencing objects --- packages/codegen/src/codegen | 45 +- .../codegen/loop-statement-with-optional.txt | 12 +- .../object-declaration-field-types.txt | 391 ++++++++++++++++++ 3 files changed, 433 insertions(+), 15 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index 549da5ea3..c11df1c40 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -250,7 +250,7 @@ export obj Codegen { AST.createVariableDeclaration( AST.createType(self._type(t)), tmpVar, - self._defaultInitializerDeclaration(t), + self._defaultInitializerExpression(t), ), ) @@ -404,14 +404,14 @@ export obj Codegen { t := it.asAlias() return self._defaultInitializerExpression(t.t) } elif it.name == "any" { - return AST.createCastExpression(AST.createType(self._("d4_any_t")), AST.createInitializerListExpression([ + return AST.createInitializerListExpression([ AST.createLiteral("-1"), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), - ])) + ]) } return self._defaultInitializerDeclaration(it) @@ -422,14 +422,14 @@ export obj Codegen { t := it.asAlias() return self._defaultInitializerDeclaration(t.t) } elif it.name == "any" { - return AST.createInitializerListExpression([ + return AST.createCastExpression(AST.createType(self._("d4_any_t")), AST.createInitializerListExpression([ AST.createLiteral("-1"), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), AST.createIdentifier(self._("NULL")), - ]) + ])) } elif it.name == "bool" { return AST.createIdentifier(self._("false")) } elif it.name == "char" { @@ -1025,7 +1025,7 @@ export obj Codegen { arg := contextExtra.arguments[argIdx] paramsArgs.push(self._generateExpression(ref arg.argument.expression)) } else { - paramsArgs.push(self._defaultInitializerExpression(param.t)) + paramsArgs.push(self._defaultInitializerDeclaration(param.t)) } } @@ -1183,7 +1183,7 @@ export obj Codegen { fields.push( propertyValue == nil - ? self._defaultInitializerExpression(property.t) + ? self._defaultInitializerDeclaration(property.t) : self._generateExpression(propertyValue) ) } @@ -2118,6 +2118,15 @@ export obj Codegen { return name } + self.entities.push(CodegenEntity{ + name: name, + codeName: name, + context: item, + generate: (mut self: ref Codegen, entity: ref CodegenEntity) -> GenerateReturnType { + return AST.createEmptyStatement() + } + }) + loop i := 0; i < item.properties.len; i++ { property := item.properties[i] @@ -2128,6 +2137,8 @@ export obj Codegen { self._typeGen(property.t) } + self._removeEntity(name) + self.entities.push(CodegenEntity{ name: name, codeName: name, @@ -2680,6 +2691,19 @@ export obj Codegen { return ref file.reader } + fn _removeEntity (mut self: ref Self, name: str) void { + loop i := self.entities.len - 1; i >= 0; i-- { + entity := self.entities[i] + + if entity.name == name { + self.entities.remove(i) + return + } + } + + throw error_NewError("Entity '" + name + "' can't be removed") + } + fn _tm (mut self: ref Self) ref Analyzer.TypeMap { mut file := self.analyzer.files.last() return ref file.tm @@ -2943,10 +2967,13 @@ export obj Codegen { // TODO: throw runtime error if not possible to cast innerExpression := AST.createCastExpression( AST.createType(self._(typeName)), - createASTPropertyAccess(expression, "ctx") + createASTPropertyAccess(expression, "ctx"), ) expression = AST.createUnaryExpression("*", innerExpression.wrap()) + } elif targetType.isOptional() && !t.isOptional() { + allocOptionalType := self._generateOptionalType(targetType).slice(0, -2) + "_alloc" + expression = AST.createCallExpression(AST.createIdentifier(self._(allocOptionalType)), [expression]) } elif !targetType.isReference() && t.isReference() { expression = AST.createUnaryExpression("*", expression) } elif targetType.isReference() && !t.isReference() { @@ -2957,7 +2984,7 @@ export obj Codegen { expression = AST.createCallExpression(AST.createIdentifier(allocTargetType), [ AST.createIdentifier(self._(typeDef)), - expression + expression, ]) } elif !targetType.isUnion() && t.isUnion() { typeName := self._typeDef(targetType) diff --git a/packages/codegen/test/codegen/loop-statement-with-optional.txt b/packages/codegen/test/codegen/loop-statement-with-optional.txt index 077cb9fe3..6efe4f20e 100644 --- a/packages/codegen/test/codegen/loop-statement-with-optional.txt +++ b/packages/codegen/test/codegen/loop-statement-with-optional.txt @@ -42,17 +42,17 @@ int main (void) { d4_opt_int_t b_0; d4_opt_int_t c_0; { - d4_opt_int_t i_0 = 0; + d4_opt_int_t i_0 = d4_opt_int_alloc(0); for (; !d4_opt_int_eq(i_0, NULL); i_0 = d4_opt_int_realloc(i_0, NULL)) {} d4_opt_int_free(i_0); } - a_0 = 0; + a_0 = d4_opt_int_alloc(0); for (; !d4_opt_int_eq(a_0, NULL); a_0 = d4_opt_int_realloc(a_0, NULL)) {} - b_0 = 0; + b_0 = d4_opt_int_alloc(0); while (!d4_opt_int_eq(b_0, NULL)) { b_0 = d4_opt_int_realloc(b_0, NULL); } - c_0 = 0; + c_0 = d4_opt_int_alloc(0); while (1) { if (d4_opt_int_eq(c_0, NULL)) { break; @@ -60,14 +60,14 @@ int main (void) { c_0 = d4_opt_int_realloc(c_0, NULL); } { - d4_opt_int_t d_0 = 0; + d4_opt_int_t d_0 = d4_opt_int_alloc(0); while (!d4_opt_int_eq(d_0, NULL)) { d_0 = d4_opt_int_realloc(d_0, NULL); } d4_opt_int_free(d_0); } { - d4_opt_int_t e_0 = 0; + d4_opt_int_t e_0 = d4_opt_int_alloc(0); for (;; e_0 = d4_opt_int_realloc(e_0, NULL)) { if (d4_opt_int_eq(e_0, NULL)) { break; diff --git a/packages/codegen/test/codegen/object-declaration-field-types.txt b/packages/codegen/test/codegen/object-declaration-field-types.txt index afe86e65b..691ee8fea 100644 --- a/packages/codegen/test/codegen/object-declaration-field-types.txt +++ b/packages/codegen/test/codegen/object-declaration-field-types.txt @@ -88,4 +88,395 @@ main { t21 = Test10{a: "test"} } ===== code ===== +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TYPE_int 1 +#define TYPE_str 2 +D4_OBJECT_FORWARD_DECLARE(Test1) +D4_OBJECT_FORWARD_DECLARE(Test2) +D4_OBJECT_FORWARD_DECLARE(Test3) +D4_OBJECT_FORWARD_DECLARE(Test4) +D4_OBJECT_FORWARD_DECLARE(Test5) +D4_OBJECT_FORWARD_DECLARE(Test6) +D4_OBJECT_FORWARD_DECLARE(Test7) +D4_OBJECT_FORWARD_DECLARE(Test8) +D4_OBJECT_FORWARD_DECLARE(Test9) +D4_OBJECT_FORWARD_DECLARE(Test10) +D4_OBJECT_DECLARE(Test1, { + d4_str_t a; +}, const d4_str_t a) +D4_OBJECT_DECLARE(Test2, { + d4_any_t a; +}, const d4_any_t a) +D4_ARRAY_DECLARE(int, int32_t) +D4_OBJECT_DECLARE(Test3, { + d4_arr_int_t a; + d4_arr_str_t b; +}, const d4_arr_int_t a, const d4_arr_str_t b) +D4_MAP_DECLARE(str, d4_str_t, int, int32_t) +D4_MAP_DECLARE(str, d4_str_t, str, d4_str_t) +D4_OBJECT_DECLARE(Test4, { + d4_map_strMSintME_t a; + d4_map_strMSstrME_t b; +}, const d4_map_strMSintME_t a, const d4_map_strMSstrME_t b) +D4_OBJECT_DECLARE(Test5, { + d4_obj_Test1_t a; +}, const d4_obj_Test1_t a) +D4_OPTIONAL_DECLARE(int, int32_t) +D4_OBJECT_DECLARE(Test6, { + d4_opt_int_t a; +}, const d4_opt_int_t a) +D4_REFERENCE_DECLARE(int, int32_t) +D4_OBJECT_DECLARE(Test7, { + d4_ref_int_t a; +}, const d4_ref_int_t a) +D4_OPTIONAL_DECLARE(obj_Test8, d4_obj_Test8_t) +D4_OBJECT_DECLARE(Test8, { + d4_opt_obj_Test8_t a; +}, const d4_opt_obj_Test8_t a) +D4_OBJECT_DECLARE(Test9, { + d4_str_t a; +}, const d4_str_t a) +D4_UNION_DECLARE(intUSstr, { + int32_t v1; + d4_str_t v2; +}) +D4_OBJECT_DECLARE(Test10, { + d4_union_intUSstrUE_t a; +}, const d4_union_intUSstrUE_t a) +D4_ANY_DECLARE(int, int32_t) +D4_ANY_DECLARE(str, d4_str_t) +D4_OBJECT_DEFINE(Test1, Test1, { + self.a = d4_str_copy(a); +}, { + result.a = d4_str_copy(self.a); +}, { + return d4_str_eq(self.a, rhs.a); +}, { + d4_str_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_str_quoted_escape(self.a)); +}, const d4_str_t a) +D4_OBJECT_DEFINE(Test2, Test2, { + self.a = d4_any_copy(a); +}, { + result.a = d4_any_copy(self.a); +}, { + return d4_any_eq(self.a, rhs.a); +}, { + d4_any_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_any_str(self.a)); +}, const d4_any_t a) +D4_ARRAY_DEFINE(int, int32_t, int32_t, element, lhs_element == rhs_element, (void) element, d4_int_str(element)) +D4_OBJECT_DEFINE(Test3, Test3, { + self.a = d4_arr_int_copy(a); + self.b = d4_arr_str_copy(b); +}, { + result.a = d4_arr_int_copy(self.a); + result.b = d4_arr_str_copy(self.b); +}, { + return d4_arr_int_eq(self.a, rhs.a) && d4_arr_str_eq(self.b, rhs.b); +}, { + d4_arr_int_free(self.a); + d4_arr_str_free(self.b); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_arr_int_str(self.a)); + result = d4_obj_str_append(result, d4_str_alloc(L"b"), d4_arr_str_str(self.b)); +}, const d4_arr_int_t a, const d4_arr_str_t b) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), int, int32_t, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_MAP_DEFINE(str, d4_str_t, d4_str_t, d4_str_copy(key), d4_str_eq(lhs_key, rhs_key), d4_str_free(key), d4_str_copy(key), d4_str_quoted_escape(key), str, d4_str_t, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_quoted_escape(val)) +D4_OBJECT_DEFINE(Test4, Test4, { + self.a = d4_map_strMSintME_copy(a); + self.b = d4_map_strMSstrME_copy(b); +}, { + result.a = d4_map_strMSintME_copy(self.a); + result.b = d4_map_strMSstrME_copy(self.b); +}, { + return d4_map_strMSintME_eq(self.a, rhs.a) && d4_map_strMSstrME_eq(self.b, rhs.b); +}, { + d4_map_strMSintME_free(self.a); + d4_map_strMSstrME_free(self.b); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_map_strMSintME_str(self.a)); + result = d4_obj_str_append(result, d4_str_alloc(L"b"), d4_map_strMSstrME_str(self.b)); +}, const d4_map_strMSintME_t a, const d4_map_strMSstrME_t b) +D4_OBJECT_DEFINE(Test5, Test5, { + self.a = d4_obj_Test1_copy(a); +}, { + result.a = d4_obj_Test1_copy(self.a); +}, { + return d4_obj_Test1_eq(self.a, rhs.a); +}, { + d4_obj_Test1_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_obj_Test1_str(self.a)); +}, const d4_obj_Test1_t a) +D4_OPTIONAL_DEFINE(int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_OBJECT_DEFINE(Test6, Test6, { + self.a = d4_opt_int_copy(a); +}, { + result.a = d4_opt_int_copy(self.a); +}, { + return d4_opt_int_eq(self.a, rhs.a); +}, { + d4_opt_int_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_opt_int_str(self.a)); +}, const d4_opt_int_t a) +D4_OBJECT_DEFINE(Test7, Test7, { + self.a = a; +}, { + result.a = self.a; +}, { + return self.a == rhs.a; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_int_str(*self.a)); +}, const d4_ref_int_t a) +D4_OPTIONAL_DEFINE(obj_Test8, d4_obj_Test8_t, d4_obj_Test8_copy(val), d4_obj_Test8_eq(lhs_val, rhs_val), d4_obj_Test8_free(val), d4_obj_Test8_str(val)) +D4_OBJECT_DEFINE(Test8, Test8, { + self.a = d4_opt_obj_Test8_copy(a); +}, { + result.a = d4_opt_obj_Test8_copy(self.a); +}, { + return d4_opt_obj_Test8_eq(self.a, rhs.a); +}, { + d4_opt_obj_Test8_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_opt_obj_Test8_str(self.a)); +}, const d4_opt_obj_Test8_t a) +D4_OBJECT_DEFINE(Test9, Test9, { + self.a = d4_str_copy(a); +}, { + result.a = d4_str_copy(self.a); +}, { + return d4_str_eq(self.a, rhs.a); +}, { + d4_str_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_str_quoted_escape(self.a)); +}, const d4_str_t a) +D4_UNION_DEFINE(intUSstr, { + if (self.type == TYPE_int) self.data.v1 = va_arg(args, int32_t); + if (self.type == TYPE_str) self.data.v2 = d4_str_copy(va_arg(args, d4_str_t)); +}, { + if (self.type == TYPE_int) result.data.v1 = self.data.v1; + if (self.type == TYPE_str) result.data.v2 = d4_str_copy(self.data.v2); +}, { + if (self.type == TYPE_int) return self.data.v1 == rhs.data.v1; + if (self.type == TYPE_str) return d4_str_eq(self.data.v2, rhs.data.v2); +}, { + if (self.type == TYPE_str) d4_str_free(self.data.v2); +}, { + if (self.type == TYPE_int) return d4_int_str(self.data.v1); + if (self.type == TYPE_str) return d4_str_copy(self.data.v2); +}) +D4_OBJECT_DEFINE(Test10, Test10, { + self.a = d4_union_intUSstrUE_copy(a); +}, { + result.a = d4_union_intUSstrUE_copy(self.a); +}, { + return d4_union_intUSstrUE_eq(self.a, rhs.a); +}, { + d4_union_intUSstrUE_free(self.a); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_union_intUSstrUE_str(self.a)); +}, const d4_union_intUSstrUE_t a) +D4_ANY_DEFINE(TYPE_int, int, int32_t, val, lhs_val == rhs_val, (void) val, d4_int_str(val)) +D4_ANY_DEFINE(TYPE_str, str, d4_str_t, d4_str_copy(val), d4_str_eq(lhs_val, rhs_val), d4_str_free(val), d4_str_copy(val)) +int main (void) { + d4_str_t __THE_1 = d4_str_empty_val; + d4_str_t __THE_2 = d4_str_empty_val; + d4_obj_Test1_t __THE_3 = d4_obj_Test1_alloc(d4_str_empty_val); + d4_any_t __THE_4 = {-1, NULL, NULL, NULL, NULL, NULL}; + d4_obj_Test2_t t3_0; + d4_obj_Test2_t t4_0; + d4_str_t __THE_5 = d4_str_empty_val; + d4_any_t __THE_6 = {-1, NULL, NULL, NULL, NULL, NULL}; + d4_obj_Test2_t __THE_7 = d4_obj_Test2_alloc((d4_any_t) {-1, NULL, NULL, NULL, NULL, NULL}); + d4_arr_int_t __THE_8 = d4_arr_int_alloc(0); + d4_str_t __THE_9 = d4_str_empty_val; + d4_str_t __THE_10 = d4_str_empty_val; + d4_arr_str_t __THE_11 = d4_arr_str_alloc(0); + d4_obj_Test3_t t5_0; + d4_obj_Test3_t t6_0; + d4_arr_int_t __THE_12 = d4_arr_int_alloc(0); + d4_str_t __THE_13 = d4_str_empty_val; + d4_str_t __THE_14 = d4_str_empty_val; + d4_arr_str_t __THE_15 = d4_arr_str_alloc(0); + d4_obj_Test3_t __THE_16 = d4_obj_Test3_alloc(d4_arr_int_alloc(0), d4_arr_str_alloc(0)); + d4_str_t __THE_17 = d4_str_empty_val; + d4_str_t __THE_18 = d4_str_empty_val; + d4_str_t __THE_19 = d4_str_empty_val; + d4_map_strMSintME_t __THE_20 = d4_map_strMSintME_alloc(0); + d4_str_t __THE_21 = d4_str_empty_val; + d4_str_t __THE_22 = d4_str_empty_val; + d4_str_t __THE_23 = d4_str_empty_val; + d4_str_t __THE_24 = d4_str_empty_val; + d4_map_strMSstrME_t __THE_25 = d4_map_strMSstrME_alloc(0); + d4_obj_Test4_t t7_0; + d4_obj_Test4_t t8_0; + d4_str_t __THE_26 = d4_str_empty_val; + d4_str_t __THE_27 = d4_str_empty_val; + d4_map_strMSintME_t __THE_28 = d4_map_strMSintME_alloc(0); + d4_str_t __THE_29 = d4_str_empty_val; + d4_str_t __THE_30 = d4_str_empty_val; + d4_str_t __THE_31 = d4_str_empty_val; + d4_str_t __THE_32 = d4_str_empty_val; + d4_map_strMSstrME_t __THE_33 = d4_map_strMSstrME_alloc(0); + d4_obj_Test4_t __THE_34 = d4_obj_Test4_alloc(d4_map_strMSintME_alloc(0), d4_map_strMSstrME_alloc(0)); + d4_str_t __THE_35 = d4_str_empty_val; + d4_obj_Test1_t __THE_36 = d4_obj_Test1_alloc(d4_str_empty_val); + d4_obj_Test5_t t9_0; + d4_obj_Test5_t t10_0; + d4_str_t __THE_37 = d4_str_empty_val; + d4_obj_Test1_t __THE_38 = d4_obj_Test1_alloc(d4_str_empty_val); + d4_obj_Test5_t __THE_39 = d4_obj_Test5_alloc(d4_obj_Test1_alloc(d4_str_empty_val)); + d4_str_t __THE_40 = d4_str_empty_val; + d4_obj_Test1_t __THE_41 = d4_obj_Test1_alloc(d4_str_empty_val); + d4_obj_Test6_t t11_0; + d4_obj_Test6_t t12_0; + d4_opt_int_t __THE_42 = NULL; + d4_obj_Test6_t __THE_43 = d4_obj_Test6_alloc(NULL); + d4_obj_Test7_t t13_0; + d4_obj_Test7_t t14_0; + d4_obj_Test7_t __THE_44 = d4_obj_Test7_alloc(NULL); + d4_obj_Test8_t t15_0; + d4_obj_Test8_t t16_0; + d4_obj_Test8_t t17_0; + d4_obj_Test8_t __THE_45 = d4_obj_Test8_alloc(NULL); + d4_str_t __THE_46 = d4_str_empty_val; + d4_obj_Test9_t t18_0; + d4_obj_Test9_t t19_0; + d4_str_t __THE_47 = d4_str_empty_val; + d4_obj_Test9_t __THE_48 = d4_obj_Test9_alloc(d4_str_empty_val); + d4_union_intUSstrUE_t __THE_49 = d4_union_intUSstrUE_alloc(-1); + d4_obj_Test10_t t20_0; + d4_obj_Test10_t t21_0; + d4_str_t __THE_50 = d4_str_empty_val; + d4_union_intUSstrUE_t __THE_51 = d4_union_intUSstrUE_alloc(-1); + d4_obj_Test10_t __THE_52 = d4_obj_Test10_alloc(d4_union_intUSstrUE_alloc(-1)); + int32_t i1_0 = 1; + int32_t i2_0 = 2; + d4_obj_Test1_t t1_0 = d4_obj_Test1_alloc(__THE_1 = d4_str_alloc(L"")); + d4_obj_Test1_t t2_0 = d4_obj_Test1_copy(t1_0); + t2_0 = d4_obj_Test1_realloc(t2_0, __THE_3 = d4_obj_Test1_alloc(__THE_2 = d4_str_alloc(L"test"))); + t3_0 = d4_obj_Test2_alloc(__THE_4 = d4_any_int_alloc(1)); + t4_0 = d4_obj_Test2_copy(t3_0); + t4_0 = d4_obj_Test2_realloc(t4_0, __THE_7 = d4_obj_Test2_alloc(__THE_6 = d4_any_str_alloc(__THE_5 = d4_str_alloc(L"test2")))); + t5_0 = d4_obj_Test3_alloc(__THE_8 = d4_arr_int_alloc(3, 1, 2, 3), __THE_11 = d4_arr_str_alloc(2, __THE_9 = d4_str_alloc(L"string1"), __THE_10 = d4_str_alloc(L"string2"))); + t6_0 = d4_obj_Test3_copy(t5_0); + t6_0 = d4_obj_Test3_realloc(t6_0, __THE_16 = d4_obj_Test3_alloc(__THE_12 = d4_arr_int_alloc(2, 4, 5), __THE_15 = d4_arr_str_alloc(2, __THE_13 = d4_str_alloc(L"string3"), __THE_14 = d4_str_alloc(L"string4")))); + t7_0 = d4_obj_Test4_alloc(__THE_20 = d4_map_strMSintME_alloc(3, __THE_17 = d4_str_alloc(L"key1"), 1, __THE_18 = d4_str_alloc(L"key2"), 2, __THE_19 = d4_str_alloc(L"key3"), 3), __THE_25 = d4_map_strMSstrME_alloc(2, __THE_21 = d4_str_alloc(L"key1"), __THE_22 = d4_str_alloc(L"string1"), __THE_23 = d4_str_alloc(L"key2"), __THE_24 = d4_str_alloc(L"string2"))); + t8_0 = d4_obj_Test4_copy(t7_0); + t8_0 = d4_obj_Test4_realloc(t8_0, __THE_34 = d4_obj_Test4_alloc(__THE_28 = d4_map_strMSintME_alloc(2, __THE_26 = d4_str_alloc(L"key1"), 4, __THE_27 = d4_str_alloc(L"key2"), 5), __THE_33 = d4_map_strMSstrME_alloc(2, __THE_29 = d4_str_alloc(L"key1"), __THE_30 = d4_str_alloc(L"string3"), __THE_31 = d4_str_alloc(L"key2"), __THE_32 = d4_str_alloc(L"string4")))); + t9_0 = d4_obj_Test5_alloc(__THE_36 = d4_obj_Test1_alloc(__THE_35 = d4_str_alloc(L"string1"))); + t10_0 = d4_obj_Test5_copy(t9_0); + t10_0 = d4_obj_Test5_realloc(t10_0, __THE_39 = d4_obj_Test5_alloc(__THE_38 = d4_obj_Test1_alloc(__THE_37 = d4_str_alloc(L"string2")))); + t10_0.a = d4_obj_Test1_realloc(t10_0.a, t9_0.a); + t10_0.a = d4_obj_Test1_realloc(t10_0.a, __THE_41 = d4_obj_Test1_alloc(__THE_40 = d4_str_alloc(L"string3"))); + t11_0 = d4_obj_Test6_alloc(NULL); + t12_0 = d4_obj_Test6_copy(t11_0); + t12_0 = d4_obj_Test6_realloc(t12_0, __THE_43 = d4_obj_Test6_alloc(__THE_42 = d4_opt_int_alloc(3))); + t13_0 = d4_obj_Test7_alloc(&i1_0); + t14_0 = d4_obj_Test7_copy(t13_0); + t14_0 = d4_obj_Test7_realloc(t14_0, __THE_44 = d4_obj_Test7_alloc(&i2_0)); + t15_0 = d4_obj_Test8_alloc(NULL); + t16_0 = d4_obj_Test8_alloc(d4_opt_obj_Test8_alloc(t15_0)); + t17_0 = d4_obj_Test8_copy(t16_0); + t17_0 = d4_obj_Test8_realloc(t17_0, __THE_45 = d4_obj_Test8_alloc(d4_opt_obj_Test8_alloc(t16_0))); + t18_0 = d4_obj_Test9_alloc(__THE_46 = d4_str_alloc(L"test1")); + t19_0 = d4_obj_Test9_copy(t18_0); + t19_0 = d4_obj_Test9_realloc(t19_0, __THE_48 = d4_obj_Test9_alloc(__THE_47 = d4_str_alloc(L"test2"))); + t20_0 = d4_obj_Test10_alloc(__THE_49 = d4_union_intUSstrUE_alloc(TYPE_int, 1)); + t21_0 = d4_obj_Test10_copy(t20_0); + t21_0 = d4_obj_Test10_realloc(t21_0, __THE_52 = d4_obj_Test10_alloc(__THE_51 = d4_union_intUSstrUE_alloc(TYPE_str, __THE_50 = d4_str_alloc(L"test")))); + d4_obj_Test10_free(__THE_52); + d4_union_intUSstrUE_free(__THE_51); + d4_str_free(__THE_50); + d4_obj_Test10_free(t21_0); + d4_obj_Test10_free(t20_0); + d4_union_intUSstrUE_free(__THE_49); + d4_obj_Test9_free(__THE_48); + d4_str_free(__THE_47); + d4_obj_Test9_free(t19_0); + d4_obj_Test9_free(t18_0); + d4_str_free(__THE_46); + d4_obj_Test8_free(__THE_45); + d4_obj_Test8_free(t17_0); + d4_obj_Test8_free(t16_0); + d4_obj_Test8_free(t15_0); + d4_obj_Test7_free(__THE_44); + d4_obj_Test7_free(t14_0); + d4_obj_Test7_free(t13_0); + d4_obj_Test6_free(__THE_43); + d4_opt_int_free(__THE_42); + d4_obj_Test6_free(t12_0); + d4_obj_Test6_free(t11_0); + d4_obj_Test1_free(__THE_41); + d4_str_free(__THE_40); + d4_obj_Test5_free(__THE_39); + d4_obj_Test1_free(__THE_38); + d4_str_free(__THE_37); + d4_obj_Test5_free(t10_0); + d4_obj_Test5_free(t9_0); + d4_obj_Test1_free(__THE_36); + d4_str_free(__THE_35); + d4_obj_Test4_free(__THE_34); + d4_map_strMSstrME_free(__THE_33); + d4_str_free(__THE_32); + d4_str_free(__THE_31); + d4_str_free(__THE_30); + d4_str_free(__THE_29); + d4_map_strMSintME_free(__THE_28); + d4_str_free(__THE_27); + d4_str_free(__THE_26); + d4_obj_Test4_free(t8_0); + d4_obj_Test4_free(t7_0); + d4_map_strMSstrME_free(__THE_25); + d4_str_free(__THE_24); + d4_str_free(__THE_23); + d4_str_free(__THE_22); + d4_str_free(__THE_21); + d4_map_strMSintME_free(__THE_20); + d4_str_free(__THE_19); + d4_str_free(__THE_18); + d4_str_free(__THE_17); + d4_obj_Test3_free(__THE_16); + d4_arr_str_free(__THE_15); + d4_str_free(__THE_14); + d4_str_free(__THE_13); + d4_arr_int_free(__THE_12); + d4_obj_Test3_free(t6_0); + d4_obj_Test3_free(t5_0); + d4_arr_str_free(__THE_11); + d4_str_free(__THE_10); + d4_str_free(__THE_9); + d4_arr_int_free(__THE_8); + d4_obj_Test2_free(__THE_7); + d4_any_free(__THE_6); + d4_str_free(__THE_5); + d4_obj_Test2_free(t4_0); + d4_obj_Test2_free(t3_0); + d4_any_free(__THE_4); + d4_obj_Test1_free(__THE_3); + d4_str_free(__THE_2); + d4_obj_Test1_free(t2_0); + d4_obj_Test1_free(t1_0); + d4_str_free(__THE_1); +} ===== output ===== From 924277386c7934fb6419dd18cb11f1b4ad6f44d8 Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Mon, 13 Jan 2025 07:47:09 +0200 Subject: [PATCH 47/48] Update contributing guide CLI links --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c0e568a4..65caa85ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,14 +21,14 @@ By participating in this project you agree to abide by its terms. ### On Unix ```shell -curl -fsSL https://cdn.thelang.io/cli | bash +curl -fsSL sh.thelang.io | bash the run scripts/pre-process-codegen -o scripts/a.out ``` ### On Windows ```powershell -(New-Object System.Net.WebClient).DownloadString('https://cdn.thelang.io/cli-win') | iex +irm ps1.thelang.io | iex the run pre-process-codegen -o scripts/a.exe ``` From 1497cb92c6dd10a06c74240f85517b3d431a000b Mon Sep 17 00:00:00 2001 From: Aaron Delasy Date: Mon, 13 Jan 2025 10:45:53 +0200 Subject: [PATCH 48/48] codegen: Object forward declaration tests --- packages/codegen/src/codegen | 6 +- ...object-declaration-forward-declaration.txt | 113 +++++++++++++----- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/packages/codegen/src/codegen b/packages/codegen/src/codegen index c11df1c40..2d655d7da 100644 --- a/packages/codegen/src/codegen +++ b/packages/codegen/src/codegen @@ -38,12 +38,11 @@ import sortStrAsc from "./utils" // TODO: test MainDeclaration statement // TODO: test call expression -// TODO: test VariableDeclaration statement -// TODO: test expressions with types other than primitive - // TODO: implement EnumDeclaration statement // TODO: implement TypeAliasDeclaration statement +// TODO: test VariableDeclaration statement +// TODO: test expressions with types other than primitive // TODO: test If statement // TODO: add more tests for Loop statement with other types @@ -59,6 +58,7 @@ import sortStrAsc from "./utils" // TODO: implement async functions // TODO: implement await expression +// TODO: automatic reference counter and object reference cycles // TODO: check whether all tests are transferred from old codegen export obj CodegenAPIBuiltin { diff --git a/packages/codegen/test/codegen/object-declaration-forward-declaration.txt b/packages/codegen/test/codegen/object-declaration-forward-declaration.txt index ab504ea11..2512260db 100644 --- a/packages/codegen/test/codegen/object-declaration-forward-declaration.txt +++ b/packages/codegen/test/codegen/object-declaration-forward-declaration.txt @@ -1,37 +1,84 @@ -obj Person { - mut name: str; - mut age: int; - mut pets: (ref Pet)[]; -} -obj Pet { - mut name: str - mut age: int - mut owner: ref Person -} - -obj Test1 { - t3: Test3 -} -obj Test2 { - a: int -} -obj Test3 { - a: int -} -obj Test4 { - t2: Test2 -} - +obj Test1 { t3: Test3 } +obj Test2 { a: int } +obj Test3 { t4: Test4 } +obj Test4 { t2: Test2 } main { - mut person := Person{name: "Daniel", age: 28} - dog := Pet{name: "Eric", age: 4, owner: ref person} - cat := Pet{name: "Sam", age: 8, owner: ref person} - - person.pets.push(ref dog) - person.pets.push(ref cat) - - t1 := Test1{t3: Test3{a: 2}} - t2 := Test4{t2: Test2{a: 1}} + t := Test1{t3: Test3{t4: Test4{t2: Test2{a: 1}}}} } ===== code ===== +#include +#include +#include +#include +#include +D4_OBJECT_FORWARD_DECLARE(Test2) +D4_OBJECT_FORWARD_DECLARE(Test4) +D4_OBJECT_FORWARD_DECLARE(Test3) +D4_OBJECT_FORWARD_DECLARE(Test1) +D4_OBJECT_DECLARE(Test2, { + int32_t a; +}, const int32_t a) +D4_OBJECT_DECLARE(Test4, { + d4_obj_Test2_t t2; +}, const d4_obj_Test2_t t2) +D4_OBJECT_DECLARE(Test3, { + d4_obj_Test4_t t4; +}, const d4_obj_Test4_t t4) +D4_OBJECT_DECLARE(Test1, { + d4_obj_Test3_t t3; +}, const d4_obj_Test3_t t3) +D4_OBJECT_DEFINE(Test2, Test2, { + self.a = a; +}, { + result.a = self.a; +}, { + return self.a == rhs.a; +}, { + (void) self; +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"a"), d4_int_str(self.a)); +}, const int32_t a) +D4_OBJECT_DEFINE(Test4, Test4, { + self.t2 = d4_obj_Test2_copy(t2); +}, { + result.t2 = d4_obj_Test2_copy(self.t2); +}, { + return d4_obj_Test2_eq(self.t2, rhs.t2); +}, { + d4_obj_Test2_free(self.t2); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"t2"), d4_obj_Test2_str(self.t2)); +}, const d4_obj_Test2_t t2) +D4_OBJECT_DEFINE(Test3, Test3, { + self.t4 = d4_obj_Test4_copy(t4); +}, { + result.t4 = d4_obj_Test4_copy(self.t4); +}, { + return d4_obj_Test4_eq(self.t4, rhs.t4); +}, { + d4_obj_Test4_free(self.t4); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"t4"), d4_obj_Test4_str(self.t4)); +}, const d4_obj_Test4_t t4) +D4_OBJECT_DEFINE(Test1, Test1, { + self.t3 = d4_obj_Test3_copy(t3); +}, { + result.t3 = d4_obj_Test3_copy(self.t3); +}, { + return d4_obj_Test3_eq(self.t3, rhs.t3); +}, { + d4_obj_Test3_free(self.t3); +}, { + result = d4_obj_str_append(result, d4_str_alloc(L"t3"), d4_obj_Test3_str(self.t3)); +}, const d4_obj_Test3_t t3) +int main (void) { + d4_obj_Test2_t __THE_1 = d4_obj_Test2_alloc(0); + d4_obj_Test4_t __THE_2 = d4_obj_Test4_alloc(d4_obj_Test2_alloc(0)); + d4_obj_Test3_t __THE_3 = d4_obj_Test3_alloc(d4_obj_Test4_alloc(d4_obj_Test2_alloc(0))); + d4_obj_Test1_t t_0 = d4_obj_Test1_alloc(__THE_3 = d4_obj_Test3_alloc(__THE_2 = d4_obj_Test4_alloc(__THE_1 = d4_obj_Test2_alloc(1)))); + d4_obj_Test1_free(t_0); + d4_obj_Test3_free(__THE_3); + d4_obj_Test4_free(__THE_2); + d4_obj_Test2_free(__THE_1); +} ===== output =====