From 610e25950312c87d58449ebe1ba9f7fbaf0e39b8 Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 17 Oct 2025 13:32:00 +0200 Subject: [PATCH] Better type mapping --- compiler/NubLang.CLI/Program.cs | 70 +------ compiler/NubLang/Ast/NubType.cs | 13 ++ compiler/NubLang/Ast/TypeChecker.cs | 14 +- compiler/NubLang/Diagnostics/Diagnostic.cs | 2 +- compiler/NubLang/Generation/Generator.cs | 222 ++++++++++++++------- compiler/NubLang/Syntax/Parser.cs | 28 ++- compiler/NubLang/Syntax/Syntax.cs | 2 + 7 files changed, 198 insertions(+), 153 deletions(-) diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index b952f2a..e3db4d8 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -5,78 +5,10 @@ using NubLang.Generation; using NubLang.Modules; using NubLang.Syntax; -List files = []; -List headerFiles = []; - -for (var i = 0; i < args.Length; i++) -{ - if (args[i] == "-h") - { - i += 1; - if (i > args.Length - 1) - { - Console.Error.WriteLine("Expected argument after -h"); - } - - headerFiles.Add(args[i]); - } - else - { - files.Add(args[i]); - } -} - var diagnostics = new List(); var syntaxTrees = new List(); -foreach (var headerFile in headerFiles) -{ - using var bindingsGenerateProc = new Process(); - // todo(nub31): Embed python file to remove absolute path - bindingsGenerateProc.StartInfo = new ProcessStartInfo("python3", ["/home/oliste/repos/nub-lang/bindings/generate.py", headerFile]) - { - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - UseShellExecute = false, - }; - - bindingsGenerateProc.Start(); - - var output = await bindingsGenerateProc.StandardOutput.ReadToEndAsync(); - var error = await bindingsGenerateProc.StandardError.ReadToEndAsync(); - - await bindingsGenerateProc.WaitForExitAsync(); - - if (bindingsGenerateProc.ExitCode != 0) - { - Console.Error.WriteLine($"Failed to automatically generate bindings for header file {headerFile}\n{error}"); - return 1; - } - - var tokenizer = new Tokenizer(headerFile, output); - tokenizer.Tokenize(); - diagnostics.AddRange(tokenizer.Diagnostics); - - if (tokenizer.Diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error)) - { - Console.Error.WriteLine($"Binding generator produced invalid syntax for header file {headerFile}"); - return 1; - } - - var parser = new Parser(); - var syntaxTree = parser.Parse(tokenizer.Tokens); - - if (parser.Diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error)) - { - Console.Error.WriteLine($"Binding generator produced invalid syntax tree for header file {headerFile}"); - return 1; - } - - syntaxTrees.Add(syntaxTree); -} - -foreach (var file in files) +foreach (var file in args) { var tokenizer = new Tokenizer(file, File.ReadAllText(file)); tokenizer.Tokenize(); diff --git a/compiler/NubLang/Ast/NubType.cs b/compiler/NubLang/Ast/NubType.cs index dea20f0..65d4ff4 100644 --- a/compiler/NubLang/Ast/NubType.cs +++ b/compiler/NubLang/Ast/NubType.cs @@ -141,6 +141,19 @@ public class NubArrayType(NubType elementType) : NubType public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType); } +public class NubConstArrayType(NubType elementType, int size) : NubType +{ + public override bool IsValueType => false; + public override bool IsScalar => true; + + public NubType ElementType { get; } = elementType; + public int Size { get; } = size; + + public override string ToString() => $"[{Size}]{ElementType}"; + public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size; + public override int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size); +} + public class NubCStringType : NubType { public override bool IsValueType => false; diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index 4c0684d..b4d9651 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -329,7 +329,7 @@ public sealed class TypeChecker AddressOfSyntax expression => CheckAddressOf(expression), ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression), ArrayInitializerSyntax expression => CheckArrayInitializer(expression), - BinaryExpressionSyntax expression => CheckBinaryExpression(expression), + BinaryExpressionSyntax expression => CheckBinaryExpression(expression, expectedType), UnaryExpressionSyntax expression => CheckUnaryExpression(expression, expectedType), DereferenceSyntax expression => CheckDereference(expression), MemberFuncCallSyntax expression => CheckMemberFuncCall(expression), @@ -427,7 +427,7 @@ public sealed class TypeChecker return new ArrayInitializerNode(type, capacity, elementType); } - private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression) + private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType) { var op = expression.Operator switch { @@ -457,6 +457,7 @@ public sealed class TypeChecker case BinaryOperatorSyntax.Equal: case BinaryOperatorSyntax.NotEqual: { + // note(nub31): Expected type should not be passed down since the resulting type is different than operands var left = CheckExpression(expression.Left); if (left.Type is not NubIntType and not NubFloatType and not NubBoolType) { @@ -475,6 +476,7 @@ public sealed class TypeChecker case BinaryOperatorSyntax.LessThan: case BinaryOperatorSyntax.LessThanOrEqual: { + // note(nub31): Expected type should not be passed down since the resulting type is different than operands var left = CheckExpression(expression.Left); if (left.Type is not NubIntType and not NubFloatType) { @@ -491,6 +493,7 @@ public sealed class TypeChecker case BinaryOperatorSyntax.LogicalAnd: case BinaryOperatorSyntax.LogicalOr: { + // note(nub31): Expected type should not be passed down since the resulting type is different than operands var left = CheckExpression(expression.Left); if (left.Type is not NubBoolType) { @@ -506,7 +509,7 @@ public sealed class TypeChecker } case BinaryOperatorSyntax.Plus: { - var left = CheckExpression(expression.Left); + var left = CheckExpression(expression.Left, expectedType); if (left.Type is NubIntType or NubFloatType or NubStringType or NubCStringType) { var right = CheckExpression(expression.Right, left.Type); @@ -523,7 +526,7 @@ public sealed class TypeChecker case BinaryOperatorSyntax.Divide: case BinaryOperatorSyntax.Modulo: { - var left = CheckExpression(expression.Left); + var left = CheckExpression(expression.Left, expectedType); if (left.Type is not NubIntType and not NubFloatType) { throw new TypeCheckerException(Diagnostic @@ -542,7 +545,7 @@ public sealed class TypeChecker case BinaryOperatorSyntax.BitwiseXor: case BinaryOperatorSyntax.BitwiseOr: { - var left = CheckExpression(expression.Left); + var left = CheckExpression(expression.Left, expectedType); if (left.Type is not NubIntType) { throw new TypeCheckerException(Diagnostic @@ -988,6 +991,7 @@ public sealed class TypeChecker FloatTypeSyntax f => new NubFloatType(f.Width), FuncTypeSyntax func => new NubFuncType(func.Parameters.Select(ResolveType).ToList(), ResolveType(func.ReturnType)), ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType)), + ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType), arr.Size), PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType)), StringTypeSyntax => new NubStringType(), CustomTypeSyntax c => ResolveCustomType(c), diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs index 43c8cdb..9dd9195 100644 --- a/compiler/NubLang/Diagnostics/Diagnostic.cs +++ b/compiler/NubLang/Diagnostics/Diagnostic.cs @@ -193,7 +193,7 @@ public class Diagnostic return sb.ToString(); } - catch (Exception e) + catch (Exception) { return ConsoleColors.Colorize("Failed to generate error message", ConsoleColors.Red); } diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs index d9dbfbe..c9db13e 100644 --- a/compiler/NubLang/Generation/Generator.cs +++ b/compiler/NubLang/Generation/Generator.cs @@ -8,7 +8,9 @@ public class Generator private readonly HashSet _structTypes; private readonly IndentedTextWriter _writer; private readonly Stack> _deferStack = []; + private readonly Stack<(string Name, NubFuncType FuncType)> _funcDefs = []; private int _tmpIndex; + private int _funcDefIndex; public Generator(List definitions, HashSet structTypes) { @@ -23,47 +25,69 @@ public class Generator return $"_t{++_tmpIndex}"; } - private static string MapType(NubType nubType) + private string MapType(NubType nubType) { return nubType switch { - NubArrayType => "uintptr_t", + NubArrayType arrayType => MapType(arrayType.ElementType), + NubConstArrayType arrayType => MapType(arrayType.ElementType), NubBoolType => "bool", - NubCStringType => "char*", + NubCStringType => "char", NubFloatType floatType => floatType.Width switch { 32 => "f32", 64 => "f64", _ => throw new ArgumentOutOfRangeException() }, - NubFuncType => "uintptr_t", - NubIntType intType => intType.Signed switch + NubFuncType funcType => MapFuncType(funcType), + NubIntType intType => (intType.Signed, intType.Width) switch { - true => intType.Width switch - { - 8 => "i8", - 16 => "i16", - 32 => "i32", - 64 => "i64", - _ => throw new ArgumentOutOfRangeException() - }, - false => intType.Width switch - { - 8 => "u8", - 16 => "u16", - 32 => "u32", - 64 => "u64", - _ => throw new ArgumentOutOfRangeException() - }, + (false, 8) => "u8", + (false, 16) => "u16", + (false, 32) => "u32", + (false, 64) => "u64", + (true, 8) => "i8", + (true, 16) => "i16", + (true, 32) => "i32", + (true, 64) => "i64", + _ => throw new ArgumentOutOfRangeException() }, - NubPointerType => "uintptr_t", - NubStringType => "uintptr_t", + NubPointerType pointerType => MapType(pointerType.BaseType), + NubStringType => throw new NotImplementedException(), NubStructType structType => StructName(structType.Module, structType.Name), NubVoidType => "void", _ => throw new ArgumentOutOfRangeException(nameof(nubType)) }; } + private string MapFuncType(NubFuncType funcType) + { + var name = $"_func_type_def{++_funcDefIndex}"; + _funcDefs.Push((name, funcType)); + return name; + } + + private string MapNameWithType(NubType nubType, string name) + { + var prefix = ""; + var postfix = ""; + + switch (nubType) + { + case NubCStringType or NubPointerType: + prefix = "*"; + break; + case NubArrayType: + postfix = "[]"; + break; + case NubConstArrayType constArrayType: + postfix = $"[{constArrayType.Size}]"; + break; + } + + return $"{MapType(nubType)} {prefix}{name}{postfix}"; + } + private static string FuncName(string module, string name, string? externSymbol) { return externSymbol ?? $"{module}_{name}"; @@ -81,46 +105,45 @@ public class Generator public string Emit() { - _writer.WriteLine(""" - typedef __builtin_va_list va_list; + var header = """ + typedef __builtin_va_list va_list; - #define va_start(ap, last) __builtin_va_start(ap, last) - #define va_arg(ap, type) __builtin_va_arg(ap, type) - #define va_end(ap) __builtin_va_end(ap) - #define va_copy(dest, src) __builtin_va_copy(dest, src) + #define va_start(ap, last) __builtin_va_start(ap, last) + #define va_arg(ap, type) __builtin_va_arg(ap, type) + #define va_end(ap) __builtin_va_end(ap) + #define va_copy(dest, src) __builtin_va_copy(dest, src) - #define NULL ((void*)0) + #define NULL ((void*)0) - typedef unsigned long size_t; - typedef unsigned long uintptr_t; + typedef unsigned long size_t; + typedef unsigned long uintptr_t; - #define offsetof(type, member) __builtin_offsetof(type, member) + #define offsetof(type, member) __builtin_offsetof(type, member) - typedef unsigned char u8; - typedef signed char i8; - typedef unsigned short u16; - typedef signed short i16; - typedef unsigned int u32; - typedef signed int i32; - typedef unsigned long long u64; - typedef signed long long i64; + typedef unsigned char u8; + typedef signed char i8; + typedef unsigned short u16; + typedef signed short i16; + typedef unsigned int u32; + typedef signed int i32; + typedef unsigned long long u64; + typedef signed long long i64; - typedef float f32; - typedef double f64; + typedef float f32; + typedef double f64; - #define I8_C(x) x - #define U8_C(x) x##U + #define I8_C(x) x + #define U8_C(x) x##U - #define I16_C(x) x - #define U16_C(x) x##U + #define I16_C(x) x + #define U16_C(x) x##U - #define I32_C(x) x - #define U32_C(x) x##U + #define I32_C(x) x + #define U32_C(x) x##U - #define I64_C(x) x##LL - #define U64_C(x) x##ULL - """); - _writer.WriteLine(); + #define I64_C(x) x##LL + #define U64_C(x) x##ULL + """; foreach (var structType in _structTypes) { @@ -130,7 +153,7 @@ public class Generator { foreach (var field in structType.Fields) { - _writer.WriteLine($"{MapType(field.Type)} {field.Name};"); + _writer.WriteLine($"{MapNameWithType(field.Type, field.Name)};"); } } @@ -144,11 +167,11 @@ public class Generator { appendNewLine = true; var parameters = funcNode.Signature.Parameters.Count != 0 - ? string.Join(", ", funcNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}")) + ? string.Join(", ", funcNode.Signature.Parameters.Select(x => MapNameWithType(x.Type, x.Name))) : "void"; var name = FuncName(funcNode.Module, funcNode.Name, funcNode.ExternSymbol); - _writer.WriteLine($"{MapType(funcNode.Signature.ReturnType)} {name}({parameters});"); + _writer.WriteLine($"{MapNameWithType(funcNode.Signature.ReturnType, name)}({parameters});"); } if (appendNewLine) @@ -161,11 +184,11 @@ public class Generator foreach (var structFuncNode in structNode.Functions) { var parameters = structFuncNode.Signature.Parameters.Count != 0 - ? string.Join(", ", structFuncNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}")) + ? string.Join(", ", structFuncNode.Signature.Parameters.Select(x => MapNameWithType(x.Type, x.Name))) : "void"; var name = StructFuncName(structNode.Module, structNode.Name, structFuncNode.Name); - _writer.WriteLine($"{MapType(structFuncNode.Signature.ReturnType)} {name}({parameters})"); + _writer.WriteLine($"{MapNameWithType(structFuncNode.Signature.ReturnType, name)}({parameters})"); EmitBlock(structFuncNode.Body); _writer.WriteLine(); } @@ -176,7 +199,7 @@ public class Generator if (funcNode.Body == null) continue; var parameters = funcNode.Signature.Parameters.Count != 0 - ? string.Join(", ", funcNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}")) + ? string.Join(", ", funcNode.Signature.Parameters.Select(x => MapNameWithType(x.Type, x.Name))) : "void"; if (funcNode.ExternSymbol == null) @@ -185,12 +208,26 @@ public class Generator } var name = FuncName(funcNode.Module, funcNode.Name, funcNode.ExternSymbol); - _writer.WriteLine($"{MapType(funcNode.Signature.ReturnType)} {name}({parameters})"); + _writer.WriteLine($"{MapNameWithType(funcNode.Signature.ReturnType, name)}({parameters})"); EmitBlock(funcNode.Body); _writer.WriteLine(); } - return _writer.ToString(); + List typedefs = []; + + while (_funcDefs.TryPop(out var funcTypeDef)) + { + var returnType = MapType(funcTypeDef.FuncType.ReturnType); + var paramList = string.Join(", ", funcTypeDef.FuncType.Parameters.Select((type, i) => MapNameWithType(type, $"arg{i}"))); + if (funcTypeDef.FuncType.Parameters.Count == 0) + { + paramList = "void"; + } + + typedefs.Add($"typedef {returnType} (*{funcTypeDef.Name})({paramList});"); + } + + return header + "\n\n" + string.Join('\n', typedefs) + "\n\n" + _writer; } private void EmitStatement(StatementNode statementNode) @@ -296,7 +333,7 @@ public class Generator if (_deferStack.Peek().Count != 0) { var tmp = NewTmp(); - _writer.WriteLine($"{MapType(returnNode.Value.Type)} {tmp} = {returnValue};"); + _writer.WriteLine($"{MapNameWithType(returnNode.Value.Type, tmp)} = {returnValue};"); var blockDefers = _deferStack.Peek(); for (var i = blockDefers.Count - 1; i >= 0; i--) @@ -330,11 +367,11 @@ public class Generator if (variableDeclarationNode.Assignment != null) { var value = EmitExpression(variableDeclarationNode.Assignment); - _writer.WriteLine($"{MapType(variableDeclarationNode.Type)} {variableDeclarationNode.Name} = {value};"); + _writer.WriteLine($"{MapNameWithType(variableDeclarationNode.Type, variableDeclarationNode.Name)} = {value};"); } else { - _writer.WriteLine($"{MapType(variableDeclarationNode.Type)} {variableDeclarationNode.Name};"); + _writer.WriteLine($"{MapNameWithType(variableDeclarationNode.Type, variableDeclarationNode.Name)};"); } } @@ -383,14 +420,12 @@ public class Generator { var array = EmitExpression(arrayIndexAccessNode.Target); var index = EmitExpression(arrayIndexAccessNode.Index); - return $"(({MapType(arrayIndexAccessNode.Type)}*){array})[{index}]"; + return $"{array}[{index}]"; } private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode) { - var capacity = EmitExpression(arrayInitializerNode.Capacity); - var elementType = MapType(arrayInitializerNode.ElementType); - return $"({elementType}[{capacity}]){{0}}"; + throw new NotImplementedException(); } private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode) @@ -432,13 +467,32 @@ public class Generator private string EmitConvertFloat(ConvertFloatNode convertFloatNode) { var value = EmitExpression(convertFloatNode.Value); - return $"({MapType(convertFloatNode.Type)}){value}"; + var targetCast = convertFloatNode.TargetType.Width switch + { + 32 => "f32", + 64 => "f64", + _ => throw new ArgumentOutOfRangeException() + }; + + return $"({targetCast}){value}"; } private string EmitConvertInt(ConvertIntNode convertIntNode) { var value = EmitExpression(convertIntNode.Value); - return $"({MapType(convertIntNode.Type)}){value}"; + var targetType = (convertIntNode.TargetType.Signed, convertIntNode.TargetType.Width) switch + { + (false, 8) => "u8", + (false, 16) => "u16", + (false, 32) => "u32", + (false, 64) => "u64", + (true, 8) => "i8", + (true, 16) => "i16", + (true, 32) => "i32", + (true, 64) => "i64", + _ => throw new ArgumentOutOfRangeException() + }; + return $"({targetType}){value}"; } private string EmitCStringLiteral(CStringLiteralNode cStringLiteralNode) @@ -449,7 +503,7 @@ public class Generator private string EmitDereference(DereferenceNode dereferenceNode) { var pointer = EmitExpression(dereferenceNode.Target); - return $"*({MapType(dereferenceNode.Type)}*){pointer}"; + return $"*{pointer}"; } private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode) @@ -477,7 +531,19 @@ public class Generator private string EmitFloatToIntBuiltin(FloatToIntBuiltinNode floatToIntBuiltinNode) { var value = EmitExpression(floatToIntBuiltinNode.Value); - return $"({MapType(floatToIntBuiltinNode.Type)}){value}"; + var targetType = (floatToIntBuiltinNode.TargetType.Signed, floatToIntBuiltinNode.TargetType.Width) switch + { + (false, 8) => "u8", + (false, 16) => "u16", + (false, 32) => "u32", + (false, 64) => "u64", + (true, 8) => "i8", + (true, 16) => "i16", + (true, 32) => "i32", + (true, 64) => "i64", + _ => throw new ArgumentOutOfRangeException() + }; + return $"({targetType}){value}"; } private string EmitFuncCall(FuncCallNode funcCallNode) @@ -508,7 +574,7 @@ public class Generator private string EmitAddressOf(AddressOfNode addressOfNode) { var value = EmitExpression(addressOfNode.LValue); - return $"(uintptr_t)&{value}"; + return $"&{value}"; } private string EmitLValueIdentifier(LValueIdentifierNode lValueIdentifierNode) @@ -523,6 +589,11 @@ public class Generator private string EmitSizeBuiltin(SizeBuiltinNode sizeBuiltinNode) { + if (sizeBuiltinNode.TargetType is NubConstArrayType constArrayType) + { + return $"sizeof({MapType(constArrayType.ElementType)}) * {constArrayType.Size}"; + } + return $"sizeof({MapType(sizeBuiltinNode.TargetType)})"; } @@ -539,9 +610,10 @@ public class Generator private string EmitStructFuncCall(StructFuncCallNode structFuncCallNode) { + var thisParameter = EmitExpression(structFuncCallNode.StructExpression); var name = StructFuncName(structFuncCallNode.Module, structFuncCallNode.StructName, structFuncCallNode.FuncName); var parameterNames = structFuncCallNode.Parameters.Select(EmitExpression).ToList(); - return $"{name}({string.Join(", ", parameterNames)})"; + return $"{name}({thisParameter}, {string.Join(", ", parameterNames)})"; } private string EmitStructInitializer(StructInitializerNode structInitializerNode) @@ -557,7 +629,7 @@ public class Generator ? "0" : string.Join(", ", initValues); - return $"({MapType(structInitializerNode.Type)}){{{initString}}}"; + return $"({StructName(structInitializerNode.StructType.Module, structInitializerNode.StructType.Name)}){{{initString}}}"; } private string EmitUIntLiteral(UIntLiteralNode uIntLiteralNode) diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index 67fddaa..c1e3130 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -807,9 +807,18 @@ public sealed class Parser if (TryExpectSymbol(Symbol.OpenBracket)) { - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - return new ArrayTypeSyntax(GetTokens(startIndex), baseType); + if (TryExpectIntLiteral(out var intLiteral)) + { + ExpectSymbol(Symbol.CloseBracket); + var baseType = ParseType(); + return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, int.Parse(intLiteral.Value)); + } + else + { + ExpectSymbol(Symbol.CloseBracket); + var baseType = ParseType(); + return new ArrayTypeSyntax(GetTokens(startIndex), baseType); + } } throw new ParseException(Diagnostic @@ -930,6 +939,19 @@ public sealed class Parser return identifier; } + private bool TryExpectIntLiteral([NotNullWhen(true)] out IntLiteralToken? stringLiteral) + { + if (CurrentToken is IntLiteralToken token) + { + stringLiteral = token; + Next(); + return true; + } + + stringLiteral = null; + return false; + } + private FloatLiteralToken ExpectFloatLiteral() { var token = ExpectToken(); diff --git a/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs index 235a2d8..d60be9e 100644 --- a/compiler/NubLang/Syntax/Syntax.cs +++ b/compiler/NubLang/Syntax/Syntax.cs @@ -144,6 +144,8 @@ public record CStringTypeSyntax(List Tokens) : TypeSyntax(Tokens); public record ArrayTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); +public record ConstArrayTypeSyntax(List Tokens, TypeSyntax BaseType, int Size) : TypeSyntax(Tokens); + public record CustomTypeSyntax(List Tokens, string Module, string Name) : TypeSyntax(Tokens); public record StructTemplateTypeSyntax(List Tokens, List TemplateParameters, string Module, string Name) : TypeSyntax(Tokens);