Simplify generator

This commit is contained in:
nub31
2025-06-02 21:47:09 +02:00
parent e874b8e155
commit 003701c192
5 changed files with 253 additions and 163 deletions

View File

@@ -6,12 +6,10 @@ struct Human {
}
export func main(args: []^string) {
let human = [1]Human
human[0] = alloc Human {
let me = alloc Human {
name = "oliver"
age = 123
age = 32
}
c::printf("%s\n", human[1].name)
c::printf("%s is %d years old\n", me.name, me.age)
}

View File

@@ -86,143 +86,175 @@ public class Generator
return _builder.ToString();
}
private static string SQT(NubType type)
private enum TypeContext
{
switch (type)
{
case NubPrimitiveType primitiveType:
{
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U64:
case PrimitiveTypeKind.String:
case PrimitiveTypeKind.Any:
return "l";
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.U32:
case PrimitiveTypeKind.I16:
case PrimitiveTypeKind.U16:
case PrimitiveTypeKind.I8:
case PrimitiveTypeKind.U8:
case PrimitiveTypeKind.Bool:
return "w";
case PrimitiveTypeKind.F64:
return "d";
case PrimitiveTypeKind.F32:
return "s";
default:
throw new ArgumentOutOfRangeException();
}
}
case NubStructType:
case NubPointerType:
case NubArrayType:
{
return "l";
}
default:
{
throw new NotImplementedException();
}
}
Struct,
FuncDef,
FuncCall,
}
private static string EQT(NubType type)
private static string QBEType(NubType type, TypeContext context)
{
switch (type)
return context switch
{
case NubPrimitiveType primitiveType:
TypeContext.Struct => type switch
{
switch (primitiveType.Kind)
NubArrayType => "l", // TODO: Arrays in structs are pointers for now
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U64:
case PrimitiveTypeKind.String:
return "l";
case PrimitiveTypeKind.Any:
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.U32:
return "w";
case PrimitiveTypeKind.I16:
case PrimitiveTypeKind.U16:
return "h";
case PrimitiveTypeKind.I8:
case PrimitiveTypeKind.U8:
return "b";
case PrimitiveTypeKind.Bool:
case PrimitiveTypeKind.F64:
return "d";
case PrimitiveTypeKind.F32:
return "s";
default:
throw new ArgumentOutOfRangeException();
}
}
case NubStructType nubCustomType:
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
{
return ":" + nubCustomType.Name;
}
case NubPointerType:
case NubArrayType:
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.String => "l",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in function definitions"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
TypeContext.FuncCall => type switch
{
return "l";
}
default:
{
throw new NotImplementedException();
}
}
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType pointerType => pointerType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.String => "l",
PrimitiveTypeKind.Any => "l",
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
_ => throw new ArgumentOutOfRangeException(nameof(context), context, null)
};
}
private static string FQT(NubType type)
private static string QBEStore(NubType type)
{
switch (type)
return $"store{type switch
{
case NubPrimitiveType primitiveType:
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U64:
case PrimitiveTypeKind.String:
case PrimitiveTypeKind.Any:
return "l";
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.U32:
return "w";
case PrimitiveTypeKind.I16:
return "sh";
case PrimitiveTypeKind.U16:
return "uh";
case PrimitiveTypeKind.I8:
return "sb";
case PrimitiveTypeKind.U8:
return "ub";
case PrimitiveTypeKind.Bool:
return "b";
case PrimitiveTypeKind.F64:
return "d";
case PrimitiveTypeKind.F32:
return "s";
default:
throw new ArgumentOutOfRangeException();
}
}
case NubStructType nubCustomType:
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 store instructions"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
}}";
}
private static string QBELoad(NubType type)
{
return $"load{type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
return ":" + nubCustomType.Name;
}
case NubPointerType:
case NubArrayType:
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.String => "l",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in load instructions"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
}}";
}
private static string QBEAssign(NubType type)
{
return $"={type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
return "l";
}
default:
{
throw new NotImplementedException();
}
}
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "w",
PrimitiveTypeKind.I8 => "w",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "w",
PrimitiveTypeKind.U8 => "w",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.String => "l",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in variables"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
}}";
}
private int QbeTypeSize(NubType type)
@@ -297,7 +329,7 @@ public class Generator
_builder.Append("function ");
if (node.ReturnType.HasValue)
{
_builder.Append($"{FQT(node.ReturnType.Value)} ");
_builder.Append($"{QBEType(node.ReturnType.Value, TypeContext.FuncDef)} ");
}
else if (!node.ReturnType.HasValue && node.Name == "main")
{
@@ -307,7 +339,7 @@ public class Generator
_builder.Append('$');
_builder.Append(_funcNames[node]);
var parameterStrings = node.Parameters.Select(parameter => parameter.Variadic ? "..." : $"{FQT(parameter.Type)} %{parameter.Name}");
var parameterStrings = node.Parameters.Select(parameter => parameter.Variadic ? "..." : $"{QBEType(parameter.Type, TypeContext.FuncDef)} %{parameter.Name}");
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
_builder.AppendLine("@start");
@@ -316,7 +348,7 @@ public class Generator
{
var parameterName = parameter.Name;
switch (FQT(parameter.Type))
switch (QBEType(parameter.Type, TypeContext.FuncDef))
{
case "sb":
parameterName = GenVarName();
@@ -337,7 +369,7 @@ public class Generator
}
var pointerName = GenVarName();
_builder.AppendLine($" %{pointerName} ={SQT(parameter.Type)} alloc8 {QbeTypeSize(parameter.Type)}");
_builder.AppendLine($" %{pointerName} {QBEAssign(parameter.Type)} alloc8 {QbeTypeSize(parameter.Type)}");
_builder.AppendLine($" storel %{parameterName}, %{pointerName}");
_variables[parameter.Name] = new Variable
@@ -347,6 +379,8 @@ public class Generator
};
}
_builder.AppendLine();
GenerateBlock(node.Body);
if (node.Body.Statements.LastOrDefault() is not ReturnNode)
@@ -370,7 +404,7 @@ public class Generator
private void GenerateStructDefinition(StructDefinitionNode structDefinition)
{
var fields = structDefinition.Fields.Select(f => EQT(f.Type));
var fields = structDefinition.Fields.Select(f => QBEType(f.Type, TypeContext.Struct));
_builder.AppendLine($"type :{structDefinition.Name} = {{ {string.Join(", ", fields)} }}");
}
@@ -387,6 +421,9 @@ public class Generator
case ContinueNode:
GenerateContinue();
break;
case DereferenceAssignmentNode dereferenceAssignment:
GenerateDereferenceAssignment(dereferenceAssignment);
break;
case IfNode ifStatement:
GenerateIf(ifStatement);
break;
@@ -439,7 +476,7 @@ public class Generator
}
else
{
_builder.AppendLine($" store{SQT(arrayType.BaseType)} {value}, %{offsetName}");
_builder.AppendLine($" {QBEStore(arrayType.BaseType)} {value}, %{offsetName}");
}
}
@@ -465,6 +502,21 @@ public class Generator
_codeIsReachable = false;
}
private void GenerateDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment)
{
var location = GenerateExpression(dereferenceAssignment.Dereference.Expression);
var value = GenerateExpression(dereferenceAssignment.Value);
if (IsLargeType(dereferenceAssignment.Value.Type))
{
_builder.AppendLine($" blit {value}, {location}, {QbeTypeSize(dereferenceAssignment.Value.Type)}");
}
else
{
_builder.AppendLine($" {QBEStore(dereferenceAssignment.Value.Type)} {value}, {location}");
}
}
private void GenerateIf(IfNode ifStatement)
{
var trueLabel = GenLabelName();
@@ -532,16 +584,31 @@ public class Generator
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value?.Type!;
_builder.AppendLine($" %{pointerName} ={SQT(type)} alloc8 {QbeTypeSize(type)}");
_builder.AppendLine($" %{pointerName} {QBEAssign(type)} alloc8 {QbeTypeSize(type)}");
if (variableDeclaration.Value.HasValue)
{
var result = GenerateExpression(variableDeclaration.Value.Value);
_builder.AppendLine($" storel {result}, %{pointerName}");
if (IsLargeType(type))
{
_builder.AppendLine($" blit {result}, %{pointerName}, {QbeTypeSize(type)}");
}
else
{
_builder.AppendLine($" {QBEStore(type)} {result}, %{pointerName}");
}
}
else
{
_builder.AppendLine($" storel 0, %{pointerName}");
if (IsLargeType(type))
{
_builder.AppendLine($" blit 0, %{pointerName}, {QbeTypeSize(type)}");
}
else
{
_builder.AppendLine($" {QBEStore(type)} 0, %{pointerName}");
}
}
_variables[variableDeclaration.Name] = new Variable
@@ -615,7 +682,7 @@ public class Generator
else
{
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} ={SQT(arrayType)} load{SQT(arrayType)} %{resultPointerName}");
_builder.AppendLine($" %{outputName} {QBEAssign(arrayType)} {QBELoad(arrayType)} %{resultPointerName}");
return $"%{outputName}";
}
}
@@ -666,7 +733,7 @@ public class Generator
{
var result = GenerateExpression(dereference.Expression);
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} ={SQT(dereference.Type)} load{SQT(dereference.Type)} {result}");
_builder.AppendLine($" %{outputName} {QBEAssign(dereference.Type)} {QBELoad(dereference.Type)} {result}");
return $"%{outputName}";
}
@@ -1379,7 +1446,7 @@ public class Generator
else
{
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} ={SQT(identifier.Type)} load{SQT(identifier.Type)} {variable.Pointer}");
_builder.AppendLine($" %{outputName} {QBEAssign(identifier.Type)} {QBELoad(identifier.Type)} {variable.Pointer}");
return $"%{outputName}";
}
}
@@ -1436,7 +1503,7 @@ public class Generator
}
else
{
_builder.AppendLine($" store{SQT(field.Type)} {var}, %{offsetName}");
_builder.AppendLine($" {QBEStore(field.Type)} {var}, %{offsetName}");
}
}
else if (field.Value.HasValue)
@@ -1451,7 +1518,7 @@ public class Generator
}
else
{
_builder.AppendLine($" store{SQT(field.Type)} {var}, %{offsetName}");
_builder.AppendLine($" {QBEStore(field.Type)} {var}, %{offsetName}");
}
}
else
@@ -1542,7 +1609,7 @@ public class Generator
else
{
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} ={SQT(memberAccess.Type)} load{SQT(memberAccess.Type)} %{offsetName}");
_builder.AppendLine($" %{outputName} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} %{offsetName}");
return $"%{outputName}";
}
@@ -1556,8 +1623,6 @@ public class Generator
private string GenerateFuncCall(FuncCallNode funcCall)
{
var outputName = GenVarName();
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
if (funcDefinition == null)
{
@@ -1573,33 +1638,26 @@ public class Generator
parameterStrings.Add("...");
}
NubType expectedType;
if (i < funcDefinition.Parameters.Count)
{
expectedType = funcDefinition.Parameters[i].Type;
}
else if (funcDefinition.Parameters[^1].Variadic)
{
expectedType = funcDefinition.Parameters[^1].Type;
}
else
{
throw new Exception($"Parameters for func {funcCall} does not not match");
}
var parameter = funcCall.Parameters[i];
var result = GenerateExpression(parameter);
var qbeParameterType = SQT(expectedType.Equals(NubPrimitiveType.Any) ? parameter.Type : expectedType);
var qbeParameterType = QBEType(parameter.Type, TypeContext.FuncCall);
parameterStrings.Add($"{qbeParameterType} {result}");
}
var funcName = _funcNames[funcDefinition];
var call = $"call ${funcName}({string.Join(", ", parameterStrings)})";
_builder.AppendLine($" %{outputName} ={SQT(funcCall.Type)} {call}");
return $"%{outputName}";
if (funcDefinition.ReturnType.HasValue)
{
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} {QBEAssign(funcCall.Type)} call ${funcName}({string.Join(", ", parameterStrings)})");
return $"%{outputName}";
}
else
{
_builder.AppendLine($" call ${funcName}({string.Join(", ", parameterStrings)})");
return "this should never show up!";
}
}
private string GenVarName()

View File

@@ -223,24 +223,32 @@ public class Parser
{
case Symbol.Assign:
{
Next();
switch (expr)
{
case MemberAccessNode memberAccess:
{
Next();
var value = ParseExpression();
return new MemberAssignmentNode(GetTokensForNode(startIndex), memberAccess, value);
}
case ArrayIndexAccessNode arrayIndexAccess:
{
Next();
var value = ParseExpression();
return new ArrayIndexAssignmentNode(GetTokensForNode(startIndex), arrayIndexAccess, value);
}
case IdentifierNode identifier:
{
Next();
var value = ParseExpression();
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier, value);
}
case DereferenceNode dereference:
{
Next();
var value = ParseExpression();
return new DereferenceAssignmentNode(GetTokensForNode(startIndex), dereference, value);
}
}
break;

View File

@@ -0,0 +1,10 @@
using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Parsing.Expressions;
namespace Nub.Lang.Frontend.Parsing.Statements;
public class DereferenceAssignmentNode(IReadOnlyList<Token> tokens, DereferenceNode dereference, ExpressionNode value) : StatementNode(tokens)
{
public DereferenceNode Dereference { get; } = dereference;
public ExpressionNode Value { get; } = value;
}

View File

@@ -138,6 +138,9 @@ public class TypeChecker
case BreakNode:
case ContinueNode:
break;
case DereferenceAssignmentNode dereferenceAssignment:
TypeCheckDereferenceAssignment(dereferenceAssignment);
break;
default:
ReportError($"Unsupported statement type: {statement.GetType().Name}", statement);
break;
@@ -347,6 +350,19 @@ public class TypeChecker
}
}
private void TypeCheckDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment)
{
var dereferenceType = TypeCheckExpression(dereferenceAssignment.Dereference);
if (dereferenceType == null) return;
var valueType = TypeCheckExpression(dereferenceAssignment.Value);
if (valueType == null) return;
if (!NubType.IsCompatibleWith(dereferenceType, valueType))
{
ReportError($"'{valueType}' is not assignable to type '{dereferenceType}'", dereferenceAssignment);
}
}
private NubType? TypeCheckExpression(ExpressionNode expression)
{
var resultType = expression switch