...
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -42,5 +42,6 @@ public enum Symbol
|
||||
DoubleColon,
|
||||
Namespace,
|
||||
Let,
|
||||
Alloc
|
||||
Alloc,
|
||||
Calls
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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++;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
9
src/runtime/core/syscall.asm
Normal file
9
src/runtime/core/syscall.asm
Normal file
@@ -0,0 +1,9 @@
|
||||
section .text
|
||||
global core_syscall
|
||||
core_syscall:
|
||||
mov rax, rdi
|
||||
mov rdi, rsi
|
||||
mov rsi, rdx
|
||||
mov r10, rcx
|
||||
syscall
|
||||
ret
|
||||
@@ -9,6 +9,9 @@ _start:
|
||||
mov rax, 60
|
||||
syscall
|
||||
|
||||
; String comparison function null-terminated strings
|
||||
; Arguments: rdi = lhs (*char), rsi = rhs (*char)
|
||||
; Returns: 1 if equal, else 0
|
||||
global nub_strcmp
|
||||
nub_strcmp:
|
||||
xor rdx, rdx
|
||||
@@ -28,6 +31,9 @@ nub_strcmp:
|
||||
mov rax, 1
|
||||
ret
|
||||
|
||||
; Panic function with message
|
||||
; Arguments: rdi = message (*char), rsi = message length (long)
|
||||
; Remarks: exits the program
|
||||
global nub_panic
|
||||
nub_panic:
|
||||
mov rdx, rsi
|
||||
@@ -39,73 +45,50 @@ nub_panic:
|
||||
mov rdi, 101
|
||||
syscall
|
||||
|
||||
; TODO: This is ai-generated. Should be re-implemented in the future
|
||||
; Memory set function
|
||||
; Arguments: rdi = destination pointer, rsi = value (byte), rdx = count
|
||||
; Returns: rdi (original destination pointer)
|
||||
global nub_memset
|
||||
nub_memset:
|
||||
; Save original destination for return value
|
||||
mov rax, rdi
|
||||
push rdi ; Save original destination for return value
|
||||
mov rcx, rdx ; Load count into counter register
|
||||
mov al, sil ; Move byte value to al (lower 8 bits of rsi)
|
||||
|
||||
; Handle zero length
|
||||
test rdx, rdx
|
||||
; Handle zero count case
|
||||
test rcx, rcx
|
||||
jz .done
|
||||
|
||||
; For small sizes, use simple byte-by-byte loop
|
||||
cmp rdx, 16
|
||||
jb .byte_loop
|
||||
|
||||
; Prepare value for bulk setting
|
||||
; Replicate the byte across all 8 bytes of rsi
|
||||
and rsi, 0xFF ; Ensure only low byte is used
|
||||
mov rcx, rsi ; rcx = byte value
|
||||
shl rsi, 8
|
||||
or rsi, rcx ; rsi = byte | (byte << 8)
|
||||
mov rcx, rsi
|
||||
shl rsi, 16
|
||||
or rsi, rcx ; rsi = 4 copies of byte
|
||||
mov rcx, rsi
|
||||
shl rsi, 32
|
||||
or rsi, rcx ; rsi = 8 copies of byte
|
||||
|
||||
; Align to 8-byte boundary if needed
|
||||
mov rcx, rdi
|
||||
and rcx, 7 ; rcx = bytes until 8-byte aligned
|
||||
jz .aligned
|
||||
|
||||
; Fill bytes until aligned
|
||||
neg rcx
|
||||
add rcx, 8 ; rcx = bytes to fill for alignment
|
||||
cmp rcx, rdx
|
||||
jbe .align_loop
|
||||
mov rcx, rdx ; Don't go past end
|
||||
.align_loop:
|
||||
mov [rdi], sil
|
||||
inc rdi
|
||||
dec rdx
|
||||
dec rcx
|
||||
jnz .align_loop
|
||||
|
||||
.aligned:
|
||||
; Fill 8 bytes at a time
|
||||
mov rcx, rdx
|
||||
shr rcx, 3 ; rcx = number of 8-byte chunks
|
||||
jz .remainder
|
||||
.quad_loop:
|
||||
mov [rdi], rsi
|
||||
add rdi, 8
|
||||
dec rcx
|
||||
jnz .quad_loop
|
||||
|
||||
; Handle remainder bytes
|
||||
and rdx, 7 ; rdx = remaining bytes
|
||||
.remainder:
|
||||
test rdx, rdx
|
||||
jz .done
|
||||
|
||||
.byte_loop:
|
||||
mov [rdi], sil
|
||||
inc rdi
|
||||
dec rdx
|
||||
jnz .byte_loop
|
||||
.loop:
|
||||
mov [rdi], al ; Store byte at current position
|
||||
inc rdi ; Move to next byte
|
||||
dec rcx ; Decrement counter
|
||||
jnz .loop ; Continue if counter not zero
|
||||
|
||||
.done:
|
||||
ret
|
||||
pop rax ; Return original destination pointer
|
||||
ret
|
||||
|
||||
; Memory copy function
|
||||
; Arguments: rdi = destination, rsi = source, rdx = count
|
||||
; Returns: rdi (original destination pointer)
|
||||
global nub_memcpy
|
||||
nub_memcpy:
|
||||
push rdi ; Save original destination for return value
|
||||
mov rcx, rdx ; Load count into counter register
|
||||
|
||||
; Handle zero count case
|
||||
test rcx, rcx
|
||||
jz .done
|
||||
|
||||
; Simple byte-by-byte copy (no overlap handling)
|
||||
.loop:
|
||||
mov al, [rsi] ; Load byte from source
|
||||
mov [rdi], al ; Store byte to destination
|
||||
inc rsi ; Move to next source byte
|
||||
inc rdi ; Move to next destination byte
|
||||
dec rcx ; Decrement counter
|
||||
jnz .loop ; Continue if counter not zero
|
||||
|
||||
.done:
|
||||
pop rax ; Return original destination pointer
|
||||
ret
|
||||
|
||||
Reference in New Issue
Block a user