From 4210ad878b2989faf14b3280f398b619b7a36281 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 9 Mar 2026 16:38:09 +0100 Subject: [PATCH] tmp... --- compiler/Generator.cs | 667 ++++++++++++++++++++------------------ compiler/ModuleGraph.cs | 29 +- compiler/NubType.cs | 37 +++ compiler/Parser.cs | 14 +- compiler/Program.cs | 10 +- compiler/Tokenizer.cs | 616 ++++++++++++++++++----------------- compiler/TypeChecker.cs | 79 +++-- examples/program/main.nub | 23 +- 8 files changed, 800 insertions(+), 675 deletions(-) diff --git a/compiler/Generator.cs b/compiler/Generator.cs index 1f371c1..105e1d8 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -1,7 +1,5 @@ using System.Diagnostics; -using System.Security.Principal; using System.Text; -using Microsoft.VisualBasic; namespace Compiler; @@ -36,8 +34,24 @@ public class Generator writer.WriteLine($$""" int main(int argc, char *argv[]) { - return {{entryPoint}}(); + struct nub_dynamic_array_52747d8c11b7118c args = (struct nub_dynamic_array_52747d8c11b7118c){0}; + + for (int i = 0; i < argc; ++i) + { + auto nubstring = nub_core_string_from_cstr(argv[i]); + da_append(&args, nubstring); + } + + int returnValue = {{entryPoint}}(args); + + for (int i = 0; i < args.count; ++i) + { + nub_core_string_rc_dec(args.items[i]); + } + + return returnValue; } + """); writer.WriteLine(); @@ -104,6 +118,143 @@ public class Generator #include #include + #define FLAG_STRING_LITERAL 1 + #define STRING_DEBUG 1 + + #if STRING_DEBUG + #define STR_DBG(fmt, ...) fprintf(stderr, "[STR] " fmt "\n", ##__VA_ARGS__) + #else + #define STR_DBG(fmt, ...) + #endif + + struct nub_core_string + { + char *data; + size_t length; + uint32_t ref; + uint32_t flags; + }; + + #if STRING_DEBUG + static size_t nub_core_string_allocs = 0; + static size_t nub_core_string_frees = 0; + + __attribute__((destructor)) + static void string_debug_report(void) + { + fprintf(stderr, "[STR] REPORT allocs=%zu frees=%zu leaks=%zu\n", nub_core_string_allocs, nub_core_string_frees, nub_core_string_allocs - nub_core_string_frees); + } + #endif + + static inline void nub_core_string_rc_inc(struct nub_core_string *string) + { + if (string == NULL) + { + STR_DBG("INC null string"); + return; + } + + if (string->flags & FLAG_STRING_LITERAL) + return; + + string->ref += 1; + + STR_DBG("INC: str=%p ref=%u \"%s\"", (void*)string, string->ref, string->data); + } + + static inline void nub_core_string_rc_dec(struct nub_core_string *string) + { + if (string == NULL) + { + STR_DBG("DEC null string"); + return; + } + + if (string->flags & FLAG_STRING_LITERAL) + return; + + if (string->ref == 0) + { + STR_DBG("ERROR: DEC on zero refcount str=%p", (void*)string); + return; + } + + string->ref -= 1; + + STR_DBG("DEC: str=%p ref=%u \"%s\"", (void*)string, string->ref, string->data); + + if (string->ref == 0) + { + STR_DBG("FREE: str=%p data=%p \"%s\"", (void*)string, (void*)string->data, string->data); + + char *data_ptr = string->data; + struct nub_core_string *str_ptr = string; + + #if STRING_DEBUG + nub_core_string_frees++; + + memset(data_ptr, 0xDD, str_ptr->length); + memset(str_ptr, 0xDD, sizeof(*str_ptr)); + #endif + + free(data_ptr); + free(str_ptr); + } + } + + static inline struct nub_core_string *nub_core_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; + + #if STRING_DEBUG + nub_core_string_allocs++; + STR_DBG("NEW concat: str=%p ref=%u \"%s\"", (void*)result, result->ref, result->data); + #endif + + return result; + } + + static inline struct nub_core_string *nub_core_string_from_cstr(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; + + #if STRING_DEBUG + nub_core_string_allocs++; + STR_DBG("NEW from_cstr: str=%p ref=%u \"%s\"", (void*)result, result->ref, result->data); + #endif + + return result; + } + + #define da_append(xs, x) \ + do { \ + if ((xs)->count >= (xs)->capacity) { \ + if ((xs)->capacity == 0) (xs)->capacity = 256; \ + else (xs)->capacity *= 2; \ + (xs)->items = realloc((xs)->items, (xs)->capacity*sizeof(*(xs)->items)); \ + } \ + (xs)->items[(xs)->count++] = (x); \ + } while (0) """); while (referencedTypes.Count != 0) @@ -143,232 +294,111 @@ public class Generator switch (type) { - case NubTypeString: - { - writer.WriteLine - ( - """ - #define FLAG_STRING_LITERAL 1 - #define STRING_DEBUG 1 + case NubTypeArray arrayType: + { + var name = NameMangler.Mangle("dynamic", "array", arrayType); - #if STRING_DEBUG - #define STR_DBG(fmt, ...) fprintf(stderr, "[STR] " fmt "\n", ##__VA_ARGS__) - #else - #define STR_DBG(fmt, ...) - #endif - - struct nub_core_string + writer.WriteLine + ( + $$""" + struct {{name}} { - char *data; - size_t length; - uint32_t ref; - uint32_t flags; + size_t count; + size_t capacity; + {{CType(arrayType.ElementType)}} *items; }; - - #if STRING_DEBUG - static size_t nub_core_string_allocs = 0; - static size_t nub_core_string_frees = 0; - - __attribute__((destructor)) - static void string_debug_report(void) - { - fprintf(stderr, "[STR] REPORT allocs=%zu frees=%zu leaks=%zu\n", nub_core_string_allocs, nub_core_string_frees, nub_core_string_allocs - nub_core_string_frees); - } - #endif - - static inline void nub_core_string_rc_inc(struct nub_core_string *string) - { - if (string == NULL) - { - STR_DBG("INC null string"); - return; - } - - if (string->flags & FLAG_STRING_LITERAL) - return; - - string->ref += 1; - - STR_DBG("INC: str=%p ref=%u \"%s\"", (void*)string, string->ref, string->data); - } - - static inline void nub_core_string_rc_dec(struct nub_core_string *string) - { - if (string == NULL) - { - STR_DBG("DEC null string"); - return; - } - - if (string->flags & FLAG_STRING_LITERAL) - return; - - if (string->ref == 0) - { - STR_DBG("ERROR: DEC on zero refcount str=%p", (void*)string); - return; - } - - string->ref -= 1; - - STR_DBG("DEC: str=%p ref=%u \"%s\"", (void*)string, string->ref, string->data); - - if (string->ref == 0) - { - STR_DBG("FREE: str=%p data=%p \"%s\"", (void*)string, (void*)string->data, string->data); - - char *data_ptr = string->data; - struct nub_core_string *str_ptr = string; - - #if STRING_DEBUG - nub_core_string_frees++; - - memset(data_ptr, 0xDD, str_ptr->length); - memset(str_ptr, 0xDD, sizeof(*str_ptr)); - #endif - - free(data_ptr); - free(str_ptr); - } - } - - static inline struct nub_core_string *nub_core_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; - - #if STRING_DEBUG - nub_core_string_allocs++; - STR_DBG("NEW concat: str=%p ref=%u \"%s\"", (void*)result, result->ref, result->data); - #endif - - return result; - } - - static inline struct nub_core_string *nub_core_string_from_cstr(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; - - #if STRING_DEBUG - nub_core_string_allocs++; - STR_DBG("NEW from_cstr: str=%p ref=%u \"%s\"", (void*)result, result->ref, result->data); - #endif - - return result; - } - """ - ); - - break; - } + ); + break; + } case NubTypeStruct structType: - { - if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) - throw new UnreachableException(); - - foreach (var field in structInfo.Fields) - EmitTypeDefinitionIfNotEmitted(field.Type); - - writer.Write("struct "); - - if (structInfo.Packed) - writer.Write("__attribute__((__packed__)) "); - - writer.WriteLine(NameMangler.Mangle(structType.Module, structType.Name, structType)); - writer.WriteLine("{"); - using (writer.Indent()) { + if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) + throw new UnreachableException(); + foreach (var field in structInfo.Fields) - { - writer.WriteLine($"{CType(field.Type, field.Name)};"); - } - } - writer.WriteLine("};"); - writer.WriteLine(); + EmitTypeDefinitionIfNotEmitted(field.Type); - break; - } - case NubTypeAnonymousStruct anonymousStructType: - { - foreach (var field in anonymousStructType.Fields) - EmitTypeDefinitionIfNotEmitted(field.Type); + writer.Write("struct "); - writer.WriteLine($"struct {NameMangler.Mangle("anonymous", "struct", anonymousStructType)}"); - writer.WriteLine("{"); - using (writer.Indent()) - { - foreach (var field in anonymousStructType.Fields) - { - writer.WriteLine($"{CType(field.Type, field.Name)};"); - } - } - writer.WriteLine("};"); - writer.WriteLine(); + if (structInfo.Packed) + writer.Write("__attribute__((__packed__)) "); - break; - } - case NubTypeEnum enumType: - { - if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) - throw new UnreachableException(); - - foreach (var variant in enumInfo.Variants) - { - if (variant.Type is not null) - { - EmitTypeDefinitionIfNotEmitted(variant.Type); - } - } - - writer.WriteLine($"struct {NameMangler.Mangle(enumType.Module, enumType.Name, enumType)}"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("uint32_t tag;"); - writer.WriteLine("union"); + writer.WriteLine(NameMangler.Mangle(structType.Module, structType.Name, structType)); writer.WriteLine("{"); using (writer.Indent()) { - foreach (var variant in enumInfo.Variants) + foreach (var field in structInfo.Fields) { - if (variant.Type is not null) - { - writer.WriteLine($"{CType(variant.Type, variant.Name)};"); - } + writer.WriteLine($"{CType(field.Type, field.Name)};"); } } writer.WriteLine("};"); - } - writer.WriteLine("};"); - writer.WriteLine(); + writer.WriteLine(); - break; - } + break; + } + case NubTypeAnonymousStruct anonymousStructType: + { + foreach (var field in anonymousStructType.Fields) + EmitTypeDefinitionIfNotEmitted(field.Type); + + writer.WriteLine($"struct {NameMangler.Mangle("anonymous", "struct", anonymousStructType)}"); + writer.WriteLine("{"); + using (writer.Indent()) + { + foreach (var field in anonymousStructType.Fields) + { + writer.WriteLine($"{CType(field.Type, field.Name)};"); + } + } + writer.WriteLine("};"); + writer.WriteLine(); + + break; + } + case NubTypeEnum enumType: + { + if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) + throw new UnreachableException(); + + foreach (var variant in enumInfo.Variants) + { + if (variant.Type is not null) + { + EmitTypeDefinitionIfNotEmitted(variant.Type); + } + } + + writer.WriteLine($"struct {NameMangler.Mangle(enumType.Module, enumType.Name, enumType)}"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine("uint32_t tag;"); + writer.WriteLine("union"); + writer.WriteLine("{"); + using (writer.Indent()) + { + foreach (var variant in enumInfo.Variants) + { + if (variant.Type is not null) + { + writer.WriteLine($"{CType(variant.Type, variant.Name)};"); + } + } + } + writer.WriteLine("};"); + } + writer.WriteLine("};"); + writer.WriteLine(); + + break; + } case NubTypeEnumVariant variantType: - { - EmitTypeDefinitionIfNotEmitted(variantType.EnumType); - break; - } + { + EmitTypeDefinitionIfNotEmitted(variantType.EnumType); + break; + } } } @@ -563,6 +593,8 @@ public class Generator TypedNodeExpressionStructMemberAccess expression => EmitExpressionMemberAccess(expression), TypedNodeExpressionStringLength expression => EmitExpressionStringLength(expression), TypedNodeExpressionStringPointer expression => EmitExpressionStringPointer(expression), + TypedNodeExpressionArrayCount expression => EmitExpressionArrayCount(expression), + TypedNodeExpressionArrayPointer expression => EmitExpressionArrayPointer(expression), TypedNodeExpressionLocalIdent expression => expression.Name, TypedNodeExpressionGlobalIdent expression => EmitNodeExpressionGlobalIdent(expression), TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression), @@ -584,7 +616,7 @@ public class Generator return name; } - var op = expression.Operation switch + var op = expression.Operation switch { TypedNodeExpressionBinary.Op.Add => $"({left} + {right})", TypedNodeExpressionBinary.Op.Subtract => $"({left} - {right})", @@ -712,6 +744,18 @@ public class Generator return $"{target}->data"; } + private string EmitExpressionArrayCount(TypedNodeExpressionArrayCount expression) + { + var target = EmitExpression(expression.Target); + return $"{target}.count"; + } + + private string EmitExpressionArrayPointer(TypedNodeExpressionArrayPointer expression) + { + var target = EmitExpression(expression.Target); + return $"{target}.items"; + } + private string EmitNodeExpressionGlobalIdent(TypedNodeExpressionGlobalIdent expression) { if (!moduleGraph.TryResolveIdentifier(expression.Module, expression.Name, true, out var info)) @@ -747,6 +791,7 @@ public class Generator NubTypePointer type => CType(type.To) + (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)))})", + NubTypeArray type => $"struct {NameMangler.Mangle("dynamic", "array", type)}" + (varName != null ? $" {varName}" : ""), _ => throw new ArgumentOutOfRangeException(nameof(node), node, null) }; } @@ -787,69 +832,69 @@ public class Generator switch (type) { case NubTypeString: - { - writer.WriteLine($"nub_core_string_rc_inc({value});"); - break; - } + { + writer.WriteLine($"nub_core_string_rc_inc({value});"); + break; + } case NubTypeStruct structType: - { - if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) - throw new UnreachableException(); + { + if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) + throw new UnreachableException(); - foreach (var field in structInfo.Fields) - { - EmitCopyConstructor($"{value}.{field.Name}", field.Type); - } - break; - } - case NubTypeAnonymousStruct anonymousStructType: - { - foreach (var field in anonymousStructType.Fields) - { - EmitCopyConstructor($"{value}.{field.Name}", field.Type); - } - break; - } - case NubTypeEnum enumType: - { - if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) - throw new UnreachableException(); - - writer.WriteLine($"switch ({value}.tag)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - for (int i = 0; i < enumInfo.Variants.Count; i++) + foreach (var field in structInfo.Fields) { - Module.TypeInfoEnum.Variant variant = enumInfo.Variants[i]; + EmitCopyConstructor($"{value}.{field.Name}", field.Type); + } + break; + } + case NubTypeAnonymousStruct anonymousStructType: + { + foreach (var field in anonymousStructType.Fields) + { + EmitCopyConstructor($"{value}.{field.Name}", field.Type); + } + break; + } + case NubTypeEnum enumType: + { + if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) + throw new UnreachableException(); - if (variant.Type is not null) + writer.WriteLine($"switch ({value}.tag)"); + writer.WriteLine("{"); + using (writer.Indent()) + { + for (int i = 0; i < enumInfo.Variants.Count; i++) { - writer.WriteLine($"case {i}:"); - writer.WriteLine("{"); - using (writer.Indent()) + Module.TypeInfoEnum.Variant variant = enumInfo.Variants[i]; + + if (variant.Type is not null) { - EmitCopyConstructor($"{value}.{variant.Name}", variant.Type); - writer.WriteLine("break;"); + writer.WriteLine($"case {i}:"); + writer.WriteLine("{"); + using (writer.Indent()) + { + EmitCopyConstructor($"{value}.{variant.Name}", variant.Type); + writer.WriteLine("break;"); + } + writer.WriteLine("}"); } - writer.WriteLine("}"); } } + writer.WriteLine("}"); + break; } - writer.WriteLine("}"); - break; - } case NubTypeEnumVariant enumVariantType: - { - if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) - throw new UnreachableException(); + { + if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) + throw new UnreachableException(); - var variant = enumInfo.Variants.First(x => x.Name == enumVariantType.Variant); - if (variant.Type is not null) - EmitCopyConstructor($"{value}.{variant.Name}", variant.Type); + var variant = enumInfo.Variants.First(x => x.Name == enumVariantType.Variant); + if (variant.Type is not null) + EmitCopyConstructor($"{value}.{variant.Name}", variant.Type); - break; - } + break; + } } } @@ -858,68 +903,68 @@ public class Generator switch (type) { case NubTypeString: - { - writer.WriteLine($"nub_core_string_rc_dec({value});"); - break; - } + { + writer.WriteLine($"nub_core_string_rc_dec({value});"); + break; + } case NubTypeStruct structType: - { - if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) - throw new UnreachableException(); + { + if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) + throw new UnreachableException(); - foreach (var field in structInfo.Fields) - { - EmitCopyDestructor($"{value}.{field.Name}", field.Type); - } - break; - } - case NubTypeAnonymousStruct anonymousStructType: - { - foreach (var field in anonymousStructType.Fields) - { - EmitCopyDestructor($"{value}.{field.Name}", field.Type); - } - break; - } - case NubTypeEnum enumType: - { - if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) - throw new UnreachableException(); - - writer.WriteLine($"switch ({value}.tag)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - for (int i = 0; i < enumInfo.Variants.Count; i++) + foreach (var field in structInfo.Fields) { - var variant = enumInfo.Variants[i]; - if (variant.Type is not null) + EmitCopyDestructor($"{value}.{field.Name}", field.Type); + } + break; + } + case NubTypeAnonymousStruct anonymousStructType: + { + foreach (var field in anonymousStructType.Fields) + { + EmitCopyDestructor($"{value}.{field.Name}", field.Type); + } + break; + } + case NubTypeEnum enumType: + { + if (!moduleGraph.TryResolveType(enumType.Module, enumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) + throw new UnreachableException(); + + writer.WriteLine($"switch ({value}.tag)"); + writer.WriteLine("{"); + using (writer.Indent()) + { + for (int i = 0; i < enumInfo.Variants.Count; i++) { - writer.WriteLine($"case {i}:"); - writer.WriteLine("{"); - using (writer.Indent()) + var variant = enumInfo.Variants[i]; + if (variant.Type is not null) { - EmitCopyDestructor($"{value}.{variant.Name}", variant.Type); - writer.WriteLine("break;"); + writer.WriteLine($"case {i}:"); + writer.WriteLine("{"); + using (writer.Indent()) + { + EmitCopyDestructor($"{value}.{variant.Name}", variant.Type); + writer.WriteLine("break;"); + } + writer.WriteLine("}"); } - writer.WriteLine("}"); } } + writer.WriteLine("}"); + break; } - writer.WriteLine("}"); - break; - } case NubTypeEnumVariant enumVariantType: - { - if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) - throw new UnreachableException(); + { + if (!moduleGraph.TryResolveType(enumVariantType.EnumType.Module, enumVariantType.EnumType.Name, true, out var info) || info is not Module.TypeInfoEnum enumInfo) + throw new UnreachableException(); - var variant = enumInfo.Variants.First(x => x.Name == enumVariantType.Variant); - if (variant.Type is not null) - EmitCopyDestructor($"{value}.{variant.Name}", variant.Type); + var variant = enumInfo.Variants.First(x => x.Name == enumVariantType.Variant); + if (variant.Type is not null) + EmitCopyDestructor($"{value}.{variant.Name}", variant.Type); - break; - } + break; + } } } diff --git a/compiler/ModuleGraph.cs b/compiler/ModuleGraph.cs index 6d9120e..fb45918 100644 --- a/compiler/ModuleGraph.cs +++ b/compiler/ModuleGraph.cs @@ -85,21 +85,21 @@ public class ModuleGraph switch (type) { case Manifest.Module.TypeInfoStruct s: - { - var info = new Module.TypeInfoStruct(Module.DefinitionSource.Imported, s.Exported, s.Packed); - var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList(); - info.SetFields(fields); - module.AddType(name, info); - break; - } + { + var info = new Module.TypeInfoStruct(Module.DefinitionSource.Imported, s.Exported, s.Packed); + var fields = s.Fields.Select(x => new Module.TypeInfoStruct.Field(x.Name, x.Type)).ToList(); + info.SetFields(fields); + module.AddType(name, info); + break; + } case Manifest.Module.TypeInfoEnum e: - { - var info = new Module.TypeInfoEnum(Module.DefinitionSource.Imported, e.Exported); - var variants = e.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList(); - info.SetVariants(variants); - module.AddType(name, info); - break; - } + { + var info = new Module.TypeInfoEnum(Module.DefinitionSource.Imported, e.Exported); + var variants = e.Variants.Select(v => new Module.TypeInfoEnum.Variant(v.Name, v.Type)).ToList(); + info.SetVariants(variants); + module.AddType(name, info); + break; + } default: throw new ArgumentOutOfRangeException(nameof(type)); } @@ -211,6 +211,7 @@ public class ModuleGraph NodeTypeUInt type => NubTypeUInt.Get(type.Width), NodeTypeString => NubTypeString.Instance, NodeTypeVoid => NubTypeVoid.Instance, + NodeTypeArray type => NubTypeArray.Get(ResolveType(type.ElementType, currentModule)), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } diff --git a/compiler/NubType.cs b/compiler/NubType.cs index b3df3ce..2fa7f87 100644 --- a/compiler/NubType.cs +++ b/compiler/NubType.cs @@ -256,6 +256,28 @@ public class NubTypeFunc : NubType private record Signature(IReadOnlyList Parameters, NubType ReturnType); } +public class NubTypeArray : NubType +{ + private static readonly Dictionary Cache = new(); + + public static NubTypeArray Get(NubType to) + { + if (!Cache.TryGetValue(to, out var ptr)) + Cache[to] = ptr = new NubTypeArray(to); + + return ptr; + } + + public NubType ElementType { get; } + + private NubTypeArray(NubType elementType) + { + ElementType = elementType; + } + + public override string ToString() => $"[]{ElementType}"; +} + public class TypeEncoder { public static string Encode(NubType type) @@ -355,6 +377,12 @@ public class TypeEncoder sb.Append(')'); break; + case NubTypeArray a: + sb.Append("A("); + EncodeType(sb, a.ElementType); + sb.Append(')'); + break; + default: throw new NotSupportedException(type.GetType().Name); } @@ -396,6 +424,7 @@ public class TypeDecoder 'F' => DecodeFunc(), 'T' => DecodeStruct(), 'E' => DecodeEnum(), + 'A' => DecodeArray(), _ => throw new Exception($"'{start}' is not a valid start to a type") }; } @@ -531,6 +560,14 @@ public class TypeDecoder throw new Exception($"Expected 'V' or 'N'"); } + private NubTypeArray DecodeArray() + { + Expect('('); + var elementType = DecodeType(); + Expect(')'); + return NubTypeArray.Get(elementType); + } + private bool TryPeek(out char c) { if (index >= encoded.Length) diff --git a/compiler/Parser.cs b/compiler/Parser.cs index d1507ed..3f48c0e 100644 --- a/compiler/Parser.cs +++ b/compiler/Parser.cs @@ -74,7 +74,7 @@ public class Parser } } - modifier_done: + modifier_done: if (TryExpectKeyword(Keyword.Func)) { @@ -524,6 +524,13 @@ public class Parser return new NodeTypeAnonymousStruct(TokensFrom(startIndex), fields); } + if (TryExpectSymbol(Symbol.OpenSquare)) + { + ExpectSymbol(Symbol.CloseSquare); + var elementType = ParseType(); + return new NodeTypeArray(TokensFrom(startIndex), elementType); + } + if (TryExpectIdent(out var ident)) { switch (ident.Ident) @@ -1035,6 +1042,11 @@ public class NodeTypeAnonymousStruct(List tokens, List tokens, NodeType elementType) : NodeType(tokens) +{ + public NodeType ElementType { get; } = elementType; +} + public class NodeTypePointer(List tokens, NodeType to) : NodeType(tokens) { public NodeType To { get; } = to; diff --git a/compiler/Program.cs b/compiler/Program.cs index 11b4a84..adb898a 100644 --- a/compiler/Program.cs +++ b/compiler/Program.cs @@ -144,9 +144,15 @@ if (!compileLib) return 1; } - if (entryPointType.Parameters.Any()) + if (entryPointType.Parameters.Count != 1) { - DiagnosticFormatter.Print(Diagnostic.Error($"Entrypoint must not take any parameters").Build(), Console.Error); + DiagnosticFormatter.Print(Diagnostic.Error($"Entrypoint must take exaxtly one parameter").Build(), Console.Error); + return 1; + } + + if (entryPointType.Parameters[0] is not NubTypeArray { ElementType: NubTypeString }) + { + DiagnosticFormatter.Print(Diagnostic.Error($"First parameter of entrypoint must be a string array").Build(), Console.Error); return 1; } diff --git a/compiler/Tokenizer.cs b/compiler/Tokenizer.cs index a340f28..c6d3431 100644 --- a/compiler/Tokenizer.cs +++ b/compiler/Tokenizer.cs @@ -77,336 +77,346 @@ public class Tokenizer switch (c) { case '0' when Peek(1) is 'x': - { - Consume(); - Consume(); - - var parsed = BigInteger.Zero; - var seenDigit = false; - - while (TryPeek(out c)) { - if (c == '_') - { - Consume(); - continue; - } - - if (!char.IsAsciiHexDigit(c)) - break; - - seenDigit = true; - parsed <<= 4; - Consume(); - parsed += c switch + Consume(); + + var parsed = BigInteger.Zero; + var seenDigit = false; + + while (TryPeek(out c)) { - >= '0' and <= '9' => c - '0', - >= 'a' and <= 'f' => c - 'a' + 10, - >= 'A' and <= 'F' => c - 'A' + 10, - _ => 0 - }; + if (c == '_') + { + Consume(); + continue; + } + + if (!char.IsAsciiHexDigit(c)) + break; + + seenDigit = true; + parsed <<= 4; + + Consume(); + parsed += c switch + { + >= '0' and <= '9' => c - '0', + >= 'a' and <= 'f' => c - 'a' + 10, + >= 'A' and <= 'F' => c - 'A' + 10, + _ => 0 + }; + } + + if (!seenDigit) + throw new CompileException(Diagnostic.Error("Expected hexadecimal digits after 0x").At(fileName, line, startColumn, column - startColumn).Build()); + + return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); } - - if (!seenDigit) - throw new CompileException(Diagnostic.Error("Expected hexadecimal digits after 0x").At(fileName, line, startColumn, column - startColumn).Build()); - - return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); - } case '0' when Peek(1) is 'b': - { - Consume(); - Consume(); - - var parsed = BigInteger.Zero; - var seenDigit = false; - - while (TryPeek(out c)) { - if (c == '_') + Consume(); + Consume(); + + var parsed = BigInteger.Zero; + var seenDigit = false; + + while (TryPeek(out c)) { - Consume(); - continue; + if (c == '_') + { + Consume(); + continue; + } + + if (c is not '0' and not '1') + break; + + seenDigit = true; + parsed <<= 1; + + if (Consume() == '1') + parsed += BigInteger.One; } - if (c is not '0' and not '1') - break; + if (!seenDigit) + throw new CompileException(Diagnostic.Error("Expected binary digits after 0b").At(fileName, line, startColumn, column - startColumn).Build()); - seenDigit = true; - parsed <<= 1; - - if (Consume() == '1') - parsed += BigInteger.One; + return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); } - - if (!seenDigit) - throw new CompileException(Diagnostic.Error("Expected binary digits after 0b").At(fileName, line, startColumn, column - startColumn).Build()); - - return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); - } default: - { - var parsed = BigInteger.Zero; - - while (TryPeek(out c)) { - if (c == '_') + var parsed = BigInteger.Zero; + + while (TryPeek(out c)) { - Consume(); - continue; + if (c == '_') + { + Consume(); + continue; + } + + if (!char.IsDigit(c)) + break; + + parsed *= 10; + parsed += Consume() - '0'; } - if (!char.IsDigit(c)) - break; - - parsed *= 10; - parsed += Consume() - '0'; + return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); } - - return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); - } } } switch (c) { case '"': - { - Consume(); - var buf = new StringBuilder(); - - while (true) - { - if (!TryPeek(out c)) - throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 0).Build()); - - if (c == '"') - break; - - if (c == '\n') - throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 1).Build()); - - buf.Append(Consume()); - } - - Consume(); - return new TokenStringLiteral(line, startColumn, column - startColumn, buf.ToString()); - } - - case '{': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenCurly); - } - case '}': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseCurly); - } - case '(': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenParen); - } - case ')': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseParen); - } - case ',': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Comma); - } - case '.': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period); - } - case ':' when Peek(1) is ':': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ColonColon); - } - case ':': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Colon); - } - case '^': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Caret); - } - case '!' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.BangEqual); - } - case '!': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Bang); - } - case '=' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.EqualEqual); - } - case '=': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Equal); - } - case '<' when Peek(1) is '<': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanLessThan); - } - case '<' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanEqual); - } - case '<': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThan); - } - case '>' when Peek(1) is '>': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanGreaterThan); - } - case '>' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanEqual); - } - case '>': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThan); - } - case '+' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PlusEqual); - } - case '+': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Plus); - } - case '-' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.MinusEqual); - } - case '-': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Minus); - } - case '*' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.StarEqual); - } - case '*': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Star); - } - case '/' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlashEqual); - } - case '/': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlash); - } - case '%' when Peek(1) is '=': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PercentEqual); - } - case '%': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Percent); - } - case '&' when Peek(1) is '&': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.AmpersandAmpersand); - } - case '&': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Ampersand); - } - case '|' when Peek(1) is '|': - { - Consume(); - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PipePipe); - } - case '|': - { - Consume(); - return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Pipe); - } - default: - { - if (char.IsLetter(c) || c == '_') { + Consume(); var buf = new StringBuilder(); - while (TryPeek(out c) && (char.IsLetterOrDigit(c) || c == '_')) - buf.Append(Consume()); - - var value = buf.ToString(); - - return value switch + while (true) { - "func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func), - "struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct), - "packed" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Packed), - "enum" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Enum), - "new" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.New), - "match" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Match), - "let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let), - "if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If), - "else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else), - "while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While), - "return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return), - "module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module), - "export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export), - "extern" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Extern), - "true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true), - "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), - _ => new TokenIdent(line, startColumn, column - startColumn, value) - }; + if (!TryPeek(out c)) + throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 0).Build()); + + if (c == '"') + break; + + if (c == '\n') + throw new CompileException(Diagnostic.Error("Unterminated string literal").At(fileName, line, column, 1).Build()); + + buf.Append(Consume()); + } + + Consume(); + return new TokenStringLiteral(line, startColumn, column - startColumn, buf.ToString()); } - throw new CompileException(Diagnostic.Error($"Unexpected character '{c}'").At(fileName, line, column, 1).Build()); - } + case '{': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenCurly); + } + case '}': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseCurly); + } + case '[': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenSquare); + } + case ']': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseSquare); + } + case '(': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenParen); + } + case ')': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseParen); + } + case ',': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Comma); + } + case '.': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period); + } + case ':' when Peek(1) is ':': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ColonColon); + } + case ':': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Colon); + } + case '^': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Caret); + } + case '!' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.BangEqual); + } + case '!': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Bang); + } + case '=' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.EqualEqual); + } + case '=': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Equal); + } + case '<' when Peek(1) is '<': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanLessThan); + } + case '<' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanEqual); + } + case '<': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThan); + } + case '>' when Peek(1) is '>': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanGreaterThan); + } + case '>' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanEqual); + } + case '>': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThan); + } + case '+' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PlusEqual); + } + case '+': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Plus); + } + case '-' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.MinusEqual); + } + case '-': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Minus); + } + case '*' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.StarEqual); + } + case '*': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Star); + } + case '/' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlashEqual); + } + case '/': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlash); + } + case '%' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PercentEqual); + } + case '%': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Percent); + } + case '&' when Peek(1) is '&': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.AmpersandAmpersand); + } + case '&': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Ampersand); + } + case '|' when Peek(1) is '|': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PipePipe); + } + case '|': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Pipe); + } + default: + { + if (char.IsLetter(c) || c == '_') + { + var buf = new StringBuilder(); + + while (TryPeek(out c) && (char.IsLetterOrDigit(c) || c == '_')) + buf.Append(Consume()); + + var value = buf.ToString(); + + return value switch + { + "func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func), + "struct" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Struct), + "packed" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Packed), + "enum" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Enum), + "new" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.New), + "match" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Match), + "let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let), + "if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If), + "else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else), + "while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While), + "return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return), + "module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module), + "export" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Export), + "extern" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Extern), + "true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true), + "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), + _ => new TokenIdent(line, startColumn, column - startColumn, value) + }; + } + + throw new CompileException(Diagnostic.Error($"Unexpected character '{c}'").At(fileName, line, column, 1).Build()); + } } } @@ -497,6 +507,8 @@ public enum Symbol CloseCurly, OpenParen, CloseParen, + OpenSquare, + CloseSquare, Comma, Period, Colon, @@ -566,6 +578,8 @@ public static class TokenExtensions Symbol.CloseCurly => "}", Symbol.OpenParen => "(", Symbol.CloseParen => ")", + Symbol.OpenSquare => "[", + Symbol.CloseSquare => "]", Symbol.Comma => ",", Symbol.Period => ".", Symbol.Colon => ":", diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index 75a5e45..89a5c41 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -244,7 +244,7 @@ public class TypeChecker } if (uncoveredCases.Any()) - throw BasicError($"Match statement does not cover the following cases: {string.Join(", ", uncoveredCases)}", statement); + throw BasicError($"Match statement does not cover the following cases: {string.Join(", ", uncoveredCases)}", statement); return new TypedNodeStatementMatch(statement.Tokens, target, cases); } @@ -470,42 +470,54 @@ public class TypeChecker switch (target.Type) { case NubTypeString stringType: - { - switch (expression.Name.Ident) { - case "length": - return new TypedNodeExpressionStringLength(expression.Tokens, NubTypeUInt.Get(64), target); - case "ptr": - return new TypedNodeExpressionStringPointer(expression.Tokens, NubTypePointer.Get(NubTypeUInt.Get(8)), target); - default: - throw BasicError($"'{expression.Name.Ident}' is not a member of type string", expression.Name); + switch (expression.Name.Ident) + { + case "length": + return new TypedNodeExpressionStringLength(expression.Tokens, NubTypeUInt.Get(64), target); + case "ptr": + return new TypedNodeExpressionStringPointer(expression.Tokens, NubTypePointer.Get(NubTypeUInt.Get(8)), target); + default: + throw BasicError($"'{expression.Name.Ident}' is not a member of type {stringType}", expression.Name); + } + } + case NubTypeArray arrayType: + { + switch (expression.Name.Ident) + { + case "count": + return new TypedNodeExpressionArrayCount(expression.Tokens, NubTypeUInt.Get(64), target); + case "ptr": + return new TypedNodeExpressionArrayPointer(expression.Tokens, NubTypePointer.Get(arrayType.ElementType), target); + default: + throw BasicError($"'{expression.Name.Ident}' is not a member of type {arrayType}", expression.Name); + } } - } case NubTypeStruct structType: - { - if (!moduleGraph.TryResolveModule(structType.Module, out var module)) - throw BasicError($"Module '{structType.Module}' not found", expression.Target); + { + if (!moduleGraph.TryResolveModule(structType.Module, out var module)) + throw BasicError($"Module '{structType.Module}' not found", expression.Target); - if (!module.TryResolveType(structType.Name, currentModule == structType.Module, out var typeDef)) - throw BasicError($"Type '{structType.Name}' not found in module '{structType.Module}'", expression.Target); + if (!module.TryResolveType(structType.Name, currentModule == structType.Module, out var typeDef)) + throw BasicError($"Type '{structType.Name}' not found in module '{structType.Module}'", expression.Target); - if (typeDef is not Module.TypeInfoStruct structDef) - throw BasicError($"Type '{target.Type}' is not a struct", expression.Target); + if (typeDef is not Module.TypeInfoStruct structDef) + throw BasicError($"Type '{target.Type}' is not a struct", expression.Target); - var field = structDef.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident); - if (field == null) - throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); + var field = structDef.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident); + if (field == null) + throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); - return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name); - } + return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name); + } case NubTypeAnonymousStruct anonymousStructType: - { - var field = anonymousStructType.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident); - if (field == null) - throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); + { + var field = anonymousStructType.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident); + if (field == null) + throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target); - return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name); - } + return new TypedNodeExpressionStructMemberAccess(expression.Tokens, field.Type, target, expression.Name); + } default: throw BasicError($"{target.Type} has no member '{expression.Name.Ident}'", target); } @@ -676,6 +688,7 @@ public class TypeChecker NodeTypeUInt type => NubTypeUInt.Get(type.Width), NodeTypeString => NubTypeString.Instance, NodeTypeVoid => NubTypeVoid.Instance, + NodeTypeArray type => NubTypeArray.Get(ResolveType(type.ElementType)), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } @@ -965,6 +978,16 @@ public class TypedNodeExpressionStringPointer(List tokens, NubType type, public TypedNodeExpression Target { get; } = target; } +public class TypedNodeExpressionArrayCount(List tokens, NubType type, TypedNodeExpression target) : TypedNodeExpression(tokens, type) +{ + public TypedNodeExpression Target { get; } = target; +} + +public class TypedNodeExpressionArrayPointer(List tokens, NubType type, TypedNodeExpression target) : TypedNodeExpression(tokens, type) +{ + public TypedNodeExpression Target { get; } = target; +} + public class TypedNodeExpressionFuncCall(List tokens, NubType type, TypedNodeExpression target, List parameters) : TypedNodeExpression(tokens, type) { public TypedNodeExpression Target { get; } = target; diff --git a/examples/program/main.nub b/examples/program/main.nub index b094d08..63849fd 100644 --- a/examples/program/main.nub +++ b/examples/program/main.nub @@ -10,25 +10,12 @@ enum Message { Say: string } -func main(): i32 { - core::println("Hello, world!") - core::println("Hello" + "World") - - let message = getMessage() - - let newStr = new string("cstring".ptr) - - core::println(newStr) - - match message { - Quit { - core::println("quit") - } - Say msg { - core::println(msg) - } +func main(args: []string): i32 { + let i = 0 + while i < args.count { + // core::print(args[i]) + i = i + 1 } - return 0 }