From 5105a689df2b81f6410325fcb3fe4d3e9e691400 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 9 Feb 2026 12:58:23 +0100 Subject: [PATCH] ... --- compiler/.gitignore | 6 +- .../.idea/.idea.Compiler/.idea/.gitignore | 13 - compiler/.idea/.idea.Compiler/.idea/.name | 1 - .../.idea/.idea.Compiler/.idea/encodings.xml | 4 - .../.idea.Compiler/.idea/indexLayout.xml | 10 - compiler/.idea/.idea.Compiler/.idea/vcs.xml | 6 - compiler/{Compiler => }/Compiler.csproj | 0 compiler/Compiler/.gitignore | 1 - compiler/{Compiler => }/Diagnostic.cs | 0 compiler/{Compiler => }/Generator.cs | 0 compiler/NubLang.CLI/NubLang.CLI.csproj | 16 - compiler/NubLang.CLI/Program.cs | 43 - compiler/NubLang/Ast/CompilationUnit.cs | 15 - compiler/NubLang/Ast/Node.cs | 590 --------- compiler/NubLang/Ast/NubType.cs | 165 --- compiler/NubLang/Ast/TypeChecker.cs | 1167 ----------------- compiler/NubLang/Diagnostics/Diagnostic.cs | 398 ------ compiler/NubLang/Diagnostics/SourceSpan.cs | 112 -- compiler/NubLang/NubLang.csproj | 10 - compiler/NubLang/Sugar/DeSugar.cs | 6 - compiler/NubLang/Syntax/Module.cs | 47 - compiler/NubLang/Syntax/Parser.cs | 951 -------------- compiler/NubLang/Syntax/Syntax.cs | 147 --- compiler/NubLang/Syntax/Token.cs | 77 -- compiler/NubLang/Syntax/Tokenizer.cs | 331 ----- compiler/NubLang/Variant.cs | 75 -- compiler/{Compiler => }/Parser.cs | 0 compiler/{Compiler => }/Program.cs | 0 compiler/{Compiler => }/Tokenizer.cs | 0 compiler/{Compiler => }/TypeChecker.cs | 0 compiler/{Compiler => }/test.nub | 0 31 files changed, 4 insertions(+), 4187 deletions(-) delete mode 100644 compiler/.idea/.idea.Compiler/.idea/.gitignore delete mode 100644 compiler/.idea/.idea.Compiler/.idea/.name delete mode 100644 compiler/.idea/.idea.Compiler/.idea/encodings.xml delete mode 100644 compiler/.idea/.idea.Compiler/.idea/indexLayout.xml delete mode 100644 compiler/.idea/.idea.Compiler/.idea/vcs.xml rename compiler/{Compiler => }/Compiler.csproj (100%) delete mode 100644 compiler/Compiler/.gitignore rename compiler/{Compiler => }/Diagnostic.cs (100%) rename compiler/{Compiler => }/Generator.cs (100%) delete mode 100644 compiler/NubLang.CLI/NubLang.CLI.csproj delete mode 100644 compiler/NubLang.CLI/Program.cs delete mode 100644 compiler/NubLang/Ast/CompilationUnit.cs delete mode 100644 compiler/NubLang/Ast/Node.cs delete mode 100644 compiler/NubLang/Ast/NubType.cs delete mode 100644 compiler/NubLang/Ast/TypeChecker.cs delete mode 100644 compiler/NubLang/Diagnostics/Diagnostic.cs delete mode 100644 compiler/NubLang/Diagnostics/SourceSpan.cs delete mode 100644 compiler/NubLang/NubLang.csproj delete mode 100644 compiler/NubLang/Sugar/DeSugar.cs delete mode 100644 compiler/NubLang/Syntax/Module.cs delete mode 100644 compiler/NubLang/Syntax/Parser.cs delete mode 100644 compiler/NubLang/Syntax/Syntax.cs delete mode 100644 compiler/NubLang/Syntax/Token.cs delete mode 100644 compiler/NubLang/Syntax/Tokenizer.cs delete mode 100644 compiler/NubLang/Variant.cs rename compiler/{Compiler => }/Parser.cs (100%) rename compiler/{Compiler => }/Program.cs (100%) rename compiler/{Compiler => }/Tokenizer.cs (100%) rename compiler/{Compiler => }/TypeChecker.cs (100%) rename compiler/{Compiler => }/test.nub (100%) diff --git a/compiler/.gitignore b/compiler/.gitignore index c6cc67a..66cc8b5 100644 --- a/compiler/.gitignore +++ b/compiler/.gitignore @@ -1,4 +1,4 @@ -# Common IntelliJ Platform excludes +# Common IntelliJ Platform excludes # User specific **/.idea/**/workspace.xml @@ -31,4 +31,6 @@ _UpgradeReport_Files/ Thumbs.db Desktop.ini -.DS_Store \ No newline at end of file +.DS_Store + +out.c \ No newline at end of file diff --git a/compiler/.idea/.idea.Compiler/.idea/.gitignore b/compiler/.idea/.idea.Compiler/.idea/.gitignore deleted file mode 100644 index cda1cf4..0000000 --- a/compiler/.idea/.idea.Compiler/.idea/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Rider ignored files -/modules.xml -/projectSettingsUpdater.xml -/contentModel.xml -/.idea.Compiler.iml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/compiler/.idea/.idea.Compiler/.idea/.name b/compiler/.idea/.idea.Compiler/.idea/.name deleted file mode 100644 index b92b7a3..0000000 --- a/compiler/.idea/.idea.Compiler/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Compiler \ No newline at end of file diff --git a/compiler/.idea/.idea.Compiler/.idea/encodings.xml b/compiler/.idea/.idea.Compiler/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/compiler/.idea/.idea.Compiler/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml b/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml deleted file mode 100644 index 2135b43..0000000 --- a/compiler/.idea/.idea.Compiler/.idea/indexLayout.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Runtime - - - - - \ No newline at end of file diff --git a/compiler/.idea/.idea.Compiler/.idea/vcs.xml b/compiler/.idea/.idea.Compiler/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/compiler/.idea/.idea.Compiler/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/compiler/Compiler/Compiler.csproj b/compiler/Compiler.csproj similarity index 100% rename from compiler/Compiler/Compiler.csproj rename to compiler/Compiler.csproj diff --git a/compiler/Compiler/.gitignore b/compiler/Compiler/.gitignore deleted file mode 100644 index eb306b3..0000000 --- a/compiler/Compiler/.gitignore +++ /dev/null @@ -1 +0,0 @@ -out.c \ No newline at end of file diff --git a/compiler/Compiler/Diagnostic.cs b/compiler/Diagnostic.cs similarity index 100% rename from compiler/Compiler/Diagnostic.cs rename to compiler/Diagnostic.cs diff --git a/compiler/Compiler/Generator.cs b/compiler/Generator.cs similarity index 100% rename from compiler/Compiler/Generator.cs rename to compiler/Generator.cs diff --git a/compiler/NubLang.CLI/NubLang.CLI.csproj b/compiler/NubLang.CLI/NubLang.CLI.csproj deleted file mode 100644 index 0550d0f..0000000 --- a/compiler/NubLang.CLI/NubLang.CLI.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - nubc - Exe - net9.0 - enable - enable - true - - - - - - - diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs deleted file mode 100644 index 4806c9d..0000000 --- a/compiler/NubLang.CLI/Program.cs +++ /dev/null @@ -1,43 +0,0 @@ -using NubLang.Ast; -using NubLang.Diagnostics; -using NubLang.Syntax; - -var diagnostics = new List(); -var syntaxTrees = new List(); - -foreach (var file in args) -{ - var tokenizer = new Tokenizer(file, File.ReadAllText(file)); - tokenizer.Tokenize(); - diagnostics.AddRange(tokenizer.Diagnostics); - - var parser = new Parser(); - var syntaxTree = parser.Parse(tokenizer.Tokens); - diagnostics.AddRange(parser.Diagnostics); - - syntaxTrees.Add(syntaxTree); -} - -var modules = Module.Collect(syntaxTrees); -var compilationUnits = new List(); - -for (var i = 0; i < args.Length; i++) -{ - var typeChecker = new TypeChecker(syntaxTrees[i], modules); - var compilationUnit = typeChecker.Check(); - - compilationUnits.Add(compilationUnit); - diagnostics.AddRange(typeChecker.Diagnostics); -} - -foreach (var diagnostic in diagnostics) -{ - Console.Error.WriteLine(diagnostic.FormatANSI()); -} - -if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)) -{ - return 1; -} - -return 0; \ No newline at end of file diff --git a/compiler/NubLang/Ast/CompilationUnit.cs b/compiler/NubLang/Ast/CompilationUnit.cs deleted file mode 100644 index f2115c1..0000000 --- a/compiler/NubLang/Ast/CompilationUnit.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace NubLang.Ast; - -public sealed class CompilationUnit -{ - public CompilationUnit(List functions, List importedStructTypes, List importedFunctions) - { - Functions = functions; - ImportedStructTypes = importedStructTypes; - ImportedFunctions = importedFunctions; - } - - public List Functions { get; } - public List ImportedStructTypes { get; } - public List ImportedFunctions { get; } -} \ No newline at end of file diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs deleted file mode 100644 index aca79fd..0000000 --- a/compiler/NubLang/Ast/Node.cs +++ /dev/null @@ -1,590 +0,0 @@ -using NubLang.Syntax; - -namespace NubLang.Ast; - -public abstract class Node(List tokens) -{ - public List Tokens { get; } = tokens; - - public abstract IEnumerable Children(); - - public IEnumerable Descendants() - { - foreach (var child in Children()) - { - foreach (var descendant in child.DescendantsAndSelf()) - { - yield return descendant; - } - } - } - - public IEnumerable DescendantsAndSelf() - { - yield return this; - foreach (var descendant in Descendants()) - { - yield return descendant; - } - } -} - -#region Definitions - -public abstract class DefinitionNode(List tokens, string module, string name) : Node(tokens) -{ - public string Module { get; } = module; - public string Name { get; } = name; -} - -public class FuncParameterNode(List tokens, string name, NubType type) : Node(tokens) -{ - public string Name { get; } = name; - public NubType Type { get; } = type; - - public override IEnumerable Children() - { - return []; - } -} - -public class FuncPrototypeNode(List tokens, string module, string name, string? externSymbol, List parameters, NubType returnType) : Node(tokens) -{ - public string Module { get; } = module; - public string Name { get; } = name; - public string? ExternSymbol { get; } = externSymbol; - public List Parameters { get; } = parameters; - public NubType ReturnType { get; } = returnType; - - public override IEnumerable Children() - { - return Parameters; - } -} - -public class FuncNode(List tokens, FuncPrototypeNode prototype, BlockNode? body) : DefinitionNode(tokens, prototype.Module, prototype.Name) -{ - public FuncPrototypeNode Prototype { get; } = prototype; - public BlockNode? Body { get; } = body; - - public override IEnumerable Children() - { - yield return Prototype; - if (Body != null) - { - yield return Body; - } - } -} - -#endregion - -#region Statements - -public abstract class StatementNode(List tokens) : Node(tokens); - -public abstract class TerminalStatementNode(List tokens) : StatementNode(tokens); - -public class BlockNode(List tokens, List statements) : StatementNode(tokens) -{ - public List Statements { get; } = statements; - - public override IEnumerable Children() - { - return Statements; - } -} - -public class StatementFuncCallNode(List tokens, FuncCallNode funcCall) : StatementNode(tokens) -{ - public FuncCallNode FuncCall { get; } = funcCall; - - public override IEnumerable Children() - { - yield return FuncCall; - } -} - -public class ReturnNode(List tokens, ExpressionNode? value) : TerminalStatementNode(tokens) -{ - public ExpressionNode? Value { get; } = value; - - public override IEnumerable Children() - { - if (Value != null) yield return Value; - } -} - -public class AssignmentNode(List tokens, LValueExpressionNode target, ExpressionNode value) : StatementNode(tokens) -{ - public LValueExpressionNode Target { get; } = target; - public ExpressionNode Value { get; } = value; - - public override IEnumerable Children() - { - yield return Target; - yield return Value; - } -} - -public class IfNode(List tokens, ExpressionNode condition, BlockNode body, Variant? @else) : StatementNode(tokens) -{ - public ExpressionNode Condition { get; } = condition; - public BlockNode Body { get; } = body; - public Variant? Else { get; } = @else; - - public override IEnumerable Children() - { - yield return Condition; - yield return Body; - if (Else.HasValue) - { - yield return Else.Value.Match(x => x, x => x); - } - } -} - -public class VariableDeclarationNode(List tokens, string name, ExpressionNode? assignment, NubType type) : StatementNode(tokens) -{ - public string Name { get; } = name; - public ExpressionNode? Assignment { get; } = assignment; - public NubType Type { get; } = type; - - public override IEnumerable Children() - { - if (Assignment != null) yield return Assignment; - } -} - -public class ContinueNode(List tokens) : TerminalStatementNode(tokens) -{ - public override IEnumerable Children() - { - return []; - } -} - -public class BreakNode(List tokens) : TerminalStatementNode(tokens) -{ - public override IEnumerable Children() - { - return []; - } -} - -public class WhileNode(List tokens, ExpressionNode condition, BlockNode body) : StatementNode(tokens) -{ - public ExpressionNode Condition { get; } = condition; - public BlockNode Body { get; } = body; - - public override IEnumerable Children() - { - yield return Condition; - yield return Body; - } -} - -public class ForSliceNode(List tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens) -{ - public string ElementName { get; } = elementName; - public string? IndexName { get; } = indexName; - public ExpressionNode Target { get; } = target; - public BlockNode Body { get; } = body; - - public override IEnumerable Children() - { - yield return Target; - yield return Body; - } -} - -public class ForConstArrayNode(List tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens) -{ - public string ElementName { get; } = elementName; - public string? IndexName { get; } = indexName; - public ExpressionNode Target { get; } = target; - public BlockNode Body { get; } = body; - - public override IEnumerable Children() - { - yield return Target; - yield return Body; - } -} - -public class DeferNode(List tokens, StatementNode statement) : StatementNode(tokens) -{ - public StatementNode Statement { get; } = statement; - - public override IEnumerable Children() - { - yield return Statement; - } -} - -#endregion - -#region Expressions - -public enum UnaryOperator -{ - Negate, - Invert -} - -public enum BinaryOperator -{ - Equal, - NotEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual, - LogicalAnd, - LogicalOr, - Plus, - Minus, - Multiply, - Divide, - Modulo, - LeftShift, - RightShift, - BitwiseAnd, - BitwiseXor, - BitwiseOr -} - -public abstract class ExpressionNode(List tokens, NubType type) : Node(tokens) -{ - public NubType Type { get; } = type; -} - -public abstract class LValueExpressionNode(List tokens, NubType type) : ExpressionNode(tokens, type); - -public abstract class RValueExpressionNode(List tokens, NubType type) : ExpressionNode(tokens, type); - -public abstract class IntermediateExpression(List tokens) : ExpressionNode(tokens, new NubVoidType()); - -public class StringLiteralNode(List tokens, string value) : RValueExpressionNode(tokens, new NubStringType()) -{ - public string Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class CStringLiteralNode(List tokens, string value) : RValueExpressionNode(tokens, new NubPointerType(new NubIntType(true, 8))) -{ - public string Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class I8LiteralNode(List tokens, sbyte value) : RValueExpressionNode(tokens, new NubIntType(true, 8)) -{ - public sbyte Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class I16LiteralNode(List tokens, short value) : RValueExpressionNode(tokens, new NubIntType(true, 16)) -{ - public short Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class I32LiteralNode(List tokens, int value) : RValueExpressionNode(tokens, new NubIntType(true, 32)) -{ - public int Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class I64LiteralNode(List tokens, long value) : RValueExpressionNode(tokens, new NubIntType(true, 64)) -{ - public long Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class U8LiteralNode(List tokens, byte value) : RValueExpressionNode(tokens, new NubIntType(false, 8)) -{ - public byte Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class U16LiteralNode(List tokens, ushort value) : RValueExpressionNode(tokens, new NubIntType(false, 16)) -{ - public ushort Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class U32LiteralNode(List tokens, uint value) : RValueExpressionNode(tokens, new NubIntType(false, 32)) -{ - public uint Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class U64LiteralNode(List tokens, ulong value) : RValueExpressionNode(tokens, new NubIntType(false, 64)) -{ - public ulong Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class Float32LiteralNode(List tokens, float value) : RValueExpressionNode(tokens, new NubFloatType(32)) -{ - public float Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class Float64LiteralNode(List tokens, double value) : RValueExpressionNode(tokens, new NubFloatType(64)) -{ - public double Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class BoolLiteralNode(List tokens, NubType type, bool value) : RValueExpressionNode(tokens, type) -{ - public bool Value { get; } = value; - - public override IEnumerable Children() - { - return []; - } -} - -public class BinaryExpressionNode(List tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : RValueExpressionNode(tokens, type) -{ - public ExpressionNode Left { get; } = left; - public BinaryOperator Operator { get; } = @operator; - public ExpressionNode Right { get; } = right; - - public override IEnumerable Children() - { - yield return Left; - yield return Right; - } -} - -public class UnaryExpressionNode(List tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : RValueExpressionNode(tokens, type) -{ - public UnaryOperator Operator { get; } = @operator; - public ExpressionNode Operand { get; } = operand; - - public override IEnumerable Children() - { - yield return Operand; - } -} - -public class FuncCallNode(List tokens, NubType type, ExpressionNode expression, List parameters) : RValueExpressionNode(tokens, type) -{ - public ExpressionNode Expression { get; } = expression; - public List Parameters { get; } = parameters; - - public override IEnumerable Children() - { - yield return Expression; - foreach (var expressionNode in Parameters) - { - yield return expressionNode; - } - } -} - -public class VariableIdentifierNode(List tokens, NubType type, string name) : LValueExpressionNode(tokens, type) -{ - public string Name { get; } = name; - - public override IEnumerable Children() - { - return []; - } -} - -public class FuncIdentifierNode(List tokens, NubType type, string module, string name, string? externSymbol) : RValueExpressionNode(tokens, type) -{ - public string Module { get; } = module; - public string Name { get; } = name; - public string? ExternSymbol { get; } = externSymbol; - - public override IEnumerable Children() - { - return []; - } -} - -public class ArrayInitializerNode(List tokens, NubType type, List values) : RValueExpressionNode(tokens, type) -{ - public List Values { get; } = values; - - public override IEnumerable Children() - { - return Values; - } -} - -public class ConstArrayInitializerNode(List tokens, NubType type, List values) : RValueExpressionNode(tokens, type) -{ - public List Values { get; } = values; - - public override IEnumerable Children() - { - return Values; - } -} - -public class ArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type) -{ - public ExpressionNode Target { get; } = target; - public ExpressionNode Index { get; } = index; - - public override IEnumerable Children() - { - yield return Target; - yield return Index; - } -} - -public class ConstArrayIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type) -{ - public ExpressionNode Target { get; } = target; - public ExpressionNode Index { get; } = index; - - public override IEnumerable Children() - { - yield return Target; - yield return Index; - } -} - -public class SliceIndexAccessNode(List tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type) -{ - public ExpressionNode Target { get; } = target; - public ExpressionNode Index { get; } = index; - - public override IEnumerable Children() - { - yield return Target; - yield return Index; - } -} - -public class AddressOfNode(List tokens, NubType type, LValueExpressionNode lValue) : RValueExpressionNode(tokens, type) -{ - public LValueExpressionNode LValue { get; } = lValue; - - public override IEnumerable Children() - { - yield return LValue; - } -} - -public class StructFieldAccessNode(List tokens, NubType type, ExpressionNode target, string field) : LValueExpressionNode(tokens, type) -{ - public ExpressionNode Target { get; } = target; - public string Field { get; } = field; - - public override IEnumerable Children() - { - yield return Target; - } -} - -public class StructInitializerNode(List tokens, NubType type, Dictionary initializers) : RValueExpressionNode(tokens, type) -{ - public Dictionary Initializers { get; } = initializers; - - public override IEnumerable Children() - { - foreach (var initializer in Initializers) - { - yield return initializer.Value; - } - } -} - -public class DereferenceNode(List tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type) -{ - public ExpressionNode Target { get; } = target; - - public override IEnumerable Children() - { - yield return Target; - } -} - -public class SizeNode(List tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64)) -{ - public NubType TargetType { get; } = TargetType; - - public override IEnumerable Children() - { - return []; - } -} - -public class CastNode(List tokens, NubType type, ExpressionNode value) : RValueExpressionNode(tokens, type) -{ - public ExpressionNode Value { get; } = value; - - public override IEnumerable Children() - { - yield return Value; - } -} - -public class EnumReferenceIntermediateNode(List tokens, string module, string name) : IntermediateExpression(tokens) -{ - public string Module { get; } = module; - public string Name { get; } = name; - - public override IEnumerable Children() - { - return []; - } -} - -#endregion \ No newline at end of file diff --git a/compiler/NubLang/Ast/NubType.cs b/compiler/NubLang/Ast/NubType.cs deleted file mode 100644 index ef0981b..0000000 --- a/compiler/NubLang/Ast/NubType.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System.Security.Cryptography; -using System.Text; - -namespace NubLang.Ast; - -public abstract class NubType : IEquatable -{ - public override bool Equals(object? obj) => obj is NubType other && Equals(other); - public abstract bool Equals(NubType? other); - - public abstract override int GetHashCode(); - public abstract override string ToString(); - - public static bool operator ==(NubType? left, NubType? right) => Equals(left, right); - public static bool operator !=(NubType? left, NubType? right) => !Equals(left, right); -} - -public class NubVoidType : NubType -{ - public override string ToString() => "void"; - public override bool Equals(NubType? other) => other is NubVoidType; - public override int GetHashCode() => HashCode.Combine(typeof(NubVoidType)); -} - -public sealed class NubIntType(bool signed, int width) : NubType -{ - public bool Signed { get; } = signed; - public int Width { get; } = width; - - public override string ToString() => $"{(Signed ? "i" : "u")}{Width}"; - public override bool Equals(NubType? other) => other is NubIntType @int && @int.Width == Width && @int.Signed == Signed; - public override int GetHashCode() => HashCode.Combine(typeof(NubIntType), Signed, Width); -} - -public sealed class NubFloatType(int width) : NubType -{ - public int Width { get; } = width; - - public override string ToString() => $"f{Width}"; - public override bool Equals(NubType? other) => other is NubFloatType @float && @float.Width == Width; - public override int GetHashCode() => HashCode.Combine(typeof(NubFloatType), Width); -} - -public class NubBoolType : NubType -{ - public override string ToString() => "bool"; - public override bool Equals(NubType? other) => other is NubBoolType; - public override int GetHashCode() => HashCode.Combine(typeof(NubBoolType)); -} - -public sealed class NubPointerType(NubType baseType) : NubType -{ - public NubType BaseType { get; } = baseType; - - public override string ToString() => "^" + BaseType; - public override bool Equals(NubType? other) => other is NubPointerType pointer && BaseType.Equals(pointer.BaseType); - public override int GetHashCode() => HashCode.Combine(typeof(NubPointerType), BaseType); -} - -public class NubFuncType(List parameters, NubType returnType) : NubType -{ - public List Parameters { get; } = parameters; - public NubType ReturnType { get; } = returnType; - - 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); - - public override int GetHashCode() - { - var hash = new HashCode(); - hash.Add(typeof(NubFuncType)); - hash.Add(ReturnType); - foreach (var param in Parameters) - { - hash.Add(param); - } - - return hash.ToHashCode(); - } -} - -public class NubStructType(string module, string name, List fields) : NubType -{ - public string Module { get; } = module; - public string Name { get; } = name; - public List Fields { get; set; } = fields; - - 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); -} - -public class NubStructFieldType(string name, NubType type, bool hasDefaultValue) -{ - public string Name { get; } = name; - public NubType Type { get; } = type; - public bool HasDefaultValue { get; } = hasDefaultValue; -} - -public class NubSliceType(NubType elementType) : NubType -{ - public NubType ElementType { get; } = elementType; - - public override string ToString() => "[]" + ElementType; - public override bool Equals(NubType? other) => other is NubSliceType slice && ElementType.Equals(slice.ElementType); - public override int GetHashCode() => HashCode.Combine(typeof(NubSliceType), ElementType); -} - -public class NubConstArrayType(NubType elementType, long size) : NubType -{ - public NubType ElementType { get; } = elementType; - public long Size { get; } = size; - - public override string ToString() => $"[{Size}]{ElementType}"; - public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size; - public override int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size); -} - -public class NubArrayType(NubType elementType) : NubType -{ - public NubType ElementType { get; } = elementType; - - public override string ToString() => $"[?]{ElementType}"; - public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType); - public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType); -} - -public class NubStringType : NubType -{ - public override string ToString() => "string"; - public override bool Equals(NubType? other) => other is NubStringType; - public override int GetHashCode() => HashCode.Combine(typeof(NubStringType)); -} - -public static class NameMangler -{ - public static string Mangle(params IEnumerable types) - { - var readable = string.Join(":", types.Select(EncodeType)); - return ComputeShortHash(readable); - } - - private static string EncodeType(NubType node) => node switch - { - NubVoidType => "V", - NubBoolType => "B", - NubIntType i => (i.Signed ? "I" : "U") + i.Width, - NubFloatType f => "F" + f.Width, - NubStringType => "S", - NubArrayType a => $"A({EncodeType(a.ElementType)})", - NubConstArrayType ca => $"CA({EncodeType(ca.ElementType)})", - NubSliceType a => $"SL{EncodeType(a.ElementType)}()", - NubPointerType p => $"P({EncodeType(p.BaseType)})", - NubFuncType fn => $"FN({string.Join(":", fn.Parameters.Select(EncodeType))}:{EncodeType(fn.ReturnType)})", - NubStructType st => $"ST({st.Module}:{st.Name})", - _ => throw new NotSupportedException($"Cannot encode type: {node}") - }; - - private static string ComputeShortHash(string input) - { - var bytes = Encoding.UTF8.GetBytes(input); - var hash = SHA256.HashData(bytes); - return Convert.ToHexString(hash[..8]).ToLower(); - } -} \ No newline at end of file diff --git a/compiler/NubLang/Ast/TypeChecker.cs b/compiler/NubLang/Ast/TypeChecker.cs deleted file mode 100644 index 4224115..0000000 --- a/compiler/NubLang/Ast/TypeChecker.cs +++ /dev/null @@ -1,1167 +0,0 @@ -using System.Diagnostics; -using NubLang.Diagnostics; -using NubLang.Syntax; - -namespace NubLang.Ast; - -public sealed class TypeChecker -{ - private readonly SyntaxTree _syntaxTree; - private readonly Dictionary _importedModules; - - private readonly Stack _scopes = []; - private readonly Dictionary<(string Module, string Name), NubType> _typeCache = new(); - private readonly HashSet<(string Module, string Name)> _resolvingTypes = []; - - private Scope Scope => _scopes.Peek(); - - public List Diagnostics { get; } = []; - - public TypeChecker(SyntaxTree syntaxTree, Dictionary modules) - { - _syntaxTree = syntaxTree; - _importedModules = modules - .Where(x => syntaxTree.Imports.Contains(x.Key) || _syntaxTree.ModuleName == x.Key) - .ToDictionary(); - } - - public CompilationUnit Check() - { - _scopes.Clear(); - _typeCache.Clear(); - _resolvingTypes.Clear(); - - var functions = new List(); - - using (BeginRootScope(_syntaxTree.ModuleName)) - { - foreach (var funcSyntax in _syntaxTree.Definitions.OfType()) - { - try - { - functions.Add(CheckFuncDefinition(funcSyntax)); - } - catch (TypeCheckerException e) - { - Diagnostics.Add(e.Diagnostic); - } - } - } - - var importedStructTypes = new List(); - var importedFunctions = new List(); - - foreach (var (name, module) in _importedModules) - { - using (BeginRootScope(name)) - { - foreach (var structSyntax in module.Structs(true)) - { - try - { - var fields = structSyntax.Fields - .Select(f => new NubStructFieldType(f.Name, ResolveType(f.Type), f.Value != null)) - .ToList(); - - importedStructTypes.Add(new NubStructType(name, structSyntax.Name, fields)); - } - catch (TypeCheckerException e) - { - Diagnostics.Add(e.Diagnostic); - } - } - - foreach (var funcSyntax in module.Functions(true)) - { - try - { - importedFunctions.Add(CheckFuncPrototype(funcSyntax.Prototype)); - } - catch (TypeCheckerException e) - { - Diagnostics.Add(e.Diagnostic); - } - } - } - } - - return new CompilationUnit(functions, importedStructTypes, importedFunctions); - } - - private ScopeDisposer BeginScope() - { - _scopes.Push(Scope.SubScope()); - return new ScopeDisposer(this); - } - - private ScopeDisposer BeginRootScope(string moduleName) - { - _scopes.Push(new Scope(moduleName)); - return new ScopeDisposer(this); - } - - private sealed class ScopeDisposer(TypeChecker owner) : IDisposable - { - private bool _disposed; - - public void Dispose() - { - if (_disposed) return; - owner._scopes.Pop(); - _disposed = true; - } - } - - private FuncNode CheckFuncDefinition(FuncSyntax node) - { - using (BeginScope()) - { - var prototype = CheckFuncPrototype(node.Prototype); - - Scope.SetReturnType(prototype.ReturnType); - foreach (var parameter in prototype.Parameters) - { - Scope.DeclareVariable(new Variable(parameter.Name, parameter.Type)); - } - - var body = node.Body == null ? null : CheckBlock(node.Body); - return new FuncNode(node.Tokens, prototype, body); - } - } - - private AssignmentNode CheckAssignment(AssignmentSyntax statement) - { - var target = CheckExpression(statement.Target); - if (target is not LValueExpressionNode lValue) - { - throw new TypeCheckerException(Diagnostic.Error("Cannot assign to an rvalue").At(statement).Build()); - } - - var value = CheckExpression(statement.Value, lValue.Type); - - if (value.Type != lValue.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Cannot assign {value.Type} to {lValue.Type}") - .At(statement.Value) - .Build()); - } - - return new AssignmentNode(statement.Tokens, lValue, value); - } - - private IfNode CheckIf(IfSyntax statement) - { - var condition = CheckExpression(statement.Condition); - var body = CheckBlock(statement.Body); - Variant? elseStatement = null; - if (statement.Else.HasValue) - { - elseStatement = statement.Else.Value.Match>(elif => CheckIf(elif), el => CheckBlock(el)); - } - - return new IfNode(statement.Tokens, condition, body, elseStatement); - } - - private ReturnNode CheckReturn(ReturnSyntax statement) - { - ExpressionNode? value = null; - - if (statement.Value != null) - { - var expectedReturnType = Scope.GetReturnType(); - value = CheckExpression(statement.Value, expectedReturnType); - } - - return new ReturnNode(statement.Tokens, value); - } - - private StatementNode CheckStatementExpression(StatementExpressionSyntax statement) - { - var expression = CheckExpression(statement.Expression); - - return expression switch - { - FuncCallNode funcCall => new StatementFuncCallNode(statement.Tokens, funcCall), - _ => throw new TypeCheckerException(Diagnostic.Error("Expressions statements can only be function calls").At(statement).Build()) - }; - } - - private VariableDeclarationNode CheckVariableDeclaration(VariableDeclarationSyntax statement) - { - NubType? type = null; - ExpressionNode? assignmentNode = null; - - if (statement.ExplicitType != null) - { - type = ResolveType(statement.ExplicitType); - } - - if (statement.Assignment != null) - { - assignmentNode = CheckExpression(statement.Assignment, type); - - if (type == null) - { - type = assignmentNode.Type; - } - else if (assignmentNode.Type != type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Cannot assign {assignmentNode.Type} to variable of type {type}") - .At(statement.Assignment) - .Build()); - } - } - - if (type == null) - { - throw new TypeCheckerException(Diagnostic - .Error($"Cannot infer type of variable {statement.Name}") - .At(statement) - .Build()); - } - - Scope.DeclareVariable(new Variable(statement.Name, type)); - - return new VariableDeclarationNode(statement.Tokens, statement.Name, assignmentNode, type); - } - - private WhileNode CheckWhile(WhileSyntax statement) - { - var condition = CheckExpression(statement.Condition); - var body = CheckBlock(statement.Body); - return new WhileNode(statement.Tokens, condition, body); - } - - private StatementNode CheckFor(ForSyntax forSyntax) - { - var target = CheckExpression(forSyntax.Target); - - - switch (target.Type) - { - case NubSliceType sliceType: - { - using (BeginScope()) - { - Scope.DeclareVariable(new Variable(forSyntax.ElementName, sliceType.ElementType)); - if (forSyntax.IndexName != null) - { - Scope.DeclareVariable(new Variable(forSyntax.IndexName, new NubIntType(false, 64))); - } - - var body = CheckBlock(forSyntax.Body); - return new ForSliceNode(forSyntax.Tokens, forSyntax.ElementName, forSyntax.IndexName, target, body); - } - } - case NubConstArrayType constArrayType: - { - using (BeginScope()) - { - Scope.DeclareVariable(new Variable(forSyntax.ElementName, constArrayType.ElementType)); - if (forSyntax.IndexName != null) - { - Scope.DeclareVariable(new Variable(forSyntax.IndexName, new NubIntType(false, 64))); - } - - var body = CheckBlock(forSyntax.Body); - return new ForConstArrayNode(forSyntax.Tokens, forSyntax.ElementName, forSyntax.IndexName, target, body); - } - } - default: - { - throw new TypeCheckerException(Diagnostic - .Error($"Cannot iterate over type {target.Type} which does not have size information") - .At(forSyntax.Target) - .Build()); - } - } - } - - private FuncPrototypeNode CheckFuncPrototype(FuncPrototypeSyntax statement) - { - var parameters = new List(); - foreach (var parameter in statement.Parameters) - { - parameters.Add(new FuncParameterNode(parameter.Tokens, parameter.Name, ResolveType(parameter.Type))); - } - - return new FuncPrototypeNode(statement.Tokens, Scope.Module, statement.Name, statement.ExternSymbol, parameters, ResolveType(statement.ReturnType)); - } - - private ExpressionNode CheckExpression(ExpressionSyntax node, NubType? expectedType = null) - { - var result = node switch - { - AddressOfSyntax expression => CheckAddressOf(expression, expectedType), - ArrayIndexAccessSyntax expression => CheckArrayIndexAccess(expression, expectedType), - ArrayInitializerSyntax expression => CheckArrayInitializer(expression, expectedType), - BinaryExpressionSyntax expression => CheckBinaryExpression(expression, expectedType), - UnaryExpressionSyntax expression => CheckUnaryExpression(expression, expectedType), - DereferenceSyntax expression => CheckDereference(expression, expectedType), - FuncCallSyntax expression => CheckFuncCall(expression, expectedType), - LocalIdentifierSyntax expression => CheckLocalIdentifier(expression, expectedType), - ModuleIdentifierSyntax expression => CheckModuleIdentifier(expression, expectedType), - BoolLiteralSyntax expression => CheckBoolLiteral(expression, expectedType), - StringLiteralSyntax expression => CheckStringLiteral(expression, expectedType), - IntLiteralSyntax expression => CheckIntLiteral(expression, expectedType), - FloatLiteralSyntax expression => CheckFloatLiteral(expression, expectedType), - MemberAccessSyntax expression => CheckMemberAccess(expression, expectedType), - StructInitializerSyntax expression => CheckStructInitializer(expression, expectedType), - SizeSyntax expression => new SizeNode(node.Tokens, ResolveType(expression.Type)), - CastSyntax expression => CheckCast(expression, expectedType), - _ => throw new ArgumentOutOfRangeException(nameof(node)) - }; - - if (expectedType != null) - { - if (result.Type == expectedType) - { - return result; - } - - if (IsCastAllowed(result.Type, expectedType)) - { - return new CastNode(result.Tokens, expectedType, result); - } - } - - return result; - } - - private ExpressionNode CheckCast(CastSyntax expression, NubType? expectedType) - { - if (expectedType == null) - { - throw new TypeCheckerException(Diagnostic - .Error("Unable to infer target type of cast") - .At(expression) - .WithHelp("Specify target type where value is used") - .Build()); - } - - var value = CheckExpression(expression.Value, expectedType); - - if (value.Type == expectedType) - { - Diagnostics.Add(Diagnostic - .Warning("Target type of cast is same as the value. Cast is unnecessary") - .At(expression) - .Build()); - - return value; - } - - if (!IsCastAllowed(value.Type, expectedType, false)) - { - throw new TypeCheckerException(Diagnostic - .Error($"Cannot cast from {value.Type} to {expectedType}") - .Build()); - } - - return new CastNode(expression.Tokens, expectedType, value); - } - - private static bool IsCastAllowed(NubType from, NubType to, bool strict = true) - { - // note(nub31): Implicit casts - switch (from) - { - case NubIntType fromInt when to is NubIntType toInt && fromInt.Width < toInt.Width: - case NubPointerType when to is NubPointerType { BaseType: NubVoidType }: - case NubConstArrayType constArrayType1 when to is NubArrayType arrayType && constArrayType1.ElementType == arrayType.ElementType: - case NubConstArrayType constArrayType3 when to is NubSliceType sliceType2 && constArrayType3.ElementType == sliceType2.ElementType: - { - return true; - } - } - - if (!strict) - { - // note(nub31): Explicit casts - switch (from) - { - case NubIntType when to is NubIntType: - case NubIntType when to is NubFloatType: - case NubFloatType when to is NubIntType: - case NubFloatType when to is NubFloatType: - case NubPointerType when to is NubPointerType: - case NubPointerType when to is NubIntType: - case NubIntType when to is NubPointerType: - { - return true; - } - } - } - - - return false; - } - - private AddressOfNode CheckAddressOf(AddressOfSyntax expression, NubType? expectedType) - { - var target = CheckExpression(expression.Target, (expectedType as NubPointerType)?.BaseType); - if (target is not LValueExpressionNode lvalue) - { - throw new TypeCheckerException(Diagnostic.Error("Cannot take address of an rvalue").At(expression).Build()); - } - - var type = new NubPointerType(target.Type); - return new AddressOfNode(expression.Tokens, type, lvalue); - } - - private ExpressionNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression, NubType? _) - { - var index = CheckExpression(expression.Index); - if (index.Type is not NubIntType) - { - throw new TypeCheckerException(Diagnostic - .Error("Array indexer must be of type int") - .At(expression.Index) - .Build()); - } - - var target = CheckExpression(expression.Target); - - return target.Type switch - { - NubArrayType arrayType => new ArrayIndexAccessNode(expression.Tokens, arrayType.ElementType, target, index), - NubConstArrayType constArrayType => new ConstArrayIndexAccessNode(expression.Tokens, constArrayType.ElementType, target, index), - NubSliceType sliceType => new SliceIndexAccessNode(expression.Tokens, sliceType.ElementType, target, index), - _ => throw new TypeCheckerException(Diagnostic.Error($"Cannot use array indexer on type {target.Type}").At(expression).Build()) - }; - } - - private ExpressionNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType) - { - var elementType = expectedType switch - { - NubArrayType arrayType => arrayType.ElementType, - NubConstArrayType constArrayType => constArrayType.ElementType, - NubSliceType sliceType => sliceType.ElementType, - _ => null - }; - - if (elementType == null) - { - var firstValue = expression.Values.FirstOrDefault(); - if (firstValue != null) - { - elementType = CheckExpression(firstValue).Type; - } - } - - if (elementType == null) - { - throw new TypeCheckerException(Diagnostic - .Error("Unable to infer type of array initializer") - .At(expression) - .WithHelp("Provide a type for a variable assignment") - .Build()); - } - - var values = new List(); - foreach (var valueExpression in expression.Values) - { - var value = CheckExpression(valueExpression, elementType); - if (value.Type != elementType) - { - throw new TypeCheckerException(Diagnostic - .Error("Value in array initializer is not the same as the array type") - .At(valueExpression) - .Build()); - } - - 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, expression.Values.Count), values) - }; - } - - private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType) - { - var op = expression.Operator switch - { - BinaryOperatorSyntax.Equal => BinaryOperator.Equal, - BinaryOperatorSyntax.NotEqual => BinaryOperator.NotEqual, - BinaryOperatorSyntax.GreaterThan => BinaryOperator.GreaterThan, - BinaryOperatorSyntax.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual, - BinaryOperatorSyntax.LessThan => BinaryOperator.LessThan, - BinaryOperatorSyntax.LessThanOrEqual => BinaryOperator.LessThanOrEqual, - BinaryOperatorSyntax.LogicalAnd => BinaryOperator.LogicalAnd, - BinaryOperatorSyntax.LogicalOr => BinaryOperator.LogicalOr, - BinaryOperatorSyntax.Plus => BinaryOperator.Plus, - BinaryOperatorSyntax.Minus => BinaryOperator.Minus, - BinaryOperatorSyntax.Multiply => BinaryOperator.Multiply, - BinaryOperatorSyntax.Divide => BinaryOperator.Divide, - BinaryOperatorSyntax.Modulo => BinaryOperator.Modulo, - BinaryOperatorSyntax.LeftShift => BinaryOperator.LeftShift, - BinaryOperatorSyntax.RightShift => BinaryOperator.RightShift, - BinaryOperatorSyntax.BitwiseAnd => BinaryOperator.BitwiseAnd, - BinaryOperatorSyntax.BitwiseXor => BinaryOperator.BitwiseXor, - BinaryOperatorSyntax.BitwiseOr => BinaryOperator.BitwiseOr, - _ => throw new ArgumentOutOfRangeException() - }; - - switch (expression.Operator) - { - case BinaryOperatorSyntax.Equal: - case BinaryOperatorSyntax.NotEqual: - { - var left = CheckExpression(expression.Left); - if (left.Type is not NubIntType and not NubFloatType and not NubBoolType) - { - throw new TypeCheckerException(Diagnostic - .Error("Equal and not equal operators must must be used with int, float or bool types") - .At(expression.Left) - .Build()); - } - - var right = CheckExpression(expression.Right, left.Type); - if (right.Type != left.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}") - .At(expression.Right) - .Build()); - } - - return new BinaryExpressionNode(expression.Tokens, new NubBoolType(), left, op, right); - } - case BinaryOperatorSyntax.GreaterThan: - case BinaryOperatorSyntax.GreaterThanOrEqual: - case BinaryOperatorSyntax.LessThan: - case BinaryOperatorSyntax.LessThanOrEqual: - { - var left = CheckExpression(expression.Left); - if (left.Type is not NubIntType and not NubFloatType) - { - throw new TypeCheckerException(Diagnostic - .Error("Greater than and less than operators must must be used with int or float types") - .At(expression.Left) - .Build()); - } - - var right = CheckExpression(expression.Right, left.Type); - if (right.Type != left.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}") - .At(expression.Right) - .Build()); - } - - return new BinaryExpressionNode(expression.Tokens, new NubBoolType(), left, op, right); - } - case BinaryOperatorSyntax.LogicalAnd: - case BinaryOperatorSyntax.LogicalOr: - { - var left = CheckExpression(expression.Left); - if (left.Type is not NubBoolType) - { - throw new TypeCheckerException(Diagnostic - .Error("Logical and/or must must be used with bool types") - .At(expression.Left) - .Build()); - } - - var right = CheckExpression(expression.Right, left.Type); - if (right.Type != left.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}") - .At(expression.Right) - .Build()); - } - - return new BinaryExpressionNode(expression.Tokens, new NubBoolType(), left, op, right); - } - case BinaryOperatorSyntax.Plus: - { - var left = CheckExpression(expression.Left, expectedType); - if (left.Type is not NubIntType and not NubFloatType) - { - throw new TypeCheckerException(Diagnostic - .Error("The plus operator must only be used with int and float types") - .At(expression.Left) - .Build()); - } - - var right = CheckExpression(expression.Right, left.Type); - if (right.Type != left.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}") - .At(expression.Right) - .Build()); - } - - return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right); - } - case BinaryOperatorSyntax.Minus: - case BinaryOperatorSyntax.Multiply: - case BinaryOperatorSyntax.Divide: - case BinaryOperatorSyntax.Modulo: - { - var left = CheckExpression(expression.Left, expectedType); - if (left.Type is not NubIntType and not NubFloatType) - { - throw new TypeCheckerException(Diagnostic - .Error("Math operators must be used with int or float types") - .At(expression.Left) - .Build()); - } - - var right = CheckExpression(expression.Right, left.Type); - if (right.Type != left.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}") - .At(expression.Right) - .Build()); - } - - return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right); - } - case BinaryOperatorSyntax.LeftShift: - case BinaryOperatorSyntax.RightShift: - case BinaryOperatorSyntax.BitwiseAnd: - case BinaryOperatorSyntax.BitwiseXor: - case BinaryOperatorSyntax.BitwiseOr: - { - var left = CheckExpression(expression.Left, expectedType); - if (left.Type is not NubIntType) - { - throw new TypeCheckerException(Diagnostic - .Error("Bitwise operators must be used with int types") - .At(expression.Left) - .Build()); - } - - var right = CheckExpression(expression.Right, left.Type); - if (right.Type != left.Type) - { - throw new TypeCheckerException(Diagnostic - .Error($"Expected type {left.Type} from left side of binary expression, but got {right.Type}") - .At(expression.Right) - .Build()); - } - - return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right); - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - - private UnaryExpressionNode CheckUnaryExpression(UnaryExpressionSyntax expression, NubType? expectedType) - { - switch (expression.Operator) - { - case UnaryOperatorSyntax.Negate: - { - var operand = CheckExpression(expression.Operand, expectedType); - if (operand.Type is not NubIntType { Signed: true } and not NubFloatType) - { - throw new TypeCheckerException(Diagnostic - .Error("Negation operator must be used with signed integer or float types") - .At(expression) - .Build()); - } - - return new UnaryExpressionNode(expression.Tokens, operand.Type, UnaryOperator.Negate, operand); - } - case UnaryOperatorSyntax.Invert: - { - var operand = CheckExpression(expression.Operand, expectedType); - if (operand.Type is not NubBoolType) - { - throw new TypeCheckerException(Diagnostic - .Error("Invert operator must be used with booleans") - .At(expression) - .Build()); - } - - return new UnaryExpressionNode(expression.Tokens, operand.Type, UnaryOperator.Invert, operand); - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - } - - private DereferenceNode CheckDereference(DereferenceSyntax expression, NubType? _) - { - var target = CheckExpression(expression.Target); - if (target.Type is not NubPointerType pointerType) - { - throw new TypeCheckerException(Diagnostic.Error($"Cannot dereference non-pointer type {target.Type}").At(expression).Build()); - } - - return new DereferenceNode(expression.Tokens, pointerType.BaseType, target); - } - - private FuncCallNode CheckFuncCall(FuncCallSyntax expression, NubType? _) - { - var accessor = CheckExpression(expression.Expression); - if (accessor.Type is not NubFuncType funcType) - { - throw new TypeCheckerException(Diagnostic.Error($"Cannot call non-function type {accessor.Type}").At(expression.Expression).Build()); - } - - if (expression.Parameters.Count != funcType.Parameters.Count) - { - throw new TypeCheckerException(Diagnostic - .Error($"Function {funcType} expects {funcType.Parameters.Count} parameters but got {expression.Parameters.Count}") - .At(expression.Parameters.LastOrDefault(expression)) - .Build()); - } - - var parameters = new List(); - for (var i = 0; i < expression.Parameters.Count; i++) - { - var parameter = expression.Parameters[i]; - var expectedParameterType = funcType.Parameters[i]; - - var parameterExpression = CheckExpression(parameter, expectedParameterType); - if (parameterExpression.Type != expectedParameterType) - { - throw new TypeCheckerException(Diagnostic - .Error($"Parameter {i + 1} does not match the type {expectedParameterType} for function {funcType}") - .At(parameter) - .Build()); - } - - parameters.Add(parameterExpression); - } - - return new FuncCallNode(expression.Tokens, funcType.ReturnType, accessor, parameters); - } - - private ExpressionNode? CheckIdentifier(ExpressionSyntax expression, string moduleName, string name) - { - if (!_importedModules.TryGetValue(moduleName, out var module)) - { - throw new TypeCheckerException(Diagnostic - .Error($"Module {moduleName} not found") - .WithHelp($"import \"{moduleName}\"") - .At(expression) - .Build()); - } - - var function = module.Functions(IsCurretModule(moduleName)).FirstOrDefault(x => x.Name == name); - if (function != null) - { - using (BeginRootScope(moduleName)) - { - var parameters = function.Prototype.Parameters.Select(x => ResolveType(x.Type)).ToList(); - var type = new NubFuncType(parameters, ResolveType(function.Prototype.ReturnType)); - return new FuncIdentifierNode(expression.Tokens, type, moduleName, name, function.Prototype.ExternSymbol); - } - } - - var enumDef = module.Enums(IsCurretModule(moduleName)).FirstOrDefault(x => x.Name == name); - if (enumDef != null) - { - return new EnumReferenceIntermediateNode(expression.Tokens, moduleName, name); - } - - return null; - } - - private ExpressionNode CheckLocalIdentifier(LocalIdentifierSyntax expression, NubType? _) - { - // note(nub31): Local identifiers can be variables or a symbol in a module - var scopeIdent = Scope.LookupVariable(expression.Name); - if (scopeIdent != null) - { - return new VariableIdentifierNode(expression.Tokens, scopeIdent.Type, expression.Name); - } - - var ident = CheckIdentifier(expression, Scope.Module, expression.Name); - if (ident == null) - { - throw new TypeCheckerException(Diagnostic - .Error($"There is no identifier named {expression.Name}") - .At(expression) - .Build()); - } - - return ident; - } - - private ExpressionNode CheckModuleIdentifier(ModuleIdentifierSyntax expression, NubType? _) - { - // note(nub31): Unlike local identifiers, module identifiers does not look for local variables - var ident = CheckIdentifier(expression, expression.Module, expression.Name); - if (ident == null) - { - throw new TypeCheckerException(Diagnostic - .Error($"Module {expression.Module} does not export a member named {expression.Name}") - .At(expression) - .Build()); - } - - return ident; - } - - private ExpressionNode CheckStringLiteral(StringLiteralSyntax expression, NubType? expectedType) - { - if (expectedType is NubPointerType { BaseType: NubIntType { Signed: true, Width: 8 } }) - { - return new CStringLiteralNode(expression.Tokens, expression.Value); - } - - return new StringLiteralNode(expression.Tokens, expression.Value); - } - - private ExpressionNode CheckIntLiteral(IntLiteralSyntax expression, NubType? expectedType) - { - if (expectedType is NubIntType intType) - { - return intType.Width switch - { - 8 => intType.Signed ? new I8LiteralNode(expression.Tokens, Convert.ToSByte(expression.Value, expression.Base)) : new U8LiteralNode(expression.Tokens, Convert.ToByte(expression.Value, expression.Base)), - 16 => intType.Signed ? new I16LiteralNode(expression.Tokens, Convert.ToInt16(expression.Value, expression.Base)) : new U16LiteralNode(expression.Tokens, Convert.ToUInt16(expression.Value, expression.Base)), - 32 => intType.Signed ? new I32LiteralNode(expression.Tokens, Convert.ToInt32(expression.Value, expression.Base)) : new U32LiteralNode(expression.Tokens, Convert.ToUInt32(expression.Value, expression.Base)), - 64 => intType.Signed ? new I64LiteralNode(expression.Tokens, Convert.ToInt64(expression.Value, expression.Base)) : new U64LiteralNode(expression.Tokens, Convert.ToUInt64(expression.Value, expression.Base)), - _ => throw new ArgumentOutOfRangeException() - }; - } - - if (expectedType is NubFloatType floatType) - { - return floatType.Width switch - { - 32 => new Float32LiteralNode(expression.Tokens, Convert.ToSingle(expression.Value)), - 64 => new Float64LiteralNode(expression.Tokens, Convert.ToDouble(expression.Value)), - _ => throw new ArgumentOutOfRangeException() - }; - } - - return new I64LiteralNode(expression.Tokens, Convert.ToInt64(expression.Value, expression.Base)); - } - - private ExpressionNode CheckFloatLiteral(FloatLiteralSyntax expression, NubType? expectedType) - { - if (expectedType is NubFloatType floatType) - { - return floatType.Width switch - { - 32 => new Float32LiteralNode(expression.Tokens, Convert.ToSingle(expression.Value)), - 64 => new Float64LiteralNode(expression.Tokens, Convert.ToDouble(expression.Value)), - _ => throw new ArgumentOutOfRangeException() - }; - } - - return new Float64LiteralNode(expression.Tokens, Convert.ToDouble(expression.Value)); - } - - private BoolLiteralNode CheckBoolLiteral(BoolLiteralSyntax expression, NubType? _) - { - return new BoolLiteralNode(expression.Tokens, new NubBoolType(), expression.Value); - } - - private ExpressionNode CheckMemberAccess(MemberAccessSyntax expression, NubType? _) - { - var target = CheckExpression(expression.Target); - - if (target is EnumReferenceIntermediateNode enumReferenceIntermediate) - { - var enumDef = _importedModules[enumReferenceIntermediate.Module] - .Enums(IsCurretModule(enumReferenceIntermediate.Module)) - .First(x => x.Name == enumReferenceIntermediate.Name); - - var field = enumDef.Fields.FirstOrDefault(x => x.Name == expression.Member); - if (field == null) - { - throw new TypeCheckerException(Diagnostic - .Error($"Enum {Scope.Module}::{enumReferenceIntermediate.Name} does not have a field named {expression.Member}") - .At(enumDef) - .Build()); - } - - var enumType = enumDef.Type != null ? ResolveType(enumDef.Type) : new NubIntType(false, 64); - if (enumType is not NubIntType enumIntType) - { - throw new TypeCheckerException(Diagnostic.Error("Enum type must be an int type").At(enumDef.Type).Build()); - } - - return enumIntType.Width switch - { - 8 => enumIntType.Signed ? new I8LiteralNode(expression.Tokens, (sbyte)field.Value) : new U8LiteralNode(expression.Tokens, (byte)field.Value), - 16 => enumIntType.Signed ? new I16LiteralNode(expression.Tokens, (short)field.Value) : new U16LiteralNode(expression.Tokens, (ushort)field.Value), - 32 => enumIntType.Signed ? new I32LiteralNode(expression.Tokens, (int)field.Value) : new U32LiteralNode(expression.Tokens, (uint)field.Value), - 64 => enumIntType.Signed ? new I64LiteralNode(expression.Tokens, field.Value) : new U64LiteralNode(expression.Tokens, (ulong)field.Value), - _ => throw new ArgumentOutOfRangeException() - }; - } - - if (target.Type is NubStructType structType) - { - var field = structType.Fields.FirstOrDefault(x => x.Name == expression.Member); - if (field == null) - { - throw new TypeCheckerException(Diagnostic - .Error($"Struct {target.Type} does not have a field with the name {expression.Member}") - .At(expression) - .Build()); - } - - return new StructFieldAccessNode(expression.Tokens, field.Type, target, expression.Member); - } - - throw new TypeCheckerException(Diagnostic - .Error($"Cannot access struct member {expression.Member} on type {target.Type}") - .At(expression) - .Build()); - } - - private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression, NubType? expectedType) - { - NubStructType? structType = null; - - if (expression.StructType != null) - { - var checkedType = ResolveType(expression.StructType); - if (checkedType is not NubStructType checkedStructType) - { - throw new UnreachableException("Parser fucked up"); - } - - structType = checkedStructType; - } - else if (expectedType is NubStructType expectedStructType) - { - structType = expectedStructType; - } - - if (structType == null) - { - throw new TypeCheckerException(Diagnostic - .Error("Cannot get implicit type of struct") - .WithHelp("Specify struct type with struct {type_name} syntax") - .At(expression) - .Build()); - } - - var initializers = new Dictionary(); - - foreach (var initializer in expression.Initializers) - { - var typeField = structType.Fields.FirstOrDefault(x => x.Name == initializer.Key); - if (typeField == null) - { - Diagnostics.AddRange(Diagnostic - .Error($"Struct {structType.Name} does not have a field named {initializer.Key}") - .At(initializer.Value) - .Build()); - - continue; - } - - initializers.Add(initializer.Key, CheckExpression(initializer.Value, typeField.Type)); - } - - var missingFields = structType.Fields - .Where(x => !x.HasDefaultValue && !initializers.ContainsKey(x.Name)) - .Select(x => x.Name) - .ToArray(); - - if (missingFields.Length != 0) - { - Diagnostics.Add(Diagnostic - .Warning($"Fields {string.Join(", ", missingFields)} are not initialized") - .At(expression) - .Build()); - } - - return new StructInitializerNode(expression.Tokens, structType, initializers); - } - - private BlockNode CheckBlock(BlockSyntax node) - { - using (BeginScope()) - { - var statements = new List(); - foreach (var statement in node.Statements) - { - try - { - statements.Add(CheckStatement(statement)); - } - catch (TypeCheckerException e) - { - Diagnostics.Add(e.Diagnostic); - } - } - - return new BlockNode(node.Tokens, statements); - } - } - - private StatementNode CheckStatement(StatementSyntax statement) - { - return statement switch - { - AssignmentSyntax assignmentStmt => CheckAssignment(assignmentStmt), - BlockSyntax blockStmt => CheckBlock(blockStmt), - BreakSyntax => new BreakNode(statement.Tokens), - ContinueSyntax => new ContinueNode(statement.Tokens), - IfSyntax ifStmt => CheckIf(ifStmt), - ReturnSyntax retStmt => CheckReturn(retStmt), - StatementExpressionSyntax stmtExpr => CheckStatementExpression(stmtExpr), - VariableDeclarationSyntax varDeclStmt => CheckVariableDeclaration(varDeclStmt), - WhileSyntax whileStmt => CheckWhile(whileStmt), - DeferSyntax defer => new DeferNode(statement.Tokens, CheckStatement(defer.Statement)), - ForSyntax forSyntax => CheckFor(forSyntax), - _ => throw new ArgumentOutOfRangeException(nameof(statement)) - }; - } - - private NubType ResolveType(TypeSyntax type) - { - return type switch - { - ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType)), - BoolTypeSyntax => new NubBoolType(), - IntTypeSyntax i => new NubIntType(i.Signed, i.Width), - FloatTypeSyntax f => new NubFloatType(f.Width), - FuncTypeSyntax func => new NubFuncType(func.Parameters.Select(ResolveType).ToList(), ResolveType(func.ReturnType)), - SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType)), - ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType), arr.Size), - PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType)), - StringTypeSyntax => new NubStringType(), - CustomTypeSyntax c => ResolveCustomType(c), - VoidTypeSyntax => new NubVoidType(), - _ => throw new NotSupportedException($"Unknown type syntax: {type}") - }; - } - - private NubType ResolveCustomType(CustomTypeSyntax customType) - { - if (!_importedModules.TryGetValue(customType.Module ?? Scope.Module, out var module)) - { - throw new TypeCheckerException(Diagnostic - .Error($"Module {customType.Module ?? Scope.Module} not found") - .WithHelp($"import \"{customType.Module ?? Scope.Module}\"") - .At(customType) - .Build()); - } - - var enumDef = module.Enums(IsCurretModule(customType.Module)).FirstOrDefault(x => x.Name == customType.Name); - if (enumDef != null) - { - return enumDef.Type != null ? ResolveType(enumDef.Type) : new NubIntType(false, 64); - } - - var structDef = module.Structs(IsCurretModule(customType.Module)).FirstOrDefault(x => x.Name == customType.Name); - if (structDef != null) - { - var key = (customType.Module ?? Scope.Module, customType.Name); - - if (_typeCache.TryGetValue(key, out var cachedType)) - { - return cachedType; - } - - if (!_resolvingTypes.Add(key)) - { - var placeholder = new NubStructType(customType.Module ?? Scope.Module, customType.Name, []); - _typeCache[key] = placeholder; - return placeholder; - } - - try - { - var result = new NubStructType(customType.Module ?? Scope.Module, structDef.Name, []); - _typeCache[key] = result; - - var fields = structDef.Fields - .Select(x => new NubStructFieldType(x.Name, ResolveType(x.Type), x.Value != null)) - .ToList(); - - result.Fields.AddRange(fields); - return result; - } - finally - { - _resolvingTypes.Remove(key); - } - } - - throw new TypeCheckerException(Diagnostic - .Error($"Type {customType.Name} not found in module {customType.Module ?? Scope.Module}") - .At(customType) - .Build()); - } - - private bool IsCurretModule(string? module) - { - if (module == null) - { - return true; - } - - return module == Scope.Module; - } -} - -public record Variable(string Name, NubType Type); - -public class Scope(string module, Scope? parent = null) -{ - private NubType? _returnType; - private readonly List _variables = []; - public string Module { get; } = module; - - public void DeclareVariable(Variable variable) - { - _variables.Add(variable); - } - - public void SetReturnType(NubType returnType) - { - _returnType = returnType; - } - - public NubType? GetReturnType() - { - return _returnType ?? parent?.GetReturnType(); - } - - public Variable? LookupVariable(string name) - { - var variable = _variables.FirstOrDefault(x => x.Name == name); - if (variable != null) - { - return variable; - } - - return parent?.LookupVariable(name); - } - - public Scope SubScope() - { - return new Scope(Module, this); - } -} - -public class TypeCheckerException : Exception -{ - public Diagnostic Diagnostic { get; } - - public TypeCheckerException(Diagnostic diagnostic) : base(diagnostic.Message) - { - Diagnostic = diagnostic; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs deleted file mode 100644 index bf3cf54..0000000 --- a/compiler/NubLang/Diagnostics/Diagnostic.cs +++ /dev/null @@ -1,398 +0,0 @@ -using System.Text; -using NubLang.Syntax; - -namespace NubLang.Diagnostics; - -public class Diagnostic -{ - public class DiagnosticBuilder - { - private readonly DiagnosticSeverity _severity; - private readonly string _message; - private SourceSpan? _span; - private string? _help; - - public DiagnosticBuilder(DiagnosticSeverity severity, string message) - { - _severity = severity; - _message = message; - } - - public DiagnosticBuilder At(SyntaxNode? node) - { - if (node != null) - { - _span = SourceSpan.Merge(node.Tokens.Select(x => x.Span)); - } - - return this; - } - - public DiagnosticBuilder At(Token? token) - { - if (token != null) - { - At(token.Span); - } - - return this; - } - - public DiagnosticBuilder At(SourceSpan? span) - { - if (span != null) - { - _span = span; - } - - return this; - } - - public DiagnosticBuilder At(string filePath, int line, int column) - { - _span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column)); - return this; - } - - public DiagnosticBuilder WithHelp(string help) - { - _help = help; - return this; - } - - public Diagnostic Build() => new(_severity, _message, _help, _span); - } - - public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message); - public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message); - public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message); - - public DiagnosticSeverity Severity { get; } - public string Message { get; } - public string? Help { get; } - public SourceSpan? Span { get; } - - private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span) - { - Severity = severity; - Message = message; - Help = help; - Span = span; - } - - public string FormatANSI() - { - var sb = new StringBuilder(); - - sb.Append(Severity switch - { - DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red), - DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow), - DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue), - _ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White) - }); - - if (Span.HasValue) - { - sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint)); - } - - sb.Append(": "); - sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite)); - - if (Span.HasValue) - { - sb.AppendLine(); - var text = File.ReadAllText(Span.Value.FilePath); - - var tokenizer = new Tokenizer(Span.Value.FilePath, text); - tokenizer.Tokenize(); - - var lines = text.Split('\n'); - - var startLine = Span.Value.Start.Line; - var endLine = Span.Value.End.Line; - - const int CONTEXT_LINES = 3; - - var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES); - var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES); - - var numberPadding = contextEndLine.ToString().Length; - - var codePadding = 0; - for (var i = contextStartLine - 1; i < contextEndLine && i < lines.Length; i++) - { - var lineLength = lines[i].Length; - if (lineLength > codePadding) - { - codePadding = lineLength; - } - } - - sb.Append('╭'); - sb.Append(new string('─', numberPadding + 2)); - sb.Append('┬'); - sb.Append(new string('─', codePadding + 2)); - sb.Append('╮'); - sb.AppendLine(); - - for (var i = contextStartLine; i <= contextEndLine; i++) - { - var line = lines[i - 1]; - - sb.Append("│ "); - sb.Append(i.ToString().PadRight(numberPadding)); - sb.Append(" │ "); - sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens)); - // sb.Append(line.PadRight(codePadding)); - sb.Append(" │"); - sb.AppendLine(); - - if (i >= startLine && i <= endLine) - { - var markerStartColumn = 1; - var markerEndColumn = line.Length; - - if (i == startLine) - { - markerStartColumn = Span.Value.Start.Column; - } - - if (i == endLine) - { - markerEndColumn = Span.Value.End.Column; - } - - var markerLength = markerEndColumn - markerStartColumn; - var marker = new string('^', markerLength); - - var markerColor = Severity switch - { - DiagnosticSeverity.Info => ConsoleColors.Blue, - DiagnosticSeverity.Warning => ConsoleColors.Yellow, - DiagnosticSeverity.Error => ConsoleColors.Red, - _ => ConsoleColors.White - }; - - sb.Append("│ "); - sb.Append(new string(' ', numberPadding)); - sb.Append(" │ "); - sb.Append(new string(' ', markerStartColumn - 1)); - sb.Append(ConsoleColors.Colorize(marker, markerColor)); - sb.Append(new string(' ', codePadding - (markerStartColumn - 1) - markerLength)); - sb.Append(" │"); - sb.AppendLine(); - } - } - - sb.Append('╰'); - sb.Append(new string('─', numberPadding + 2)); - sb.Append('┴'); - sb.Append(new string('─', codePadding + 2)); - sb.Append('╯'); - } - - if (Help != null) - { - sb.AppendLine(); - sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan)); - } - - return sb.ToString(); - } - - private static string ApplySyntaxHighlighting(string line, int lineNumber, List tokens) - { - var sb = new StringBuilder(); - var lineTokens = tokens - .Where(t => t.Span.Start.Line == lineNumber) - .OrderBy(t => t.Span.Start.Column) - .ToList(); - - if (lineTokens.Count == 0) - { - return line; - } - - var currentColumn = 1; - - foreach (var token in lineTokens) - { - var tokenStart = token.Span.Start.Column; - var tokenEnd = token.Span.End.Column; - - if (tokenStart > currentColumn && currentColumn - 1 < line.Length) - { - var beforeLength = Math.Min(tokenStart - currentColumn, line.Length - (currentColumn - 1)); - if (beforeLength > 0) - { - var beforeToken = line.Substring(currentColumn - 1, beforeLength); - sb.Append(beforeToken); - } - } - - var tokenLength = tokenEnd - tokenStart; - if (tokenStart >= 1 && tokenStart - 1 < line.Length && tokenLength > 0) - { - var availableLength = line.Length - (tokenStart - 1); - var actualLength = Math.Min(tokenLength, availableLength); - - if (actualLength > 0) - { - var tokenText = line.Substring(tokenStart - 1, actualLength); - var coloredToken = ColorizeToken(token, tokenText); - sb.Append(coloredToken); - } - } - - currentColumn = tokenEnd; - } - - if (currentColumn - 1 < line.Length) - { - var remaining = line[(currentColumn - 1)..]; - sb.Append(remaining); - } - - return sb.ToString(); - } - - private static string ColorizeToken(Token token, string tokenText) - { - switch (token) - { - case IdentifierToken: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightWhite); - } - case StringLiteralToken: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Green); - } - case IntLiteralToken: - case FloatLiteralToken: - case BoolLiteralToken: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Magenta); - } - case SymbolToken symbolToken: - { - switch (symbolToken.Symbol) - { - case Symbol.Func: - case Symbol.Return: - case Symbol.If: - case Symbol.Else: - case Symbol.While: - case Symbol.Break: - case Symbol.Continue: - case Symbol.Struct: - case Symbol.Let: - case Symbol.Extern: - case Symbol.For: - case Symbol.In: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Bold + ConsoleColors.Blue); - } - case Symbol.Assign: - case Symbol.Bang: - case Symbol.Equal: - case Symbol.NotEqual: - case Symbol.LessThan: - case Symbol.LessThanOrEqual: - case Symbol.GreaterThan: - case Symbol.GreaterThanOrEqual: - case Symbol.Plus: - case Symbol.Minus: - case Symbol.Star: - case Symbol.ForwardSlash: - case Symbol.Caret: - case Symbol.Ampersand: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.Yellow); - } - case Symbol.Colon: - case Symbol.OpenParen: - case Symbol.CloseParen: - case Symbol.OpenBrace: - case Symbol.CloseBrace: - case Symbol.OpenBracket: - case Symbol.CloseBracket: - case Symbol.Comma: - case Symbol.Period: - case Symbol.Semi: - { - return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightBlack); - } - } - - break; - } - } - - return tokenText; - } -} - -public enum DiagnosticSeverity -{ - Info, - Warning, - Error -} - -public static class ConsoleColors -{ - public const string Reset = "\e[0m"; - public const string Bold = "\e[1m"; - public const string Faint = "\e[2m"; - public const string Italic = "\e[3m"; - public const string Underline = "\e[4m"; - public const string SlowBlink = "\e[5m"; - public const string RapidBlink = "\e[6m"; - public const string SwapBgAndFg = "\e[7m"; - public const string Conceal = "\e[8m"; - public const string CrossedOut = "\e[9m"; - - public const string DefaultFont = "\e[10m"; - public const string AltFont1 = "\e[11m"; - public const string AltFont2 = "\e[12m"; - public const string AltFont3 = "\e[13m"; - public const string AltFont4 = "\e[14m"; - public const string AltFont5 = "\e[15m"; - public const string AltFont6 = "\e[16m"; - public const string AltFont7 = "\e[17m"; - public const string AltFont8 = "\e[18m"; - public const string AltFont9 = "\e[19m"; - - public const string Black = "\e[30m"; - public const string Red = "\e[31m"; - public const string Green = "\e[32m"; - public const string Yellow = "\e[33m"; - public const string Blue = "\e[34m"; - public const string Magenta = "\e[35m"; - public const string Cyan = "\e[36m"; - public const string White = "\e[37m"; - - public const string BrightBlack = "\e[90m"; - public const string BrightRed = "\e[91m"; - public const string BrightGreen = "\e[92m"; - public const string BrightYellow = "\e[93m"; - public const string BrightBlue = "\e[94m"; - public const string BrightMagenta = "\e[95m"; - public const string BrightCyan = "\e[96m"; - public const string BrightWhite = "\e[97m"; - - private static bool IsColorSupported() - { - var term = Environment.GetEnvironmentVariable("TERM"); - var colorTerm = Environment.GetEnvironmentVariable("COLORTERM"); - return !string.IsNullOrEmpty(term) || !string.IsNullOrEmpty(colorTerm) || !Console.IsOutputRedirected; - } - - public static string Colorize(string text, string color) - { - return IsColorSupported() ? $"{color}{text}{Reset}" : text; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Diagnostics/SourceSpan.cs b/compiler/NubLang/Diagnostics/SourceSpan.cs deleted file mode 100644 index 121fe6e..0000000 --- a/compiler/NubLang/Diagnostics/SourceSpan.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace NubLang.Diagnostics; - -public readonly struct SourceSpan : IEquatable, IComparable -{ - public static SourceSpan Merge(params IEnumerable spans) - { - var spanArray = spans as SourceSpan[] ?? spans.ToArray(); - if (spanArray.Length == 0) - { - return new SourceSpan(string.Empty, new SourceLocation(0, 0), new SourceLocation(0, 0)); - } - - var minStart = spanArray.Min(s => s.Start); - var maxEnd = spanArray.Max(s => s.End); - - return new SourceSpan(spanArray[0].FilePath, minStart, maxEnd); - } - - public SourceSpan(string filePath, SourceLocation start, SourceLocation end) - { - if (start > end) - { - throw new ArgumentException("Start location cannot be after end location"); - } - - FilePath = filePath; - Start = start; - End = end; - } - - public string FilePath { get; } - public SourceLocation Start { get; } - public SourceLocation End { get; } - - public override string ToString() - { - if (Start == End) - { - return $"{FilePath}:{Start}"; - } - - if (Start.Line == End.Line) - { - return Start.Column == End.Column ? $"{FilePath}:{Start}" : $"{FilePath}:{Start.Line}:{Start.Column}-{End.Column}"; - } - - return $"{FilePath}:{Start}-{End}"; - } - - public bool Equals(SourceSpan other) => Start == other.Start && End == other.End; - public override bool Equals(object? obj) => obj is SourceSpan other && Equals(other); - public override int GetHashCode() => HashCode.Combine(typeof(SourceSpan), Start, End); - - public static bool operator ==(SourceSpan left, SourceSpan right) => Equals(left, right); - public static bool operator !=(SourceSpan left, SourceSpan right) => !Equals(left, right); - - public static bool operator <(SourceSpan left, SourceSpan right) => left.CompareTo(right) < 0; - public static bool operator <=(SourceSpan left, SourceSpan right) => left.CompareTo(right) <= 0; - public static bool operator >(SourceSpan left, SourceSpan right) => left.CompareTo(right) > 0; - public static bool operator >=(SourceSpan left, SourceSpan right) => left.CompareTo(right) >= 0; - - public int CompareTo(SourceSpan other) - { - var startComparison = Start.CompareTo(other.Start); - return startComparison != 0 ? startComparison : End.CompareTo(other.End); - } -} - -public readonly struct SourceLocation : IEquatable, IComparable -{ - public SourceLocation(int line, int column) - { - Line = line; - Column = column; - } - - public int Line { get; } - public int Column { get; } - - public override string ToString() - { - return $"{Line}:{Column}"; - } - - public override bool Equals(object? obj) - { - return obj is SourceLocation other && Equals(other); - } - - public bool Equals(SourceLocation other) - { - return Line == other.Line && Column == other.Column; - } - - public override int GetHashCode() - { - return HashCode.Combine(typeof(SourceLocation), Line, Column); - } - - public static bool operator ==(SourceLocation left, SourceLocation right) => Equals(left, right); - public static bool operator !=(SourceLocation left, SourceLocation right) => !Equals(left, right); - public static bool operator <(SourceLocation left, SourceLocation right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column); - public static bool operator >(SourceLocation left, SourceLocation right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column); - public static bool operator <=(SourceLocation left, SourceLocation right) => left.Line <= right.Line || (left.Line == right.Line && left.Column <= right.Column); - public static bool operator >=(SourceLocation left, SourceLocation right) => left.Line >= right.Line || (left.Line == right.Line && left.Column >= right.Column); - - public int CompareTo(SourceLocation other) - { - var lineComparison = Line.CompareTo(other.Line); - return lineComparison != 0 ? lineComparison : Column.CompareTo(other.Column); - } -} \ No newline at end of file diff --git a/compiler/NubLang/NubLang.csproj b/compiler/NubLang/NubLang.csproj deleted file mode 100644 index b682a68..0000000 --- a/compiler/NubLang/NubLang.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net9.0 - enable - enable - true - - - diff --git a/compiler/NubLang/Sugar/DeSugar.cs b/compiler/NubLang/Sugar/DeSugar.cs deleted file mode 100644 index 6856ab8..0000000 --- a/compiler/NubLang/Sugar/DeSugar.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NubLang.Sugar; - -public class DeSugar -{ - -} \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Module.cs b/compiler/NubLang/Syntax/Module.cs deleted file mode 100644 index b5776ae..0000000 --- a/compiler/NubLang/Syntax/Module.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace NubLang.Syntax; - -public sealed class Module -{ - public static Dictionary Collect(List syntaxTrees) - { - var modules = new Dictionary(); - foreach (var syntaxTree in syntaxTrees) - { - if (!modules.TryGetValue(syntaxTree.ModuleName, out var module)) - { - module = new Module(); - modules.Add(syntaxTree.ModuleName, module); - } - - module._definitions.AddRange(syntaxTree.Definitions); - } - - return modules; - } - - private readonly List _definitions = []; - - public List Structs(bool includePrivate) - { - return _definitions - .OfType() - .Where(x => x.Exported || includePrivate) - .ToList(); - } - - public List Functions(bool includePrivate) - { - return _definitions - .OfType() - .Where(x => x.Exported || includePrivate) - .ToList(); - } - - public List Enums(bool includePrivate) - { - return _definitions - .OfType() - .Where(x => x.Exported || includePrivate) - .ToList(); - } -} \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs deleted file mode 100644 index b6aacd6..0000000 --- a/compiler/NubLang/Syntax/Parser.cs +++ /dev/null @@ -1,951 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using NubLang.Diagnostics; - -namespace NubLang.Syntax; - -public sealed class Parser -{ - private List _tokens = []; - private int _tokenIndex; - - private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null; - private bool HasToken => CurrentToken != null; - - public List Diagnostics { get; } = []; - - public SyntaxTree Parse(List tokens) - { - Diagnostics.Clear(); - _tokens = tokens; - _tokenIndex = 0; - - string? moduleName = null; - var imports = new List(); - var definitions = new List(); - - while (HasToken) - { - try - { - var startIndex = _tokenIndex; - - if (TryExpectSymbol(Symbol.Import)) - { - var name = ExpectStringLiteral(); - if (imports.Contains(name.Value)) - { - Diagnostics.Add(Diagnostic - .Warning($"Module {name.Value} is imported twice") - .At(name) - .WithHelp($"Remove duplicate import \"{name.Value}\"") - .Build()); - } - else - { - imports.Add(name.Value); - } - - continue; - } - - if (TryExpectSymbol(Symbol.Module)) - { - if (moduleName != null) - { - throw new ParseException(Diagnostic - .Error("Module is declared more than once") - .At(CurrentToken) - .WithHelp("Remove duplicate module declaration") - .Build()); - } - - moduleName = ExpectStringLiteral().Value; - continue; - } - - var exported = TryExpectSymbol(Symbol.Export); - - if (TryExpectSymbol(Symbol.Extern)) - { - var externSymbol = ExpectStringLiteral(); - ExpectSymbol(Symbol.Func); - definitions.Add(ParseFunc(startIndex, exported, externSymbol.Value)); - continue; - } - - var keyword = ExpectSymbol(); - DefinitionSyntax definition = keyword.Symbol switch - { - Symbol.Func => ParseFunc(startIndex, exported, null), - Symbol.Struct => ParseStruct(startIndex, exported), - Symbol.Enum => ParseEnum(startIndex, exported), - _ => throw new ParseException(Diagnostic - .Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'") - .WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'") - .At(keyword) - .Build()) - }; - - definitions.Add(definition); - } - catch (ParseException e) - { - Diagnostics.Add(e.Diagnostic); - while (HasToken) - { - if (CurrentToken is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct }) - { - break; - } - - Next(); - } - } - } - - return new SyntaxTree(definitions, moduleName ?? "default", imports); - } - - private FuncParameterSyntax ParseFuncParameter() - { - var startIndex = _tokenIndex; - var name = ExpectIdentifier(); - ExpectSymbol(Symbol.Colon); - var type = ParseType(); - - return new FuncParameterSyntax(GetTokens(startIndex), name.Value, type); - } - - private FuncSyntax ParseFunc(int startIndex, bool exported, string? externSymbol) - { - var name = ExpectIdentifier(); - List parameters = []; - - ExpectSymbol(Symbol.OpenParen); - - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseFuncParameter()); - - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new VoidTypeSyntax([]); - - var prototype = new FuncPrototypeSyntax(GetTokens(startIndex), name.Value, exported, externSymbol, parameters, returnType); - - BlockSyntax? body = null; - var bodyStartIndex = _tokenIndex; - if (TryExpectSymbol(Symbol.OpenBrace)) - { - body = ParseBlock(bodyStartIndex); - } - - return new FuncSyntax(GetTokens(startIndex), prototype, body); - } - - private StructSyntax ParseStruct(int startIndex, bool exported) - { - var name = ExpectIdentifier(); - - ExpectSymbol(Symbol.OpenBrace); - - List fields = []; - - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var memberStartIndex = _tokenIndex; - - var fieldName = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Colon); - var fieldType = ParseType(); - - ExpressionSyntax? fieldValue = null; - - if (TryExpectSymbol(Symbol.Assign)) - { - fieldValue = ParseExpression(); - } - - fields.Add(new StructFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldType, fieldValue)); - } - - return new StructSyntax(GetTokens(startIndex), name.Value, exported, fields); - } - - private EnumSyntax ParseEnum(int startIndex, bool exported) - { - var name = ExpectIdentifier(); - - TypeSyntax? type = null; - - if (TryExpectSymbol(Symbol.Colon)) - { - type = ParseType(); - } - - List fields = []; - - ExpectSymbol(Symbol.OpenBrace); - - long value = -1; - - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var memberStartIndex = _tokenIndex; - var fieldName = ExpectIdentifier().Value; - long fieldValue; - - if (TryExpectSymbol(Symbol.Assign)) - { - if (!TryExpectIntLiteral(out var intLiteralToken)) - { - throw new ParseException(Diagnostic - .Error("Value of enum field must be an integer literal") - .At(CurrentToken) - .Build()); - } - - fieldValue = Convert.ToInt64(intLiteralToken.Value, intLiteralToken.Base); - value = fieldValue; - } - else - { - fieldValue = value + 1; - value = fieldValue; - } - - fields.Add(new EnumFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldValue)); - } - - return new EnumSyntax(GetTokens(startIndex), name.Value, exported, type, fields); - } - - private StatementSyntax ParseStatement() - { - var startIndex = _tokenIndex; - - if (TryExpectSymbol(out var symbol)) - { - switch (symbol) - { - case Symbol.OpenBrace: - return ParseBlock(startIndex); - case Symbol.Return: - return ParseReturn(startIndex); - case Symbol.If: - return ParseIf(startIndex); - case Symbol.While: - return ParseWhile(startIndex); - case Symbol.For: - return ParseFor(startIndex); - case Symbol.Let: - return ParseVariableDeclaration(startIndex); - case Symbol.Defer: - return ParseDefer(startIndex); - case Symbol.Break: - return new BreakSyntax(GetTokens(startIndex)); - case Symbol.Continue: - return new ContinueSyntax(GetTokens(startIndex)); - } - } - - var expr = ParseExpression(); - - if (TryExpectSymbol(Symbol.Assign)) - { - var value = ParseExpression(); - return new AssignmentSyntax(GetTokens(startIndex), expr, value); - } - - return new StatementExpressionSyntax(GetTokens(startIndex), expr); - } - - private VariableDeclarationSyntax ParseVariableDeclaration(int startIndex) - { - var name = ExpectIdentifier().Value; - - TypeSyntax? explicitType = null; - if (TryExpectSymbol(Symbol.Colon)) - { - explicitType = ParseType(); - } - - ExpressionSyntax? assignment = null; - if (TryExpectSymbol(Symbol.Assign)) - { - assignment = ParseExpression(); - } - - return new VariableDeclarationSyntax(GetTokens(startIndex), name, explicitType, assignment); - } - - private DeferSyntax ParseDefer(int startIndex) - { - var statement = ParseStatement(); - return new DeferSyntax(GetTokens(startIndex), statement); - } - - private ReturnSyntax ParseReturn(int startIndex) - { - ExpressionSyntax? value = null; - - if (!TryExpectSymbol(Symbol.Semi)) - { - value = ParseExpression(); - } - - return new ReturnSyntax(GetTokens(startIndex), value); - } - - private IfSyntax ParseIf(int startIndex) - { - var condition = ParseExpression(); - var body = ParseBlock(); - - Variant? elseStatement = null; - - var elseStartIndex = _tokenIndex; - if (TryExpectSymbol(Symbol.Else)) - { - if (TryExpectSymbol(Symbol.If)) - { - elseStatement = (Variant)ParseIf(elseStartIndex); - } - else - { - elseStatement = (Variant)ParseBlock(); - } - } - - return new IfSyntax(GetTokens(startIndex), condition, body, elseStatement); - } - - private WhileSyntax ParseWhile(int startIndex) - { - var condition = ParseExpression(); - var body = ParseBlock(); - return new WhileSyntax(GetTokens(startIndex), condition, body); - } - - private ForSyntax ParseFor(int startIndex) - { - var itemName = ExpectIdentifier().Value; - string? indexName = null; - - if (TryExpectSymbol(Symbol.Comma)) - { - indexName = ExpectIdentifier().Value; - } - - ExpectSymbol(Symbol.In); - var target = ParseExpression(); - var body = ParseBlock(); - - return new ForSyntax(GetTokens(startIndex), itemName, indexName, target, body); - } - - private ExpressionSyntax ParseExpression(int precedence = 0) - { - var startIndex = _tokenIndex; - var left = ParsePrimaryExpression(); - - while (CurrentToken is SymbolToken symbolToken && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence) - { - Next(); - var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1); - left = new BinaryExpressionSyntax(GetTokens(startIndex), left, op.Value, right); - } - - return left; - } - - private static int GetBinaryOperatorPrecedence(BinaryOperatorSyntax operatorSyntax) - { - return operatorSyntax switch - { - BinaryOperatorSyntax.Multiply => 10, - BinaryOperatorSyntax.Divide => 10, - BinaryOperatorSyntax.Modulo => 10, - - BinaryOperatorSyntax.Plus => 9, - BinaryOperatorSyntax.Minus => 9, - - BinaryOperatorSyntax.LeftShift => 8, - BinaryOperatorSyntax.RightShift => 8, - - BinaryOperatorSyntax.GreaterThan => 7, - BinaryOperatorSyntax.GreaterThanOrEqual => 7, - BinaryOperatorSyntax.LessThan => 7, - BinaryOperatorSyntax.LessThanOrEqual => 7, - - BinaryOperatorSyntax.Equal => 7, - BinaryOperatorSyntax.NotEqual => 7, - - BinaryOperatorSyntax.BitwiseAnd => 6, - BinaryOperatorSyntax.BitwiseXor => 5, - BinaryOperatorSyntax.BitwiseOr => 4, - - BinaryOperatorSyntax.LogicalAnd => 3, - BinaryOperatorSyntax.LogicalOr => 2, - - _ => throw new ArgumentOutOfRangeException(nameof(operatorSyntax), operatorSyntax, null) - }; - } - - private bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryOperatorSyntax? binaryExpressionOperator) - { - switch (symbol) - { - case Symbol.Equal: - binaryExpressionOperator = BinaryOperatorSyntax.Equal; - return true; - case Symbol.NotEqual: - binaryExpressionOperator = BinaryOperatorSyntax.NotEqual; - return true; - case Symbol.LessThan: - binaryExpressionOperator = BinaryOperatorSyntax.LessThan; - return true; - case Symbol.LessThanOrEqual: - binaryExpressionOperator = BinaryOperatorSyntax.LessThanOrEqual; - return true; - case Symbol.GreaterThan: - binaryExpressionOperator = BinaryOperatorSyntax.GreaterThan; - return true; - case Symbol.GreaterThanOrEqual: - binaryExpressionOperator = BinaryOperatorSyntax.GreaterThanOrEqual; - return true; - case Symbol.And: - binaryExpressionOperator = BinaryOperatorSyntax.LogicalAnd; - return true; - case Symbol.Or: - binaryExpressionOperator = BinaryOperatorSyntax.LogicalOr; - return true; - case Symbol.Plus: - binaryExpressionOperator = BinaryOperatorSyntax.Plus; - return true; - case Symbol.Minus: - binaryExpressionOperator = BinaryOperatorSyntax.Minus; - return true; - case Symbol.Star: - binaryExpressionOperator = BinaryOperatorSyntax.Multiply; - return true; - case Symbol.ForwardSlash: - binaryExpressionOperator = BinaryOperatorSyntax.Divide; - return true; - case Symbol.Percent: - binaryExpressionOperator = BinaryOperatorSyntax.Modulo; - return true; - case Symbol.LeftShift: - binaryExpressionOperator = BinaryOperatorSyntax.LeftShift; - return true; - case Symbol.RightShift: - binaryExpressionOperator = BinaryOperatorSyntax.RightShift; - return true; - case Symbol.Ampersand: - binaryExpressionOperator = BinaryOperatorSyntax.BitwiseAnd; - return true; - case Symbol.Pipe: - binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr; - return true; - case Symbol.Caret: - binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor; - return true; - default: - binaryExpressionOperator = null; - return false; - } - } - - private ExpressionSyntax ParsePrimaryExpression() - { - var startIndex = _tokenIndex; - var token = ExpectToken(); - var expr = token switch - { - BoolLiteralToken boolLiteral => new BoolLiteralSyntax(GetTokens(startIndex), boolLiteral.Value), - StringLiteralToken stringLiteral => new StringLiteralSyntax(GetTokens(startIndex), stringLiteral.Value), - FloatLiteralToken floatLiteral => new FloatLiteralSyntax(GetTokens(startIndex), floatLiteral.Value), - IntLiteralToken intLiteral => new IntLiteralSyntax(GetTokens(startIndex), intLiteral.Value, intLiteral.Base), - IdentifierToken identifier => ParseIdentifier(startIndex, identifier), - SymbolToken symbolToken => symbolToken.Symbol switch - { - Symbol.OpenParen => ParseParenthesizedExpression(), - Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()), - Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()), - Symbol.OpenBracket => ParseArrayInitializer(startIndex), - Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()), - Symbol.Struct => ParseStructInitializer(startIndex), - Symbol.At => ParseBuiltinFunction(startIndex), - _ => throw new ParseException(Diagnostic - .Error($"Unexpected symbol '{symbolToken.Symbol}' in expression") - .WithHelp("Expected '(', '-', '!', '[' or '{'") - .At(symbolToken) - .Build()) - }, - _ => throw new ParseException(Diagnostic - .Error($"Unexpected token '{token.GetType().Name}' in expression") - .WithHelp("Expected literal, identifier, or parenthesized expression") - .At(token) - .Build()) - }; - - return ParsePostfixOperators(expr); - } - - private ExpressionSyntax ParseBuiltinFunction(int startIndex) - { - var name = ExpectIdentifier(); - ExpectSymbol(Symbol.OpenParen); - - switch (name.Value) - { - case "size": - { - var type = ParseType(); - ExpectSymbol(Symbol.CloseParen); - return new SizeSyntax(GetTokens(startIndex), type); - } - case "cast": - { - var expression = ParseExpression(); - ExpectSymbol(Symbol.CloseParen); - return new CastSyntax(GetTokens(startIndex), expression); - } - default: - { - throw new ParseException(Diagnostic.Error($"Unknown builtin {name.Value}").At(name).Build()); - } - } - } - - private ExpressionSyntax ParseIdentifier(int startIndex, IdentifierToken identifier) - { - if (TryExpectSymbol(Symbol.DoubleColon)) - { - var name = ExpectIdentifier(); - return new ModuleIdentifierSyntax(GetTokens(startIndex), identifier.Value, name.Value); - } - - return new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value); - } - - private ExpressionSyntax ParseParenthesizedExpression() - { - var expression = ParseExpression(); - ExpectSymbol(Symbol.CloseParen); - return expression; - } - - private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr) - { - 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); - continue; - } - - if (TryExpectSymbol(Symbol.Period)) - { - var member = ExpectIdentifier().Value; - expr = new MemberAccessSyntax(GetTokens(startIndex), expr, member); - continue; - } - - if (TryExpectSymbol(Symbol.OpenBracket)) - { - var index = ParseExpression(); - ExpectSymbol(Symbol.CloseBracket); - expr = new ArrayIndexAccessSyntax(GetTokens(startIndex), expr, index); - continue; - } - - if (TryExpectSymbol(Symbol.OpenParen)) - { - var parameters = new List(); - - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - expr = new FuncCallSyntax(GetTokens(startIndex), expr, parameters); - continue; - } - - break; - } - - return expr; - } - - private ExpressionSyntax ParseArrayInitializer(int startIndex) - { - var values = new List(); - while (!TryExpectSymbol(Symbol.CloseBracket)) - { - values.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseBracket); - break; - } - } - - return new ArrayInitializerSyntax(GetTokens(startIndex), values); - } - - private StructInitializerSyntax ParseStructInitializer(int startIndex) - { - TypeSyntax? type = null; - if (!TryExpectSymbol(Symbol.OpenBrace)) - { - type = ParseType(); - ExpectSymbol(Symbol.OpenBrace); - } - - var initializers = ParseStructInitializerBody(); - - return new StructInitializerSyntax(GetTokens(startIndex), type, initializers); - } - - private Dictionary ParseStructInitializerBody() - { - Dictionary initializers = []; - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - initializers.Add(name, value); - } - - return initializers; - } - - private BlockSyntax ParseBlock() - { - var startIndex = _tokenIndex; - ExpectSymbol(Symbol.OpenBrace); - return ParseBlock(startIndex); - } - - private BlockSyntax ParseBlock(int startIndex) - { - List statements = []; - - while (!TryExpectSymbol(Symbol.CloseBrace)) - { - try - { - statements.Add(ParseStatement()); - } - catch (ParseException ex) - { - Diagnostics.Add(ex.Diagnostic); - if (HasToken) - { - Next(); - } - else - { - break; - } - } - } - - return new BlockSyntax(GetTokens(startIndex), statements); - } - - private TypeSyntax ParseType() - { - var startIndex = _tokenIndex; - if (TryExpectIdentifier(out var name)) - { - if (name.Value[0] == 'u' && int.TryParse(name.Value[1..], out var size)) - { - if (size is not 8 and not 16 and not 32 and not 64) - { - throw new ParseException(Diagnostic - .Error("Arbitrary uint size is not supported") - .WithHelp("Use u8, u16, u32 or u64") - .At(name) - .Build()); - } - - return new IntTypeSyntax(GetTokens(startIndex), false, size); - } - - if (name.Value[0] == 'i' && int.TryParse(name.Value[1..], out size)) - { - if (size is not 8 and not 16 and not 32 and not 64) - { - throw new ParseException(Diagnostic - .Error("Arbitrary int size is not supported") - .WithHelp("Use i8, i16, i32 or i64") - .At(name) - .Build()); - } - - return new IntTypeSyntax(GetTokens(startIndex), true, size); - } - - if (name.Value[0] == 'f' && int.TryParse(name.Value[1..], out size)) - { - if (size is not 32 and not 64) - { - throw new ParseException(Diagnostic - .Error("Arbitrary float size is not supported") - .WithHelp("Use f32 or f64") - .At(name) - .Build()); - } - - return new FloatTypeSyntax(GetTokens(startIndex), size); - } - - switch (name.Value) - { - case "void": - return new VoidTypeSyntax(GetTokens(startIndex)); - case "string": - return new StringTypeSyntax(GetTokens(startIndex)); - case "bool": - return new BoolTypeSyntax(GetTokens(startIndex)); - default: - { - string? module = null; - - if (TryExpectSymbol(Symbol.DoubleColon)) - { - var customTypeName = ExpectIdentifier(); - module = name.Value; - name = customTypeName; - } - - return new CustomTypeSyntax(GetTokens(startIndex), module, name.Value); - } - } - } - - if (TryExpectSymbol(Symbol.Caret)) - { - var baseType = ParseType(); - return new PointerTypeSyntax(GetTokens(startIndex), baseType); - } - - if (TryExpectSymbol(Symbol.Func)) - { - ExpectSymbol(Symbol.OpenParen); - - List parameters = []; - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseType()); - if (!TryExpectSymbol(Symbol.Comma)) - { - ExpectSymbol(Symbol.CloseParen); - break; - } - } - - var returnType = TryExpectSymbol(Symbol.Colon) - ? ParseType() - : new VoidTypeSyntax([]); - - return new FuncTypeSyntax(GetTokens(startIndex), parameters, returnType); - } - - if (TryExpectSymbol(Symbol.OpenBracket)) - { - if (TryExpectIntLiteral(out var intLiteral)) - { - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, Convert.ToInt64(intLiteral.Value, intLiteral.Base)); - } - else if (TryExpectSymbol(Symbol.QuestionMark)) - { - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - return new ArrayTypeSyntax(GetTokens(startIndex), baseType); - } - else - { - ExpectSymbol(Symbol.CloseBracket); - var baseType = ParseType(); - return new SliceTypeSyntax(GetTokens(startIndex), baseType); - } - } - - throw new ParseException(Diagnostic - .Error("Invalid type syntax") - .WithHelp("Expected type name, '^' for pointer, or '[]' for array") - .At(CurrentToken) - .Build()); - } - - private Token ExpectToken() - { - if (!HasToken) - { - throw new ParseException(Diagnostic - .Error("Unexpected end of file") - .WithHelp("Expected more tokens to complete the syntax") - .At(_tokens[^1]) - .Build()); - } - - var token = CurrentToken!; - Next(); - return token; - } - - private SymbolToken ExpectSymbol() - { - var token = ExpectToken(); - if (token is not SymbolToken symbol) - { - throw new ParseException(Diagnostic - .Error($"Expected symbol, but found {token.GetType().Name}") - .WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.") - .At(token) - .Build()); - } - - return symbol; - } - - private void ExpectSymbol(Symbol expectedSymbol) - { - var token = ExpectSymbol(); - if (token.Symbol != expectedSymbol) - { - throw new ParseException(Diagnostic - .Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'") - .WithHelp($"Insert '{expectedSymbol}' here") - .At(token) - .Build()); - } - } - - private bool TryExpectSymbol(out Symbol symbol) - { - if (CurrentToken is SymbolToken symbolToken) - { - Next(); - symbol = symbolToken.Symbol; - return true; - } - - symbol = default; - return false; - } - - private bool TryExpectSymbol(Symbol symbol) - { - if (CurrentToken is SymbolToken symbolToken && symbolToken.Symbol == symbol) - { - Next(); - return true; - } - - return false; - } - - private bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier) - { - if (CurrentToken is IdentifierToken identifierToken) - { - identifier = identifierToken; - Next(); - return true; - } - - identifier = null; - return false; - } - - private IdentifierToken ExpectIdentifier() - { - var token = ExpectToken(); - if (token is not IdentifierToken identifier) - { - throw new ParseException(Diagnostic - .Error($"Expected identifier, but found {token.GetType().Name}") - .WithHelp("Provide a valid identifier name here") - .At(token) - .Build()); - } - - return identifier; - } - - private bool TryExpectIntLiteral([NotNullWhen(true)] out IntLiteralToken? stringLiteral) - { - if (CurrentToken is IntLiteralToken token) - { - stringLiteral = token; - Next(); - return true; - } - - stringLiteral = null; - return false; - } - - private StringLiteralToken ExpectStringLiteral() - { - var token = ExpectToken(); - if (token is not StringLiteralToken identifier) - { - throw new ParseException(Diagnostic - .Error($"Expected string literal, but found {token.GetType().Name}") - .WithHelp("Provide a valid string literal") - .At(token) - .Build()); - } - - return identifier; - } - - private void Next() - { - _tokenIndex++; - } - - private List GetTokens(int tokenStartIndex) - { - return _tokens.Skip(tokenStartIndex).Take(_tokenIndex - tokenStartIndex).ToList(); - } -} - -public record SyntaxTree(List Definitions, string ModuleName, List Imports); - -public class ParseException : Exception -{ - public Diagnostic Diagnostic { get; } - - public ParseException(Diagnostic diagnostic) : base(diagnostic.Message) - { - Diagnostic = diagnostic; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Syntax.cs b/compiler/NubLang/Syntax/Syntax.cs deleted file mode 100644 index 8d4472c..0000000 --- a/compiler/NubLang/Syntax/Syntax.cs +++ /dev/null @@ -1,147 +0,0 @@ -namespace NubLang.Syntax; - -public abstract record SyntaxNode(List Tokens); - -#region Definitions - -public abstract record DefinitionSyntax(List Tokens, string Name, bool Exported) : SyntaxNode(Tokens); - -public record FuncParameterSyntax(List Tokens, string Name, TypeSyntax Type) : SyntaxNode(Tokens); - -public record FuncPrototypeSyntax(List Tokens, string Name, bool Exported, string? ExternSymbol, List Parameters, TypeSyntax ReturnType) : SyntaxNode(Tokens); - -public record FuncSyntax(List Tokens, FuncPrototypeSyntax Prototype, BlockSyntax? Body) : DefinitionSyntax(Tokens, Prototype.Name, Prototype.Exported); - -public record StructFieldSyntax(List Tokens, string Name, TypeSyntax Type, ExpressionSyntax? Value) : SyntaxNode(Tokens); - -public record StructSyntax(List Tokens, string Name, bool Exported, List Fields) : DefinitionSyntax(Tokens, Name, Exported); - -public record EnumFieldSyntax(List Tokens, string Name, long Value) : SyntaxNode(Tokens); - -public record EnumSyntax(List Tokens, string Name, bool Exported, TypeSyntax? Type, List Fields) : DefinitionSyntax(Tokens, Name, Exported); - -public enum UnaryOperatorSyntax -{ - Negate, - Invert -} - -public enum BinaryOperatorSyntax -{ - Equal, - NotEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual, - LogicalAnd, - LogicalOr, - Plus, - Minus, - Multiply, - Divide, - Modulo, - LeftShift, - RightShift, - BitwiseAnd, - BitwiseXor, - BitwiseOr, -} - -#endregion - -#region Statements - -public abstract record StatementSyntax(List Tokens) : SyntaxNode(Tokens); - -public record BlockSyntax(List Tokens, List Statements) : StatementSyntax(Tokens); - -public record StatementExpressionSyntax(List Tokens, ExpressionSyntax Expression) : StatementSyntax(Tokens); - -public record ReturnSyntax(List Tokens, ExpressionSyntax? Value) : StatementSyntax(Tokens); - -public record AssignmentSyntax(List Tokens, ExpressionSyntax Target, ExpressionSyntax Value) : StatementSyntax(Tokens); - -public record IfSyntax(List Tokens, ExpressionSyntax Condition, BlockSyntax Body, Variant? Else) : StatementSyntax(Tokens); - -public record VariableDeclarationSyntax(List Tokens, string Name, TypeSyntax? ExplicitType, ExpressionSyntax? Assignment) : StatementSyntax(Tokens); - -public record ContinueSyntax(List Tokens) : StatementSyntax(Tokens); - -public record BreakSyntax(List Tokens) : StatementSyntax(Tokens); - -public record DeferSyntax(List Tokens, StatementSyntax Statement) : StatementSyntax(Tokens); - -public record WhileSyntax(List Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); - -public record ForSyntax(List Tokens, string ElementName, string? IndexName, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens); - -#endregion - -#region Expressions - -public abstract record ExpressionSyntax(List Tokens) : SyntaxNode(Tokens); - -public record BinaryExpressionSyntax(List Tokens, ExpressionSyntax Left, BinaryOperatorSyntax Operator, ExpressionSyntax Right) : ExpressionSyntax(Tokens); - -public record UnaryExpressionSyntax(List Tokens, UnaryOperatorSyntax Operator, ExpressionSyntax Operand) : ExpressionSyntax(Tokens); - -public record FuncCallSyntax(List Tokens, ExpressionSyntax Expression, List Parameters) : ExpressionSyntax(Tokens); - -public record LocalIdentifierSyntax(List Tokens, string Name) : ExpressionSyntax(Tokens); - -public record ModuleIdentifierSyntax(List Tokens, string Module, string Name) : ExpressionSyntax(Tokens); - -public record ArrayInitializerSyntax(List Tokens, List Values) : ExpressionSyntax(Tokens); - -public record ArrayIndexAccessSyntax(List Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : ExpressionSyntax(Tokens); - -public record AddressOfSyntax(List Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens); - -public record IntLiteralSyntax(List Tokens, string Value, int Base) : ExpressionSyntax(Tokens); - -public record StringLiteralSyntax(List Tokens, string Value) : ExpressionSyntax(Tokens); - -public record BoolLiteralSyntax(List Tokens, bool Value) : ExpressionSyntax(Tokens); - -public record FloatLiteralSyntax(List Tokens, string Value) : ExpressionSyntax(Tokens); - -public record MemberAccessSyntax(List Tokens, ExpressionSyntax Target, string Member) : ExpressionSyntax(Tokens); - -public record StructInitializerSyntax(List Tokens, TypeSyntax? StructType, Dictionary Initializers) : ExpressionSyntax(Tokens); - -public record DereferenceSyntax(List Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens); - -public record SizeSyntax(List Tokens, TypeSyntax Type) : ExpressionSyntax(Tokens); - -public record CastSyntax(List Tokens, ExpressionSyntax Value) : ExpressionSyntax(Tokens); - -#endregion - -#region Types - -public abstract record TypeSyntax(List Tokens) : SyntaxNode(Tokens); - -public record FuncTypeSyntax(List Tokens, List Parameters, TypeSyntax ReturnType) : TypeSyntax(Tokens); - -public record PointerTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); - -public record VoidTypeSyntax(List Tokens) : TypeSyntax(Tokens); - -public record IntTypeSyntax(List Tokens, bool Signed, int Width) : TypeSyntax(Tokens); - -public record FloatTypeSyntax(List Tokens, int Width) : TypeSyntax(Tokens); - -public record BoolTypeSyntax(List Tokens) : TypeSyntax(Tokens); - -public record StringTypeSyntax(List Tokens) : TypeSyntax(Tokens); - -public record SliceTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); - -public record ArrayTypeSyntax(List Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); - -public record ConstArrayTypeSyntax(List Tokens, TypeSyntax BaseType, long Size) : TypeSyntax(Tokens); - -public record CustomTypeSyntax(List Tokens, string? Module, string Name) : TypeSyntax(Tokens); - -#endregion \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Token.cs b/compiler/NubLang/Syntax/Token.cs deleted file mode 100644 index 66f0980..0000000 --- a/compiler/NubLang/Syntax/Token.cs +++ /dev/null @@ -1,77 +0,0 @@ -using NubLang.Diagnostics; - -namespace NubLang.Syntax; - -public enum Symbol -{ - // Control - If, - Else, - While, - For, - In, - Break, - Continue, - Return, - Let, - Defer, - - // Declaration - Func, - Struct, - Enum, - Import, - Module, - - // Modifier - Extern, - Export, - - Colon, - DoubleColon, - OpenParen, - CloseParen, - OpenBrace, - CloseBrace, - OpenBracket, - CloseBracket, - Comma, - Period, - Assign, - Bang, - Equal, - NotEqual, - LessThan, - LessThanOrEqual, - GreaterThan, - GreaterThanOrEqual, - Plus, - Minus, - Star, - ForwardSlash, - Caret, - Ampersand, - Semi, - Percent, - LeftShift, - RightShift, - Pipe, - And, - Or, - At, - QuestionMark, -} - -public abstract record Token(SourceSpan Span); - -public record IdentifierToken(SourceSpan Span, string Value) : Token(Span); - -public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span); - -public record StringLiteralToken(SourceSpan Span, string Value) : Token(Span); - -public record BoolLiteralToken(SourceSpan Span, bool Value) : Token(Span); - -public record FloatLiteralToken(SourceSpan Span, string Value) : Token(Span); - -public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span); \ No newline at end of file diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs deleted file mode 100644 index 565ab43..0000000 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ /dev/null @@ -1,331 +0,0 @@ -using NubLang.Diagnostics; - -namespace NubLang.Syntax; - -public sealed class Tokenizer -{ - private static readonly Dictionary Keywords = new() - { - ["func"] = Symbol.Func, - ["if"] = Symbol.If, - ["else"] = Symbol.Else, - ["while"] = Symbol.While, - ["for"] = Symbol.For, - ["in"] = Symbol.In, - ["break"] = Symbol.Break, - ["continue"] = Symbol.Continue, - ["return"] = Symbol.Return, - ["struct"] = Symbol.Struct, - ["let"] = Symbol.Let, - ["extern"] = Symbol.Extern, - ["module"] = Symbol.Module, - ["export"] = Symbol.Export, - ["import"] = Symbol.Import, - ["defer"] = Symbol.Defer, - ["enum"] = Symbol.Enum, - }; - - private static readonly Dictionary Symbols = new() - { - [['=', '=']] = Symbol.Equal, - [['!', '=']] = Symbol.NotEqual, - [['<', '=']] = Symbol.LessThanOrEqual, - [['>', '=']] = Symbol.GreaterThanOrEqual, - [['<', '<']] = Symbol.LeftShift, - [['>', '>']] = Symbol.RightShift, - [['&', '&']] = Symbol.And, - [['|', '|']] = Symbol.Or, - [[':', ':']] = Symbol.DoubleColon, - [[':']] = Symbol.Colon, - [['(']] = Symbol.OpenParen, - [[')']] = Symbol.CloseParen, - [['{']] = Symbol.OpenBrace, - [['}']] = Symbol.CloseBrace, - [['[']] = Symbol.OpenBracket, - [[']']] = Symbol.CloseBracket, - [[',']] = Symbol.Comma, - [['.']] = Symbol.Period, - [['=']] = Symbol.Assign, - [['<']] = Symbol.LessThan, - [['>']] = Symbol.GreaterThan, - [['+']] = Symbol.Plus, - [['-']] = Symbol.Minus, - [['*']] = Symbol.Star, - [['/']] = Symbol.ForwardSlash, - [['!']] = Symbol.Bang, - [['^']] = Symbol.Caret, - [['&']] = Symbol.Ampersand, - [[';']] = Symbol.Semi, - [['%']] = Symbol.Percent, - [['|']] = Symbol.Pipe, - [['@']] = Symbol.At, - [['?']] = Symbol.QuestionMark, - }; - - private static readonly (char[] Pattern, Symbol Symbol)[] OrderedSymbols = Symbols - .OrderByDescending(kvp => kvp.Key.Length) - .Select(kvp => (kvp.Key, kvp.Value)) - .ToArray(); - - private readonly string _fileName; - private readonly string _content; - private int _index = 0; - private int _line = 1; - private int _column = 1; - - public Tokenizer(string fileName, string content) - { - _fileName = fileName; - _content = content; - } - - public List Diagnostics { get; } = []; - public List Tokens { get; } = []; - - public void Tokenize() - { - Diagnostics.Clear(); - Tokens.Clear(); - _index = 0; - _line = 1; - _column = 1; - - while (Peek().HasValue) - { - try - { - var current = Peek()!.Value; - if (char.IsWhiteSpace(current)) - { - if (current is '\n') - { - _line += 1; - // note(nub31): Next increments the column, so 0 is correct here - _column = 0; - } - - Next(); - continue; - } - - if (current == '/' && Peek(1) == '/') - { - // note(nub31): Keep newline so next iteration increments the line counter - while (Peek() is not '\n') - { - Next(); - } - - continue; - } - - Tokens.Add(ParseToken(current, _line, _column)); - } - catch (TokenizerException e) - { - Diagnostics.Add(e.Diagnostic); - Next(); - } - } - } - - private Token ParseToken(char current, int lineStart, int columnStart) - { - if (char.IsLetter(current) || current == '_') - { - var buffer = string.Empty; - - while (Peek() != null && (char.IsLetterOrDigit(Peek()!.Value) || Peek() == '_')) - { - buffer += Peek(); - Next(); - } - - if (Keywords.TryGetValue(buffer, out var keywordSymbol)) - { - return new SymbolToken(CreateSpan(lineStart, columnStart), keywordSymbol); - } - - if (buffer is "true" or "false") - { - return new BoolLiteralToken(CreateSpan(lineStart, columnStart), Convert.ToBoolean(buffer)); - } - - return new IdentifierToken(CreateSpan(lineStart, columnStart), buffer); - } - - if (char.IsDigit(current)) - { - var buffer = string.Empty; - - if (current == '0' && Peek(1) is 'x') - { - buffer += "0x"; - Next(); - Next(); - while (Peek() != null && Uri.IsHexDigit(Peek()!.Value)) - { - buffer += Peek()!.Value; - Next(); - } - - if (buffer.Length <= 2) - { - throw new TokenizerException(Diagnostic - .Error("Invalid hex literal, no digits found") - .At(_fileName, _line, _column) - .Build()); - } - - return new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 16); - } - - if (current == '0' && Peek(1) is 'b') - { - buffer += "0b"; - Next(); - Next(); - while (Peek() != null && (Peek() == '0' || Peek() == '1')) - { - buffer += Peek()!.Value; - Next(); - } - - if (buffer.Length <= 2) - { - throw new TokenizerException(Diagnostic - .Error("Invalid binary literal, no digits found") - .At(_fileName, _line, _column) - .Build()); - } - - return new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 2); - } - - var isFloat = false; - while (Peek() != null) - { - var next = Peek()!.Value; - if (next == '.') - { - if (isFloat) - { - throw new TokenizerException(Diagnostic - .Error("More than one period found in float literal") - .At(_fileName, _line, _column) - .Build()); - } - - isFloat = true; - buffer += next; - Next(); - } - else if (char.IsDigit(next)) - { - buffer += next; - Next(); - } - else - { - break; - } - } - - if (isFloat) - { - return new FloatLiteralToken(CreateSpan(lineStart, columnStart), buffer); - } - else - { - return new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 10); - } - } - - if (current == '"') - { - Next(); - var buffer = string.Empty; - - while (true) - { - var next = Peek(); - if (!next.HasValue) - { - throw new TokenizerException(Diagnostic - .Error("Unclosed string literal") - .At(_fileName, _line, _column) - .Build()); - } - - if (next is '\n') - { - _line += 1; - break; - } - - if (next is '"') - { - Next(); - break; - } - - buffer += next; - Next(); - } - - return new StringLiteralToken(CreateSpan(lineStart, columnStart), buffer); - } - - foreach (var (pattern, symbol) in OrderedSymbols) - { - for (var i = 0; i < pattern.Length; i++) - { - var c = Peek(i); - if (!c.HasValue || c.Value != pattern[i]) break; - - if (i == pattern.Length - 1) - { - for (var j = 0; j <= i; j++) - { - Next(); - } - - return new SymbolToken(CreateSpan(lineStart, columnStart), symbol); - } - } - } - - throw new TokenizerException(Diagnostic.Error($"Unknown token '{current}'").Build()); - } - - private SourceSpan CreateSpan(int lineStart, int columnStart) - { - return new SourceSpan(_fileName, new SourceLocation(lineStart, columnStart), new SourceLocation(_line, _column)); - } - - private char? Peek(int offset = 0) - { - if (_index + offset < _content.Length) - { - return _content[_index + offset]; - } - - return null; - } - - private void Next() - { - _index += 1; - _column += 1; - } -} - -public class TokenizerException : Exception -{ - public Diagnostic Diagnostic { get; } - - public TokenizerException(Diagnostic diagnostic) : base(diagnostic.Message) - { - Diagnostic = diagnostic; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Variant.cs b/compiler/NubLang/Variant.cs deleted file mode 100644 index c9898b8..0000000 --- a/compiler/NubLang/Variant.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace NubLang; - -public readonly struct Variant where T1 : notnull where T2 : notnull -{ - public Variant() - { - throw new InvalidOperationException("Variant must be initialized with a value"); - } - - public Variant(T1 value) - { - _value = value; - } - - public Variant(T2 value) - { - _value = value; - } - - private readonly object _value; - - public void Match(Action on1, Action on2) - { - switch (_value) - { - case T1 v1: - on1(v1); - break; - case T2 v2: - on2(v2); - break; - default: - throw new InvalidCastException(); - } - } - - public T Match(Func on1, Func on2) - { - return _value switch - { - T1 v1 => on1(v1), - T2 v2 => on2(v2), - _ => throw new InvalidCastException() - }; - } - - public bool IsCase1([NotNullWhen(true)] out T1? value) - { - if (_value is T1 converted) - { - value = converted; - return true; - } - - value = default; - return false; - } - - public bool IsCase2([NotNullWhen(true)] out T2? value) - { - if (_value is T2 converted) - { - value = converted; - return true; - } - - value = default; - return false; - } - - public static implicit operator Variant(T1 value) => new(value); - public static implicit operator Variant(T2 value) => new(value); -} \ No newline at end of file diff --git a/compiler/Compiler/Parser.cs b/compiler/Parser.cs similarity index 100% rename from compiler/Compiler/Parser.cs rename to compiler/Parser.cs diff --git a/compiler/Compiler/Program.cs b/compiler/Program.cs similarity index 100% rename from compiler/Compiler/Program.cs rename to compiler/Program.cs diff --git a/compiler/Compiler/Tokenizer.cs b/compiler/Tokenizer.cs similarity index 100% rename from compiler/Compiler/Tokenizer.cs rename to compiler/Tokenizer.cs diff --git a/compiler/Compiler/TypeChecker.cs b/compiler/TypeChecker.cs similarity index 100% rename from compiler/Compiler/TypeChecker.cs rename to compiler/TypeChecker.cs diff --git a/compiler/Compiler/test.nub b/compiler/test.nub similarity index 100% rename from compiler/Compiler/test.nub rename to compiler/test.nub