From faf506f409f42d9667aee42f9f74b7464d330ada Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 1 Mar 2026 00:12:16 +0100 Subject: [PATCH] static string literals --- compiler/Generator.cs | 151 +++++++++++++++++++++++--------------- compiler/TypeChecker.cs | 18 +++++ examples/build.sh | 2 +- examples/core/file.nub | 24 ------ examples/program/main.nub | 7 +- 5 files changed, 112 insertions(+), 90 deletions(-) delete mode 100644 examples/core/file.nub diff --git a/compiler/Generator.cs b/compiler/Generator.cs index de28bb6..a2ebf17 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -22,6 +22,7 @@ public class Generator private readonly string? entryPoint; private IndentedTextWriter writer = new(); private HashSet referencedTypes = new(); + private readonly Dictionary referencedStringLiterals = new(); private readonly HashSet emittedTypes = new(); private readonly Stack scopes = new(); private int tmpNameIndex = 0; @@ -98,6 +99,7 @@ public class Generator #include #include #include + #include """); @@ -108,6 +110,22 @@ public class Generator referencedTypes.Remove(type); } + foreach (var (name, value) in referencedStringLiterals) + { + writer.WriteLine + ( + $$""" + static struct nub_core_string {{name}} = { + .data = "{{value}}", + .length = {{value.Length}}, + .ref = 0, + .flags = FLAG_STRING_LITERAL + }; + + """ + ); + } + var header = writer.ToString(); return $"{header}\n{declarations}\n{implementations}"; @@ -122,55 +140,71 @@ public class Generator switch (type) { - case NubTypeString stringType: + case NubTypeString: { - writer.WriteLine($"struct {NameMangler.Mangle("core", "string", stringType)}"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("char *data;"); - writer.WriteLine("size_t length;"); - writer.WriteLine("int32_t ref;"); - writer.WriteLine("uint32_t flags;"); - } - writer.WriteLine("};"); - writer.WriteLine(); + writer.WriteLine + ( + """ + #define FLAG_STRING_LITERAL 1 - writer.WriteLine("#define FLAG_STRING_LITERAL 1"); - writer.WriteLine(); - - writer.WriteLine($"static inline void rc_inc({CType(NubTypeString.Instance, "string")})"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine($"string->ref += 1;"); - } - writer.WriteLine("}"); - writer.WriteLine(); - - writer.WriteLine($"static inline void rc_dec({CType(NubTypeString.Instance, "string")})"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine($"string->ref -= 1;"); - writer.WriteLine($"if (string->ref <= 0)"); - writer.WriteLine("{"); - using (writer.Indent()) + struct nub_core_string { - writer.WriteLine($"if ((string->flags & FLAG_STRING_LITERAL) == 0)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine($"free(string->data);"); - } - writer.WriteLine("}"); - writer.WriteLine($"free(string);"); - } - writer.WriteLine("}"); + char *data; + size_t length; + uint32_t ref; + uint32_t flags; + }; - } - writer.WriteLine("}"); - writer.WriteLine(); + static inline void string_rc_inc(struct nub_core_string *string) + { + if (string->flags & FLAG_STRING_LITERAL) + return; + + string->ref += 1; + } + + static inline void string_rc_dec(struct nub_core_string *string) + { + if (string->flags & FLAG_STRING_LITERAL) + return; + + string->ref -= 1; + + if (string->ref == 0) + { + free(string->data); + free(string); + } + } + + static inline struct nub_core_string *string_concat(struct nub_core_string *left, struct nub_core_string *right) + { + size_t new_length = left->length + right->length; + struct nub_core_string *result = malloc(sizeof(struct nub_core_string)); + result->data = malloc(new_length + 1); + memcpy(result->data, left->data, left->length); + memcpy(result->data + left->length, right->data, right->length); + result->data[new_length] = '\0'; + result->length = new_length; + result->ref = 1; + result->flags = 0; + return result; + } + + static inline struct nub_core_string *string_from_cstr(const char* cstr) + { + size_t len = strlen(cstr); + struct nub_core_string *result = malloc(sizeof(struct nub_core_string)); + result->data = malloc(len + 1); + memcpy(result->data, cstr, len + 1); + result->length = len; + result->ref = 1; + result->flags = 0; + return result; + } + + """ + ); break; } @@ -460,6 +494,14 @@ public class Generator var left = EmitExpression(expression.Left); var right = EmitExpression(expression.Right); + if (expression.Operation == TypedNodeExpressionBinary.Op.Add && expression.Left.Type is NubTypeString && expression.Right.Type is NubTypeString) + { + var name = TmpName(); + scopes.Peek().DeconstructableNames.Add((name, expression.Type)); + writer.WriteLine($"{CType(NubTypeString.Instance, name)} = string_concat({left}, {right});"); + return name; + } + return expression.Operation switch { TypedNodeExpressionBinary.Op.Add => $"({left} + {right})", @@ -496,17 +538,8 @@ public class Generator private string EmitExpressionStringLiteral(TypedNodeExpressionStringLiteral expression) { var name = TmpName(); - scopes.Peek().DeconstructableNames.Add((name, expression.Type)); - - var variable = CType(expression.Type, name); - - writer.WriteLine($"{variable} = malloc(sizeof(struct {NameMangler.Mangle("core", "string", expression.Type)}));"); - writer.WriteLine($"{name}->data = \"{expression.Value.Value}\";"); - writer.WriteLine($"{name}->length = {expression.Value.Value.Length};"); - writer.WriteLine($"{name}->ref = 1;"); - writer.WriteLine($"{name}->flags = FLAG_STRING_LITERAL;"); - - return name; + referencedStringLiterals.Add(name, expression.Value.Value); + return $"&{name}"; } private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression) @@ -599,7 +632,7 @@ public class Generator NubTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"), - NubTypeString type => $"struct {NameMangler.Mangle("core", "string", type)}" + (varName != null ? $" *{varName}" : "*"), + NubTypeString type => "struct nub_core_string" + (varName != null ? $" *{varName}" : "*"), NubTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})", _ => throw new ArgumentOutOfRangeException(nameof(node), node, null) }; @@ -642,7 +675,7 @@ public class Generator { case NubTypeString: { - writer.WriteLine($"rc_inc({value});"); + writer.WriteLine($"string_rc_inc({value});"); break; } case NubTypeStruct structType: @@ -709,7 +742,7 @@ public class Generator { case NubTypeString: { - writer.WriteLine($"rc_dec({value});"); + writer.WriteLine($"string_rc_dec({value});"); break; } case NubTypeStruct structType: diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index aa7fa7a..dfd8bf3 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -255,6 +255,24 @@ public class TypeChecker switch (expression.Operation) { case NodeExpressionBinary.Op.Add: + { + if (left.Type is NubTypeString) + { + if (right.Type is not NubTypeString) + throw BasicError("Right hand side of string concatination operator must be a string", right); + + return new TypedNodeExpressionBinary(expression.Tokens, NubTypeString.Instance, left, CheckExpressionBinaryOperation(expression.Operation), right); + } + + if (left.Type is not NubTypeSInt and not NubTypeUInt) + throw BasicError($"Unsupported type for left hand side arithmetic operation: {left.Type}", left); + + if (right.Type is not NubTypeSInt and not NubTypeUInt) + throw BasicError($"Unsupported type for right hand side arithmetic operation: {right.Type}", right); + + type = left.Type; + break; + } case NodeExpressionBinary.Op.Subtract: case NodeExpressionBinary.Op.Multiply: case NodeExpressionBinary.Op.Divide: diff --git a/examples/build.sh b/examples/build.sh index 8c85818..a175add 100755 --- a/examples/build.sh +++ b/examples/build.sh @@ -1,7 +1,7 @@ set -e pushd core -dotnet run --project ../../compiler sys.nub print.nub file.nub --type=lib +dotnet run --project ../../compiler sys.nub print.nub --type=lib popd pushd program diff --git a/examples/core/file.nub b/examples/core/file.nub deleted file mode 100644 index 95f28b0..0000000 --- a/examples/core/file.nub +++ /dev/null @@ -1,24 +0,0 @@ -module file - -export func read_text(path: string): ^u8 { - let fd = sys::open(path.ptr 0 0o644) - if fd < 0 { - return ^u8(0) // failed to open - } - - let buf_size: u64 = 4096 - let buf = c::malloc(buf_size) as ^u8 - if buf == ^u8(0) { - return ^u8(0) - } - - let bytes_read = sys::read(fd, buf, buf_size) - if bytes_read < 0 { - c::free(buf) - return ^u8(0) - } - - buf[bytes_read] = 0 - - return buf -} \ No newline at end of file diff --git a/examples/program/main.nub b/examples/program/main.nub index 6a48019..2e80c54 100644 --- a/examples/program/main.nub +++ b/examples/program/main.nub @@ -1,12 +1,7 @@ module main -extern func puts(text: ^u8) - func main(): i32 { core::println("Hello, world!") - - let file = file::read_text("file.nub") - puts(file) - + core::println("Hello" + "World") return 0 } \ No newline at end of file