From cb2411a7eb257bdcbda8722e6d3cc2f22aadf3d4 Mon Sep 17 00:00:00 2001 From: Oliver Stene Date: Sun, 8 Feb 2026 00:05:59 +0100 Subject: [PATCH] ... --- compiler/Compiler.sln | 6 + compiler/Compiler/Compiler.csproj | 10 + compiler/Compiler/Generator.cs | 216 +++++++ compiler/Compiler/Parser.cs | 377 +++++++++++ compiler/Compiler/Program.cs | 16 + compiler/Compiler/Tokenizer.cs | 410 ++++++++++++ compiler/NubLang.CLI/Program.cs | 50 +- compiler/NubLang/Generation/CType.cs | 97 --- compiler/NubLang/Generation/Generator.cs | 590 ------------------ .../NubLang/Generation/IndentedTextWriter.cs | 70 --- compiler/NubLang/Sugar/DeSugar.cs | 6 + 11 files changed, 1042 insertions(+), 806 deletions(-) create mode 100644 compiler/Compiler/Compiler.csproj create mode 100644 compiler/Compiler/Generator.cs create mode 100644 compiler/Compiler/Parser.cs create mode 100644 compiler/Compiler/Program.cs create mode 100644 compiler/Compiler/Tokenizer.cs delete mode 100644 compiler/NubLang/Generation/CType.cs delete mode 100644 compiler/NubLang/Generation/Generator.cs delete mode 100644 compiler/NubLang/Generation/IndentedTextWriter.cs create mode 100644 compiler/NubLang/Sugar/DeSugar.cs diff --git a/compiler/Compiler.sln b/compiler/Compiler.sln index 292d374..8e8e87b 100644 --- a/compiler/Compiler.sln +++ b/compiler/Compiler.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compiler", "Compiler\Compiler.csproj", "{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Debug|Any CPU.Build.0 = Debug|Any CPU {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.ActiveCfg = Release|Any CPU {A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.Build.0 = Release|Any CPU + {C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/compiler/Compiler/Compiler.csproj b/compiler/Compiler/Compiler.csproj new file mode 100644 index 0000000..85b4959 --- /dev/null +++ b/compiler/Compiler/Compiler.csproj @@ -0,0 +1,10 @@ + + + + Exe + net9.0 + enable + enable + + + diff --git a/compiler/Compiler/Generator.cs b/compiler/Compiler/Generator.cs new file mode 100644 index 0000000..b2acf3a --- /dev/null +++ b/compiler/Compiler/Generator.cs @@ -0,0 +1,216 @@ +using System.Globalization; +using System.Text; + +namespace Compiler; + +public sealed class Generator(List nodes) +{ + public static string Emit(List nodes) + { + return new Generator(nodes).Emit(); + } + + private IndentedTextWriter writer = new(); + + private string Emit() + { + writer.WriteLine(""" + struct string { + const char *data; + int length; + }; + + """); + + foreach (var node in nodes.OfType()) + { + var parameters = node.Parameters.Select(x => $"{CType(x.Type)} {x.Name.Ident}"); + writer.WriteLine($"{CType(node.ReturnType)} {node.Name.Ident}({string.Join(", ", parameters)});"); + } + + writer.WriteLine(); + + foreach (var node in nodes.OfType()) + { + var parameters = node.Parameters.Select(x => $"{CType(x.Type)} {x.Name.Ident}"); + writer.WriteLine($"{CType(node.ReturnType)} {node.Name.Ident}({string.Join(", ", parameters)})"); + writer.WriteLine("{"); + using (writer.Indent()) + { + EmitStatement(node.Body); + } + + writer.WriteLine("}"); + writer.WriteLine(); + } + + return writer.ToString(); + } + + private void EmitStatement(NodeStatement node) + { + switch (node) + { + case NodeStatementBlock statement: + EmitStatementBlock(statement); + break; + case NodeStatementFuncCall statement: + EmitStatementFuncCall(statement); + break; + } + } + + private void EmitStatementBlock(NodeStatementBlock node) + { + writer.WriteLine("{"); + using (writer.Indent()) + { + foreach (var statement in node.Statements) + EmitStatement(statement); + } + + writer.WriteLine("}"); + } + + private void EmitStatementFuncCall(NodeStatementFuncCall node) + { + var name = EmitExpression(node.Func); + var parameterValues = node.Parameters.Select(EmitExpression).ToList(); + writer.WriteLine($"{name}({string.Join(", ", parameterValues)});"); + } + + private string EmitExpression(NodeExpression node) + { + return node switch + { + NodeExpressionBoolLiteral expression => EmitExpressionBoolLiteral(expression), + NodeExpressionFloatLiteral expression => EmitExpressionFloatLiteral(expression), + NodeExpressionIntLiteral expression => EmitExpressionIntLiteral(expression), + NodeExpressionStringLiteral expression => EmitExpressionStringLiteral(expression), + NodeExpressionIdent expression => EmitExpressionIdent(expression), + }; + } + + private string EmitExpressionBoolLiteral(NodeExpressionBoolLiteral expression) + { + return expression.Value.Value ? "1" : "0"; + } + + private string EmitExpressionFloatLiteral(NodeExpressionFloatLiteral expression) + { + return expression.Value.Value.ToString(CultureInfo.InvariantCulture); + } + + private string EmitExpressionIntLiteral(NodeExpressionIntLiteral expression) + { + return expression.Value.Value.ToString(); + } + + private string EmitExpressionStringLiteral(NodeExpressionStringLiteral expression) + { + return $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}"; + } + + private string EmitExpressionIdent(NodeExpressionIdent expression) + { + return expression.Value.Ident; + } + + private static string CType(NodeType node) + { + return node switch + { + NodeTypeCustom type => $"struct {type.Name.Ident}", + NodeTypeFloat type => type.Width switch + { + 32 => "float", + 64 => "double", + }, + NodeTypePointer type => $"{CType(type.To)}*", + NodeTypeSInt type => type.Width switch + { + 8 => "byte", + 16 => "short", + 32 => "int", + 64 => "long", + }, + NodeTypeUInt type => type.Width switch + { + 8 => "unsigned byte", + 16 => "unsigned short", + 32 => "unsigned int", + 64 => "unsigned long", + }, + NodeTypeString => "struct string", + NubTypeVoid => "void", + _ => throw new ArgumentOutOfRangeException(nameof(node)) + }; + } +} + +internal class IndentedTextWriter +{ + private readonly StringBuilder _builder = new(); + private int _indentLevel; + + public IDisposable Indent() + { + _indentLevel++; + return new IndentScope(this); + } + + public void WriteLine(string text) + { + WriteIndent(); + _builder.AppendLine(text); + } + + public void Write(string text) + { + WriteIndent(); + _builder.Append(text); + } + + public void WriteLine() + { + _builder.AppendLine(); + } + + public override string ToString() + { + return _builder.ToString(); + } + + private void WriteIndent() + { + if (_builder.Length > 0) + { + var lastChar = _builder[^1]; + if (lastChar != '\n' && lastChar != '\r') + return; + } + + for (var i = 0; i < _indentLevel; i++) + { + _builder.Append(" "); + } + } + + private class IndentScope : IDisposable + { + private readonly IndentedTextWriter _writer; + private bool _disposed; + + public IndentScope(IndentedTextWriter writer) + { + _writer = writer; + } + + public void Dispose() + { + if (_disposed) return; + _writer._indentLevel--; + _disposed = true; + } + } +} \ No newline at end of file diff --git a/compiler/Compiler/Parser.cs b/compiler/Compiler/Parser.cs new file mode 100644 index 0000000..820574a --- /dev/null +++ b/compiler/Compiler/Parser.cs @@ -0,0 +1,377 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Compiler; + +public sealed class Parser(List tokens) +{ + public static List Parse(List tokens) + { + return new Parser(tokens).Parse(); + } + + private int index; + + private List Parse() + { + var nodes = new List(); + + while (Peek() != null) + nodes.Add(ParseDefinition()); + + return nodes; + } + + private NodeDefinition ParseDefinition() + { + var startIndex = index; + + if (TryExpectKeyword(Keyword.Func)) + { + var name = ExpectIdent(); + var parameters = new List(); + + ExpectSymbol(Symbol.OpenParen); + while (!TryExpectSymbol(Symbol.CloseParen)) + { + var paramStartIndex = index; + var parameterName = ExpectIdent(); + ExpectSymbol(Symbol.Colon); + var parameterType = ParseType(); + parameters.Add(new NodeDefinitionFunc.Param(TokensFrom(paramStartIndex), parameterName, parameterType)); + } + + ExpectSymbol(Symbol.Colon); + var returnType = ParseType(); + + var body = ParseStatement(); + + return new NodeDefinitionFunc(TokensFrom(startIndex), name, parameters, body, returnType); + } + + throw new Exception("Not a valid definition"); + } + + private NodeStatement ParseStatement() + { + var startIndex = index; + + if (TryExpectSymbol(Symbol.OpenCurly)) + { + var statements = new List(); + while (!TryExpectSymbol(Symbol.CloseCurly)) + statements.Add(ParseStatement()); + + return new NodeStatementBlock(TokensFrom(startIndex), statements); + } + + var expression = ParseExpression(); + var parameters = new List(); + + ExpectSymbol(Symbol.OpenParen); + while (!TryExpectSymbol(Symbol.CloseParen)) + parameters.Add(ParseExpression()); + + return new NodeStatementFuncCall(TokensFrom(startIndex), expression, parameters); + } + + private NodeExpression ParseExpression() + { + var startIndex = index; + + if (TryExpectIntLiteral(out var intLiteral)) + { + return new NodeExpressionIntLiteral(TokensFrom(startIndex), intLiteral); + } + + if (TryExpectFloatLiteral(out var floatLiteral)) + { + return new NodeExpressionFloatLiteral(TokensFrom(startIndex), floatLiteral); + } + + if (TryExpectStringLiteral(out var stringLiteral)) + { + return new NodeExpressionStringLiteral(TokensFrom(startIndex), stringLiteral); + } + + if (TryExpectBoolLiteral(out var boolLiteral)) + { + return new NodeExpressionBoolLiteral(TokensFrom(startIndex), boolLiteral); + } + + if (TryExpectIdent(out var ident)) + { + return new NodeExpressionIdent(TokensFrom(startIndex), ident); + } + + throw new Exception("Not a valid expression"); + } + + private NodeType ParseType() + { + var startIndex = index; + + if (TryExpectSymbol(Symbol.Caret)) + { + var to = ParseType(); + return new NodeTypePointer(TokensFrom(startIndex), to); + } + + if (TryExpectIdent(out var ident)) + { + switch (ident.Ident) + { + case "void": + return new NubTypeVoid(TokensFrom(startIndex)); + case "string": + return new NodeTypeString(TokensFrom(startIndex)); + case "i8": + return new NodeTypeSInt(TokensFrom(startIndex), 8); + case "i16": + return new NodeTypeSInt(TokensFrom(startIndex), 16); + case "i32": + return new NodeTypeSInt(TokensFrom(startIndex), 32); + case "i64": + return new NodeTypeSInt(TokensFrom(startIndex), 64); + case "u8": + return new NodeTypeUInt(TokensFrom(startIndex), 8); + case "u16": + return new NodeTypeUInt(TokensFrom(startIndex), 16); + case "u32": + return new NodeTypeUInt(TokensFrom(startIndex), 32); + case "u64": + return new NodeTypeUInt(TokensFrom(startIndex), 64); + case "f32": + return new NodeTypeFloat(TokensFrom(startIndex), 32); + case "f64": + return new NodeTypeFloat(TokensFrom(startIndex), 64); + default: + return new NodeTypeCustom(TokensFrom(startIndex), ident); + } + } + + throw new Exception("Not a valid type"); + } + + private List TokensFrom(int startIndex) + { + return tokens.GetRange(startIndex, index - startIndex); + } + + private bool TryExpectKeyword(Keyword keyword) + { + if (Peek() is TokenKeyword token && token.Keyword == keyword) + { + Consume(); + return true; + } + + return false; + } + + private void ExpectSymbol(Symbol symbol) + { + if (Peek() is TokenSymbol token && token.Symbol == symbol) + { + Consume(); + return; + } + + throw new Exception($"Expected symbol '{symbol}'"); + } + + private bool TryExpectSymbol(Symbol symbol) + { + if (Peek() is TokenSymbol token && token.Symbol == symbol) + { + Consume(); + return true; + } + + return false; + } + + private TokenIdent ExpectIdent() + { + if (Peek() is TokenIdent token) + { + Consume(); + return token; + } + + throw new Exception("Expected ident"); + } + + private bool TryExpectIdent([NotNullWhen(true)] out TokenIdent? ident) + { + if (Peek() is TokenIdent token) + { + Consume(); + ident = token; + return true; + } + + ident = null; + return false; + } + + private bool TryExpectIntLiteral([NotNullWhen(true)] out TokenIntLiteral? intLiteral) + { + if (Peek() is TokenIntLiteral token) + { + Consume(); + intLiteral = token; + return true; + } + + intLiteral = null; + return false; + } + + private bool TryExpectFloatLiteral([NotNullWhen(true)] out TokenFloatLiteral? floatLiteral) + { + if (Peek() is TokenFloatLiteral token) + { + Consume(); + floatLiteral = token; + return true; + } + + floatLiteral = null; + return false; + } + + private bool TryExpectStringLiteral([NotNullWhen(true)] out TokenStringLiteral? stringLiteral) + { + if (Peek() is TokenStringLiteral token) + { + Consume(); + stringLiteral = token; + return true; + } + + stringLiteral = null; + return false; + } + + private bool TryExpectBoolLiteral([NotNullWhen(true)] out TokenBoolLiteral? boolLiteral) + { + if (Peek() is TokenBoolLiteral token) + { + Consume(); + boolLiteral = token; + return true; + } + + boolLiteral = null; + return false; + } + + private Token Consume() + { + if (index >= tokens.Count) + throw new Exception("End of tokens"); + + return tokens[index++]; + } + + private Token? Peek(int offset = 0) + { + if (index + offset >= tokens.Count) + return null; + + return tokens[index + offset]; + } +} + +public abstract class Node(List tokens) +{ + public List Tokens = tokens; +} + +public abstract class NodeDefinition(List tokens) : Node(tokens); + +public sealed class NodeDefinitionFunc(List tokens, TokenIdent name, List parameters, NodeStatement body, NodeType returnType) + : NodeDefinition(tokens) +{ + public TokenIdent Name = name; + public List Parameters = parameters; + public NodeStatement Body = body; + public NodeType ReturnType = returnType; + + public sealed class Param(List tokens, TokenIdent name, NodeType type) : Node(tokens) + { + public TokenIdent Name = name; + public NodeType Type = type; + } +} + +public abstract class NodeStatement(List tokens) : Node(tokens); + +public sealed class NodeStatementBlock(List tokens, List statements) : NodeStatement(tokens) +{ + public List Statements = statements; +} + +public sealed class NodeStatementFuncCall(List tokens, NodeExpression func, List parameters) : NodeStatement(tokens) +{ + public NodeExpression Func = func; + public List Parameters = parameters; +} + +public abstract class NodeExpression(List tokens) : Node(tokens); + +public sealed class NodeExpressionIntLiteral(List tokens, TokenIntLiteral value) : NodeExpression(tokens) +{ + public TokenIntLiteral Value = value; +} + +public sealed class NodeExpressionFloatLiteral(List tokens, TokenFloatLiteral value) : NodeExpression(tokens) +{ + public TokenFloatLiteral Value = value; +} + +public sealed class NodeExpressionStringLiteral(List tokens, TokenStringLiteral value) : NodeExpression(tokens) +{ + public TokenStringLiteral Value = value; +} + +public sealed class NodeExpressionBoolLiteral(List tokens, TokenBoolLiteral value) : NodeExpression(tokens) +{ + public TokenBoolLiteral Value = value; +} + +public sealed class NodeExpressionIdent(List tokens, TokenIdent value) : NodeExpression(tokens) +{ + public TokenIdent Value = value; +} + +public abstract class NodeType(List tokens) : Node(tokens); + +public sealed class NubTypeVoid(List tokens) : NodeType(tokens); + +public sealed class NodeTypeUInt(List tokens, int width) : NodeType(tokens) +{ + public int Width = width; +} + +public sealed class NodeTypeSInt(List tokens, int width) : NodeType(tokens) +{ + public int Width = width; +} + +public sealed class NodeTypeFloat(List tokens, int width) : NodeType(tokens) +{ + public int Width = width; +} + +public sealed class NodeTypeString(List tokens) : NodeType(tokens); + +public sealed class NodeTypeCustom(List tokens, TokenIdent name) : NodeType(tokens) +{ + public TokenIdent Name = name; +} + +public sealed class NodeTypePointer(List tokens, NodeType to) : NodeType(tokens) +{ + public NodeType To = to; +} \ No newline at end of file diff --git a/compiler/Compiler/Program.cs b/compiler/Compiler/Program.cs new file mode 100644 index 0000000..c89edc7 --- /dev/null +++ b/compiler/Compiler/Program.cs @@ -0,0 +1,16 @@ +using Compiler; + +const string contents = """ + func main(): void { + do_something("test") + } + + func do_something(text: string): void { + } + """; + +var tokens = Tokenizer.Tokenize(contents); +var nodes = Parser.Parse(tokens); +var output = Generator.Emit(nodes); + +Console.WriteLine(output); \ No newline at end of file diff --git a/compiler/Compiler/Tokenizer.cs b/compiler/Compiler/Tokenizer.cs new file mode 100644 index 0000000..e809b55 --- /dev/null +++ b/compiler/Compiler/Tokenizer.cs @@ -0,0 +1,410 @@ +using System.Numerics; +using System.Text; + +namespace Compiler; + +public sealed class Tokenizer(string contents) +{ + public static List Tokenize(string contents) + { + return new Tokenizer(contents).Tokenize(); + } + + private int index; + private int line = 1; + private int column = 1; + + private List Tokenize() + { + var tokens = new List(); + + while (true) + { + if (!TryPeek(out var c)) + break; + + if (char.IsWhiteSpace(c)) + { + Consume(); + continue; + } + + tokens.Add(ParseToken()); + } + + return tokens; + } + + private Token ParseToken() + { + var startColumn = column; + var c = Peek()!.Value; + + if (char.IsDigit(c)) + { + switch (c) + { + case '0' when Peek(1) is 'x': + { + Consume(); + Consume(); + + var parsed = BigInteger.Zero; + + while (TryPeek(out c)) + { + if (c == '_') + { + Consume(); + continue; + } + + if (!char.IsAsciiHexDigit(c)) + break; + + parsed <<= 4; + + Consume(); + parsed += c switch + { + >= '0' and <= '9' => c - '0', + >= 'a' and <= 'f' => c - 'a' + 10, + >= 'A' and <= 'F' => c - 'A' + 10, + _ => 0 + }; + } + + return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); + } + case '0' when Peek(1) is 'b': + { + Consume(); + Consume(); + + var parsed = BigInteger.Zero; + + while (TryPeek(out c)) + { + if (c == '_') + { + Consume(); + continue; + } + + if (c is not '0' and not '1') + break; + + parsed <<= 1; + if (Consume() == '1') + parsed += BigInteger.One; + } + + return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); + } + default: + { + var parsed = BigInteger.Zero; + + while (TryPeek(out c)) + { + if (c == '_') + { + Consume(); + continue; + } + + if (!char.IsDigit(c)) + break; + + parsed *= 10; + parsed += Consume() - '0'; + } + + return new TokenIntLiteral(line, startColumn, column - startColumn, parsed); + } + } + } + + switch (c) + { + case '"': + { + Consume(); + + var buf = new StringBuilder(); + + while (TryPeek(out c) && c != '"') + buf.Append(Consume()); + + Consume(); + + return new TokenStringLiteral(line, startColumn, column - startColumn, buf.ToString()); + } + case '{': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenCurly); + } + case '}': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseCurly); + } + case '(': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.OpenParen); + } + case ')': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.CloseParen); + } + case ',': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Comma); + } + case ':': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Colon); + } + case '^': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Caret); + } + case '!' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.BangEqual); + } + case '!': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Bang); + } + case '=' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.EqualEqual); + } + case '=': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Equal); + } + case '<' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThanEqual); + } + case '<': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.LessThan); + } + case '>' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThanEqual); + } + case '>': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.GreaterThan); + } + case '+' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.PlusEqual); + } + case '+': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Plus); + } + case '-' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.MinusEqual); + } + case '-': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Minus); + } + case '*' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.StarEqual); + } + case '*': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Star); + } + case '/' when Peek(1) is '=': + { + Consume(); + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlashEqual); + } + case '/': + { + Consume(); + return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ForwardSlash); + } + default: + { + if (char.IsLetter(c) || c == '_') + { + var buf = new StringBuilder(); + + while (TryPeek(out c) && (char.IsLetterOrDigit(c) || c == '_')) + buf.Append(Consume()); + + var value = buf.ToString(); + + return value switch + { + "func" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Func), + "let" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Let), + "if" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.If), + "true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true), + "false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false), + _ => new TokenIdent(line, startColumn, column - startColumn, value) + }; + } + + throw new Exception($"Unexpected character '{c}'"); + } + } + } + + private char Consume() + { + if (index >= contents.Length) + throw new Exception("End of tokens"); + + var c = contents[index]; + + if (c == '\n') + { + line += 1; + column = 1; + } + else + { + column += 1; + } + + index += 1; + + return c; + } + + private char? Peek(int offset = 0) + { + if (index + offset >= contents.Length) + return null; + + return contents[index + offset]; + } + + private bool TryPeek(out char c) + { + if (index >= contents.Length) + { + c = '\0'; + return false; + } + + c = contents[index]; + return true; + } +} + +public abstract class Token(int line, int column, int length) +{ + public int Line = line; + public int Column = column; + public int Length = length; +} + +public sealed class TokenIdent(int line, int column, int length, string ident) : Token(line, column, length) +{ + public string Ident = ident; +} + +public sealed class TokenIntLiteral(int line, int column, int length, BigInteger value) : Token(line, column, length) +{ + public BigInteger Value = value; +} + +public sealed class TokenFloatLiteral(int line, int column, int length, decimal value) : Token(line, column, length) +{ + public decimal Value = value; +} + +public sealed class TokenStringLiteral(int line, int column, int length, string value) : Token(line, column, length) +{ + public string Value = value; +} + +public sealed class TokenBoolLiteral(int line, int column, int length, bool value) : Token(line, column, length) +{ + public bool Value = value; +} + +public enum Symbol +{ + OpenCurly, + CloseCurly, + OpenParen, + CloseParen, + Comma, + Colon, + Caret, + Bang, + Equal, + EqualEqual, + BangEqual, + LessThan, + LessThanEqual, + GreaterThan, + GreaterThanEqual, + Plus, + PlusEqual, + Minus, + MinusEqual, + Star, + StarEqual, + ForwardSlash, + ForwardSlashEqual, +} + +public sealed class TokenSymbol(int line, int column, int length, Symbol symbol) : Token(line, column, length) +{ + public Symbol Symbol = symbol; +} + +public enum Keyword +{ + Func, + Let, + If, +} + +public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length) +{ + public Keyword Keyword = keyword; +} \ No newline at end of file diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index fb51798..4806c9d 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -1,7 +1,5 @@ -using System.Diagnostics; -using NubLang.Ast; +using NubLang.Ast; using NubLang.Diagnostics; -using NubLang.Generation; using NubLang.Syntax; var diagnostics = new List(); @@ -42,50 +40,4 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro return 1; } -var cPaths = new List(); - -Directory.CreateDirectory(".build"); - -for (var i = 0; i < args.Length; i++) -{ - var file = args[i]; - var compilationUnit = compilationUnits[i]; - - var generator = new Generator(compilationUnit); - var directory = Path.GetDirectoryName(file); - if (!string.IsNullOrWhiteSpace(directory)) - { - Directory.CreateDirectory(Path.Combine(".build", directory)); - } - - var path = Path.Combine(".build", Path.ChangeExtension(file, "c")); - File.WriteAllText(path, generator.Emit()); - cPaths.Add(path); -} - -var objectPaths = new List(); - -foreach (var cPath in cPaths) -{ - var objectPath = Path.ChangeExtension(cPath, "o"); - using var compileProcess = Process.Start("clang", [ - "-ffreestanding", "-std=c23", - "-g", "-c", - "-o", objectPath, - cPath, - ]); - - compileProcess.WaitForExit(); - - if (compileProcess.ExitCode != 0) - { - Console.Error.WriteLine($"clang failed with exit code {compileProcess.ExitCode}"); - return 1; - } - - objectPaths.Add(objectPath); -} - -Console.Out.WriteLine(string.Join(' ', objectPaths)); - return 0; \ No newline at end of file diff --git a/compiler/NubLang/Generation/CType.cs b/compiler/NubLang/Generation/CType.cs deleted file mode 100644 index 9fe4287..0000000 --- a/compiler/NubLang/Generation/CType.cs +++ /dev/null @@ -1,97 +0,0 @@ -using NubLang.Ast; - -namespace NubLang.Generation; - -public static class CType -{ - public static string Create(NubType type, string? variableName = null, bool constArraysAsPointers = true) - { - return type switch - { - NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""), - NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""), - NubIntType intType => CreateIntType(intType, variableName), - NubFloatType floatType => CreateFloatType(floatType, variableName), - NubPointerType ptr => CreatePointerType(ptr, variableName), - NubSliceType => "struct nub_slice" + (variableName != null ? $" {variableName}" : ""), - NubStringType => "struct nub_string" + (variableName != null ? $" {variableName}" : ""), - NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers), - NubArrayType arr => CreateArrayType(arr, variableName), - NubFuncType fn => CreateFuncType(fn, variableName), - NubStructType st => $"struct {st.Module}_{st.Name}_{NameMangler.Mangle(st)}" + (variableName != null ? $" {variableName}" : ""), - _ => throw new NotSupportedException($"C type generation not supported for: {type}") - }; - } - - private static string CreateIntType(NubIntType intType, string? varName) - { - var cType = intType.Width switch - { - 8 => intType.Signed ? "char" : "unsigned char", - 16 => intType.Signed ? "short" : "unsigned short", - 32 => intType.Signed ? "int" : "unsigned int", - 64 => intType.Signed ? "long long" : "unsigned long long", - _ => throw new NotSupportedException($"Unsupported integer width: {intType.Width}") - }; - return cType + (varName != null ? $" {varName}" : ""); - } - - private static string CreateFloatType(NubFloatType floatType, string? varName) - { - var cType = floatType.Width switch - { - 32 => "float", - 64 => "double", - _ => throw new NotSupportedException($"Unsupported float width: {floatType.Width}") - }; - return cType + (varName != null ? $" {varName}" : ""); - } - - private static string CreatePointerType(NubPointerType ptr, string? varName) - { - var baseType = Create(ptr.BaseType); - return baseType + "*" + (varName != null ? $" {varName}" : ""); - } - - private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool inStructDef) - { - var elementType = Create(arr.ElementType); - - // Treat const arrays as pointers unless in a struct definition - if (!inStructDef) - { - return elementType + "*" + (varName != null ? $" {varName}" : ""); - } - - if (varName != null) - { - return $"{elementType} {varName}[{arr.Size}]"; - } - - return $"{elementType}[{arr.Size}]"; - } - - private static string CreateArrayType(NubArrayType arr, string? varName) - { - var elementType = Create(arr.ElementType); - return elementType + "*" + (varName != null ? $" {varName}" : ""); - } - - private static string CreateFuncType(NubFuncType fn, string? varName) - { - var returnType = Create(fn.ReturnType); - var parameters = string.Join(", ", fn.Parameters.Select(p => Create(p))); - - if (string.IsNullOrEmpty(parameters)) - { - parameters = "void"; - } - - if (varName != null) - { - return $"{returnType} (*{varName})({parameters})"; - } - - return $"{returnType} (*)({parameters})"; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Generation/Generator.cs b/compiler/NubLang/Generation/Generator.cs deleted file mode 100644 index e7fc18e..0000000 --- a/compiler/NubLang/Generation/Generator.cs +++ /dev/null @@ -1,590 +0,0 @@ -using System.Diagnostics; -using System.Text; -using NubLang.Ast; -using NubLang.Syntax; - -namespace NubLang.Generation; - -public class Generator -{ - private readonly CompilationUnit _compilationUnit; - private readonly IndentedTextWriter _writer; - private readonly Stack> _deferStack = []; - private int _tmpIndex; - - public Generator(CompilationUnit compilationUnit) - { - _compilationUnit = compilationUnit; - _writer = new IndentedTextWriter(); - } - - // todo(nub31): Handle name collisions - private string NewTmp() - { - return $"_t{++_tmpIndex}"; - } - - private static string FuncName(string module, string name, string? externSymbol) - { - return externSymbol ?? $"{module}_{name}"; - } - - public string Emit() - { - _writer.WriteLine(""" - struct nub_string - { - unsigned long long length; - char *data; - }; - - struct nub_slice - { - unsigned long long length; - void *data; - }; - - """); - - foreach (var structType in _compilationUnit.ImportedStructTypes) - { - _writer.WriteLine(CType.Create(structType)); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - foreach (var field in structType.Fields) - { - _writer.WriteLine($"{CType.Create(field.Type, field.Name, constArraysAsPointers: false)};"); - } - } - - _writer.WriteLine("};"); - _writer.WriteLine(); - } - - // note(nub31): Forward declarations - foreach (var prototype in _compilationUnit.ImportedFunctions) - { - EmitLine(prototype.Tokens.FirstOrDefault()); - var parameters = prototype.Parameters.Count != 0 - ? string.Join(", ", prototype.Parameters.Select(x => CType.Create(x.Type, x.Name))) - : "void"; - - var name = FuncName(prototype.Module, prototype.Name, prototype.ExternSymbol); - _writer.WriteLine($"{CType.Create(prototype.ReturnType, name)}({parameters});"); - _writer.WriteLine(); - } - - // note(nub31): Normal functions - foreach (var funcNode in _compilationUnit.Functions) - { - if (funcNode.Body == null) continue; - - EmitLine(funcNode.Tokens.FirstOrDefault()); - var parameters = funcNode.Prototype.Parameters.Count != 0 - ? string.Join(", ", funcNode.Prototype.Parameters.Select(x => CType.Create(x.Type, x.Name))) - : "void"; - - var name = FuncName(funcNode.Module, funcNode.Name, funcNode.Prototype.ExternSymbol); - _writer.WriteLine($"{CType.Create(funcNode.Prototype.ReturnType, name)}({parameters})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - EmitBlock(funcNode.Body); - } - - _writer.WriteLine("}"); - _writer.WriteLine(); - } - - return _writer.ToString(); - } - - private void EmitStatement(StatementNode statementNode) - { - EmitLine(statementNode.Tokens.FirstOrDefault()); - switch (statementNode) - { - case AssignmentNode assignmentNode: - EmitAssignment(assignmentNode); - break; - case BlockNode blockNode: - _writer.WriteLine("{"); - using (_writer.Indent()) - { - EmitBlock(blockNode); - } - - _writer.WriteLine("}"); - break; - case BreakNode breakNode: - EmitBreak(breakNode); - break; - case ContinueNode continueNode: - EmitContinue(continueNode); - break; - case DeferNode deferNode: - EmitDefer(deferNode); - break; - case ForConstArrayNode forConstArrayNode: - EmitForConstArray(forConstArrayNode); - break; - case ForSliceNode forSliceNode: - EmitForSlice(forSliceNode); - break; - case IfNode ifNode: - EmitIf(ifNode); - break; - case ReturnNode returnNode: - EmitReturn(returnNode); - break; - case StatementFuncCallNode statementFuncCallNode: - EmitStatementFuncCall(statementFuncCallNode); - break; - case VariableDeclarationNode variableDeclarationNode: - EmitVariableDeclaration(variableDeclarationNode); - break; - case WhileNode whileNode: - EmitWhile(whileNode); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statementNode)); - } - } - - private void EmitLine(Token? token) - { - if (token == null) return; - var file = token.Span.FilePath; - var line = token.Span.Start.Line; - _writer.WriteLine($"#line {line} \"{file}\""); - } - - private void EmitAssignment(AssignmentNode assignmentNode) - { - var target = EmitExpression(assignmentNode.Target); - var value = EmitExpression(assignmentNode.Value); - _writer.WriteLine($"{target} = {value};"); - } - - private void EmitBreak(BreakNode _) - { - // todo(nub31): Emit deferred statements - _writer.WriteLine("break;"); - } - - private void EmitContinue(ContinueNode _) - { - // todo(nub31): Emit deferred statements - _writer.WriteLine("continue;"); - } - - private void EmitDefer(DeferNode deferNode) - { - _deferStack.Peek().Add(deferNode); - } - - private void EmitForSlice(ForSliceNode forSliceNode) - { - var targetType = (NubSliceType)forSliceNode.Target.Type; - var target = EmitExpression(forSliceNode.Target); - var indexName = forSliceNode.IndexName ?? NewTmp(); - - _writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {target}.length; ++{indexName})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - _writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementName)} = (({CType.Create(targetType.ElementType)}*){target}.data)[{indexName}];"); - EmitBlock(forSliceNode.Body); - } - - _writer.WriteLine("}"); - } - - private void EmitForConstArray(ForConstArrayNode forConstArrayNode) - { - var targetType = (NubConstArrayType)forConstArrayNode.Target.Type; - var target = EmitExpression(forConstArrayNode.Target); - var indexName = forConstArrayNode.IndexName ?? NewTmp(); - - _writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {targetType.Size}; ++{indexName})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - _writer.WriteLine($"{CType.Create(targetType.ElementType, forConstArrayNode.ElementName)} = {target}[{indexName}];"); - EmitBlock(forConstArrayNode.Body); - } - - _writer.WriteLine("}"); - } - - private void EmitIf(IfNode ifNode, bool elseIf = false) - { - var condition = EmitExpression(ifNode.Condition); - _writer.WriteLine($"{(elseIf ? "else " : "")}if ({condition})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - EmitBlock(ifNode.Body); - } - - _writer.WriteLine("}"); - ifNode.Else?.Match - ( - elseIfNode => EmitIf(elseIfNode, true), - elseNode => - { - _writer.WriteLine("else"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - EmitBlock(elseNode); - } - - _writer.WriteLine("}"); - } - ); - } - - private void EmitReturn(ReturnNode returnNode) - { - if (returnNode.Value == null) - { - var blockDefers = _deferStack.Peek(); - for (var i = blockDefers.Count - 1; i >= 0; i--) - { - EmitStatement(blockDefers[i].Statement); - } - - _writer.WriteLine("return;"); - } - else - { - var returnValue = EmitExpression(returnNode.Value); - - if (_deferStack.Peek().Count != 0) - { - var tmp = NewTmp(); - _writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};"); - - var blockDefers = _deferStack.Peek(); - for (var i = blockDefers.Count - 1; i >= 0; i--) - { - EmitStatement(blockDefers[i].Statement); - } - - EmitLine(returnNode.Tokens.FirstOrDefault()); - _writer.WriteLine($"return {tmp};"); - } - else - { - EmitLine(returnNode.Tokens.FirstOrDefault()); - _writer.WriteLine($"return {returnValue};"); - } - } - } - - private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode) - { - var funcCall = EmitFuncCall(statementFuncCallNode.FuncCall); - _writer.WriteLine($"{funcCall};"); - } - - private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode) - { - if (variableDeclarationNode.Assignment != null) - { - var value = EmitExpression(variableDeclarationNode.Assignment); - _writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.Name)} = {value};"); - } - else - { - _writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.Name)};"); - } - } - - private void EmitWhile(WhileNode whileNode) - { - var condition = EmitExpression(whileNode.Condition); - _writer.WriteLine($"while ({condition})"); - _writer.WriteLine("{"); - using (_writer.Indent()) - { - EmitBlock(whileNode.Body); - } - - _writer.WriteLine("}"); - } - - private string EmitExpression(ExpressionNode expressionNode) - { - if (expressionNode is IntermediateExpression) - { - throw new UnreachableException("Type checker fucked up"); - } - - var expr = expressionNode switch - { - ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode), - ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode), - BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode), - BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false", - ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode), - ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode), - CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"", - DereferenceNode dereferenceNode => EmitDereference(dereferenceNode), - Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode), - Float64LiteralNode float64LiteralNode => EmitFloat64Literal(float64LiteralNode), - CastNode castNode => EmitCast(castNode), - FuncCallNode funcCallNode => EmitFuncCall(funcCallNode), - FuncIdentifierNode funcIdentifierNode => FuncName(funcIdentifierNode.Module, funcIdentifierNode.Name, funcIdentifierNode.ExternSymbol), - AddressOfNode addressOfNode => EmitAddressOf(addressOfNode), - SizeNode sizeBuiltinNode => $"sizeof({CType.Create(sizeBuiltinNode.TargetType)})", - SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode), - StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode), - StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(structFieldAccessNode), - StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode), - I8LiteralNode i8LiteralNode => EmitI8Literal(i8LiteralNode), - I16LiteralNode i16LiteralNode => EmitI16Literal(i16LiteralNode), - I32LiteralNode i32LiteralNode => EmitI32Literal(i32LiteralNode), - I64LiteralNode i64LiteralNode => EmitI64Literal(i64LiteralNode), - U8LiteralNode u8LiteralNode => EmitU8Literal(u8LiteralNode), - U16LiteralNode u16LiteralNode => EmitU16Literal(u16LiteralNode), - U32LiteralNode u32LiteralNode => EmitU32Literal(u32LiteralNode), - U64LiteralNode u64LiteralNode => EmitU64Literal(u64LiteralNode), - UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(unaryExpressionNode), - VariableIdentifierNode variableIdentifierNode => variableIdentifierNode.Name, - _ => throw new ArgumentOutOfRangeException(nameof(expressionNode)) - }; - - return $"({expr})"; - } - - private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode) - { - var target = EmitExpression(arrayIndexAccessNode.Target); - var index = EmitExpression(arrayIndexAccessNode.Index); - return $"{target}[{index}]"; - } - - private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode) - { - var values = new List(); - foreach (var value in arrayInitializerNode.Values) - { - values.Add(EmitExpression(value)); - } - - var arrayType = (NubArrayType)arrayInitializerNode.Type; - return $"({CType.Create(arrayType.ElementType)}[]){{{string.Join(", ", values)}}}"; - } - - private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode) - { - var left = EmitExpression(binaryExpressionNode.Left); - var right = EmitExpression(binaryExpressionNode.Right); - - var op = binaryExpressionNode.Operator switch - { - BinaryOperator.Plus => "+", - BinaryOperator.Minus => "-", - BinaryOperator.Multiply => "*", - BinaryOperator.Divide => "/", - BinaryOperator.Modulo => "%", - BinaryOperator.Equal => "==", - BinaryOperator.NotEqual => "!=", - BinaryOperator.LessThan => "<", - BinaryOperator.LessThanOrEqual => "<=", - BinaryOperator.GreaterThan => ">", - BinaryOperator.GreaterThanOrEqual => ">=", - BinaryOperator.LogicalAnd => "&&", - BinaryOperator.LogicalOr => "||", - BinaryOperator.BitwiseAnd => "&", - BinaryOperator.BitwiseOr => "|", - BinaryOperator.BitwiseXor => "^", - BinaryOperator.LeftShift => "<<", - BinaryOperator.RightShift => ">>", - _ => throw new ArgumentOutOfRangeException() - }; - - return $"{left} {op} {right}"; - } - - private string EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode) - { - var target = EmitExpression(constArrayIndexAccessNode.Target); - var index = EmitExpression(constArrayIndexAccessNode.Index); - // todo(nub31): We can emit bounds checking here - return $"{target}[{index}]"; - } - - private string EmitConstArrayInitializer(ConstArrayInitializerNode arrayInitializerNode) - { - var values = new List(); - foreach (var value in arrayInitializerNode.Values) - { - values.Add(EmitExpression(value)); - } - - var arrayType = (NubConstArrayType)arrayInitializerNode.Type; - return $"({CType.Create(arrayType.ElementType)}[{arrayType.Size}]){{{string.Join(", ", values)}}}"; - } - - private string EmitDereference(DereferenceNode dereferenceNode) - { - var pointer = EmitExpression(dereferenceNode.Target); - return $"*{pointer}"; - } - - private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode) - { - var str = float32LiteralNode.Value.ToString("G9", System.Globalization.CultureInfo.InvariantCulture); - if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E')) - { - str += ".0"; - } - - return str + "f"; - } - - private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode) - { - var str = float64LiteralNode.Value.ToString("G17", System.Globalization.CultureInfo.InvariantCulture); - if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E')) - { - str += ".0"; - } - - return str; - } - - private string EmitCast(CastNode castNode) - { - var value = EmitExpression(castNode.Value); - - if (castNode is { Type: NubSliceType sliceType, Value.Type: NubConstArrayType arrayType }) - { - return $"({CType.Create(sliceType)}){{.length = {arrayType.Size}, .data = (void*){value}}}"; - } - - return $"({CType.Create(castNode.Type)}){value}"; - } - - private string EmitFuncCall(FuncCallNode funcCallNode) - { - var name = EmitExpression(funcCallNode.Expression); - var parameterNames = funcCallNode.Parameters.Select(EmitExpression).ToList(); - return $"{name}({string.Join(", ", parameterNames)})"; - } - - private string EmitAddressOf(AddressOfNode addressOfNode) - { - var value = EmitExpression(addressOfNode.LValue); - return $"&{value}"; - } - - private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) - { - var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type; - var target = EmitExpression(sliceIndexAccessNode.Target); - var index = EmitExpression(sliceIndexAccessNode.Index); - // todo(nub31): We can emit bounds checking here - return $"(({CType.Create(targetType.ElementType)}*){target}.data)[{index}]"; - } - - private string EmitStringLiteral(StringLiteralNode stringLiteralNode) - { - var length = Encoding.UTF8.GetByteCount(stringLiteralNode.Value); - return $"(nub_string){{.length = {length}, .data = \"{stringLiteralNode.Value}\"}}"; - } - - private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode) - { - var structExpr = EmitExpression(structFieldAccessNode.Target); - return $"{structExpr}.{structFieldAccessNode.Field}"; - } - - private string EmitStructInitializer(StructInitializerNode structInitializerNode) - { - var initValues = new List(); - foreach (var initializer in structInitializerNode.Initializers) - { - var value = EmitExpression(initializer.Value); - initValues.Add($".{initializer.Key} = {value}"); - } - - var initString = initValues.Count == 0 - ? "0" - : string.Join(", ", initValues); - - return $"({CType.Create(structInitializerNode.Type)}){{{initString}}}"; - } - - private string EmitI8Literal(I8LiteralNode i8LiteralNode) - { - return i8LiteralNode.Value.ToString(); - } - - private string EmitI16Literal(I16LiteralNode i16LiteralNode) - { - return i16LiteralNode.Value.ToString(); - } - - private string EmitI32Literal(I32LiteralNode i32LiteralNode) - { - return i32LiteralNode.Value.ToString(); - } - - private string EmitI64Literal(I64LiteralNode i64LiteralNode) - { - return i64LiteralNode.Value + "LL"; - } - - private string EmitU8Literal(U8LiteralNode u8LiteralNode) - { - return u8LiteralNode.Value.ToString(); - } - - private string EmitU16Literal(U16LiteralNode u16LiteralNode) - { - return u16LiteralNode.Value.ToString(); - } - - private string EmitU32Literal(U32LiteralNode u32LiteralNode) - { - return u32LiteralNode.Value.ToString(); - } - - private string EmitU64Literal(U64LiteralNode u64LiteralNode) - { - return u64LiteralNode.Value + "ULL"; - } - - private string EmitUnaryExpression(UnaryExpressionNode unaryExpressionNode) - { - var value = EmitExpression(unaryExpressionNode.Operand); - - return unaryExpressionNode.Operator switch - { - UnaryOperator.Negate => $"-{value}", - UnaryOperator.Invert => $"!{value}", - _ => throw new ArgumentOutOfRangeException() - }; - } - - private void EmitBlock(BlockNode blockNode) - { - _deferStack.Push([]); - - foreach (var statementNode in blockNode.Statements) - { - EmitStatement(statementNode); - } - - var blockDefers = _deferStack.Pop(); - for (var i = blockDefers.Count - 1; i >= 0; i--) - { - EmitStatement(blockDefers[i].Statement); - } - } -} \ No newline at end of file diff --git a/compiler/NubLang/Generation/IndentedTextWriter.cs b/compiler/NubLang/Generation/IndentedTextWriter.cs deleted file mode 100644 index 5ec5274..0000000 --- a/compiler/NubLang/Generation/IndentedTextWriter.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Text; - -namespace NubLang.Generation; - -internal class IndentedTextWriter -{ - private readonly StringBuilder _builder = new(); - private int _indentLevel; - - public IDisposable Indent() - { - _indentLevel++; - return new IndentScope(this); - } - - public void WriteLine(string text) - { - WriteIndent(); - _builder.AppendLine(text); - } - - public void Write(string text) - { - WriteIndent(); - _builder.Append(text); - } - - public void WriteLine() - { - _builder.AppendLine(); - } - - public override string ToString() - { - return _builder.ToString(); - } - - private void WriteIndent() - { - if (_builder.Length > 0) - { - var lastChar = _builder[^1]; - if (lastChar != '\n' && lastChar != '\r') - return; - } - - for (var i = 0; i < _indentLevel; i++) - { - _builder.Append(" "); - } - } - - private class IndentScope : IDisposable - { - private readonly IndentedTextWriter _writer; - private bool _disposed; - - public IndentScope(IndentedTextWriter writer) - { - _writer = writer; - } - - public void Dispose() - { - if (_disposed) return; - _writer._indentLevel--; - _disposed = true; - } - } -} \ No newline at end of file diff --git a/compiler/NubLang/Sugar/DeSugar.cs b/compiler/NubLang/Sugar/DeSugar.cs new file mode 100644 index 0000000..6856ab8 --- /dev/null +++ b/compiler/NubLang/Sugar/DeSugar.cs @@ -0,0 +1,6 @@ +namespace NubLang.Sugar; + +public class DeSugar +{ + +} \ No newline at end of file