This commit is contained in:
nub31
2025-06-07 19:05:11 +02:00
parent 8ea26cea2e
commit 75c4473a5b
15 changed files with 1275 additions and 208 deletions

View File

@@ -21,7 +21,6 @@ public class Generator
private Stack<string> _continueLabels = [];
private int _variableIndex;
private int _labelIndex;
private int _funcIndex;
private bool _codeIsReachable = true;
private Dictionary<IFuncSignature, string> _funcNames = [];
@@ -35,7 +34,6 @@ public class Generator
_breakLabels = [];
_continueLabels = [];
_variableIndex = 0;
_funcIndex = 0;
_labelIndex = 0;
_codeIsReachable = true;
@@ -45,12 +43,14 @@ public class Generator
_builder.AppendLine();
}
var localFuncIndex = 0;
foreach (var funcSignature in _sourceFiles.SelectMany(f => f.Definitions).OfType<IFuncSignature>())
{
switch (funcSignature)
{
case ExternFuncDefinitionNode externFuncDefinitionNode:
_funcNames[funcSignature] = "$" + externFuncDefinitionNode.Name;
_funcNames[funcSignature] = "$" + externFuncDefinitionNode.CallName;
break;
case LocalFuncDefinitionNode localFuncDefinitionNode:
if (localFuncDefinitionNode.Exported)
@@ -59,8 +59,7 @@ public class Generator
}
else
{
var funcName = GenFuncName();
_funcNames[funcSignature] = funcName;
_funcNames[funcSignature] = $"$func{++localFuncIndex}";
}
break;
@@ -88,7 +87,6 @@ public class Generator
private enum TypeContext
{
Struct,
FuncDef,
FuncCall,
}
@@ -97,30 +95,6 @@ public class Generator
{
return context switch
{
TypeContext.Struct => type switch
{
NubArrayType => "l", // TODO: Arrays in structs are pointers for now
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "h",
PrimitiveTypeKind.I8 => "b",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "h",
PrimitiveTypeKind.U8 => "b",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.String => "l", // TODO: Strings in structs are pointers for now
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in structs"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => throw new NotImplementedException(),
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
TypeContext.FuncDef => type switch
{
NubArrayType => "l",
@@ -143,6 +117,7 @@ public class Generator
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
TypeContext.FuncCall => type switch
@@ -167,6 +142,7 @@ public class Generator
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
_ => throw new ArgumentOutOfRangeException(nameof(context), context, null)
@@ -197,6 +173,7 @@ public class Generator
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => "l",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
}}";
}
@@ -225,6 +202,7 @@ public class Generator
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => "l",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
}}";
}
@@ -253,11 +231,12 @@ public class Generator
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => "l",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
}}";
}
private int QbeTypeSize(NubType type)
private int SizeOf(NubType type)
{
switch (type)
{
@@ -291,16 +270,20 @@ public class Generator
case NubStructType nubStructType:
{
var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name);
return definition.Fields.Sum(f => QbeTypeSize(f.Type));
return definition.Fields.Sum(f => SizeOf(f.Type));
}
case NubPointerType:
case NubArrayType:
{
return 8;
}
case NubFixedArrayType nubFixedArrayType:
{
return SizeOf(nubFixedArrayType.ElementType) * nubFixedArrayType.Capacity;
}
default:
{
throw new NotImplementedException();
throw new UnreachableException();
}
}
}
@@ -313,6 +296,7 @@ public class Generator
NubPointerType => false,
NubPrimitiveType => false,
NubStructType => true,
NubFixedArrayType => true,
_ => throw new ArgumentOutOfRangeException(nameof(type))
};
}
@@ -368,7 +352,7 @@ public class Generator
}
var pointerName = GenVarName();
_builder.AppendLine($" {pointerName} {QBEAssign(parameter.Type)} alloc8 {QbeTypeSize(parameter.Type)}");
_builder.AppendLine($" {pointerName} {QBEAssign(parameter.Type)} alloc8 {SizeOf(parameter.Type)}");
_builder.AppendLine($" storel %{parameterName}, {pointerName}");
_variables[parameter.Name] = new Variable
@@ -403,8 +387,42 @@ public class Generator
private void GenerateStructDefinition(StructDefinitionNode structDefinition)
{
var fields = structDefinition.Fields.Select(f => QBEType(f.Type, TypeContext.Struct));
_builder.AppendLine($"type :{structDefinition.Name} = {{ {string.Join(", ", fields)} }}");
_builder.Append($"type :{structDefinition.Namespace}_{structDefinition.Name} = {{ ");
foreach (var structDefinitionField in structDefinition.Fields)
{
var fieldDefinition = GenerateFieldType(structDefinitionField.Type);
_builder.Append(fieldDefinition + ", ");
}
_builder.AppendLine("}");
}
private string GenerateFieldType(NubType type)
{
return type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "h",
PrimitiveTypeKind.I8 => "b",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "h",
PrimitiveTypeKind.U8 => "b",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.String => "l",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in structs"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
_ => throw new ArgumentOutOfRangeException(nameof(type))
};
}
private void GenerateStatement(StatementNode statement)
@@ -451,31 +469,59 @@ public class Generator
private void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment)
{
var arrayType = (NubArrayType)arrayIndexAssignment.ArrayIndexAccess.Array.Type;
var array = GenerateExpression(arrayIndexAssignment.ArrayIndexAccess.Array);
var index = GenerateExpression(arrayIndexAssignment.ArrayIndexAccess.Index);
GenerateArrayBoundsCheck(array, index);
var value = GenerateExpression(arrayIndexAssignment.Value);
var startName = GenVarName();
_builder.AppendLine($" {startName} =l add {array}, 8");
var adjustedIndex = GenVarName();
_builder.AppendLine($" {adjustedIndex} =l mul {index}, {QbeTypeSize(arrayType.BaseType)}");
var offsetName = GenVarName();
_builder.AppendLine($" {offsetName} =l add {startName}, {adjustedIndex}");
if (IsLargeType(arrayType.BaseType))
switch (arrayIndexAssignment.ArrayIndexAccess.Array.Type)
{
_builder.AppendLine($" blit {value}, {offsetName}, {QbeTypeSize(arrayType.BaseType)}");
}
else
{
_builder.AppendLine($" {QBEStore(arrayType.BaseType)} {value}, {offsetName}");
case NubArrayType arrayType:
{
var startName = GenVarName();
_builder.AppendLine($" {startName} =l add {array}, 8");
var adjustedIndex = GenVarName();
_builder.AppendLine($" {adjustedIndex} =l mul {index}, {SizeOf(arrayType.ElementType)}");
var offsetName = GenVarName();
_builder.AppendLine($" {offsetName} =l add {startName}, {adjustedIndex}");
if (IsLargeType(arrayType.ElementType))
{
_builder.AppendLine($" blit {value}, {offsetName}, {SizeOf(arrayType.ElementType)}");
}
else
{
_builder.AppendLine($" {QBEStore(arrayType.ElementType)} {value}, {offsetName}");
}
break;
}
case NubFixedArrayType fixedArrayType:
{
var startName = GenVarName();
_builder.AppendLine($" {startName} =l add {array}, 8");
var adjustedIndex = GenVarName();
_builder.AppendLine($" {adjustedIndex} =l mul {index}, {SizeOf(fixedArrayType.ElementType)}");
var offsetName = GenVarName();
_builder.AppendLine($" {offsetName} =l add {startName}, {adjustedIndex}");
if (IsLargeType(fixedArrayType.ElementType))
{
_builder.AppendLine($" blit {value}, {offsetName}, {SizeOf(fixedArrayType.ElementType)}");
}
else
{
_builder.AppendLine($" {QBEStore(fixedArrayType.ElementType)} {value}, {offsetName}");
}
break;
}
default:
{
throw new UnreachableException();
}
}
}
@@ -508,7 +554,7 @@ public class Generator
if (IsLargeType(dereferenceAssignment.Value.Type))
{
_builder.AppendLine($" blit {value}, {location}, {QbeTypeSize(dereferenceAssignment.Value.Type)}");
_builder.AppendLine($" blit {value}, {location}, {SizeOf(dereferenceAssignment.Value.Type)}");
}
else
{
@@ -555,7 +601,14 @@ public class Generator
var value = GenerateExpression(memberAssignment.Value);
throw new NotImplementedException();
if (IsLargeType(memberAssignment.Value.Type))
{
_builder.AppendLine($" blit {value}, {offsetName}, {SizeOf(memberAssignment.Value.Type)}");
}
else
{
_builder.AppendLine($" {QBEStore(memberAssignment.Value.Type)} {value}, {offsetName}");
}
}
private void GenerateReturn(ReturnNode @return)
@@ -579,30 +632,31 @@ public class Generator
private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration)
{
var pointerName = GenVarName();
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value?.Type!;
_builder.AppendLine($" {pointerName} {QBEAssign(type)} alloc8 {QbeTypeSize(type)}");
string pointerName;
if (variableDeclaration.Value.HasValue)
{
var result = GenerateExpression(variableDeclaration.Value.Value);
if (IsLargeType(type))
{
_builder.AppendLine($" blit {result}, {pointerName}, {QbeTypeSize(type)}");
pointerName = result;
}
else
{
pointerName = GenVarName();
_builder.AppendLine($" {pointerName} {QBEAssign(type)} alloc8 {SizeOf(type)}");
_builder.AppendLine($" {QBEStore(type)} {result}, {pointerName}");
}
}
else
{
pointerName = GenVarName();
_builder.AppendLine($" {pointerName} {QBEAssign(type)} alloc8 {SizeOf(type)}");
if (IsLargeType(type))
{
_builder.AppendLine($" blit 0, {pointerName}, {QbeTypeSize(type)}");
_builder.AppendLine($" call $nub_memset(l {pointerName}, ub 0, l {SizeOf(type)})");
}
else
{
@@ -648,6 +702,7 @@ public class Generator
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
CastNode cast => GenerateCast(cast),
DereferenceNode dereference => GenerateDereference(dereference),
FixedArrayInitializerNode fixedArrayInitializer => GenerateFixedArrayInitializer(fixedArrayInitializer),
FuncCallNode funcCallExpression => GenerateFuncCall(funcCallExpression),
IdentifierNode identifier => GenerateIdentifier(identifier),
LiteralNode literal => GenerateLiteral(literal),
@@ -660,29 +715,56 @@ public class Generator
private string GenerateArrayIndex(ArrayIndexAccessNode arrayIndexAccess)
{
var arrayType = ((NubArrayType)arrayIndexAccess.Array.Type).BaseType;
var array = GenerateExpression(arrayIndexAccess.Array);
var index = GenerateExpression(arrayIndexAccess.Index);
GenerateArrayBoundsCheck(array, index);
var firstItemPointerName = GenVarName();
_builder.AppendLine($" {firstItemPointerName} =l add {array}, 8");
var offsetPointerName = GenVarName();
_builder.AppendLine($" {offsetPointerName} =l mul {index}, {QbeTypeSize(arrayType)}");
var resultPointerName = GenVarName();
_builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}");
switch (arrayIndexAccess.Array.Type)
{
case NubArrayType arrayType:
{
var firstItemPointerName = GenVarName();
_builder.AppendLine($" {firstItemPointerName} =l add {array}, 8");
var offsetPointerName = GenVarName();
_builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(arrayType.ElementType)}");
var resultPointerName = GenVarName();
_builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}");
if (IsLargeType(arrayType))
{
return resultPointerName;
}
else
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(arrayType)} {QBELoad(arrayType)} {resultPointerName}");
return outputName;
if (IsLargeType(arrayType.ElementType))
{
return resultPointerName;
}
else
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(arrayType.ElementType)} {QBELoad(arrayType.ElementType)} {resultPointerName}");
return outputName;
}
}
case NubFixedArrayType fixedArrayType:
{
var firstItemPointerName = GenVarName();
_builder.AppendLine($" {firstItemPointerName} =l add {array}, 8");
var offsetPointerName = GenVarName();
_builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(fixedArrayType.ElementType)}");
var resultPointerName = GenVarName();
_builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}");
if (IsLargeType(fixedArrayType.ElementType))
{
return resultPointerName;
}
else
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(fixedArrayType.ElementType)} {QBELoad(fixedArrayType.ElementType)} {resultPointerName}");
return outputName;
}
}
default:
{
throw new UnreachableException();
}
}
}
@@ -714,7 +796,7 @@ public class Generator
{
var capacity = GenerateExpression(arrayInitializer.Capacity);
var capacityInBytes = GenVarName();
_builder.AppendLine($" {capacityInBytes} =l mul {capacity}, {QbeTypeSize(arrayInitializer.ElementType)}");
_builder.AppendLine($" {capacityInBytes} =l mul {capacity}, {SizeOf(arrayInitializer.ElementType)}");
var totalArraySize = GenVarName();
_builder.AppendLine($" {totalArraySize} =l add {capacityInBytes}, 8");
var outputName = GenVarName();
@@ -1483,7 +1565,7 @@ public class Generator
var structDefinition = LookupStructDefinition(structInitializer.StructType.Namespace, structInitializer.StructType.Name);
var structVar = GenVarName();
var size = structDefinition.Fields.Sum(x => QbeTypeSize(x.Type));
var size = structDefinition.Fields.Sum(x => SizeOf(x.Type));
_builder.AppendLine($" {structVar} =l alloc8 {size}");
foreach (var field in structDefinition.Fields)
@@ -1498,7 +1580,7 @@ public class Generator
if (IsLargeType(field.Type))
{
_builder.AppendLine($" blit {var}, {offsetName}, {QbeTypeSize(field.Type)}");
_builder.AppendLine($" blit {var}, {offsetName}, {SizeOf(field.Type)}");
}
else
{
@@ -1513,7 +1595,7 @@ public class Generator
if (IsLargeType(field.Type))
{
_builder.AppendLine($" blit {var}, {offsetName}, {QbeTypeSize(field.Type)}");
_builder.AppendLine($" blit {var}, {offsetName}, {SizeOf(field.Type)}");
}
else
{
@@ -1620,6 +1702,20 @@ public class Generator
}
}
private string GenerateFixedArrayInitializer(FixedArrayInitializerNode fixedArrayInitializer)
{
var capacityInBytes = SizeOf(fixedArrayInitializer.Type);
var outputName = GenVarName();
_builder.AppendLine($" {outputName} =l alloc8 {capacityInBytes + 8}");
_builder.AppendLine($" storel {fixedArrayInitializer.Capacity}, {outputName}");
var dataPtr = GenVarName();
_builder.AppendLine($" {dataPtr} =l add {outputName}, 8");
_builder.AppendLine($" call $nub_memset(l {dataPtr}, w 0, l {capacityInBytes})");
return outputName;
}
private string GenerateFuncCall(FuncCallNode funcCall)
{
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
@@ -1664,11 +1760,6 @@ public class Generator
return $"%v{++_variableIndex}";
}
private string GenFuncName()
{
return $"$f{++_funcIndex}";
}
private string GenLabelName()
{
return $"@l{++_labelIndex}";
@@ -1694,7 +1785,7 @@ public class Generator
private int LookupMemberOffset(StructDefinitionNode structDefinition, string member)
{
return structDefinition.Fields.TakeWhile(f => f.Name != member).Sum(f => QbeTypeSize(f.Type));
return structDefinition.Fields.TakeWhile(f => f.Name != member).Sum(f => SizeOf(f.Type));
}
private class Variable

View File

@@ -18,6 +18,7 @@ public class Lexer
["alloc"] = Symbol.Alloc,
["struct"] = Symbol.Struct,
["let"] = Symbol.Let,
["calls"] = Symbol.Calls,
};
private static readonly Dictionary<string, Modifier> Modifiers = new()

View File

@@ -42,5 +42,6 @@ public enum Symbol
DoubleColon,
Namespace,
Let,
Alloc
Alloc,
Calls
}

View File

@@ -16,6 +16,7 @@ public class FuncParameter(string name, NubType type, bool variadic)
public interface IFuncSignature
{
public string Name { get; }
public string Namespace { get; }
public List<FuncParameter> Parameters { get; }
public Optional<NubType> ReturnType { get; }
@@ -53,9 +54,10 @@ public interface IFuncSignature
public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}
public class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool exported) : DefinitionNode(tokens, documentation), IFuncSignature
public class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, string @namespace, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool exported) : DefinitionNode(tokens, documentation), IFuncSignature
{
public string Name { get; } = name;
public string Namespace { get; } = @namespace;
public List<FuncParameter> Parameters { get; } = parameters;
public BlockNode Body { get; } = body;
public Optional<NubType> ReturnType { get; } = returnType;
@@ -64,9 +66,11 @@ public class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<strin
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}
public class ExternFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<FuncParameter> parameters, Optional<NubType> returnType) : DefinitionNode(tokens, documentation), IFuncSignature
public class ExternFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, string @namespace, string callName, List<FuncParameter> parameters, Optional<NubType> returnType) : DefinitionNode(tokens, documentation), IFuncSignature
{
public string Name { get; } = name;
public string Namespace { get; } = @namespace;
public string CallName { get; } = callName;
public List<FuncParameter> Parameters { get; } = parameters;
public Optional<NubType> ReturnType { get; } = returnType;

View File

@@ -11,8 +11,9 @@ public class StructField(string name, NubType type, Optional<ExpressionNode> val
public Optional<ExpressionNode> Value { get; } = value;
}
public class StructDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<StructField> fields) : DefinitionNode(tokens, documentation)
public class StructDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, string @namespace, List<StructField> fields) : DefinitionNode(tokens, documentation)
{
public string Name { get; } = name;
public string Namespace { get; } = @namespace;
public List<StructField> Fields { get; } = fields;
}

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Nub.Lang.Diagnostics;
using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Parsing.Definitions;
@@ -116,7 +117,14 @@ public class Parser
.Build());
}
return new ExternFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, parameters, returnType);
var callName = name.Value;
if (TryExpectSymbol(Symbol.Calls))
{
callName = ExpectIdentifier().Value;
}
return new ExternFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, _namespace, callName, parameters, returnType);
}
var body = ParseBlock();
@@ -131,7 +139,7 @@ public class Parser
.Build());
}
return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, parameters, body, returnType, exported);
return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, _namespace, parameters, body, returnType, exported);
}
private StructDefinitionNode ParseStruct(int startIndex, List<ModifierToken> _, Optional<string> documentation)
@@ -158,7 +166,7 @@ public class Parser
variables.Add(new StructField(variableName, variableType, variableValue));
}
return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, name, variables);
return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, name, _namespace, variables);
}
private FuncParameter ParseFuncParameter()
@@ -521,11 +529,35 @@ public class Parser
}
case Symbol.OpenBracket:
{
var capacity = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
var type = ParseType();
if (Peek().TryGetValue(out var capacityToken) && capacityToken is LiteralToken { Type: NubPrimitiveType { Kind: PrimitiveTypeKind.I64 } } literalToken)
{
var capacity = int.Parse(literalToken.Value);
Next();
ExpectSymbol(Symbol.CloseBracket);
var elementType = ParseType();
if (capacity > 0)
{
expr = new FixedArrayInitializerNode(GetTokensForNode(startIndex), elementType, capacity);
}
else
{
throw new ParseException(Diagnostic
.Error("Fixed array size must be a positive integer")
.WithHelp("Use a positive integer literal for the array size")
.At(literalToken)
.Build());
}
}
else
{
var capacity = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
var type = ParseType();
expr = new ArrayInitializerNode(GetTokensForNode(startIndex), capacity, type);
expr = new ArrayInitializerNode(GetTokensForNode(startIndex), capacity, type);
}
break;
}
case Symbol.Alloc:
@@ -657,12 +689,32 @@ public class Parser
if (TryExpectSymbol(Symbol.OpenBracket))
{
ExpectSymbol(Symbol.CloseBracket);
var baseType = ParseType();
return new NubArrayType(baseType);
if (Peek().TryGetValue(out var token) && token is LiteralToken { Type: NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }, Value: var sizeValue })
{
Next();
ExpectSymbol(Symbol.CloseBracket);
var baseType = ParseType();
var size = int.Parse(sizeValue);
if (size > 0)
{
return new NubFixedArrayType(baseType, size);
}
else
{
throw new UnreachableException();
}
}
else
{
ExpectSymbol(Symbol.CloseBracket);
var baseType = ParseType();
return new NubArrayType(baseType);
}
}
if (!Peek().TryGetValue(out var token))
if (!Peek().TryGetValue(out var peekToken))
{
throw new ParseException(Diagnostic
.Error("Unexpected end of file while parsing type")
@@ -674,9 +726,9 @@ public class Parser
throw new ParseException(Diagnostic
.Error("Invalid type syntax")
.WithHelp("Expected type name, '^' for pointer, or '[]' for array")
.At(token)
.At(peekToken)
.Build());
}
}
private Token ExpectToken()
{
@@ -803,9 +855,9 @@ public class Parser
}
}
private Optional<Token> Peek()
private Optional<Token> Peek(int offset = 0)
{
var peekIndex = _index;
var peekIndex = _index + offset;
while (peekIndex < _tokens.Count && _tokens[peekIndex] is DocumentationToken)
{
peekIndex++;

View File

@@ -6,7 +6,22 @@ public abstract class NubType
{
public static bool IsCompatibleWith(NubType sourceType, NubType targetType)
{
return targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType);
if (targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType))
{
return true;
}
if (sourceType is NubFixedArrayType fixedArray && targetType is NubArrayType array && IsCompatibleWith(fixedArray.ElementType, array.ElementType))
{
return true;
}
if (sourceType.Equals(NubPrimitiveType.String) && targetType is NubArrayType arrayType && IsCompatibleWith(NubPrimitiveType.U8, arrayType.ElementType))
{
return true;
}
return false;
}
public abstract override bool Equals(object? obj);
@@ -55,27 +70,45 @@ public class NubPointerType(NubType baseType) : NubType
}
}
public class NubArrayType(NubType baseType) : NubType
public class NubArrayType(NubType elementType) : NubType
{
public NubType BaseType { get; } = baseType;
public NubType ElementType { get; } = elementType;
public override bool Equals(object? obj)
{
if (obj is NubArrayType other)
{
return BaseType.Equals(other.BaseType);
return ElementType.Equals(other.ElementType);
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(BaseType);
return HashCode.Combine(ElementType);
}
public override string ToString()
{
return "[]" + BaseType;
return "[]" + ElementType;
}
}
public class NubFixedArrayType(NubType elementType, int capacity) : NubType
{
public NubType ElementType { get; } = elementType;
public int Capacity { get; } = capacity;
public override string ToString() => $"[{Capacity}]{ElementType}";
public override bool Equals(object? obj)
{
return obj is NubFixedArrayType other && ElementType.Equals(other.ElementType) && Capacity == other.Capacity;
}
public override int GetHashCode()
{
return HashCode.Combine(ElementType, Capacity);
}
}

View File

@@ -1,3 +1,4 @@
using System.Diagnostics;
using Nub.Lang.Diagnostics;
using Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Parsing.Definitions;
@@ -245,6 +246,11 @@ public class TypeChecker
return nubPointerType.BaseType;
}
private NubType TypeCheckFixedInitializerArray(FixedArrayInitializerNode fixedArrayInitializer)
{
return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity);
}
private NubType? TypeCheckFuncCall(FuncCallNode funcCall, Node node)
{
List<NubType> parameterTypes = [];
@@ -375,11 +381,12 @@ public class TypeChecker
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
CastNode cast => TypeCheckCast(cast),
DereferenceNode dereference => TypeCheckDereference(dereference),
FixedArrayInitializerNode fixedArray => TypeCheckFixedInitializerArray(fixedArray),
FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr, funcCallExpr),
StructInitializerNode structInit => TypeCheckStructInitializer(structInit),
UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression),
MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess),
_ => ReportUnsupportedExpression(expression)
_ => throw new UnreachableException()
};
if (resultType != null)
@@ -390,30 +397,28 @@ public class TypeChecker
return resultType;
}
private NubType? ReportUnsupportedExpression(ExpressionNode expression)
{
ReportError($"Unsupported expression type: {expression.GetType().Name}", expression);
return null;
}
private NubType? TypeCheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess)
{
var expressionType = TypeCheckExpression(arrayIndexAccess.Array);
if (expressionType == null) return null;
if (expressionType is not NubArrayType arrayType)
{
ReportError($"Cannot access index of non-array type {expressionType}", arrayIndexAccess.Array);
return null;
}
var indexType = TypeCheckExpression(arrayIndexAccess.Index);
if (indexType != null && !IsInteger(indexType))
{
ReportError("Array index type must be an integer", arrayIndexAccess.Index);
}
if (expressionType is NubArrayType arrayType)
{
return arrayType.ElementType;
}
if (expressionType is NubFixedArrayType fixedArrayType)
{
return fixedArrayType.ElementType;
}
return arrayType.BaseType;
ReportError($"Cannot access index of non-array type {expressionType}", arrayIndexAccess.Array);
return null;
}
private NubType TypeCheckArrayInitializer(ArrayInitializerNode arrayInitializer)