Compare commits

...

2 Commits

Author SHA1 Message Date
nub31
faf506f409 static string literals 2026-03-01 00:12:16 +01:00
nub31
16d27c76ab ... 2026-02-28 23:19:57 +01:00
8 changed files with 132 additions and 85 deletions

6
TODO.txt Normal file
View File

@@ -0,0 +1,6 @@
Comma seperated function parameters and struct/enum mebers
Converting ^u8 to string
string formatting
string concatination
Dynamic arrays
C-style arrays

View File

@@ -22,6 +22,7 @@ public class Generator
private readonly string? entryPoint; private readonly string? entryPoint;
private IndentedTextWriter writer = new(); private IndentedTextWriter writer = new();
private HashSet<NubType> referencedTypes = new(); private HashSet<NubType> referencedTypes = new();
private readonly Dictionary<string, string> referencedStringLiterals = new();
private readonly HashSet<NubType> emittedTypes = new(); private readonly HashSet<NubType> emittedTypes = new();
private readonly Stack<Scope> scopes = new(); private readonly Stack<Scope> scopes = new();
private int tmpNameIndex = 0; private int tmpNameIndex = 0;
@@ -98,6 +99,7 @@ public class Generator
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
"""); """);
@@ -108,6 +110,22 @@ public class Generator
referencedTypes.Remove(type); 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(); var header = writer.ToString();
return $"{header}\n{declarations}\n{implementations}"; return $"{header}\n{declarations}\n{implementations}";
@@ -122,55 +140,71 @@ public class Generator
switch (type) switch (type)
{ {
case NubTypeString stringType: case NubTypeString:
{ {
writer.WriteLine($"struct {NameMangler.Mangle("core", "string", stringType)}"); writer.WriteLine
writer.WriteLine("{"); (
using (writer.Indent()) """
{ #define FLAG_STRING_LITERAL 1
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"); struct nub_core_string
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())
{ {
writer.WriteLine($"if ((string->flags & FLAG_STRING_LITERAL) == 0)"); char *data;
writer.WriteLine("{"); size_t length;
using (writer.Indent()) uint32_t ref;
{ uint32_t flags;
writer.WriteLine($"free(string->data);"); };
}
writer.WriteLine("}");
writer.WriteLine($"free(string);");
}
writer.WriteLine("}");
} static inline void string_rc_inc(struct nub_core_string *string)
writer.WriteLine("}"); {
writer.WriteLine(); 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; break;
} }
@@ -460,6 +494,14 @@ public class Generator
var left = EmitExpression(expression.Left); var left = EmitExpression(expression.Left);
var right = EmitExpression(expression.Right); 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 return expression.Operation switch
{ {
TypedNodeExpressionBinary.Op.Add => $"({left} + {right})", TypedNodeExpressionBinary.Op.Add => $"({left} + {right})",
@@ -496,17 +538,8 @@ public class Generator
private string EmitExpressionStringLiteral(TypedNodeExpressionStringLiteral expression) private string EmitExpressionStringLiteral(TypedNodeExpressionStringLiteral expression)
{ {
var name = TmpName(); var name = TmpName();
scopes.Peek().DeconstructableNames.Add((name, expression.Type)); referencedStringLiterals.Add(name, expression.Value.Value);
return $"&{name}";
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;
} }
private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression) private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression)
@@ -599,7 +632,7 @@ public class Generator
NubTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""),
NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""), NubTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""),
NubTypePointer type => CType(type.To) + (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)))})", NubTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})",
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null) _ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
}; };
@@ -642,7 +675,7 @@ public class Generator
{ {
case NubTypeString: case NubTypeString:
{ {
writer.WriteLine($"rc_inc({value});"); writer.WriteLine($"string_rc_inc({value});");
break; break;
} }
case NubTypeStruct structType: case NubTypeStruct structType:
@@ -709,7 +742,7 @@ public class Generator
{ {
case NubTypeString: case NubTypeString:
{ {
writer.WriteLine($"rc_dec({value});"); writer.WriteLine($"string_rc_dec({value});");
break; break;
} }
case NubTypeStruct structType: case NubTypeStruct structType:

View File

@@ -158,13 +158,13 @@ File.WriteAllText(".build/out.c", output);
if (compileLib) if (compileLib)
{ {
Process.Start("gcc", ["-Og", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit(); Process.Start("gcc", ["-Og", "-g", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit();
Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit(); Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit();
NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph)); NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph));
} }
else else
{ {
Process.Start("gcc", ["-Og", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit(); Process.Start("gcc", ["-Og", "-g", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit();
} }
return 0; return 0;

View File

@@ -255,6 +255,24 @@ public class TypeChecker
switch (expression.Operation) switch (expression.Operation)
{ {
case NodeExpressionBinary.Op.Add: 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.Subtract:
case NodeExpressionBinary.Op.Multiply: case NodeExpressionBinary.Op.Multiply:
case NodeExpressionBinary.Op.Divide: case NodeExpressionBinary.Op.Divide:

View File

@@ -1,7 +1,7 @@
set -e set -e
pushd core pushd core
dotnet run --project ../../compiler print.nub --type=lib dotnet run --project ../../compiler sys.nub print.nub --type=lib
popd popd
pushd program pushd program

View File

@@ -1,7 +1,10 @@
module core module core
extern func puts(text: ^u8)
export func print(text: string) { export func print(text: string) {
puts(text.ptr) sys::write(0 text.ptr text.length)
}
export func println(text: string) {
print(text)
print("\n")
} }

5
examples/core/sys.nub Normal file
View File

@@ -0,0 +1,5 @@
module sys
export extern func read(fd: u32 buf: ^u8 count: u64): i64
export extern func write(fd: u32 buf: ^u8 count: u64): i64
export extern func open(fileName: ^u8 flags: i32 mode: u16): i64

View File

@@ -1,25 +1,7 @@
module main module main
extern func puts(text: ^u8)
export func print(text: string) {
puts(text.ptr)
}
enum Message {
Tell: string
}
func main(): i32 { func main(): i32 {
let x = "test" core::println("Hello, world!")
core::println("Hello" + "World")
let y = {
abc = x
}
let a: Message = enum Message::Tell x
core::print(x)
return 0 return 0
} }