This commit is contained in:
nub31
2025-06-15 01:47:10 +02:00
parent 7c05870e24
commit 3f7499d2b9
10 changed files with 63 additions and 50 deletions

View File

@@ -1,4 +1,4 @@
namespace c
// extern func printf(fmt: ^u8, ...args: any): void
extern func puts(fmt: []u8)
extern func puts(fmt: cstring)

View File

@@ -1,7 +1,7 @@
namespace main
export func main(args: [][]u8): i64 {
let x: []u8
export func main(args: []cstring): i64 {
let x: cstring
x = args[0]

View File

@@ -89,6 +89,10 @@ foreach (var compilationUnit in compilationUnits)
File.WriteAllText(ssaPath, ssa);
var asm = await QBE.Invoke(ssa);
if (asm == null)
{
return 1;
}
var asmPath = Path.ChangeExtension(outputPath, "s");
await File.WriteAllTextAsync(asmPath, asm);

View File

@@ -4,7 +4,7 @@ namespace CLI;
public static class QBE
{
public static async Task<string> Invoke(string ssa)
public static async Task<string?> Invoke(string ssa)
{
using var qbeProcess = new Process();
qbeProcess.StartInfo = new ProcessStartInfo
@@ -22,16 +22,16 @@ public static class QBE
await qbeProcess.StandardInput.WriteAsync(ssa);
qbeProcess.StandardInput.Close();
var assemblyCode = await qbeProcess.StandardOutput.ReadToEndAsync();
var qbeErrors = await qbeProcess.StandardError.ReadToEndAsync();
await qbeProcess.WaitForExitAsync();
if (qbeProcess.ExitCode != 0)
var qbeErrors = await qbeProcess.StandardError.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(qbeErrors))
{
throw new Exception($"QBE error:\n{qbeErrors}");
await Console.Error.WriteLineAsync("qbe error:\n" + qbeErrors);
}
return assemblyCode;
var asm = await qbeProcess.StandardOutput.ReadToEndAsync();
return qbeProcess.ExitCode == 0 ? asm : null;
}
}

View File

@@ -1,7 +1,6 @@
.intel_syntax noprefix
.equ SYS_MMAP, 9
.equ SYS_MUNMAP, 11
.text
.globl nub_strlen
@@ -79,22 +78,3 @@ function_exit:
pop r12
pop rbx
ret
.text
.globl nub_string_free
# func nub_string_free(string: []u8): void
nub_string_free:
test rdi, rdi
jz free_exit
mov rsi, [rdi]
add rsi, 8
# Round up to page size
add rsi, 4095
and rsi, -4096
mov rax, SYS_MUNMAP
syscall
free_exit:
ret

View File

@@ -122,6 +122,7 @@ public static class QBEGenerator
NubStructType => "storel",
NubFixedArrayType => "storel",
NubFuncType => "storel",
NubCStringType => "storel",
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
};
}
@@ -150,6 +151,7 @@ public static class QBEGenerator
NubStructType => "loadl",
NubFixedArrayType => "loadl",
NubFuncType => "loadl",
NubCStringType => "loadl",
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
};
}
@@ -178,6 +180,7 @@ public static class QBEGenerator
NubStructType => "=l",
NubFixedArrayType => "=l",
NubFuncType => "=l",
NubCStringType => "=l",
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
};
}
@@ -204,6 +207,7 @@ public static class QBEGenerator
}
case NubPointerType:
case NubArrayType:
case NubCStringType:
case NubFuncType:
{
return 8;
@@ -263,6 +267,7 @@ public static class QBEGenerator
}
case NubPointerType:
case NubArrayType:
case NubCStringType:
case NubFuncType:
{
return 8;
@@ -300,16 +305,7 @@ public static class QBEGenerator
private static bool IsPointerType(NubType type)
{
return type switch
{
NubPointerType => false,
NubPrimitiveType => false,
NubStructType => true,
NubArrayType => true,
NubFixedArrayType => true,
NubFuncType => false,
_ => throw new ArgumentOutOfRangeException(nameof(type))
};
return type is NubStructType or NubArrayType or NubFixedArrayType;
}
private static void GenerateFuncDefinition(string name, List<FuncParameter> parameters, NubType returnType, BlockNode body, bool exported)
@@ -347,6 +343,7 @@ public static class QBEGenerator
NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()),
NubFixedArrayType => "l",
NubFuncType => "l",
NubCStringType => "l",
_ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type")
});
_builder.Append(' ');
@@ -378,6 +375,7 @@ public static class QBEGenerator
NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()),
NubFixedArrayType => "l",
NubFuncType => "l",
NubCStringType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type")
};
@@ -459,6 +457,7 @@ public static class QBEGenerator
NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()),
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
NubFuncType => "l",
NubCStringType => "l",
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
};
_builder.Append(qbeType + ", ");
@@ -1200,12 +1199,19 @@ public static class QBEGenerator
var item = GenerateExpression(memberAccess.Expression);
switch (memberAccess.Expression.Type)
{
case NubArrayType:
case NubArrayType arrayType:
{
if (memberAccess.Member == "count")
{
return item;
}
if (arrayType.ElementType is NubPrimitiveType { Kind: PrimitiveTypeKind.U8 } && memberAccess.Member == "cstring")
{
var result = VarName();
_builder.AppendLine($" {result} =l call $nub_string_to_cstring(l {item})");
return result;
}
throw new UnreachableException(nameof(memberAccess.Member));
}
@@ -1291,6 +1297,7 @@ public static class QBEGenerator
NubStructType structType => StructName(_definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue()),
NubFixedArrayType => "l",
NubFuncType => "l",
NubCStringType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
};
parameterStrings.Add($"{qbeType} {result}");

View File

@@ -670,6 +670,11 @@ public static class Parser
return new NubArrayType(NubPrimitiveType.U8);
}
if (name.Value == "cstring")
{
return new NubCStringType();
}
if (NubPrimitiveType.TryParse(name.Value, out var primitiveTypeKind))
{
return new NubPrimitiveType(primitiveTypeKind.Value);

View File

@@ -48,6 +48,24 @@ public abstract class NubType
public abstract override string ToString();
}
public class NubCStringType : NubType
{
public override bool Equals(object? obj)
{
return obj is NubCStringType other;
}
public override int GetHashCode()
{
return "cstring".GetHashCode();
}
public override string ToString()
{
return "cstring";
}
}
public class NubFuncType(NubType returnType, List<NubType> parameters) : NubType
{
public NubType ReturnType { get; } = returnType;
@@ -163,7 +181,7 @@ public class NubAnyType : NubType
public override int GetHashCode()
{
return GetType().GetHashCode();
return "any".GetHashCode();
}
}

View File

@@ -422,12 +422,6 @@ public static class TypeChecker
var exprType = CheckExpression(addressOf.Expression);
if (exprType == null) return null;
if (addressOf.Expression is not (IdentifierNode or MemberAccessNode))
{
ReportError($"Cannot take the address of {exprType}", addressOf.Expression);
return null;
}
return new NubPointerType(exprType);
}
@@ -569,13 +563,18 @@ public static class TypeChecker
switch (expressionType)
{
case NubArrayType:
case NubArrayType arrayType:
{
if (memberAccess.Member == "count")
{
return NubPrimitiveType.I64;
}
if (arrayType.ElementType is NubPrimitiveType { Kind: PrimitiveTypeKind.U8 } && memberAccess.Member == "cstring")
{
return new NubCStringType();
}
break;
}
case NubStructType structType:

View File

@@ -79,7 +79,7 @@
},
{
"name": "storage.type.primitive.nub",
"match": "\\b(i8|i16|i32|i64|u8|u16|u32|u64|f32|f64|bool|string|void|any)\\b"
"match": "\\b(i8|i16|i32|i64|u8|u16|u32|u64|f32|f64|bool|string|cstring|void|any)\\b"
},
{
"name": "storage.type.array.nub",