diff --git a/example/c.nub b/example/c.nub index fc1b657..c24b1d3 100644 --- a/example/c.nub +++ b/example/c.nub @@ -1,4 +1,4 @@ namespace c // extern func printf(fmt: ^u8, ...args: any): void -extern func puts(fmt: []u8) +extern func puts(fmt: cstring) diff --git a/example/main.nub b/example/main.nub index 9f61221..bba089e 100644 --- a/example/main.nub +++ b/example/main.nub @@ -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] diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index 899e733..d23090a 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -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); diff --git a/src/CLI/QBE.cs b/src/CLI/QBE.cs index c5c35e0..c592e12 100644 --- a/src/CLI/QBE.cs +++ b/src/CLI/QBE.cs @@ -4,7 +4,7 @@ namespace CLI; public static class QBE { - public static async Task Invoke(string ssa) + public static async Task 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; } } diff --git a/src/CLI/Runtime/nub_string.s b/src/CLI/Runtime/nub_string.s index f6e9205..879e4f8 100644 --- a/src/CLI/Runtime/nub_string.s +++ b/src/CLI/Runtime/nub_string.s @@ -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 diff --git a/src/Generation/QBE/QBEGenerator.cs b/src/Generation/QBE/QBEGenerator.cs index c08d363..406f725 100644 --- a/src/Generation/QBE/QBEGenerator.cs +++ b/src/Generation/QBE/QBEGenerator.cs @@ -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 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}"); diff --git a/src/Syntax/Parsing/Parser.cs b/src/Syntax/Parsing/Parser.cs index 6329ea8..3c1403a 100644 --- a/src/Syntax/Parsing/Parser.cs +++ b/src/Syntax/Parsing/Parser.cs @@ -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); diff --git a/src/Syntax/Typing/NubType.cs b/src/Syntax/Typing/NubType.cs index d74204a..91dac2c 100644 --- a/src/Syntax/Typing/NubType.cs +++ b/src/Syntax/Typing/NubType.cs @@ -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 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(); } } diff --git a/src/Syntax/Typing/TypeChecker.cs b/src/Syntax/Typing/TypeChecker.cs index e8bd75c..1ddab22 100644 --- a/src/Syntax/Typing/TypeChecker.cs +++ b/src/Syntax/Typing/TypeChecker.cs @@ -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: diff --git a/tools/syntax-highlighting/syntaxes/nub.tmLanguage.json b/tools/syntax-highlighting/syntaxes/nub.tmLanguage.json index 7c8da71..2c629c8 100644 --- a/tools/syntax-highlighting/syntaxes/nub.tmLanguage.json +++ b/tools/syntax-highlighting/syntaxes/nub.tmLanguage.json @@ -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",