Add void type

This commit is contained in:
nub31
2025-06-07 21:40:35 +02:00
parent d7b2aafa27
commit 87e708d95c
8 changed files with 190 additions and 188 deletions

View File

@@ -1,13 +1,13 @@
namespace c
extern func printf(fmt: ^u8, ...args: any)
extern func printf(fmt: ^u8, ...args: any): void
extern func getchar(): i32
extern func puts(fmt: ^u8)
extern func malloc(size: i64): ^any
extern func calloc(num: i64, size: i64): ^any
extern func realloc(ptr: ^any, size: i64): ^any
extern func free(ptr: ^any)
extern func malloc(size: i64): ^void
extern func calloc(num: i64, size: i64): ^void
extern func realloc(ptr: ^void, size: i64): ^void
extern func free(ptr: ^void)
extern func sin(x: f64): f64
extern func cos(x: f64): f64

View File

@@ -4,7 +4,7 @@ struct Human {
age: ^u64
}
export func main(args: []^string) {
export func main(args: []^string): i64 {
let x = [3]f64
x[0] = 1
@@ -14,4 +14,6 @@ export func main(args: []^string) {
c::printf("%f\n", x[0])
c::printf("%f\n", x[1])
c::printf("%f\n", x[2])
return 0
}

View File

@@ -116,7 +116,7 @@ public class Diagnostic
var lineNumWidth = Math.Min(endLine + contextLines, lines.Length).ToString().Length;
var contextStart = Math.Max(1, startLine - contextLines);
var contextEnd = Math.Min(lines.Length + 1, endLine + contextLines);
var contextEnd = Math.Min(lines.Length, endLine + contextLines);
var contextWidth = 0;
for (var i = contextStart; i <= contextEnd; i++)

View File

@@ -84,68 +84,6 @@ public class Generator
return _builder.ToString();
}
private enum TypeContext
{
FuncDef,
FuncCall,
}
private static string QBEType(NubType type, TypeContext context)
{
return context switch
{
TypeContext.FuncDef => type switch
{
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.Any => throw new NotSupportedException("any type cannot be used in function definitions"),
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
TypeContext.FuncCall => type switch
{
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.Any => "l",
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
},
_ => throw new ArgumentOutOfRangeException(nameof(context), context, null)
};
}
private static string QBEStore(NubType type)
{
return $"store{type switch
@@ -165,12 +103,11 @@ public class Generator
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in store instructions"),
_ => throw new ArgumentOutOfRangeException()
_ => throw new UnreachableException()
},
NubStructType => "l",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
}}";
}
@@ -193,12 +130,11 @@ public class Generator
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in load instructions"),
_ => throw new ArgumentOutOfRangeException()
_ => throw new UnreachableException()
},
NubStructType => "l",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
}}";
}
@@ -221,12 +157,11 @@ public class Generator
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
PrimitiveTypeKind.Any => throw new NotSupportedException("any type cannot be used in variables"),
_ => throw new ArgumentOutOfRangeException()
_ => throw new UnreachableException()
},
NubStructType => "l",
NubFixedArrayType => "l",
_ => throw new ArgumentOutOfRangeException(nameof(type))
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
}}";
}
@@ -240,7 +175,6 @@ public class Generator
{
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U64:
case PrimitiveTypeKind.Any:
return 8;
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.U32:
@@ -304,18 +238,59 @@ public class Generator
}
_builder.Append("function ");
if (node.ReturnType.HasValue)
if (node.ReturnType is not NubVoidType)
{
_builder.Append($"{QBEType(node.ReturnType.Value, TypeContext.FuncDef)} ");
}
else if (!node.ReturnType.HasValue && node.Name == "main")
{
_builder.Append("l ");
_builder.Append(node.ReturnType switch
{
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",
_ => throw new UnreachableException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new NotSupportedException($"'{node.ReturnType}' type cannot be used as a function return type")
});
_builder.Append(' ');
}
_builder.Append(_funcNames[node]);
var parameterStrings = node.Parameters.Select(parameter => parameter.Variadic ? "..." : $"{QBEType(parameter.Type, TypeContext.FuncDef)} %{parameter.Name}");
var parameterStrings = node.Parameters.Select(parameter => parameter.Variadic ? "..." : $"{parameter.Type switch
{
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",
_ => throw new UnreachableException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type")
}} %{parameter.Name}");
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
_builder.AppendLine("@start");
@@ -324,25 +299,25 @@ public class Generator
{
var parameterName = parameter.Name;
switch (QBEType(parameter.Type, TypeContext.FuncDef))
{
case "sb":
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extsb %{parameter.Name}");
break;
case "ub":
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extub %{parameter.Name}");
break;
case "sh":
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extsh %{parameter.Name}");
break;
case "uh":
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extuh %{parameter.Name}");
break;
}
// switch (QBEType(parameter.Type))
// {
// case "sb":
// parameterName = GenVarName();
// _builder.AppendLine($" {parameterName} =w extsb %{parameter.Name}");
// break;
// case "ub":
// parameterName = GenVarName();
// _builder.AppendLine($" {parameterName} =w extub %{parameter.Name}");
// break;
// case "sh":
// parameterName = GenVarName();
// _builder.AppendLine($" {parameterName} =w extsh %{parameter.Name}");
// break;
// case "uh":
// parameterName = GenVarName();
// _builder.AppendLine($" {parameterName} =w extuh %{parameter.Name}");
// break;
// }
if (IsLargeType(parameter.Type))
{
@@ -364,13 +339,7 @@ public class Generator
if (node.Body.Statements.LastOrDefault() is not ReturnNode)
{
if (!node.ReturnType.HasValue && node.Name == "main")
{
_builder.AppendLine();
_builder.AppendLine(" # Implicit return for main");
_builder.AppendLine(" ret 0");
}
else if (!node.ReturnType.HasValue)
if (node.ReturnType is NubVoidType)
{
_builder.AppendLine();
_builder.AppendLine(" # Implicit return");
@@ -386,40 +355,34 @@ public class Generator
_builder.Append($"type :{structDefinition.Namespace}_{structDefinition.Name} = {{ ");
foreach (var structDefinitionField in structDefinition.Fields)
{
var fieldDefinition = GenerateFieldType(structDefinitionField.Type);
_builder.Append(fieldDefinition + ", ");
var fieldQbeType = structDefinitionField.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",
_ => throw new UnreachableException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
};
_builder.Append(fieldQbeType + ", ");
}
_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.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)
{
switch (statement)
@@ -1739,13 +1702,35 @@ public class Generator
var parameter = funcCall.Parameters[i];
var result = GenerateExpression(parameter);
var qbeParameterType = QBEType(parameter.Type, TypeContext.FuncCall);
var qbeParameterType = parameter.Type switch
{
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",
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
};
parameterStrings.Add($"{qbeParameterType} {result}");
}
var funcName = _funcNames[funcDefinition];
if (funcDefinition.ReturnType.HasValue)
if (funcDefinition.ReturnType is not NubVoidType)
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcName}({string.Join(", ", parameterStrings)})");

View File

@@ -18,30 +18,30 @@ public interface IFuncSignature
public string Name { get; }
public string Namespace { get; }
public List<FuncParameter> Parameters { get; }
public Optional<NubType> ReturnType { get; }
public NubType ReturnType { get; }
public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}";
}
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 class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, string @namespace, List<FuncParameter> parameters, BlockNode body, 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;
public NubType ReturnType { get; } = returnType;
public bool Exported { get; } = exported;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}";
}
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 class ExternFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, string @namespace, string callName, List<FuncParameter> parameters, 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;
public NubType ReturnType { get; } = returnType;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){": " + ReturnType}";
}

View File

@@ -99,11 +99,7 @@ public class Parser
}
}
var returnType = Optional<NubType>.Empty();
if (TryExpectSymbol(Symbol.Colon))
{
returnType = ParseType();
}
var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType();
var isExtern = modifiers.RemoveAll(x => x.Modifier == Modifier.Extern) > 0;
if (isExtern)
@@ -665,20 +661,28 @@ public class Parser
{
if (TryExpectIdentifier(out var name))
{
if (name == "any")
{
return new NubAnyType();
}
if (name == "void")
{
return new NubVoidType();
}
if (NubPrimitiveType.TryParse(name, out var primitiveTypeKind))
{
return new NubPrimitiveType(primitiveTypeKind.Value);
}
else
{
var @namespace = _namespace;
if (TryExpectSymbol(Symbol.DoubleColon))
{
@namespace = ExpectIdentifier().Value;
}
return new NubStructType(@namespace, name);
var @namespace = _namespace;
if (TryExpectSymbol(Symbol.DoubleColon))
{
@namespace = ExpectIdentifier().Value;
}
return new NubStructType(@namespace, name);
}
if (TryExpectSymbol(Symbol.Caret))

View File

@@ -6,7 +6,7 @@ public abstract class NubType
{
public static bool IsCompatibleWith(NubType sourceType, NubType targetType)
{
if (targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType))
if (targetType is NubAnyType || sourceType.Equals(targetType))
{
return true;
}
@@ -16,11 +16,6 @@ public abstract class NubType
return true;
}
// if (sourceType.Equals(NubPrimitiveType.String) && targetType is NubArrayType arrayType && IsCompatibleWith(NubPrimitiveType.U8, arrayType.ElementType))
// {
// return true;
// }
return false;
}
@@ -136,6 +131,36 @@ public class NubFixedArrayType(NubType elementType, int capacity) : NubType
}
}
public class NubAnyType : NubType
{
public override string ToString() => "any";
public override bool Equals(object? obj)
{
return obj is NubAnyType;
}
public override int GetHashCode()
{
return GetType().GetHashCode();
}
}
public class NubVoidType : NubType
{
public override string ToString() => "void";
public override bool Equals(object? obj)
{
return obj is NubVoidType;
}
public override int GetHashCode()
{
return GetType().GetHashCode();
}
}
public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType
{
public PrimitiveTypeKind Kind { get; } = kind;
@@ -154,7 +179,6 @@ public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType
public static NubPrimitiveType F32 => new(PrimitiveTypeKind.F32);
public static NubPrimitiveType Bool => new(PrimitiveTypeKind.Bool);
public static NubPrimitiveType Any => new(PrimitiveTypeKind.Any);
public static bool TryParse(string s, [NotNullWhen(true)] out PrimitiveTypeKind? kind)
{
@@ -171,7 +195,6 @@ public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType
"f64" => PrimitiveTypeKind.F64,
"f32" => PrimitiveTypeKind.F32,
"bool" => PrimitiveTypeKind.Bool,
"any" => PrimitiveTypeKind.Any,
_ => null
};
@@ -206,7 +229,6 @@ public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType
PrimitiveTypeKind.F64 => "f64",
PrimitiveTypeKind.Bool => "bool",
PrimitiveTypeKind.Any => "any",
_ => throw new ArgumentOutOfRangeException(nameof(kind), Kind, null)
};
}
@@ -224,6 +246,5 @@ public enum PrimitiveTypeKind
U8,
F64,
F32,
Bool,
Any
Bool
}

View File

@@ -14,14 +14,12 @@ public class TypeChecker
private List<SourceFile> _sourceFiles = [];
private List<Diagnostic> _diagnostics = [];
private NubType? _currentFunctionReturnType;
private bool _hasReturnStatement;
public DiagnosticsResult TypeCheck(List<SourceFile> sourceFiles)
{
_variables = new Dictionary<string, NubType>();
_diagnostics = [];
_currentFunctionReturnType = null;
_hasReturnStatement = false;
_sourceFiles = sourceFiles;
var externFuncDefinitions = _sourceFiles
@@ -85,8 +83,7 @@ public class TypeChecker
private void TypeCheckFuncDef(LocalFuncDefinitionNode funcDef)
{
_variables.Clear();
_currentFunctionReturnType = funcDef.ReturnType.HasValue ? funcDef.ReturnType.Value : null;
_hasReturnStatement = false;
_currentFunctionReturnType = funcDef.ReturnType;
foreach (var param in funcDef.Parameters)
{
@@ -94,11 +91,6 @@ public class TypeChecker
}
TypeCheckBlock(funcDef.Body);
if (_currentFunctionReturnType != null && !_hasReturnStatement)
{
ReportError($"Function '{funcDef.Name}' must return a value of type '{_currentFunctionReturnType}'", funcDef);
}
}
private void TypeCheckBlock(BlockNode block)
@@ -292,7 +284,7 @@ public class TypeChecker
}
}
return funcDefinition.ReturnType.HasValue ? funcDefinition.ReturnType.Value : NubPrimitiveType.Any;
return funcDefinition.ReturnType;
}
private void TypeCheckIf(IfNode ifNode)
@@ -325,8 +317,6 @@ public class TypeChecker
private void TypeCheckReturn(ReturnNode returnNode)
{
_hasReturnStatement = true;
if (returnNode.Value.HasValue)
{
var returnType = TypeCheckExpression(returnNode.Value.Value, _currentFunctionReturnType);