diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index 6e835d9..adcebad 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -22,6 +22,18 @@ foreach (var file in args) syntaxTrees.Add(syntaxTree); } +foreach (var diagnostic in diagnostics) +{ + Console.Error.WriteLine(diagnostic.FormatANSI()); +} + +if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)) +{ + return 1; +} + +diagnostics.Clear(); + ModuleRepository moduleRepository; try @@ -55,6 +67,8 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro return 1; } +diagnostics.Clear(); + Directory.CreateDirectory(".build"); for (var i = 0; i < args.Length; i++) diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs index 0b6c812..c8c0bc5 100644 --- a/compiler/NubLang/Diagnostics/Diagnostic.cs +++ b/compiler/NubLang/Diagnostics/Diagnostic.cs @@ -152,7 +152,7 @@ public class Diagnostic if (i >= startLine && i <= endLine) { var markerStartColumn = 1; - var markerEndColumn = line.Length; + var markerEndColumn = line.Length + 1; if (i == startLine) { @@ -262,6 +262,10 @@ public class Diagnostic { switch (token) { + case CommentToken: + { + return ConsoleColors.Colorize(tokenText, ConsoleColors.Green); + } case IdentifierToken: { return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightWhite); diff --git a/compiler/NubLang/Generation/LlvmGenerator.cs b/compiler/NubLang/Generation/LlvmGenerator.cs index bdd0249..bfc971a 100644 --- a/compiler/NubLang/Generation/LlvmGenerator.cs +++ b/compiler/NubLang/Generation/LlvmGenerator.cs @@ -126,10 +126,10 @@ public class LlvmGenerator case BlockNode blockNode: EmitBlock(writer, blockNode); break; - case BreakNode breakNode: + case BreakNode: EmitBreak(writer); break; - case ContinueNode continueNode: + case ContinueNode: EmitContinue(writer); break; case DeferNode deferNode: @@ -380,7 +380,8 @@ public class LlvmGenerator private Tmp EmitDereference(IndentedTextWriter writer, DereferenceNode dereferenceNode) { - throw new NotImplementedException(); + var target = EmitExpression(writer, dereferenceNode.Target); + return new Tmp(target.Ident, dereferenceNode.Type, true); } private Tmp EmitUnaryExpression(IndentedTextWriter writer, UnaryExpressionNode unaryExpressionNode) diff --git a/compiler/NubLang/Syntax/Parser.cs b/compiler/NubLang/Syntax/Parser.cs index f0ee5a3..1bb36d4 100644 --- a/compiler/NubLang/Syntax/Parser.cs +++ b/compiler/NubLang/Syntax/Parser.cs @@ -9,6 +9,19 @@ public sealed class Parser private int _tokenIndex; private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null; + + private bool HasTrailingWhitespace(Token token) + { + var index = _tokens.IndexOf(token); + return index + 1 < _tokens.Count && _tokens[index + 1] is WhitespaceToken or CommentToken; + } + + private bool HasLeadingWhitespace(Token token) + { + var index = _tokens.IndexOf(token); + return index - 1 < _tokens.Count && _tokens[index - 1] is WhitespaceToken or CommentToken; + } + private bool HasToken => CurrentToken != null; public List Diagnostics { get; set; } = []; @@ -194,27 +207,36 @@ public sealed class Parser { var startIndex = _tokenIndex; - if (TryExpectSymbol(out var symbol)) + if (CurrentToken is SymbolToken symbolToken) { - switch (symbol) + switch (symbolToken.Symbol) { case Symbol.OpenBrace: + Next(); return ParseBlock(startIndex); case Symbol.Return: + Next(); return ParseReturn(startIndex); case Symbol.If: + Next(); return ParseIf(startIndex); case Symbol.While: + Next(); return ParseWhile(startIndex); case Symbol.For: + Next(); return ParseFor(startIndex); case Symbol.Let: + Next(); return ParseVariableDeclaration(startIndex); case Symbol.Defer: + Next(); return ParseDefer(startIndex); case Symbol.Break: + Next(); return new BreakSyntax(GetTokens(startIndex)); case Symbol.Continue: + Next(); return new ContinueSyntax(GetTokens(startIndex)); } } @@ -319,7 +341,7 @@ public sealed class Parser var startIndex = _tokenIndex; var left = ParsePrimaryExpression(); - while (CurrentToken is SymbolToken symbolToken && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence) + while (CurrentToken is SymbolToken symbolToken && HasLeadingWhitespace(symbolToken) && HasTrailingWhitespace(symbolToken) && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence) { Next(); var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1); @@ -463,6 +485,68 @@ public sealed class Parser return ParsePostfixOperators(expr); } + private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr) + { + if (CurrentToken == null || HasLeadingWhitespace(CurrentToken)) + { + return expr; + } + + var startIndex = _tokenIndex; + while (HasToken) + { + if (TryExpectSymbol(Symbol.Caret)) + { + expr = new DereferenceSyntax(GetTokens(startIndex), expr); + continue; + } + + if (TryExpectSymbol(Symbol.Period)) + { + var member = ExpectIdentifier(); + 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 ParseParenthesizedExpression() + { + var expression = ParseExpression(); + ExpectSymbol(Symbol.CloseParen); + return expression; + } + private AddressOfSyntax ParseAddressOf(int startIndex) { var expression = ParsePrimaryExpression(); @@ -518,63 +602,6 @@ public sealed class Parser return new LocalIdentifierSyntax(GetTokens(startIndex), identifier); } - private ExpressionSyntax ParseParenthesizedExpression() - { - var expression = ParseExpression(); - ExpectSymbol(Symbol.CloseParen); - return expression; - } - - private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr) - { - var startIndex = _tokenIndex; - while (HasToken) - { - if (TryExpectSymbol(Symbol.Caret)) - { - expr = new DereferenceSyntax(GetTokens(startIndex), expr); - continue; - } - - if (TryExpectSymbol(Symbol.Period)) - { - var member = ExpectIdentifier(); - 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(); @@ -914,6 +941,10 @@ public sealed class Parser private void Next() { _tokenIndex++; + while (_tokenIndex < _tokens.Count && _tokens[_tokenIndex] is WhitespaceToken or CommentToken) + { + _tokenIndex++; + } } private List GetTokens(int tokenStartIndex) diff --git a/compiler/NubLang/Syntax/Token.cs b/compiler/NubLang/Syntax/Token.cs index 9ad3417..38dc827 100644 --- a/compiler/NubLang/Syntax/Token.cs +++ b/compiler/NubLang/Syntax/Token.cs @@ -2,18 +2,38 @@ namespace NubLang.Syntax; -public abstract record Token(SourceSpan Span); - -public record IdentifierToken(SourceSpan Span, string Value) : Token(Span) +public abstract class Token(SourceSpan span) { + public SourceSpan Span { get; } = span; +} + +public class WhitespaceToken(SourceSpan span) : Token(span); + +public class CommentToken(SourceSpan span, string comment) : Token(span) +{ + public string Comment { get; } = comment; + + public override string ToString() + { + return "// " + Comment; + } +} + +public class IdentifierToken(SourceSpan span, string value) : Token(span) +{ + public string Value { get; } = value; + public override string ToString() { return Value; } } -public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span) +public class IntLiteralToken(SourceSpan span, string value, int @base) : Token(span) { + public string Value { get; } = value; + public int Base { get; } = @base; + private string GetNumericValue() { return Base switch @@ -43,24 +63,30 @@ public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(S } } -public record StringLiteralToken(SourceSpan Span, string Value) : Token(Span) +public class StringLiteralToken(SourceSpan span, string value) : Token(span) { + public string Value { get; } = value; + public override string ToString() { return $"\"{Value}\""; } } -public record BoolLiteralToken(SourceSpan Span, bool Value) : Token(Span) +public class BoolLiteralToken(SourceSpan span, bool value) : Token(span) { + public bool Value { get; } = value; + public override string ToString() { return Value ? "true" : "false"; } } -public record FloatLiteralToken(SourceSpan Span, string Value) : Token(Span) +public class FloatLiteralToken(SourceSpan span, string value) : Token(span) { + public string Value { get; } = value; + public float AsF32 => Convert.ToSingle(Value); public double AsF64 => Convert.ToDouble(Value); @@ -134,8 +160,10 @@ public enum Symbol QuestionMark, } -public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span) +public class SymbolToken(SourceSpan span, Symbol symbol) : Token(span) { + public Symbol Symbol { get; } = symbol; + public override string ToString() { return Symbol switch @@ -189,7 +217,6 @@ public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span) Symbol.Pipe => "|", Symbol.At => "@", Symbol.QuestionMark => "?", - Symbol.Tilde => "~", _ => "none", }; } diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs index 12a8cb4..b73abd8 100644 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ b/compiler/NubLang/Syntax/Tokenizer.cs @@ -29,32 +29,7 @@ public sealed class Tokenizer { try { - var current = _content[_index]; - - if (char.IsWhiteSpace(current)) - { - if (current == '\n') - { - _line += 1; - _column = 0; - } - - Next(); - continue; - } - - if (current == '/' && _index + 1 < _content.Length && _content[_index + 1] == '/') - { - Next(2); - while (_index < _content.Length && _content[_index] != '\n') - { - Next(); - } - - continue; - } - - tokens.Add(ParseToken(current, _line, _column)); + tokens.Add(ParseToken()); } catch (CompileException e) { @@ -66,14 +41,40 @@ public sealed class Tokenizer return tokens; } - private Token ParseToken(char current, int lineStart, int columnStart) + private Token ParseToken() { - if (char.IsDigit(current)) + var lineStart = _line; + var columnStart = _column; + + if (char.IsWhiteSpace(_content[_index])) + { + while (_index < _content.Length && char.IsWhiteSpace(_content[_index])) + { + Next(); + } + + return new WhitespaceToken(CreateSpan(lineStart, columnStart)); + } + + if (_content[_index] == '/' && _index + 1 < _content.Length && _content[_index + 1] == '/') + { + var startIndex = _index; + + Next(2); + while (_index < _content.Length && _content[_index] != '\n') + { + Next(); + } + + return new CommentToken(CreateSpan(lineStart, columnStart), _content.AsSpan(startIndex, _index - startIndex).ToString()); + } + + if (char.IsDigit(_content[_index])) { return ParseNumber(lineStart, columnStart); } - if (current == '"') + if (_content[_index] == '"') { return ParseString(lineStart, columnStart); } @@ -87,12 +88,12 @@ public sealed class Tokenizer } } - if (char.IsLetter(current) || current == '_') + if (char.IsLetter(_content[_index]) || _content[_index] == '_') { return ParseIdentifier(lineStart, columnStart); } - throw new CompileException(Diagnostic.Error($"Unknown token '{current}'").Build()); + throw new CompileException(Diagnostic.Error($"Unknown token '{_content[_index]}'").Build()); } private Token ParseNumber(int lineStart, int columnStart) @@ -382,7 +383,26 @@ public sealed class Tokenizer private void Next(int count = 1) { - _index += count; - _column += count; + for (var i = 0; i < count; i++) + { + if (_index < _content.Length) + { + if (_content[_index] == '\n') + { + _line += 1; + _column = 1; + } + else + { + _column++; + } + } + else + { + _column++; + } + + _index++; + } } } \ No newline at end of file diff --git a/examples/playgroud/main.nub b/examples/playgroud/main.nub index f8cbe17..64b27eb 100644 --- a/examples/playgroud/main.nub +++ b/examples/playgroud/main.nub @@ -9,9 +9,10 @@ struct Test extern "main" func main(argc: i64, argv: [?]^i8) { - let x = [1, 2, 3] - - test(x) + let x: ^i8 = "test" + // test + ^x^ = "uwu" + puts(x) } func test(arr: [?]i64)