diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index 0482737..e72a9f9 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using NubLang.Ast; +using NubLang.Ast; using NubLang.Diagnostics; using NubLang.Generation; using NubLang.Syntax; @@ -42,62 +41,22 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro return 1; } -var cPaths = new List(); - Directory.CreateDirectory(".build"); -var typedModules = modules.Select(x => (x.Key, TypedModule.FromModule(x.Key, x.Value, modules))).ToDictionary(); - -Directory.CreateDirectory(Path.Combine(".build", "modules")); - -foreach (var typedModule in typedModules) -{ - var header = HeaderGenerator.Generate(typedModule.Key, typedModule.Value); - var headerOut = Path.Combine(Path.Combine(".build", "modules"), typedModule.Key + ".h"); - File.WriteAllText(headerOut, header); -} - for (var i = 0; i < args.Length; i++) { var file = args[i]; var compilationUnit = compilationUnits[i]; - var generator = new Generator(compilationUnit); + var generator = new LlvmGenerator(); var directory = Path.GetDirectoryName(file); if (!string.IsNullOrWhiteSpace(directory)) { Directory.CreateDirectory(Path.Combine(".build", directory)); } - var path = Path.Combine(".build", Path.ChangeExtension(file, "c")); - File.WriteAllText(path, generator.Emit()); - cPaths.Add(path); + var path = Path.Combine(".build", Path.ChangeExtension(file, "ll")); + File.WriteAllText(path, generator.Emit(compilationUnit)); } -var objectPaths = new List(); - -foreach (var cPath in cPaths) -{ - var objectPath = Path.ChangeExtension(cPath, "o"); - using var compileProcess = Process.Start("clang", [ - "-I.build", - "-ffreestanding", "-std=c23", - "-g", "-c", - "-o", objectPath, - cPath, - ]); - - compileProcess.WaitForExit(); - - if (compileProcess.ExitCode != 0) - { - Console.Error.WriteLine($"clang failed with exit code {compileProcess.ExitCode}"); - return 1; - } - - objectPaths.Add(objectPath); -} - -Console.Out.WriteLine(string.Join(' ', objectPaths)); - return 0; \ No newline at end of file diff --git a/compiler/NubLang.LSP/WorkspaceManager.cs b/compiler/NubLang.LSP/WorkspaceManager.cs index ced47f3..2d81221 100644 --- a/compiler/NubLang.LSP/WorkspaceManager.cs +++ b/compiler/NubLang.LSP/WorkspaceManager.cs @@ -8,6 +8,7 @@ public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher) { private readonly Dictionary _syntaxTrees = new(); private readonly Dictionary> _compilationUnits = new(); + private readonly Dictionary _modules = new(); public void Init(string rootPath) { diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs index 6dd7656..14aad22 100644 --- a/compiler/NubLang/Ast/Node.cs +++ b/compiler/NubLang/Ast/Node.cs @@ -307,7 +307,11 @@ public abstract class ExpressionNode(List tokens, NubType type) : Node(to public NubType Type { get; } = type; } -public class StringLiteralNode(List tokens, string value) : ExpressionNode(tokens, new NubStringType()) +public abstract class LValue(List tokens, NubType type) : ExpressionNode(tokens, type); + +public abstract class RValue(List tokens, NubType type) : ExpressionNode(tokens, type); + +public class StringLiteralNode(List tokens, string value) : RValue(tokens, new NubStringType()) { public string Value { get; } = value; @@ -317,7 +321,7 @@ public class StringLiteralNode(List tokens, string value) : ExpressionNod } } -public class CStringLiteralNode(List tokens, string value) : ExpressionNode(tokens, new NubPointerType(new NubIntType(true, 8))) +public class CStringLiteralNode(List tokens, string value) : RValue(tokens, new NubPointerType(new NubIntType(true, 8))) { public string Value { get; } = value; @@ -327,7 +331,7 @@ public class CStringLiteralNode(List tokens, string value) : ExpressionNo } } -public class I8LiteralNode(List tokens, sbyte value) : ExpressionNode(tokens, new NubIntType(true, 8)) +public class I8LiteralNode(List tokens, sbyte value) : RValue(tokens, new NubIntType(true, 8)) { public sbyte Value { get; } = value; @@ -337,7 +341,7 @@ public class I8LiteralNode(List tokens, sbyte value) : ExpressionNode(tok } } -public class I16LiteralNode(List tokens, short value) : ExpressionNode(tokens, new NubIntType(true, 16)) +public class I16LiteralNode(List tokens, short value) : RValue(tokens, new NubIntType(true, 16)) { public short Value { get; } = value; @@ -347,7 +351,7 @@ public class I16LiteralNode(List tokens, short value) : ExpressionNode(to } } -public class I32LiteralNode(List tokens, int value) : ExpressionNode(tokens, new NubIntType(true, 32)) +public class I32LiteralNode(List tokens, int value) : RValue(tokens, new NubIntType(true, 32)) { public int Value { get; } = value; @@ -357,7 +361,7 @@ public class I32LiteralNode(List tokens, int value) : ExpressionNode(toke } } -public class I64LiteralNode(List tokens, long value) : ExpressionNode(tokens, new NubIntType(true, 64)) +public class I64LiteralNode(List tokens, long value) : RValue(tokens, new NubIntType(true, 64)) { public long Value { get; } = value; @@ -367,7 +371,7 @@ public class I64LiteralNode(List tokens, long value) : ExpressionNode(tok } } -public class U8LiteralNode(List tokens, byte value) : ExpressionNode(tokens, new NubIntType(false, 8)) +public class U8LiteralNode(List tokens, byte value) : RValue(tokens, new NubIntType(false, 8)) { public byte Value { get; } = value; @@ -377,7 +381,7 @@ public class U8LiteralNode(List tokens, byte value) : ExpressionNode(toke } } -public class U16LiteralNode(List tokens, ushort value) : ExpressionNode(tokens, new NubIntType(false, 16)) +public class U16LiteralNode(List tokens, ushort value) : RValue(tokens, new NubIntType(false, 16)) { public ushort Value { get; } = value; @@ -387,7 +391,7 @@ public class U16LiteralNode(List tokens, ushort value) : ExpressionNode(t } } -public class U32LiteralNode(List tokens, uint value) : ExpressionNode(tokens, new NubIntType(false, 32)) +public class U32LiteralNode(List tokens, uint value) : RValue(tokens, new NubIntType(false, 32)) { public uint Value { get; } = value; @@ -397,7 +401,7 @@ public class U32LiteralNode(List tokens, uint value) : ExpressionNode(tok } } -public class U64LiteralNode(List tokens, ulong value) : ExpressionNode(tokens, new NubIntType(false, 64)) +public class U64LiteralNode(List tokens, ulong value) : RValue(tokens, new NubIntType(false, 64)) { public ulong Value { get; } = value; @@ -407,7 +411,7 @@ public class U64LiteralNode(List tokens, ulong value) : ExpressionNode(to } } -public class Float32LiteralNode(List tokens, float value) : ExpressionNode(tokens, new NubFloatType(32)) +public class Float32LiteralNode(List tokens, float value) : RValue(tokens, new NubFloatType(32)) { public float Value { get; } = value; @@ -417,7 +421,7 @@ public class Float32LiteralNode(List tokens, float value) : ExpressionNod } } -public class Float64LiteralNode(List tokens, double value) : ExpressionNode(tokens, new NubFloatType(64)) +public class Float64LiteralNode(List tokens, double value) : RValue(tokens, new NubFloatType(64)) { public double Value { get; } = value; @@ -427,7 +431,7 @@ public class Float64LiteralNode(List tokens, double value) : ExpressionNo } } -public class BoolLiteralNode(List tokens, bool value) : ExpressionNode(tokens, new NubBoolType()) +public class BoolLiteralNode(List tokens, bool value) : RValue(tokens, new NubBoolType()) { public bool Value { get; } = value; @@ -437,7 +441,7 @@ public class BoolLiteralNode(List tokens, bool value) : ExpressionNode(to } } -public class BinaryExpressionNode(List tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : ExpressionNode(tokens, type) +public class BinaryExpressionNode(List tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : RValue(tokens, type) { public ExpressionNode Left { get; } = left; public BinaryOperator Operator { get; } = @operator; @@ -450,7 +454,7 @@ public class BinaryExpressionNode(List tokens, NubType type, ExpressionNo } } -public class UnaryExpressionNode(List tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : ExpressionNode(tokens, type) +public class UnaryExpressionNode(List tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : RValue(tokens, type) { public UnaryOperator Operator { get; } = @operator; public ExpressionNode Operand { get; } = operand; @@ -461,7 +465,7 @@ public class UnaryExpressionNode(List tokens, NubType type, UnaryOperator } } -public class FuncCallNode(List tokens, NubType type, ExpressionNode expression, List parameters) : ExpressionNode(tokens, type) +public class FuncCallNode(List tokens, NubType type, ExpressionNode expression, List parameters) : RValue(tokens, type) { public ExpressionNode Expression { get; } = expression; public List Parameters { get; } = parameters; @@ -476,7 +480,7 @@ public class FuncCallNode(List tokens, NubType type, ExpressionNode expre } } -public class VariableIdentifierNode(List tokens, NubType type, IdentifierToken nameToken) : ExpressionNode(tokens, type) +public class VariableIdentifierNode(List tokens, NubType type, IdentifierToken nameToken) : LValue(tokens, type) { public IdentifierToken NameToken { get; } = nameToken; @@ -486,7 +490,7 @@ public class VariableIdentifierNode(List tokens, NubType type, Identifier } } -public class FuncIdentifierNode(List tokens, NubType type, IdentifierToken moduleToken, IdentifierToken nameToken, StringLiteralToken? externSymbolToken) : ExpressionNode(tokens, type) +public class FuncIdentifierNode(List tokens, NubType type, IdentifierToken moduleToken, IdentifierToken nameToken, StringLiteralToken? externSymbolToken) : RValue(tokens, type) { public IdentifierToken ModuleToken { get; } = moduleToken; public IdentifierToken NameToken { get; } = nameToken; @@ -498,27 +502,7 @@ public class FuncIdentifierNode(List tokens, NubType type, IdentifierToke } } -public class ArrayInitializerNode(List tokens, NubType type, List values) : ExpressionNode(tokens, type) -{ - public List Values { get; } = values; - - public override IEnumerable Children() - { - return Values; - } -} - -public class ConstArrayInitializerNode(List tokens, NubType type, List values) : ExpressionNode(tokens, type) -{ - public List Values { get; } = values; - - public override IEnumerable Children() - { - return Values; - } -} - -public class ArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : ExpressionNode(tokens, type) +public class ArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValue(tokens, type) { public ExpressionNode Target { get; } = target; public ExpressionNode Index { get; } = index; @@ -530,7 +514,7 @@ public class ArrayIndexAccessNode(List tokens, NubType type, ExpressionNo } } -public class ConstArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : ExpressionNode(tokens, type) +public class ConstArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValue(tokens, type) { public ExpressionNode Target { get; } = target; public ExpressionNode Index { get; } = index; @@ -542,7 +526,7 @@ public class ConstArrayIndexAccessNode(List tokens, NubType type, Express } } -public class SliceIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : ExpressionNode(tokens, type) +public class SliceIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValue(tokens, type) { public ExpressionNode Target { get; } = target; public ExpressionNode Index { get; } = index; @@ -554,7 +538,7 @@ public class SliceIndexAccessNode(List tokens, NubType type, ExpressionNo } } -public class AddressOfNode(List tokens, NubType type, ExpressionNode target) : ExpressionNode(tokens, type) +public class AddressOfNode(List tokens, NubType type, ExpressionNode target) : RValue(tokens, type) { public ExpressionNode Target { get; } = target; @@ -564,7 +548,7 @@ public class AddressOfNode(List tokens, NubType type, ExpressionNode targ } } -public class StructFieldAccessNode(List tokens, NubType type, ExpressionNode target, IdentifierToken fieldToken) : ExpressionNode(tokens, type) +public class StructFieldAccessNode(List tokens, NubType type, ExpressionNode target, IdentifierToken fieldToken) : LValue(tokens, type) { public ExpressionNode Target { get; } = target; public IdentifierToken FieldToken { get; } = fieldToken; @@ -575,7 +559,37 @@ public class StructFieldAccessNode(List tokens, NubType type, ExpressionN } } -public class StructInitializerNode(List tokens, NubType type, Dictionary initializers) : ExpressionNode(tokens, type) +public class DereferenceNode(List tokens, NubType type, ExpressionNode target) : LValue(tokens, type) +{ + public ExpressionNode Target { get; } = target; + + public override IEnumerable Children() + { + yield return Target; + } +} + +public class SizeNode(List tokens, NubType targetType) : RValue(tokens, new NubIntType(false, 64)) +{ + public NubType TargetType { get; } = targetType; + + public override IEnumerable Children() + { + return []; + } +} + +public class CastNode(List tokens, NubType type, ExpressionNode value) : RValue(tokens, type) +{ + public ExpressionNode Value { get; } = value; + + public override IEnumerable Children() + { + yield return Value; + } +} + +public class StructInitializerNode(List tokens, NubType type, Dictionary initializers) : RValue(tokens, type) { public Dictionary Initializers { get; } = initializers; @@ -588,33 +602,13 @@ public class StructInitializerNode(List tokens, NubType type, Dictionary< } } -public class DereferenceNode(List tokens, NubType type, ExpressionNode target) : ExpressionNode(tokens, type) +public class ConstArrayInitializerNode(List tokens, NubType type, List values) : RValue(tokens, type) { - public ExpressionNode Target { get; } = target; + public List Values { get; } = values; public override IEnumerable Children() { - yield return Target; - } -} - -public class SizeNode(List tokens, NubType TargetType) : ExpressionNode(tokens, new NubIntType(false, 64)) -{ - public NubType TargetType { get; } = TargetType; - - public override IEnumerable Children() - { - return []; - } -} - -public class CastNode(List tokens, NubType type, ExpressionNode value) : ExpressionNode(tokens, type) -{ - public ExpressionNode Value { get; } = value; - - public override IEnumerable Children() - { - yield return Value; + return Values; } } diff --git a/compiler/NubLang/Ast/NubType.cs b/compiler/NubLang/Ast/NubType.cs index 173be29..ba3ac93 100644 --- a/compiler/NubLang/Ast/NubType.cs +++ b/compiler/NubLang/Ast/NubType.cs @@ -7,6 +7,7 @@ public abstract class NubType : IEquatable { public abstract ulong GetSize(); public abstract ulong GetAlignment(); + public abstract bool IsAggregate(); public override bool Equals(object? obj) => obj is NubType other && Equals(other); public abstract bool Equals(NubType? other); @@ -22,6 +23,7 @@ public class NubVoidType : NubType { public override ulong GetSize() => 8; public override ulong GetAlignment() => 8; + public override bool IsAggregate() => false; public override string ToString() => "void"; public override bool Equals(NubType? other) => other is NubVoidType; @@ -35,6 +37,7 @@ public sealed class NubIntType(bool signed, ulong width) : NubType public override ulong GetSize() => Width / 8; public override ulong GetAlignment() => Width / 8; + public override bool IsAggregate() => false; public override string ToString() => $"{(Signed ? "i" : "u")}{Width}"; public override bool Equals(NubType? other) => other is NubIntType @int && @int.Width == Width && @int.Signed == Signed; @@ -47,6 +50,7 @@ public sealed class NubFloatType(ulong width) : NubType public override ulong GetSize() => Width / 8; public override ulong GetAlignment() => Width / 8; + public override bool IsAggregate() => false; public override string ToString() => $"f{Width}"; public override bool Equals(NubType? other) => other is NubFloatType @float && @float.Width == Width; @@ -57,6 +61,7 @@ public class NubBoolType : NubType { public override ulong GetSize() => 1; public override ulong GetAlignment() => 1; + public override bool IsAggregate() => false; public override string ToString() => "bool"; public override bool Equals(NubType? other) => other is NubBoolType; @@ -69,6 +74,7 @@ public sealed class NubPointerType(NubType baseType) : NubType public override ulong GetSize() => 8; public override ulong GetAlignment() => 8; + public override bool IsAggregate() => false; public override string ToString() => "^" + BaseType; public override bool Equals(NubType? other) => other is NubPointerType pointer && BaseType.Equals(pointer.BaseType); @@ -82,6 +88,7 @@ public class NubFuncType(List parameters, NubType returnType) : NubType public override ulong GetSize() => 8; public override ulong GetAlignment() => 8; + public override bool IsAggregate() => false; public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}"; public override bool Equals(NubType? other) => other is NubFuncType func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters); @@ -107,9 +114,14 @@ public class NubStructType(string module, string name, bool packed, List Fields { get; set; } = fields; - public Dictionary GetFieldOffsets() + public int GetFieldIndex(string name) { - var offsets = new Dictionary(); + return Fields.FindIndex(x => x.Name == name); + } + + public Dictionary GetFieldOffsets() + { + var offsets = new Dictionary(); ulong offset = 0; foreach (var field in Fields) @@ -121,7 +133,7 @@ public class NubStructType(string module, string name, bool packed, List f.Type.GetAlignment()); } + public override bool IsAggregate() => true; + public override string ToString() => $"{Module}::{Name}"; public override bool Equals(NubType? other) => other is NubStructType structType && Name == structType.Name && Module == structType.Module; public override int GetHashCode() => HashCode.Combine(typeof(NubStructType), Module, Name); @@ -173,9 +187,9 @@ public class NubSliceType(NubType elementType) : NubType { public NubType ElementType { get; } = elementType; - // note(nub31): Fat pointer - public override ulong GetSize() => 16; + public override ulong GetSize() => 16; // note(nub31): Fat pointer public override ulong GetAlignment() => 8; + public override bool IsAggregate() => true; public override string ToString() => "[]" + ElementType; public override bool Equals(NubType? other) => other is NubSliceType slice && ElementType.Equals(slice.ElementType); @@ -189,6 +203,7 @@ public class NubConstArrayType(NubType elementType, ulong size) : NubType public override ulong GetSize() => ElementType.GetSize() * Size; public override ulong GetAlignment() => ElementType.GetAlignment(); + public override bool IsAggregate() => true; public override string ToString() => $"[{Size}]{ElementType}"; public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size; @@ -201,6 +216,7 @@ public class NubArrayType(NubType elementType) : NubType public override ulong GetSize() => 8; public override ulong GetAlignment() => 8; + public override bool IsAggregate() => false; // note(nub31): Just a pointer public override string ToString() => $"[?]{ElementType}"; public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType); @@ -209,9 +225,9 @@ public class NubArrayType(NubType elementType) : NubType public class NubStringType : NubType { - // note(nub31): Fat pointer - public override ulong GetSize() => 16; + public override ulong GetSize() => 16; // note(nub31): Fat pointer public override ulong GetAlignment() => 8; + public override bool IsAggregate() => true; public override string ToString() => "string"; public override bool Equals(NubType? other) => other is NubStringType; diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs index bbd31da..4dfb156 100644 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ b/compiler/NubLang/Ast/TypeChecker.cs @@ -520,7 +520,7 @@ public sealed class TypeChecker }; } - private ExpressionNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType) + private ConstArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType) { var elementType = expectedType switch { @@ -563,12 +563,7 @@ public sealed class TypeChecker values.Add(value); } - return expectedType switch - { - NubArrayType => new ArrayInitializerNode(expression.Tokens, new NubArrayType(elementType), values), - NubConstArrayType constArrayType => new ConstArrayInitializerNode(expression.Tokens, constArrayType, values), - _ => new ConstArrayInitializerNode(expression.Tokens, new NubConstArrayType(elementType, (ulong)expression.Values.Count), values) - }; + return new ConstArrayInitializerNode(expression.Tokens, new NubConstArrayType(elementType, (ulong)expression.Values.Count), values); } private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType) @@ -602,7 +597,7 @@ public sealed class TypeChecker case BinaryOperatorSyntax.NotEqual: { var left = CheckExpression(expression.Left); - if (left.Type is not NubIntType and not NubFloatType and not NubBoolType) + if (left.Type is not NubIntType and not NubFloatType and not NubBoolType and not NubPointerType) { throw new CompileException(Diagnostic .Error("Equal and not equal operators must must be used with int, float or bool types") @@ -718,6 +713,27 @@ public sealed class TypeChecker } case BinaryOperatorSyntax.LeftShift: case BinaryOperatorSyntax.RightShift: + { + var left = CheckExpression(expression.Left, expectedType); + if (left.Type is not NubIntType) + { + throw new CompileException(Diagnostic + .Error("Bitwise operators must be used with int types") + .At(expression.Left) + .Build()); + } + + var right = CheckExpression(expression.Right, left.Type); + if (right.Type is not NubIntType) + { + throw new CompileException(Diagnostic + .Error("Bitwise operators must be used with int types") + .At(expression.Right) + .Build()); + } + + return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right); + } case BinaryOperatorSyntax.BitwiseAnd: case BinaryOperatorSyntax.BitwiseXor: case BinaryOperatorSyntax.BitwiseOr: diff --git a/compiler/NubLang/Generation/CType.cs b/compiler/NubLang/Generation/CType.cs deleted file mode 100644 index 47e5a71..0000000 --- a/compiler/NubLang/Generation/CType.cs +++ /dev/null @@ -1,98 +0,0 @@ -using NubLang.Ast; - -namespace NubLang.Generation; - -public static class CType -{ - public static string Create(NubType type, string? variableName = null, bool constArraysAsPointers = true) - { - return type switch - { - NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""), - NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""), - NubIntType i => CreateIntType(i, variableName), - NubFloatType f => CreateFloatType(f, variableName), - NubPointerType p => CreatePointerType(p, variableName), - NubSliceType => "nub_slice" + (variableName != null ? $" {variableName}" : ""), - NubStringType => "nub_string" + (variableName != null ? $" {variableName}" : ""), - NubConstArrayType a => CreateConstArrayType(a, variableName, constArraysAsPointers), - NubArrayType a => CreateArrayType(a, variableName), - NubFuncType f => CreateFuncType(f, variableName), - // NubStructType s => $"{s.Module}_{s.Name}_{NameMangler.Mangle(s)}" + (variableName != null ? $" {variableName}" : ""), - NubStructType s => $"{s.Module}_{s.Name}" + (variableName != null ? $" {variableName}" : ""), - _ => throw new NotSupportedException($"C type generation not supported for: {type}") - }; - } - - private static string CreateIntType(NubIntType intType, string? varName) - { - var cType = intType.Width switch - { - 8 => intType.Signed ? "char" : "unsigned char", - 16 => intType.Signed ? "short" : "unsigned short", - 32 => intType.Signed ? "int" : "unsigned int", - 64 => intType.Signed ? "long long" : "unsigned long long", - _ => throw new NotSupportedException($"Unsupported integer width: {intType.Width}") - }; - return cType + (varName != null ? $" {varName}" : ""); - } - - private static string CreateFloatType(NubFloatType floatType, string? varName) - { - var cType = floatType.Width switch - { - 32 => "float", - 64 => "double", - _ => throw new NotSupportedException($"Unsupported float width: {floatType.Width}") - }; - return cType + (varName != null ? $" {varName}" : ""); - } - - private static string CreatePointerType(NubPointerType ptr, string? varName) - { - var baseType = Create(ptr.BaseType); - return baseType + "*" + (varName != null ? $" {varName}" : ""); - } - - private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool constArraysAsPointers) - { - var elementType = Create(arr.ElementType); - - // Treat const arrays as pointers unless in a struct definition - if (constArraysAsPointers) - { - return elementType + "*" + (varName != null ? $" {varName}" : ""); - } - - if (varName != null) - { - return $"{elementType} {varName}[{arr.Size}]"; - } - - return $"{elementType}[{arr.Size}]"; - } - - private static string CreateArrayType(NubArrayType arr, string? varName) - { - var elementType = Create(arr.ElementType); - return elementType + "*" + (varName != null ? $" {varName}" : ""); - } - - private static string CreateFuncType(NubFuncType fn, string? varName) - { - var returnType = Create(fn.ReturnType); - var parameters = string.Join(", ", fn.Parameters.Select(p => Create(p))); - - if (string.IsNullOrEmpty(parameters)) - { - parameters = "void"; - } - - if (varName != null) - { - return $"{returnType} (*{varName})({parameters})"; - } - - return $"{returnType} (*)({parameters})"; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs deleted file mode 100644 index e2f3e0a..0000000 --- a/compiler/NubLang/Generation/Generator.cs +++ /dev/null @@ -1,674 +0,0 @@ -using System.Diagnostics; -using System.Text; -using NubLang.Ast; -using NubLang.Syntax; - -namespace NubLang.Generation; - -public class Generator -{ - private readonly List _compilationUnit; - private readonly IndentedTextWriter _writer; - private readonly Stack _scopes = []; - private int _tmpIndex; - - private Scope Scope => _scopes.Peek(); - - public Generator(List compilationUnit) - { - _compilationUnit = compilationUnit; - _writer = new IndentedTextWriter(); - } - - // todo(nub31): Handle name collisions - private string NewTmp() - { - return $"_t{++_tmpIndex}"; - } - - private static string FuncName(string module, string name, string? externSymbol) - { - return externSymbol ?? $"{module}_{name}"; - } - - private string GetModuleName() - { - return _compilationUnit.OfType().First().NameToken.Value; - } - - public string Emit() - { - foreach (var importNode in _compilationUnit.OfType()) - { - _writer.WriteLine($"#include "); - } - - _writer.WriteLine($"#include "); - - _writer.WriteLine(""" - #include - - typedef struct - { - unsigned long long length; - char *data; - } nub_string; - - typedef struct - { - unsigned long long length; - void *data; - } nub_slice; - - """); - - foreach (var structType in _compilationUnit.OfType()) - { - _writer.WriteLine($"void {CType.Create(structType.StructType)}_create({CType.Create(structType.StructType)} *self)"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - foreach (var field in structType.Fields) - { - if (field.Value != null) - { - var value = EmitExpression(field.Value); - _writer.WriteLine($"self->{field.NameToken.Value} = {value}"); - } - } - } - - _writer.WriteLine("}"); - _writer.WriteLine(); - } - - // note(nub31): Normal functions - foreach (var funcNode in _compilationUnit.OfType()) - { - if (funcNode.Body == null) continue; - - EmitLine(funcNode.Tokens.FirstOrDefault()); - var parameters = funcNode.Prototype.Parameters.Count != 0 - ? string.Join(", ", funcNode.Prototype.Parameters.Select(x => CType.Create(x.Type, x.NameToken.Value))) - : "void"; - - var name = FuncName(GetModuleName(), funcNode.NameToken.Value, funcNode.Prototype.ExternSymbolToken?.Value); - _writer.WriteLine($"{CType.Create(funcNode.Prototype.ReturnType, name)}({parameters})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - EmitBlock(funcNode.Body); - } - } - - _writer.WriteLine("}"); - _writer.WriteLine(); - } - - return _writer.ToString(); - } - - private void EmitStatement(StatementNode statementNode) - { - EmitLine(statementNode.Tokens.FirstOrDefault()); - - switch (statementNode) - { - case AssignmentNode assignmentNode: - EmitAssignment(assignmentNode); - break; - case BlockNode blockNode: - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - EmitBlock(blockNode); - } - } - - _writer.WriteLine("}"); - break; - case BreakNode breakNode: - EmitBreak(breakNode); - break; - case ContinueNode continueNode: - EmitContinue(continueNode); - break; - case DeferNode deferNode: - Scope.Defer(() => EmitStatement(deferNode.Statement)); - break; - case ForConstArrayNode forConstArrayNode: - EmitForConstArray(forConstArrayNode); - break; - case ForSliceNode forSliceNode: - EmitForSlice(forSliceNode); - break; - case IfNode ifNode: - EmitIf(ifNode); - break; - case ReturnNode returnNode: - EmitReturn(returnNode); - break; - case StatementFuncCallNode statementFuncCallNode: - EmitStatementFuncCall(statementFuncCallNode); - break; - case VariableDeclarationNode variableDeclarationNode: - EmitVariableDeclaration(variableDeclarationNode); - break; - case WhileNode whileNode: - EmitWhile(whileNode); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statementNode)); - } - } - - private void EmitLine(Token? token) - { - if (token == null) return; - var file = token.Span.FilePath; - var line = token.Span.Start.Line; - _writer.WriteLine($"#line {line} \"{file}\""); - } - - private void EmitAssignment(AssignmentNode assignmentNode) - { - var target = EmitExpression(assignmentNode.Target); - var value = EmitExpression(assignmentNode.Value); - _writer.WriteLine($"{target} = {value};"); - } - - private void EmitBreak(BreakNode _) - { - // todo(nub31): Emit deferred statements - _writer.WriteLine("break;"); - } - - private void EmitContinue(ContinueNode _) - { - // todo(nub31): Emit deferred statements - _writer.WriteLine("continue;"); - } - - private void EmitForSlice(ForSliceNode forSliceNode) - { - var targetType = (NubSliceType)forSliceNode.Target.Type; - var target = EmitExpression(forSliceNode.Target); - var indexName = forSliceNode.IndexNameToken?.Value ?? NewTmp(); - - _writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {target}.length; ++{indexName})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - _writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementNameToken.Value)} = (({CType.Create(targetType.ElementType)}*){target}.data)[{indexName}];"); - EmitBlock(forSliceNode.Body); - } - } - - _writer.WriteLine("}"); - } - - private void EmitForConstArray(ForConstArrayNode forConstArrayNode) - { - var targetType = (NubConstArrayType)forConstArrayNode.Target.Type; - var target = EmitExpression(forConstArrayNode.Target); - var indexName = forConstArrayNode.IndexNameToken?.Value ?? NewTmp(); - - _writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {targetType.Size}; ++{indexName})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - _writer.WriteLine($"{CType.Create(targetType.ElementType, forConstArrayNode.ElementNameToken.Value)} = {target}[{indexName}];"); - EmitBlock(forConstArrayNode.Body); - } - } - - _writer.WriteLine("}"); - } - - private void EmitIf(IfNode ifNode, bool elseIf = false) - { - var condition = EmitExpression(ifNode.Condition); - _writer.WriteLine($"{(elseIf ? "else " : "")}if ({condition})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - EmitBlock(ifNode.Body); - } - } - - _writer.WriteLine("}"); - ifNode.Else?.Match - ( - elseIfNode => EmitIf(elseIfNode, true), - elseNode => - { - _writer.WriteLine("else"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - EmitBlock(elseNode); - } - } - - _writer.WriteLine("}"); - } - ); - } - - private void EmitReturn(ReturnNode returnNode) - { - if (returnNode.Value == null) - { - EmitScopeCleanup(); - _writer.WriteLine("return;"); - } - else - { - var returnValue = EmitExpression(returnNode.Value); - var tmp = NewTmp(); - _writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};"); - - EmitScopeCleanup(); - EmitLine(returnNode.Tokens.FirstOrDefault()); - _writer.WriteLine($"return {tmp};"); - } - } - - private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode) - { - EmitFuncCall(statementFuncCallNode.FuncCall); - } - - private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode) - { - Scope.AddVariable(variableDeclarationNode.NameToken.Value, variableDeclarationNode.Type); - - if (variableDeclarationNode.Assignment != null) - { - var value = EmitExpression(variableDeclarationNode.Assignment); - _writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.NameToken.Value)} = {value};"); - } - else - { - _writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.NameToken.Value)};"); - } - } - - private void EmitWhile(WhileNode whileNode) - { - var condition = EmitExpression(whileNode.Condition); - _writer.WriteLine($"while ({condition})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - using (BeginScope()) - { - EmitBlock(whileNode.Body); - } - } - - _writer.WriteLine("}"); - } - - private string EmitExpression(ExpressionNode expressionNode) - { - if (expressionNode is IntermediateExpression) - { - throw new UnreachableException("Type checker fucked up"); - } - - var expr = expressionNode switch - { - ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode), - ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode), - BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode), - BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false", - ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode), - ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode), - CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"", - DereferenceNode dereferenceNode => EmitDereference(dereferenceNode), - Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode), - Float64LiteralNode float64LiteralNode => EmitFloat64Literal(float64LiteralNode), - CastNode castNode => EmitCast(castNode), - FuncCallNode funcCallNode => EmitFuncCall(funcCallNode), - FuncIdentifierNode funcIdentifierNode => FuncName(funcIdentifierNode.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value), - AddressOfNode addressOfNode => EmitAddressOf(addressOfNode), - SizeNode sizeBuiltinNode => $"sizeof({CType.Create(sizeBuiltinNode.TargetType)})", - SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode), - StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode), - StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(structFieldAccessNode), - StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode), - I8LiteralNode i8LiteralNode => EmitI8Literal(i8LiteralNode), - I16LiteralNode i16LiteralNode => EmitI16Literal(i16LiteralNode), - I32LiteralNode i32LiteralNode => EmitI32Literal(i32LiteralNode), - I64LiteralNode i64LiteralNode => EmitI64Literal(i64LiteralNode), - U8LiteralNode u8LiteralNode => EmitU8Literal(u8LiteralNode), - U16LiteralNode u16LiteralNode => EmitU16Literal(u16LiteralNode), - U32LiteralNode u32LiteralNode => EmitU32Literal(u32LiteralNode), - U64LiteralNode u64LiteralNode => EmitU64Literal(u64LiteralNode), - UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(unaryExpressionNode), - VariableIdentifierNode variableIdentifierNode => variableIdentifierNode.NameToken.Value, - _ => throw new ArgumentOutOfRangeException(nameof(expressionNode)) - }; - - return $"({expr})"; - } - - private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode) - { - var target = EmitExpression(arrayIndexAccessNode.Target); - var index = EmitExpression(arrayIndexAccessNode.Index); - return $"{target}[{index}]"; - } - - private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode) - { - var values = new List(); - foreach (var value in arrayInitializerNode.Values) - { - values.Add(EmitExpression(value)); - } - - var arrayType = (NubArrayType)arrayInitializerNode.Type; - return $"({CType.Create(arrayType.ElementType)}[]){{{string.Join(", ", values)}}}"; - } - - private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode) - { - var left = EmitExpression(binaryExpressionNode.Left); - var right = EmitExpression(binaryExpressionNode.Right); - - var op = binaryExpressionNode.Operator switch - { - BinaryOperator.Plus => "+", - BinaryOperator.Minus => "-", - BinaryOperator.Multiply => "*", - BinaryOperator.Divide => "/", - BinaryOperator.Modulo => "%", - BinaryOperator.Equal => "==", - BinaryOperator.NotEqual => "!=", - BinaryOperator.LessThan => "<", - BinaryOperator.LessThanOrEqual => "<=", - BinaryOperator.GreaterThan => ">", - BinaryOperator.GreaterThanOrEqual => ">=", - BinaryOperator.LogicalAnd => "&&", - BinaryOperator.LogicalOr => "||", - BinaryOperator.BitwiseAnd => "&", - BinaryOperator.BitwiseOr => "|", - BinaryOperator.BitwiseXor => "^", - BinaryOperator.LeftShift => "<<", - BinaryOperator.RightShift => ">>", - _ => throw new ArgumentOutOfRangeException() - }; - - return $"{left} {op} {right}"; - } - - private string EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode) - { - var target = EmitExpression(constArrayIndexAccessNode.Target); - var index = EmitExpression(constArrayIndexAccessNode.Index); - // todo(nub31): We can emit bounds checking here - return $"{target}[{index}]"; - } - - private string EmitConstArrayInitializer(ConstArrayInitializerNode arrayInitializerNode) - { - var arrayType = (NubConstArrayType)arrayInitializerNode.Type; - - var tmp = NewTmp(); - _writer.WriteLine($"{CType.Create(arrayType.ElementType)} {tmp}[{arrayType.Size}] = {{0}};"); - - for (var i = 0; i < arrayInitializerNode.Values.Count; i++) - { - var valueName = EmitExpression(arrayInitializerNode.Values[i]); - _writer.WriteLine($"{tmp}[{i}] = {valueName};"); - } - - return tmp; - } - - private string EmitDereference(DereferenceNode dereferenceNode) - { - var pointer = EmitExpression(dereferenceNode.Target); - return $"*{pointer}"; - } - - private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode) - { - var str = float32LiteralNode.Value.ToString("G9", System.Globalization.CultureInfo.InvariantCulture); - if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E')) - { - str += ".0"; - } - - return str + "f"; - } - - private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode) - { - var str = float64LiteralNode.Value.ToString("G17", System.Globalization.CultureInfo.InvariantCulture); - if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E')) - { - str += ".0"; - } - - return str; - } - - private string EmitCast(CastNode castNode) - { - var value = EmitExpression(castNode.Value); - - if (castNode is { Type: NubSliceType sliceType, Value.Type: NubConstArrayType arrayType }) - { - return $"({CType.Create(sliceType)}){{.length = {arrayType.Size}, .data = (void*){value}}}"; - } - - return $"({CType.Create(castNode.Type)}){value}"; - } - - private string EmitFuncCall(FuncCallNode funcCallNode) - { - var name = EmitExpression(funcCallNode.Expression); - - var parameterNames = new List(); - foreach (var parameter in funcCallNode.Parameters) - { - parameterNames.Add(EmitExpression(parameter)); - } - - var tmp = NewTmp(); - - _writer.WriteLine($"{CType.Create(funcCallNode.Type)} {tmp} = {name}({string.Join(", ", parameterNames)});"); - - return tmp; - } - - private string EmitAddressOf(AddressOfNode addressOfNode) - { - var value = EmitExpression(addressOfNode.Target); - return $"&{value}"; - } - - private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) - { - var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type; - var target = EmitExpression(sliceIndexAccessNode.Target); - var index = EmitExpression(sliceIndexAccessNode.Index); - // todo(nub31): We can emit bounds checking here - return $"(({CType.Create(targetType.ElementType)}*){target}.data)[{index}]"; - } - - private string EmitStringLiteral(StringLiteralNode stringLiteralNode) - { - var length = Encoding.UTF8.GetByteCount(stringLiteralNode.Value); - return $"({CType.Create(stringLiteralNode.Type)}){{.length = {length}, .data = \"{stringLiteralNode.Value}\"}}"; - } - - private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode) - { - var structExpr = EmitExpression(structFieldAccessNode.Target); - return $"{structExpr}.{structFieldAccessNode.FieldToken.Value}"; - } - - private string EmitStructInitializer(StructInitializerNode structInitializerNode) - { - var structType = (NubStructType)structInitializerNode.Type; - - var tmp = NewTmp(); - _writer.WriteLine($"{CType.Create(structType)} {tmp} = ({CType.Create(structType)}){{0}};"); - _writer.WriteLine($"{CType.Create(structType)}_create(&{tmp});"); - - foreach (var initializer in structInitializerNode.Initializers) - { - var value = EmitExpression(initializer.Value); - _writer.WriteLine($"{tmp}.{initializer.Key} = {value};"); - } - - return tmp; - } - - private string EmitI8Literal(I8LiteralNode i8LiteralNode) - { - return i8LiteralNode.Value.ToString(); - } - - private string EmitI16Literal(I16LiteralNode i16LiteralNode) - { - return i16LiteralNode.Value.ToString(); - } - - private string EmitI32Literal(I32LiteralNode i32LiteralNode) - { - return i32LiteralNode.Value.ToString(); - } - - private string EmitI64Literal(I64LiteralNode i64LiteralNode) - { - return i64LiteralNode.Value + "LL"; - } - - private string EmitU8Literal(U8LiteralNode u8LiteralNode) - { - return u8LiteralNode.Value.ToString(); - } - - private string EmitU16Literal(U16LiteralNode u16LiteralNode) - { - return u16LiteralNode.Value.ToString(); - } - - private string EmitU32Literal(U32LiteralNode u32LiteralNode) - { - return u32LiteralNode.Value.ToString(); - } - - private string EmitU64Literal(U64LiteralNode u64LiteralNode) - { - return u64LiteralNode.Value + "ULL"; - } - - private string EmitUnaryExpression(UnaryExpressionNode unaryExpressionNode) - { - var value = EmitExpression(unaryExpressionNode.Operand); - - return unaryExpressionNode.Operator switch - { - UnaryOperator.Negate => $"-{value}", - UnaryOperator.Invert => $"!{value}", - _ => throw new ArgumentOutOfRangeException() - }; - } - - private void EmitBlock(BlockNode blockNode) - { - foreach (var statementNode in blockNode.Statements) - { - EmitStatement(statementNode); - - if (statementNode != blockNode.Statements.Last()) - { - _writer.WriteLine(); - } - } - } - - private void EmitScopeCleanup() - { - if (Scope.IsTerminated()) - { - return; - } - - Scope.Terminate(); - - - var deferredStack = Scope.GetDeferred(); - while (deferredStack.TryPop(out var deferred)) - { - deferred.Invoke(); - } - } - - private ScopeDisposer BeginScope() - { - _scopes.Push(new Scope()); - return new ScopeDisposer(this); - } - - private sealed class ScopeDisposer(Generator owner) : IDisposable - { - public void Dispose() - { - owner.EmitScopeCleanup(); - owner._scopes.Pop(); - } - } -} - -public class Variable(string ident, NubType type) -{ - public string Ident { get; } = ident; - public NubType Type { get; } = type; -} - -public class Scope -{ - private bool _hasTerminated; - private readonly List _deferred = []; - private readonly List _variables = []; - - public void Terminate() - { - _hasTerminated = true; - } - - public bool IsTerminated() => _hasTerminated; - - public void Defer(Action action) - { - _deferred.Add(action); - } - - public Stack GetDeferred() => new(_deferred); - - public void AddVariable(string ident, NubType type) - { - _variables.Add(new Variable(ident, type)); - } - - public Stack GetVariables() => new(_variables); -} \ No newline at end of file diff --git a/compiler/NubLang/Generation/HeaderGenerator.cs b/compiler/NubLang/Generation/HeaderGenerator.cs deleted file mode 100644 index 6b60e64..0000000 --- a/compiler/NubLang/Generation/HeaderGenerator.cs +++ /dev/null @@ -1,52 +0,0 @@ -using NubLang.Syntax; - -namespace NubLang.Generation; - -public static class HeaderGenerator -{ - private static string FuncName(string module, string name, string? externSymbol) - { - return externSymbol ?? $"{module}_{name}"; - } - - public static string Generate(string name, TypedModule module) - { - var writer = new IndentedTextWriter(); - - foreach (var import in module.Imports) - { - writer.WriteLine($"#include \"{import}.h\""); - } - - writer.WriteLine(); - - foreach (var structType in module.StructTypes) - { - writer.WriteLine("typedef struct"); - writer.WriteLine("{"); - using (writer.Indent()) - { - foreach (var field in structType.Fields) - { - writer.WriteLine($"{CType.Create(field.Type)} {field.Name};"); - } - } - - writer.WriteLine($"}} {CType.Create(structType)};"); - writer.WriteLine($"void {CType.Create(structType)}_create({CType.Create(structType)} *self);"); - writer.WriteLine(); - } - - foreach (var prototype in module.FunctionPrototypes) - { - var parameters = prototype.Parameters.Count != 0 - ? string.Join(", ", prototype.Parameters.Select(x => CType.Create(x.Type, x.NameToken.Value))) - : "void"; - - var funcName = FuncName(name, prototype.NameToken.Value, prototype.ExternSymbolToken?.Value); - writer.WriteLine($"{CType.Create(prototype.ReturnType, funcName)}({parameters});"); - } - - return writer.ToString(); - } -} \ No newline at end of file diff --git a/compiler/NubLang/Generation/LlvmGenerator.cs b/compiler/NubLang/Generation/LlvmGenerator.cs new file mode 100644 index 0000000..3e91f66 --- /dev/null +++ b/compiler/NubLang/Generation/LlvmGenerator.cs @@ -0,0 +1,979 @@ +using System.Text; +using NubLang.Ast; + +namespace NubLang.Generation; + +public class LlvmGenerator +{ + private string _module = string.Empty; + private int _tmpIndex; + private int _labelIndex; + private List<(string Name, int Size, string Text)> _stringLiterals = []; + private Stack<(string breakLabel, string continueLabel)> _loopStack = []; + + public string Emit(List topLevelNodes) + { + _stringLiterals = []; + _loopStack = []; + + var writer = new IndentedTextWriter(); + + _module = topLevelNodes.OfType().First().NameToken.Value; + writer.WriteLine($"; Module {_module}"); + writer.WriteLine(); + + writer.WriteLine("declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)"); + writer.WriteLine(); + + foreach (var structNode in topLevelNodes.OfType()) + { + var types = structNode.Fields.Select(x => MapType(x.Type)); + writer.WriteLine($"%{StructName(structNode)} = type {{ {string.Join(", ", types)} }}"); + writer.WriteLine(); + + _tmpIndex = 0; + _labelIndex = 0; + + writer.WriteLine($"define void @{StructName(structNode)}.new(ptr %self) {{"); + using (writer.Indent()) + { + foreach (var field in structNode.Fields) + { + if (field.Value != null) + { + var index = structNode.StructType.GetFieldIndex(field.NameToken.Value); + var fieldTmp = NewTmp($"struct.field.{field.NameToken.Value}"); + writer.WriteLine($"{fieldTmp} = getelementptr %{StructName(structNode)}, ptr %self, i32 0, i32 {index}"); + + EmitExpressionInto(writer, field.Value, fieldTmp); + } + } + + writer.WriteLine("ret void"); + } + + writer.WriteLine("}"); + writer.WriteLine(); + } + + foreach (var funcNode in topLevelNodes.OfType()) + { + if (funcNode.Body != null) continue; + var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}"); + + writer.WriteLine($"declare {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)})"); + writer.WriteLine(); + } + + foreach (var funcNode in topLevelNodes.OfType()) + { + if (funcNode.Body == null) continue; + + _tmpIndex = 0; + _labelIndex = 0; + + var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}"); + writer.WriteLine($"define {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)}) {{"); + + using (writer.Indent()) + { + EmitBlock(writer, funcNode.Body); + + // note(nub31): Implicit return for void functions + if (funcNode.Prototype.ReturnType is NubVoidType) + { + writer.WriteLine("ret void"); + } + } + + writer.WriteLine("}"); + writer.WriteLine(); + } + + foreach (var stringLiteral in _stringLiterals) + { + writer.WriteLine($"{stringLiteral.Name} = private unnamed_addr constant [{stringLiteral.Size} x i8] c\"{stringLiteral.Text}\\00\", align 1"); + } + + return writer.ToString(); + } + + private void EmitStatement(IndentedTextWriter writer, StatementNode statementNode) + { + switch (statementNode) + { + case AssignmentNode assignmentNode: + EmitAssignment(writer, assignmentNode); + break; + case BlockNode blockNode: + EmitBlock(writer, blockNode); + break; + case BreakNode breakNode: + EmitBreak(writer, breakNode); + break; + case ContinueNode continueNode: + EmitContinue(writer, continueNode); + break; + case DeferNode deferNode: + EmitDefer(writer, deferNode); + break; + case ForConstArrayNode forConstArrayNode: + EmitForConstArray(writer, forConstArrayNode); + break; + case ForSliceNode forSliceNode: + EmitForSlice(writer, forSliceNode); + break; + case IfNode ifNode: + EmitIf(writer, ifNode); + break; + case ReturnNode returnNode: + EmitReturn(writer, returnNode); + break; + case StatementFuncCallNode statementFuncCallNode: + EmitStatementFuncCall(writer, statementFuncCallNode); + break; + case VariableDeclarationNode variableDeclarationNode: + EmitVariableDeclaration(writer, variableDeclarationNode); + break; + case WhileNode whileNode: + EmitWhile(writer, whileNode); + break; + default: + { + throw new NotImplementedException(); + } + } + } + + private void EmitAssignment(IndentedTextWriter writer, AssignmentNode assignmentNode) + { + var target = EmitExpression(writer, assignmentNode.Target); + var value = Unwrap(writer, EmitExpression(writer, assignmentNode.Value)); + writer.WriteLine($"store {MapType(assignmentNode.Value.Type)} {value}, ptr {target.Ident}"); + } + + private void EmitBlock(IndentedTextWriter writer, BlockNode blockNode) + { + foreach (var statementNode in blockNode.Statements) + { + EmitStatement(writer, statementNode); + } + } + + private void EmitBreak(IndentedTextWriter writer, BreakNode breakNode) + { + var (breakLabel, _) = _loopStack.Peek(); + writer.WriteLine($"br label %{breakLabel}"); + } + + private void EmitContinue(IndentedTextWriter writer, ContinueNode continueNode) + { + var (_, continueLabel) = _loopStack.Peek(); + writer.WriteLine($"br label %{continueLabel}"); + } + + private void EmitDefer(IndentedTextWriter writer, DeferNode deferNode) + { + throw new NotImplementedException(); + } + + private void EmitForConstArray(IndentedTextWriter writer, ForConstArrayNode forConstArrayNode) + { + throw new NotImplementedException(); + } + + private void EmitForSlice(IndentedTextWriter writer, ForSliceNode forSliceNode) + { + throw new NotImplementedException(); + } + + private void EmitIf(IndentedTextWriter writer, IfNode ifNode) + { + var endLabel = NewLabel("if.end"); + EmitIf(writer, ifNode, endLabel); + writer.WriteLine($"{endLabel}:"); + } + + private void EmitIf(IndentedTextWriter writer, IfNode ifNode, string endLabel) + { + var condition = Unwrap(writer, EmitExpression(writer, ifNode.Condition)); + var thenLabel = NewLabel("if.then"); + var elseLabel = ifNode.Else.HasValue ? NewLabel("if.else") : endLabel; + + writer.WriteLine($"br i1 {condition}, label %{thenLabel}, label %{elseLabel}"); + + writer.WriteLine($"{thenLabel}:"); + using (writer.Indent()) + { + EmitBlock(writer, ifNode.Body); + writer.WriteLine($"br label %{endLabel}"); + } + + if (!ifNode.Else.HasValue) return; + + writer.WriteLine($"{elseLabel}:"); + using (writer.Indent()) + { + ifNode.Else.Value.Match( + nestedElseIf => EmitIf(writer, nestedElseIf, endLabel), + finalElse => + { + EmitBlock(writer, finalElse); + writer.WriteLine($"br label %{endLabel}"); + } + ); + } + } + + private void EmitReturn(IndentedTextWriter writer, ReturnNode returnNode) + { + if (returnNode.Value != null) + { + var returnValue = Unwrap(writer, EmitExpression(writer, returnNode.Value)); + writer.WriteLine($"ret {MapType(returnNode.Value.Type)} {returnValue}"); + } + else + { + writer.WriteLine("ret void"); + } + } + + private void EmitStatementFuncCall(IndentedTextWriter writer, StatementFuncCallNode statementFuncCallNode) + { + EmitFuncCall(writer, statementFuncCallNode.FuncCall); + } + + private void EmitVariableDeclaration(IndentedTextWriter writer, VariableDeclarationNode variableDeclarationNode) + { + writer.WriteLine($"%{variableDeclarationNode.NameToken.Value} = alloca {MapType(variableDeclarationNode.Type)}"); + if (variableDeclarationNode.Assignment != null) + { + EmitExpressionInto(writer, variableDeclarationNode.Assignment, $"%{variableDeclarationNode.NameToken.Value}"); + } + } + + private void EmitWhile(IndentedTextWriter writer, WhileNode whileNode) + { + var conditionLabel = NewLabel("while.condition"); + var bodyLabel = NewLabel("while.body"); + var endLabel = NewLabel("while.end"); + + _loopStack.Push((endLabel, conditionLabel)); + + writer.WriteLine($"br label %{conditionLabel}"); + + writer.WriteLine($"{conditionLabel}:"); + using (writer.Indent()) + { + var condition = Unwrap(writer, EmitExpression(writer, whileNode.Condition)); + writer.WriteLine($"br i1 {condition}, label %{bodyLabel}, label %{endLabel}"); + } + + writer.WriteLine($"{bodyLabel}:"); + using (writer.Indent()) + { + EmitBlock(writer, whileNode.Body); + writer.WriteLine($"br label %{conditionLabel}"); + } + + _loopStack.Pop(); + + writer.WriteLine($"{endLabel}:"); + } + + private Tmp EmitExpression(IndentedTextWriter writer, ExpressionNode expressionNode) + { + return expressionNode switch + { + RValue rValue => EmitRValue(writer, rValue), + LValue lValue => EmitLValue(writer, lValue), + _ => throw new ArgumentOutOfRangeException(nameof(expressionNode)) + }; + } + + private void EmitExpressionInto(IndentedTextWriter writer, ExpressionNode expressionNode, string destination) + { + switch (expressionNode) + { + case StructInitializerNode structInitializerNode: + { + EmitStructInitializer(writer, structInitializerNode, destination); + return; + } + case ConstArrayInitializerNode constArrayInitializerNode: + { + EmitConstArrayInitializer(writer, constArrayInitializerNode, destination); + return; + } + } + + var value = Unwrap(writer, EmitExpression(writer, expressionNode)); + + if (expressionNode.Type.IsAggregate()) + { + // note(nub31): Fall back to slow method creating a copy + writer.WriteLine("; Slow rvalue copy instead of direct memory write"); + writer.WriteLine($"call void @llvm.memcpy.p0.p0.i64(ptr {destination}, ptr {value}, i64 {expressionNode.Type.GetSize()}, i1 false)"); + } + else + { + writer.WriteLine($"store {MapType(expressionNode.Type)} {value}, ptr {destination}"); + } + } + + private Tmp EmitRValue(IndentedTextWriter writer, RValue rValue) + { + return rValue switch + { + AddressOfNode addressOfNode => EmitAddressOf(writer, addressOfNode), + BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(writer, binaryExpressionNode), + BoolLiteralNode boolLiteralNode => EmitBoolLiteral(writer, boolLiteralNode), + CastNode castNode => EmitCast(writer, castNode), + ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(writer, constArrayInitializerNode), + CStringLiteralNode cStringLiteralNode => EmitCStringLiteral(writer, cStringLiteralNode), + Float32LiteralNode float32LiteralNode => EmitFloat32Literal(writer, float32LiteralNode), + Float64LiteralNode float64LiteralNode => EmitFloat64Literal(writer, float64LiteralNode), + FuncCallNode funcCallNode => EmitFuncCall(writer, funcCallNode), + FuncIdentifierNode funcIdentifierNode => EmitFuncIdentifier(writer, funcIdentifierNode), + I16LiteralNode i16LiteralNode => EmitI16Literal(writer, i16LiteralNode), + I32LiteralNode i32LiteralNode => EmitI32Literal(writer, i32LiteralNode), + I64LiteralNode i64LiteralNode => EmitI64Literal(writer, i64LiteralNode), + I8LiteralNode i8LiteralNode => EmitI8Literal(writer, i8LiteralNode), + SizeNode sizeNode => EmitSize(writer, sizeNode), + StringLiteralNode stringLiteralNode => EmitStringLiteral(writer, stringLiteralNode), + StructInitializerNode structInitializerNode => EmitStructInitializer(writer, structInitializerNode), + U16LiteralNode u16LiteralNode => EmitU16Literal(writer, u16LiteralNode), + U32LiteralNode u32LiteralNode => EmitU32Literal(writer, u32LiteralNode), + U64LiteralNode u64LiteralNode => EmitU64Literal(writer, u64LiteralNode), + U8LiteralNode u8LiteralNode => EmitU8Literal(writer, u8LiteralNode), + UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(writer, unaryExpressionNode), + _ => throw new ArgumentOutOfRangeException(nameof(rValue), rValue, null) + }; + } + + private Tmp EmitLValue(IndentedTextWriter writer, LValue lValue) + { + return lValue switch + { + ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(writer, arrayIndexAccessNode), + ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(writer, constArrayIndexAccessNode), + DereferenceNode dereferenceNode => EmitDereference(writer, dereferenceNode), + SliceIndexAccessNode sliceIndexAccessNode => EmitSliceIndexAccess(writer, sliceIndexAccessNode), + StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(writer, structFieldAccessNode), + VariableIdentifierNode variableIdentifierNode => EmitVariableIdentifier(writer, variableIdentifierNode), + _ => throw new ArgumentOutOfRangeException(nameof(lValue), lValue, null) + }; + } + + private Tmp EmitAddressOf(IndentedTextWriter writer, AddressOfNode addressOfNode) + { + var target = EmitExpression(writer, addressOfNode.Target); + return new Tmp(target.Ident, addressOfNode.Type, false); + } + + private Tmp EmitArrayIndexAccess(IndentedTextWriter writer, ArrayIndexAccessNode arrayIndexAccessNode) + { + var arrayPtr = Unwrap(writer, EmitExpression(writer, arrayIndexAccessNode.Target)); + var index = Unwrap(writer, EmitExpression(writer, arrayIndexAccessNode.Index)); + + var elementType = ((NubArrayType)arrayIndexAccessNode.Target.Type).ElementType; + var ptrTmp = NewTmp("array.element"); + writer.WriteLine($"{ptrTmp} = getelementptr {MapType(elementType)}, ptr {arrayPtr}, {MapType(arrayIndexAccessNode.Index.Type)} {index}"); + + return new Tmp(ptrTmp, arrayIndexAccessNode.Type, true); + } + + private Tmp EmitBinaryExpression(IndentedTextWriter writer, BinaryExpressionNode binaryExpressionNode) + { + var left = Unwrap(writer, EmitExpression(writer, binaryExpressionNode.Left)); + var right = Unwrap(writer, EmitExpression(writer, binaryExpressionNode.Right)); + var result = NewTmp("binop"); + + var leftType = binaryExpressionNode.Left.Type; + var op = binaryExpressionNode.Operator; + + switch (op) + { + case BinaryOperator.Equal: + case BinaryOperator.NotEqual: + case BinaryOperator.GreaterThan: + case BinaryOperator.GreaterThanOrEqual: + case BinaryOperator.LessThan: + case BinaryOperator.LessThanOrEqual: + { + var cmpOp = leftType switch + { + NubIntType intType => GenerateIntComparison(op, intType.Signed), + NubFloatType => GenerateFloatComparison(op), + NubBoolType => GenerateBoolComparison(op), + NubPointerType => GeneratePointerComparison(op), + _ => throw new InvalidOperationException($"Unexpected type for comparison: {leftType}") + }; + + writer.WriteLine($"{result} = {cmpOp} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.LogicalAnd: + { + writer.WriteLine($"{result} = and i1 {left}, {right}"); + break; + } + case BinaryOperator.LogicalOr: + { + writer.WriteLine($"{result} = or i1 {left}, {right}"); + break; + } + case BinaryOperator.Plus: + { + var instruction = leftType switch + { + NubIntType => "add", + NubFloatType => "fadd", + _ => throw new InvalidOperationException($"Unexpected type for plus: {leftType}") + }; + writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.Minus: + { + var instruction = leftType switch + { + NubIntType => "sub", + NubFloatType => "fsub", + _ => throw new InvalidOperationException($"Unexpected type for minus: {leftType}") + }; + writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.Multiply: + { + var instruction = leftType switch + { + NubIntType => "mul", + NubFloatType => "fmul", + _ => throw new InvalidOperationException($"Unexpected type for multiply: {leftType}") + }; + writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.Divide: + { + var instruction = leftType switch + { + NubIntType intType => intType.Signed ? "sdiv" : "udiv", + NubFloatType => "fdiv", + _ => throw new InvalidOperationException($"Unexpected type for divide: {leftType}") + }; + writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.Modulo: + { + var instruction = leftType switch + { + NubIntType intType => intType.Signed ? "srem" : "urem", + NubFloatType => "frem", + _ => throw new InvalidOperationException($"Unexpected type for modulo: {leftType}") + }; + writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.LeftShift: + { + writer.WriteLine($"{result} = shl {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.RightShift: + { + var intType = (NubIntType)leftType; + var instruction = intType.Signed ? "ashr" : "lshr"; + writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.BitwiseAnd: + { + writer.WriteLine($"{result} = and {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.BitwiseXor: + { + writer.WriteLine($"{result} = xor {MapType(leftType)} {left}, {right}"); + break; + } + case BinaryOperator.BitwiseOr: + { + writer.WriteLine($"{result} = or {MapType(leftType)} {left}, {right}"); + break; + } + default: + throw new ArgumentOutOfRangeException(nameof(op), op, null); + } + + return new Tmp(result, binaryExpressionNode.Type, false); + } + + private string GenerateIntComparison(BinaryOperator op, bool signed) + { + return op switch + { + BinaryOperator.Equal => "icmp eq", + BinaryOperator.NotEqual => "icmp ne", + BinaryOperator.GreaterThan => signed ? "icmp sgt" : "icmp ugt", + BinaryOperator.GreaterThanOrEqual => signed ? "icmp sge" : "icmp uge", + BinaryOperator.LessThan => signed ? "icmp slt" : "icmp ult", + BinaryOperator.LessThanOrEqual => signed ? "icmp sle" : "icmp ule", + _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) + }; + } + + private string GenerateFloatComparison(BinaryOperator op) + { + return op switch + { + BinaryOperator.Equal => "fcmp oeq", + BinaryOperator.NotEqual => "fcmp one", + BinaryOperator.GreaterThan => "fcmp ogt", + BinaryOperator.GreaterThanOrEqual => "fcmp oge", + BinaryOperator.LessThan => "fcmp olt", + BinaryOperator.LessThanOrEqual => "fcmp ole", + _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) + }; + } + + private string GenerateBoolComparison(BinaryOperator op) + { + return op switch + { + BinaryOperator.Equal => "icmp eq", + BinaryOperator.NotEqual => "icmp ne", + _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) + }; + } + + private string GeneratePointerComparison(BinaryOperator op) + { + return op switch + { + BinaryOperator.Equal => "icmp eq", + BinaryOperator.NotEqual => "icmp ne", + BinaryOperator.GreaterThan => "icmp ugt", + BinaryOperator.GreaterThanOrEqual => "icmp uge", + BinaryOperator.LessThan => "icmp ult", + BinaryOperator.LessThanOrEqual => "icmp ule", + _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) + }; + } + + private Tmp EmitBoolLiteral(IndentedTextWriter writer, BoolLiteralNode boolLiteralNode) + { + return new Tmp(boolLiteralNode.Value ? "1" : "0", boolLiteralNode.Type, false); + } + + private Tmp EmitCast(IndentedTextWriter writer, CastNode castNode) + { + var source = Unwrap(writer, EmitExpression(writer, castNode.Value)); + var sourceType = castNode.Value.Type; + var targetType = castNode.Type; + + var result = NewTmp("cast"); + + switch (sourceType, targetType) + { + case (NubIntType sourceInt, NubIntType targetInt): + { + if (sourceInt.Width < targetInt.Width) + { + var op = sourceInt.Signed ? "sext" : "zext"; + writer.WriteLine($"{result} = {op} {MapType(sourceType)} {source} to {MapType(targetType)}"); + } + else if (sourceInt.Width > targetInt.Width) + { + writer.WriteLine($"{result} = trunc {MapType(sourceType)} {source} to {MapType(targetType)}"); + } + else + { + writer.WriteLine($"{result} = bitcast {MapType(sourceType)} {source} to {MapType(targetType)}"); + } + + break; + } + case (NubFloatType sourceFloat, NubFloatType targetFloat): + { + if (sourceFloat.Width < targetFloat.Width) + { + writer.WriteLine($"{result} = fpext {MapType(sourceType)} {source} to {MapType(targetType)}"); + } + else + { + writer.WriteLine($"{result} = fptrunc {MapType(sourceType)} {source} to {MapType(targetType)}"); + } + + break; + } + case (NubIntType intType, NubFloatType): + { + var intToFloatOp = intType.Signed ? "sitofp" : "uitofp"; + writer.WriteLine($"{result} = {intToFloatOp} {MapType(sourceType)} {source} to {MapType(targetType)}"); + break; + } + case (NubFloatType, NubIntType targetInt): + { + var floatToIntOp = targetInt.Signed ? "fptosi" : "fptoui"; + writer.WriteLine($"{result} = {floatToIntOp} {MapType(sourceType)} {source} to {MapType(targetType)}"); + break; + } + case (NubPointerType, NubPointerType): + case (NubPointerType, NubIntType): + case (NubIntType, NubPointerType): + { + writer.WriteLine($"{result} = inttoptr {MapType(sourceType)} {source} to {MapType(targetType)}"); + break; + } + default: + { + throw new NotImplementedException($"Cast from {sourceType} to {targetType} not implemented"); + } + } + + return new Tmp(result, castNode.Type, false); + } + + + private Tmp EmitConstArrayIndexAccess(IndentedTextWriter writer, ConstArrayIndexAccessNode constArrayIndexAccessNode) + { + var arrayPtr = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Target)); + var index = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Index)); + + var elementType = ((NubConstArrayType)constArrayIndexAccessNode.Target.Type).ElementType; + var ptrTmp = NewTmp("array.element"); + writer.WriteLine($"{ptrTmp} = getelementptr {MapType(elementType)}, ptr {arrayPtr}, {MapType(constArrayIndexAccessNode.Index.Type)} {index}"); + + return new Tmp(ptrTmp, constArrayIndexAccessNode.Type, true); + } + + private Tmp EmitConstArrayInitializer(IndentedTextWriter writer, ConstArrayInitializerNode constArrayInitializerNode, string? destination = null) + { + var arrayType = (NubConstArrayType)constArrayInitializerNode.Type; + + if (destination == null) + { + destination = NewTmp("array"); + writer.WriteLine($"{destination} = alloca {MapType(arrayType)}"); + } + + for (var i = 0; i < constArrayInitializerNode.Values.Count; i++) + { + var value = constArrayInitializerNode.Values[i]; + var indexTmp = NewTmp("array.element"); + writer.WriteLine($"{indexTmp} = getelementptr {MapType(arrayType)}, ptr {destination}, i32 0, i32 {i}"); + EmitExpressionInto(writer, value, indexTmp); + } + + return new Tmp(destination, constArrayInitializerNode.Type, false); + } + + private Tmp EmitCStringLiteral(IndentedTextWriter writer, CStringLiteralNode cStringLiteralNode) + { + var escaped = EscapeStringForLLVM(cStringLiteralNode.Value); + + var stringWithNull = cStringLiteralNode.Value + "\0"; + var length = stringWithNull.Length; + + var globalName = $"@.str.{_stringLiterals.Count}"; + + _stringLiterals.Add((globalName, length, escaped)); + + var gepTmp = NewTmp("str.ptr"); + writer.WriteLine($"{gepTmp} = getelementptr [{length} x i8], ptr {globalName}, i32 0, i32 0"); + + return new Tmp(gepTmp, cStringLiteralNode.Type, false); + } + + private string EscapeStringForLLVM(string input) + { + var result = new StringBuilder(); + foreach (var c in input) + { + if (c == '\0') + result.Append("\\00"); + else if (c == '\n') + result.Append("\\0A"); + else if (c == '\r') + result.Append("\\0D"); + else if (c == '\t') + result.Append("\\09"); + else if (c == '\\') + result.Append("\\\\"); + else if (c == '"') + result.Append("\\22"); + else if (c < 32 || c > 126) + result.Append($"\\{(int)c:X2}"); + else + result.Append(c); + } + + return result.ToString(); + } + + private Tmp EmitDereference(IndentedTextWriter writer, DereferenceNode dereferenceNode) + { + throw new NotImplementedException(); + } + + private Tmp EmitFloat32Literal(IndentedTextWriter writer, Float32LiteralNode float32LiteralNode) + { + var literal = ((double)float32LiteralNode.Value).ToString("R", System.Globalization.CultureInfo.InvariantCulture); + if (!literal.Contains('.')) + { + literal += ".0"; + } + + return new Tmp(literal, float32LiteralNode.Type, false); + } + + private Tmp EmitFloat64Literal(IndentedTextWriter writer, Float64LiteralNode float64LiteralNode) + { + var literal = float64LiteralNode.Value.ToString("R", System.Globalization.CultureInfo.InvariantCulture); + if (!literal.Contains('.')) + { + literal += ".0"; + } + + return new Tmp(literal, float64LiteralNode.Type, false); + } + + private Tmp EmitFuncCall(IndentedTextWriter writer, FuncCallNode funcCallNode) + { + var result = NewTmp(); + + var parameterStrings = new List(); + + foreach (var parameter in funcCallNode.Parameters) + { + var value = Unwrap(writer, EmitExpression(writer, parameter)); + parameterStrings.Add($"{MapType(parameter.Type)} {value}"); + } + + var functionPtr = Unwrap(writer, EmitExpression(writer, funcCallNode.Expression)); + + if (funcCallNode.Type is NubVoidType) + { + writer.WriteLine($"call {MapType(funcCallNode.Type)} {functionPtr}({string.Join(", ", parameterStrings)})"); + } + else + { + writer.WriteLine($"{result} = call {MapType(funcCallNode.Type)} {functionPtr}({string.Join(", ", parameterStrings)})"); + } + + return new Tmp(result, funcCallNode.Type, false); + } + + private Tmp EmitFuncIdentifier(IndentedTextWriter writer, FuncIdentifierNode funcIdentifierNode) + { + var name = FuncName(funcIdentifierNode.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value); + return new Tmp($"@{name}", funcIdentifierNode.Type, false); + } + + private Tmp EmitI16Literal(IndentedTextWriter writer, I16LiteralNode i16LiteralNode) + { + return new Tmp(i16LiteralNode.Value.ToString(), i16LiteralNode.Type, false); + } + + private Tmp EmitI32Literal(IndentedTextWriter writer, I32LiteralNode i32LiteralNode) + { + return new Tmp(i32LiteralNode.Value.ToString(), i32LiteralNode.Type, false); + } + + private Tmp EmitI64Literal(IndentedTextWriter writer, I64LiteralNode i64LiteralNode) + { + return new Tmp(i64LiteralNode.Value.ToString(), i64LiteralNode.Type, false); + } + + private Tmp EmitI8Literal(IndentedTextWriter writer, I8LiteralNode i8LiteralNode) + { + return new Tmp(i8LiteralNode.Value.ToString(), i8LiteralNode.Type, false); + } + + private Tmp EmitSize(IndentedTextWriter writer, SizeNode sizeNode) + { + return new Tmp(sizeNode.TargetType.GetSize().ToString(), sizeNode.Type, false); + } + + private Tmp EmitSliceIndexAccess(IndentedTextWriter writer, SliceIndexAccessNode sliceIndexAccessNode) + { + throw new NotImplementedException(); + } + + private Tmp EmitStringLiteral(IndentedTextWriter writer, StringLiteralNode stringLiteralNode) + { + throw new NotImplementedException(); + } + + private Tmp EmitStructFieldAccess(IndentedTextWriter writer, StructFieldAccessNode structFieldAccessNode) + { + var target = Unwrap(writer, EmitExpression(writer, structFieldAccessNode.Target)); + + var structType = (NubStructType)structFieldAccessNode.Target.Type; + var index = structType.GetFieldIndex(structFieldAccessNode.FieldToken.Value); + + var ptrTmp = NewTmp($"struct.field.{structFieldAccessNode.FieldToken.Value}"); + writer.WriteLine($"{ptrTmp} = getelementptr %{StructName(structType.Module, structType.Name)}, ptr {target}, i32 0, i32 {index}"); + + return new Tmp(ptrTmp, structFieldAccessNode.Type, true); + } + + private Tmp EmitStructInitializer(IndentedTextWriter writer, StructInitializerNode structInitializerNode, string? destination = null) + { + if (destination == null) + { + destination = NewTmp("struct"); + writer.WriteLine($"{destination} = alloca {MapType(structInitializerNode.Type)}"); + } + + var structType = (NubStructType)structInitializerNode.Type; + + writer.WriteLine($"call void @{StructName(structType.Module, structType.Name)}.new(ptr {destination})"); + + foreach (var (name, value) in structInitializerNode.Initializers) + { + var index = structType.GetFieldIndex(name.Value); + var fieldTmp = NewTmp($"struct.field.{name}"); + writer.WriteLine($"{fieldTmp} = getelementptr %{StructName(structType.Module, structType.Name)}, ptr {destination}, i32 0, i32 {index}"); + + EmitExpressionInto(writer, value, fieldTmp); + } + + return new Tmp(destination, structInitializerNode.Type, false); + } + + private Tmp EmitU16Literal(IndentedTextWriter writer, U16LiteralNode u16LiteralNode) + { + return new Tmp(u16LiteralNode.Value.ToString(), u16LiteralNode.Type, false); + } + + private Tmp EmitU32Literal(IndentedTextWriter writer, U32LiteralNode u32LiteralNode) + { + return new Tmp(u32LiteralNode.Value.ToString(), u32LiteralNode.Type, false); + } + + private Tmp EmitU64Literal(IndentedTextWriter writer, U64LiteralNode u64LiteralNode) + { + return new Tmp(u64LiteralNode.Value.ToString(), u64LiteralNode.Type, false); + } + + private Tmp EmitU8Literal(IndentedTextWriter writer, U8LiteralNode u8LiteralNode) + { + return new Tmp(u8LiteralNode.Value.ToString(), u8LiteralNode.Type, false); + } + + private Tmp EmitUnaryExpression(IndentedTextWriter writer, UnaryExpressionNode unaryExpressionNode) + { + var operand = Unwrap(writer, EmitExpression(writer, unaryExpressionNode.Operand)); + var result = NewTmp("unary"); + + switch (unaryExpressionNode.Operator) + { + case UnaryOperator.Negate: + switch (unaryExpressionNode.Operand.Type) + { + case NubIntType intType: + writer.WriteLine($"{result} = sub {MapType(intType)} 0, {operand}"); + break; + case NubFloatType floatType: + writer.WriteLine($"{result} = fneg {MapType(floatType)} {operand}"); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + break; + case UnaryOperator.Invert: + writer.WriteLine($"{result} = xor i1 {operand}, true"); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return new Tmp(result, unaryExpressionNode.Type, false); + } + + private Tmp EmitVariableIdentifier(IndentedTextWriter writer, VariableIdentifierNode variableIdentifierNode) + { + return new Tmp($"%{variableIdentifierNode.NameToken.Value}", variableIdentifierNode.Type, true); + } + + private string StructName(StructNode structNode) + { + return StructName(_module, structNode.NameToken.Value); + } + + private string StructName(string module, string name) + { + return $"struct.{module}.{name}"; + } + + private string FuncName(FuncPrototypeNode funcNodePrototype) + { + return FuncName(_module, funcNodePrototype.NameToken.Value, funcNodePrototype.ExternSymbolToken?.Value); + } + + private string FuncName(string module, string name, string? externSymbol) + { + if (externSymbol != null) + { + return externSymbol; + } + + return $"{module}.{name}"; + } + + private string MapType(NubType type) + { + return type switch + { + NubArrayType arrayType => $"{MapType(arrayType.ElementType)}*", + NubBoolType => "i1", + NubConstArrayType constArrayType => $"[{constArrayType.Size} x {MapType(constArrayType.ElementType)}]", + NubFloatType floatType => floatType.Width == 32 ? "float" : "double", + NubFuncType funcType => MapFuncType(funcType), + NubIntType intType => $"i{intType.Width}", + NubPointerType pointerType => $"{MapType(pointerType.BaseType)}*", + NubSliceType sliceType => throw new NotImplementedException(), + NubStringType stringType => throw new NotImplementedException(), + NubStructType structType => $"%{StructName(structType.Module, structType.Name)}", + NubVoidType => "void", + _ => throw new ArgumentOutOfRangeException(nameof(type)) + }; + } + + private string MapFuncType(NubFuncType funcType) + { + var paramTypes = string.Join(", ", funcType.Parameters.Select(MapType)); + var returnType = MapType(funcType.ReturnType); + return $"{returnType} ({paramTypes})*"; + } + + private record Tmp(string Ident, NubType Type, bool LValue); + + private string Unwrap(IndentedTextWriter writer, Tmp tmp) + { + if (tmp.LValue && !tmp.Type.IsAggregate()) + { + var newTmp = NewTmp("deref"); + writer.WriteLine($"{newTmp} = load {MapType(tmp.Type)}, ptr {tmp.Ident}"); + return newTmp; + } + + return tmp.Ident; + } + + private string NewTmp(string name = "t") + { + return $"%{name}.{++_tmpIndex}"; + } + + private string NewLabel(string name = "l") + { + return $"{name}.{++_labelIndex}"; + } +} \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index ba29e39..bf3d7bf 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -423,7 +423,7 @@ public sealed class Parser case Symbol.Pipe: binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr; return true; - case Symbol.Caret: + case Symbol.XOr: binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor; return true; default: @@ -445,6 +445,7 @@ public sealed class Parser IdentifierToken identifier => ParseIdentifier(startIndex, identifier), SymbolToken symbolToken => symbolToken.Symbol switch { + Symbol.Ampersand => new AddressOfSyntax(GetTokens(startIndex), ParsePrimaryExpression()), Symbol.OpenParen => ParseParenthesizedExpression(), Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()), Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()), @@ -517,12 +518,6 @@ public sealed class Parser var startIndex = _tokenIndex; while (HasToken) { - if (TryExpectSymbol(Symbol.Ampersand)) - { - expr = new AddressOfSyntax(GetTokens(startIndex), expr); - continue; - } - if (TryExpectSymbol(Symbol.Caret)) { expr = new DereferenceSyntax(GetTokens(startIndex), expr); diff --git a/compiler/NubLang/Syntax/Token.cs b/compiler/NubLang/Syntax/Token.cs index 31673d2..0d7e641 100644 --- a/compiler/NubLang/Syntax/Token.cs +++ b/compiler/NubLang/Syntax/Token.cs @@ -14,14 +14,29 @@ public record IdentifierToken(SourceSpan Span, string Value) : Token(Span) public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span) { - public ulong AsU64 => Convert.ToUInt64(Value, Base); - public long AsI64 => Convert.ToInt64(Value, Base); - public uint AsU32 => Convert.ToUInt32(Value, Base); - public int AsI32 => Convert.ToInt32(Value, Base); - public ushort AsU16 => Convert.ToUInt16(Value, Base); - public short AsI16 => Convert.ToInt16(Value, Base); - public byte AsU8 => Convert.ToByte(Value, Base); - public sbyte AsI8 => Convert.ToSByte(Value, Base); + private string GetNumericValue() + { + // Strip base prefixes: 0b, 0o, 0x + return Base switch + { + 2 when Value.StartsWith("0b", StringComparison.OrdinalIgnoreCase) + => Value.Substring(2), + 8 when Value.StartsWith("0o", StringComparison.OrdinalIgnoreCase) + => Value.Substring(2), + 16 when Value.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + => Value.Substring(2), + _ => Value + }; + } + + public ulong AsU64 => Convert.ToUInt64(GetNumericValue(), Base); + public long AsI64 => Convert.ToInt64(GetNumericValue(), Base); + public uint AsU32 => Convert.ToUInt32(GetNumericValue(), Base); + public int AsI32 => Convert.ToInt32(GetNumericValue(), Base); + public ushort AsU16 => Convert.ToUInt16(GetNumericValue(), Base); + public short AsI16 => Convert.ToInt16(GetNumericValue(), Base); + public byte AsU8 => Convert.ToByte(GetNumericValue(), Base); + public sbyte AsI8 => Convert.ToSByte(GetNumericValue(), Base); public float AsF32 => Convert.ToSingle(AsI32); public double AsF64 => Convert.ToDouble(AsI64); @@ -119,6 +134,7 @@ public enum Symbol Pipe, And, Or, + XOr, At, QuestionMark, } diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs index 896aa23..983bf93 100644 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ b/compiler/NubLang/Syntax/Tokenizer.cs @@ -68,19 +68,17 @@ public sealed class Tokenizer private Token ParseToken(char current, int lineStart, int columnStart) { - // Numbers if (char.IsDigit(current)) { return ParseNumber(lineStart, columnStart); } - // String literals if (current == '"') { return ParseString(lineStart, columnStart); } - // Try keywords and symbols by length (longest first) + // note(nub31): Look for keywords (longest first in case a keyword fits partially in a larger keyword) for (var i = 8; i >= 1; i--) { if (TryMatchSymbol(i, lineStart, columnStart, out var token)) @@ -89,7 +87,6 @@ public sealed class Tokenizer } } - // Identifiers if (char.IsLetter(current) || current == '_') { return ParseIdentifier(lineStart, columnStart); @@ -103,7 +100,7 @@ public sealed class Tokenizer var start = _index; var current = _content[_index]; - // Hex literal + // note(nub31): 0xFFFFFF if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'x') { Next(2); @@ -128,7 +125,7 @@ public sealed class Tokenizer 16); } - // Binary literal + // note(nub31): 0b11001100 if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'b') { Next(2); @@ -153,7 +150,7 @@ public sealed class Tokenizer 2); } - // Decimal or float + // note(nub31): 23/23.5 var isFloat = false; while (_index < _content.Length) { @@ -191,7 +188,7 @@ public sealed class Tokenizer private StringLiteralToken ParseString(int lineStart, int columnStart) { - Next(); // Skip opening quote + Next(); var start = _index; while (true) @@ -236,6 +233,20 @@ public sealed class Tokenizer var span = _content.AsSpan(_index, length); + if (span is "true") + { + Next(4); + token = new BoolLiteralToken(CreateSpan(lineStart, columnStart), true); + return true; + } + + if (span is "false") + { + Next(5); + token = new BoolLiteralToken(CreateSpan(lineStart, columnStart), false); + return true; + } + var symbol = length switch { 8 => span switch @@ -287,6 +298,7 @@ public sealed class Tokenizer "&&" => Symbol.And, "||" => Symbol.Or, "::" => Symbol.DoubleColon, + "x|" => Symbol.XOr, _ => Symbol.None }, 1 => span[0] switch @@ -361,9 +373,7 @@ public sealed class Tokenizer } } - return new IdentifierToken( - CreateSpan(lineStart, columnStart), - _content.Substring(start, _index - start)); + return new IdentifierToken(CreateSpan(lineStart, columnStart), _content.Substring(start, _index - start)); } private SourceSpan CreateSpan(int lineStart, int columnStart) diff --git a/examples/playgroud/build.sh b/examples/playgroud/build.sh index ff21919..1ca616f 100755 --- a/examples/playgroud/build.sh +++ b/examples/playgroud/build.sh @@ -2,5 +2,5 @@ set -euo pipefail -obj=$(nubc main.nub) -clang $obj ../../runtime/.build/runtime.o -o .build/out \ No newline at end of file +nubc main.nub +clang .build/main.ll -o .build/out \ No newline at end of file diff --git a/examples/playgroud/main.nub b/examples/playgroud/main.nub index 258e2dd..9b3deb9 100644 --- a/examples/playgroud/main.nub +++ b/examples/playgroud/main.nub @@ -2,20 +2,6 @@ module main extern "puts" func puts(text: ^i8) -struct Name +extern "main" func main(argc: i64, argv: [?]^i8) { - first: ^i8 - last: ^i8 -} - -struct Human -{ - age: u64 - name: Name -} - -extern "main" func main(argc: i64, argv: [?]^i8): i64 -{ - let x: [2]Human = [{}, {}] - return 0 } \ No newline at end of file