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