Compare commits
5 Commits
master
...
e77c7028b9
| Author | SHA1 | Date | |
|---|---|---|---|
| e77c7028b9 | |||
| 26d365cf4f | |||
| f2ea00b34d | |||
| cb2411a7eb | |||
| 3b75e62aa7 |
@@ -4,7 +4,7 @@ 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}") = "NubLang.LSP", "NubLang.LSP\NubLang.LSP.csproj", "{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compiler", "Compiler\Compiler.csproj", "{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -20,9 +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
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07968F84-0C2E-4D2E-8905-DC8A6140B4C0}.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
|
||||
|
||||
10
compiler/Compiler/Compiler.csproj
Normal file
10
compiler/Compiler/Compiler.csproj
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
186
compiler/Compiler/Generator.cs
Normal file
186
compiler/Compiler/Generator.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Compiler;
|
||||
|
||||
public sealed class Generator(List<NodeDefinition> nodes)
|
||||
{
|
||||
public static string Emit(List<NodeDefinition> nodes)
|
||||
{
|
||||
return new Generator(nodes).Emit();
|
||||
}
|
||||
|
||||
private IndentedTextWriter writer = new();
|
||||
|
||||
private string Emit()
|
||||
{
|
||||
writer.WriteLine("""
|
||||
#include <float.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct string {
|
||||
const char *data;
|
||||
int length;
|
||||
};
|
||||
|
||||
""");
|
||||
|
||||
foreach (var node in nodes.OfType<NodeDefinitionFunc>())
|
||||
{
|
||||
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<NodeDefinitionFunc>())
|
||||
{
|
||||
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;
|
||||
case NodeStatementReturn statement:
|
||||
EmitStatementReturn(statement);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(node), node, null);
|
||||
}
|
||||
}
|
||||
|
||||
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 void EmitStatementReturn(NodeStatementReturn statement)
|
||||
{
|
||||
var value = EmitExpression(statement.Value);
|
||||
writer.WriteLine($"return {value};");
|
||||
}
|
||||
|
||||
private string EmitExpression(NodeExpression node)
|
||||
{
|
||||
return node switch
|
||||
{
|
||||
NodeExpressionBoolLiteral expression => expression.Value.Value ? "true" : "false",
|
||||
NodeExpressionIntLiteral expression => expression.Value.Value.ToString(),
|
||||
NodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
|
||||
NodeExpressionIdent expression => expression.Value.Ident,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
||||
};
|
||||
}
|
||||
|
||||
private static string CType(NodeType node, string? varName = null)
|
||||
{
|
||||
return node switch
|
||||
{
|
||||
NodeTypeVoid => "void" + (varName != null ? $" {varName}" : ""),
|
||||
NodeTypeBool => "bool" + (varName != null ? $" {varName}" : ""),
|
||||
NodeTypeCustom type => $"struct {type}" + (varName != null ? $" {varName}" : ""),
|
||||
NodeTypeSInt type => $"int{type.Width}_t" + (varName != null ? $" {varName}" : ""),
|
||||
NodeTypeUInt type => $"uint{type.Width}_t" + (varName != null ? $" {varName}" : ""),
|
||||
NodeTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"),
|
||||
NodeTypeString => "struct string" + (varName != null ? $" {varName}" : ""),
|
||||
NodeTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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(IndentedTextWriter writer) : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) return;
|
||||
writer.indentLevel--;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
381
compiler/Compiler/Parser.cs
Normal file
381
compiler/Compiler/Parser.cs
Normal file
@@ -0,0 +1,381 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Compiler;
|
||||
|
||||
public sealed class Parser(List<Token> tokens)
|
||||
{
|
||||
public static List<NodeDefinition> Parse(List<Token> tokens)
|
||||
{
|
||||
return new Parser(tokens).Parse();
|
||||
}
|
||||
|
||||
private int index;
|
||||
|
||||
private List<NodeDefinition> Parse()
|
||||
{
|
||||
var nodes = new List<NodeDefinition>();
|
||||
|
||||
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<NodeDefinitionFunc.Param>();
|
||||
|
||||
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<NodeStatement>();
|
||||
while (!TryExpectSymbol(Symbol.CloseCurly))
|
||||
statements.Add(ParseStatement());
|
||||
|
||||
return new NodeStatementBlock(TokensFrom(startIndex), statements);
|
||||
}
|
||||
|
||||
if (TryExpectKeyword(Keyword.Return))
|
||||
{
|
||||
var value = ParseExpression();
|
||||
return new NodeStatementReturn(TokensFrom(startIndex), value);
|
||||
}
|
||||
|
||||
var expression = ParseExpression();
|
||||
var parameters = new List<NodeExpression>();
|
||||
|
||||
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 (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 (TryExpectKeyword(Keyword.Func))
|
||||
{
|
||||
var parameters = new List<NodeType>();
|
||||
|
||||
ExpectSymbol(Symbol.OpenParen);
|
||||
while (!TryExpectSymbol(Symbol.CloseParen))
|
||||
{
|
||||
parameters.Add(ParseType());
|
||||
}
|
||||
|
||||
ExpectSymbol(Symbol.Colon);
|
||||
var returnType = ParseType();
|
||||
|
||||
return new NodeTypeFunc(TokensFrom(startIndex), parameters, returnType);
|
||||
}
|
||||
|
||||
if (TryExpectIdent(out var ident))
|
||||
{
|
||||
switch (ident.Ident)
|
||||
{
|
||||
case "void":
|
||||
return new NodeTypeVoid(TokensFrom(startIndex));
|
||||
case "string":
|
||||
return new NodeTypeString(TokensFrom(startIndex));
|
||||
case "bool":
|
||||
return new NodeTypeBool(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);
|
||||
default:
|
||||
return new NodeTypeCustom(TokensFrom(startIndex), ident);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Not a valid type");
|
||||
}
|
||||
|
||||
private List<Token> 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 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<Token> tokens)
|
||||
{
|
||||
public List<Token> Tokens = tokens;
|
||||
}
|
||||
|
||||
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
|
||||
|
||||
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
|
||||
{
|
||||
public readonly TokenIdent Name = name;
|
||||
public readonly List<Param> Parameters = parameters;
|
||||
public readonly NodeStatement Body = body;
|
||||
public readonly NodeType ReturnType = returnType;
|
||||
|
||||
public sealed class Param(List<Token> tokens, TokenIdent name, NodeType type) : Node(tokens)
|
||||
{
|
||||
public readonly TokenIdent Name = name;
|
||||
public readonly NodeType Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class NodeStatement(List<Token> tokens) : Node(tokens);
|
||||
|
||||
public sealed class NodeStatementBlock(List<Token> tokens, List<NodeStatement> statements) : NodeStatement(tokens)
|
||||
{
|
||||
public readonly List<NodeStatement> Statements = statements;
|
||||
}
|
||||
|
||||
public sealed class NodeStatementFuncCall(List<Token> tokens, NodeExpression func, List<NodeExpression> parameters) : NodeStatement(tokens)
|
||||
{
|
||||
public readonly NodeExpression Func = func;
|
||||
public readonly List<NodeExpression> Parameters = parameters;
|
||||
}
|
||||
|
||||
internal class NodeStatementReturn(List<Token> tokens, NodeExpression value) : NodeStatement(tokens)
|
||||
{
|
||||
public readonly NodeExpression Value = value;
|
||||
}
|
||||
|
||||
public abstract class NodeExpression(List<Token> tokens) : Node(tokens);
|
||||
|
||||
public sealed class NodeExpressionIntLiteral(List<Token> tokens, TokenIntLiteral value) : NodeExpression(tokens)
|
||||
{
|
||||
public readonly TokenIntLiteral Value = value;
|
||||
}
|
||||
|
||||
public sealed class NodeExpressionStringLiteral(List<Token> tokens, TokenStringLiteral value) : NodeExpression(tokens)
|
||||
{
|
||||
public readonly TokenStringLiteral Value = value;
|
||||
}
|
||||
|
||||
public sealed class NodeExpressionBoolLiteral(List<Token> tokens, TokenBoolLiteral value) : NodeExpression(tokens)
|
||||
{
|
||||
public readonly TokenBoolLiteral Value = value;
|
||||
}
|
||||
|
||||
public sealed class NodeExpressionIdent(List<Token> tokens, TokenIdent value) : NodeExpression(tokens)
|
||||
{
|
||||
public readonly TokenIdent Value = value;
|
||||
}
|
||||
|
||||
public abstract class NodeType(List<Token> tokens) : Node(tokens);
|
||||
|
||||
public sealed class NodeTypeVoid(List<Token> tokens) : NodeType(tokens);
|
||||
|
||||
public sealed class NodeTypeUInt(List<Token> tokens, int width) : NodeType(tokens)
|
||||
{
|
||||
public readonly int Width = width;
|
||||
}
|
||||
|
||||
public sealed class NodeTypeSInt(List<Token> tokens, int width) : NodeType(tokens)
|
||||
{
|
||||
public readonly int Width = width;
|
||||
}
|
||||
|
||||
public sealed class NodeTypeBool(List<Token> tokens) : NodeType(tokens);
|
||||
|
||||
public sealed class NodeTypeString(List<Token> tokens) : NodeType(tokens);
|
||||
|
||||
public sealed class NodeTypeCustom(List<Token> tokens, TokenIdent name) : NodeType(tokens)
|
||||
{
|
||||
public readonly TokenIdent Name = name;
|
||||
}
|
||||
|
||||
public sealed class NodeTypePointer(List<Token> tokens, NodeType to) : NodeType(tokens)
|
||||
{
|
||||
public readonly NodeType To = to;
|
||||
}
|
||||
|
||||
public sealed class NodeTypeFunc(List<Token> tokens, List<NodeType> parameters, NodeType returnType) : NodeType(tokens)
|
||||
{
|
||||
public readonly List<NodeType> Parameters = parameters;
|
||||
public readonly NodeType ReturnType = returnType;
|
||||
}
|
||||
17
compiler/Compiler/Program.cs
Normal file
17
compiler/Compiler/Program.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Compiler;
|
||||
|
||||
const string contents = """
|
||||
func main(): i32 {
|
||||
do_something("test")
|
||||
return 69
|
||||
}
|
||||
|
||||
func do_something(text: string): void {
|
||||
}
|
||||
""";
|
||||
|
||||
var tokens = Tokenizer.Tokenize(contents);
|
||||
var nodes = Parser.Parse(tokens);
|
||||
var output = Generator.Emit(nodes);
|
||||
|
||||
Console.WriteLine(output);
|
||||
407
compiler/Compiler/Tokenizer.cs
Normal file
407
compiler/Compiler/Tokenizer.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace Compiler;
|
||||
|
||||
public sealed class Tokenizer(string contents)
|
||||
{
|
||||
public static List<Token> Tokenize(string contents)
|
||||
{
|
||||
return new Tokenizer(contents).Tokenize();
|
||||
}
|
||||
|
||||
private int index;
|
||||
private int line = 1;
|
||||
private int column = 1;
|
||||
|
||||
private List<Token> Tokenize()
|
||||
{
|
||||
var tokens = new List<Token>();
|
||||
|
||||
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),
|
||||
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
|
||||
"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 readonly 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 TokenStringLiteral(int line, int column, int length, string value) : Token(line, column, length)
|
||||
{
|
||||
public readonly string Value = value;
|
||||
}
|
||||
|
||||
public sealed class TokenBoolLiteral(int line, int column, int length, bool value) : Token(line, column, length)
|
||||
{
|
||||
public readonly 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 readonly Symbol Symbol = symbol;
|
||||
}
|
||||
|
||||
public enum Keyword
|
||||
{
|
||||
Func,
|
||||
Let,
|
||||
If,
|
||||
Return,
|
||||
}
|
||||
|
||||
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
||||
{
|
||||
public readonly Keyword Keyword = keyword;
|
||||
}
|
||||
@@ -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<Diagnostic>();
|
||||
@@ -42,50 +40,4 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro
|
||||
return 1;
|
||||
}
|
||||
|
||||
var cPaths = new List<string>();
|
||||
|
||||
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<string>();
|
||||
|
||||
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;
|
||||
@@ -1,77 +0,0 @@
|
||||
using NubLang.Ast;
|
||||
using NubLang.Syntax;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
public static class AstExtensions
|
||||
{
|
||||
public static Location ToLocation(this Node node)
|
||||
{
|
||||
if (node.Tokens.Count == 0)
|
||||
{
|
||||
return new Location();
|
||||
}
|
||||
|
||||
return new Location
|
||||
{
|
||||
Uri = node.Tokens.First().Span.FilePath,
|
||||
Range = new Range(node.Tokens.First().Span.Start.Line - 1, node.Tokens.First().Span.Start.Column - 1, node.Tokens.Last().Span.End.Line - 1, node.Tokens.Last().Span.End.Column - 1)
|
||||
};
|
||||
}
|
||||
|
||||
public static bool ContainsPosition(this Node node, int line, int character)
|
||||
{
|
||||
if (node.Tokens.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var start = node.Tokens.First().Span.Start;
|
||||
var end = node.Tokens.Last().Span.End;
|
||||
|
||||
var startLine = start.Line - 1;
|
||||
var startChar = start.Column - 1;
|
||||
var endLine = end.Line - 1;
|
||||
var endChar = end.Column - 1;
|
||||
|
||||
if (line < startLine || line > endLine) return false;
|
||||
|
||||
if (line > startLine && line < endLine) return true;
|
||||
|
||||
if (startLine == endLine)
|
||||
{
|
||||
return character >= startChar && character <= endChar;
|
||||
}
|
||||
|
||||
if (line == startLine)
|
||||
{
|
||||
return character >= startChar;
|
||||
}
|
||||
|
||||
if (line == endLine)
|
||||
{
|
||||
return character <= endChar;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static FuncNode? FunctionAtPosition(this CompilationUnit compilationUnit, int line, int character)
|
||||
{
|
||||
return compilationUnit
|
||||
.Functions
|
||||
.FirstOrDefault(x => x.ContainsPosition(line, character));
|
||||
}
|
||||
|
||||
public static Node? DeepestNodeAtPosition(this CompilationUnit compilationUnit, int line, int character)
|
||||
{
|
||||
return compilationUnit.Functions
|
||||
.SelectMany(x => x.DescendantsAndSelf())
|
||||
.Where(n => n.ContainsPosition(line, character))
|
||||
.OrderBy(n => n.Tokens.First().Span.Start.Line)
|
||||
.ThenBy(n => n.Tokens.First().Span.Start.Column)
|
||||
.LastOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
using NubLang.Ast;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
internal class CompletionHandler(WorkspaceManager workspaceManager) : CompletionHandlerBase
|
||||
{
|
||||
private readonly CompletionItem[] _definitionSnippets =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "func",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "func ${1:name}(${2:params})\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "struct",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "struct ${1:name}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "module",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "module \"$0\"",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "import",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "import \"$0\"",
|
||||
}
|
||||
];
|
||||
|
||||
private readonly CompletionItem[] _statementSnippets =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "let",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "let ${1:name} = $0",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "if",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "if ${1:condition}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "else if",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "else if ${1:condition}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "else",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "else\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "while",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "while ${1:condition}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "for",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "for ${1:name}, ${2:index} in ${3:array}\n{\n $0\n}",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "return",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "return $0",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Label = "defer",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = "defer $0",
|
||||
}
|
||||
];
|
||||
|
||||
protected override CompletionRegistrationOptions CreateRegistrationOptions(CompletionCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new CompletionRegistrationOptions();
|
||||
}
|
||||
|
||||
public override Task<CompletionList> Handle(CompletionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(HandleSync(request, cancellationToken));
|
||||
}
|
||||
|
||||
private CompletionList HandleSync(CompletionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
var completions = new List<CompletionItem>();
|
||||
var position = request.Position;
|
||||
|
||||
var uri = request.TextDocument.Uri;
|
||||
var compilationUnit = workspaceManager.GetCompilationUnit(uri);
|
||||
if (compilationUnit != null)
|
||||
{
|
||||
var function = compilationUnit.Functions.FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character));
|
||||
if (function != null)
|
||||
{
|
||||
completions.AddRange(_statementSnippets);
|
||||
|
||||
foreach (var prototype in compilationUnit.ImportedFunctions)
|
||||
{
|
||||
var parameterStrings = new List<string>();
|
||||
foreach (var (index, parameter) in prototype.Parameters.Index())
|
||||
{
|
||||
parameterStrings.AddRange($"${{{index + 1}:{parameter.Name}}}");
|
||||
}
|
||||
|
||||
completions.Add(new CompletionItem
|
||||
{
|
||||
Kind = CompletionItemKind.Function,
|
||||
Label = $"{prototype.Module}::{prototype.Name}",
|
||||
InsertTextFormat = InsertTextFormat.Snippet,
|
||||
InsertText = $"{prototype.Module}::{prototype.Name}({string.Join(", ", parameterStrings)})",
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var parameter in function.Prototype.Parameters)
|
||||
{
|
||||
completions.Add(new CompletionItem
|
||||
{
|
||||
Kind = CompletionItemKind.Variable,
|
||||
Label = parameter.Name,
|
||||
InsertText = parameter.Name,
|
||||
});
|
||||
}
|
||||
|
||||
var variables = function.Body!
|
||||
.Descendants()
|
||||
.OfType<VariableDeclarationNode>();
|
||||
|
||||
foreach (var variable in variables)
|
||||
{
|
||||
completions.Add(new CompletionItem
|
||||
{
|
||||
Kind = CompletionItemKind.Variable,
|
||||
Label = variable.Name,
|
||||
InsertText = variable.Name,
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
completions.AddRange(_definitionSnippets);
|
||||
}
|
||||
}
|
||||
|
||||
return new CompletionList(completions, false);
|
||||
}
|
||||
|
||||
public override Task<CompletionItem> Handle(CompletionItem request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new CompletionItem());
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using NubLang.Ast;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
internal class DefinitionHandler(WorkspaceManager workspaceManager) : DefinitionHandlerBase
|
||||
{
|
||||
protected override DefinitionRegistrationOptions CreateRegistrationOptions(DefinitionCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new DefinitionRegistrationOptions();
|
||||
}
|
||||
|
||||
public override Task<LocationOrLocationLinks?> Handle(DefinitionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(HandleSync(request, cancellationToken));
|
||||
}
|
||||
|
||||
private LocationOrLocationLinks? HandleSync(DefinitionParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
var uri = request.TextDocument.Uri;
|
||||
var compilationUnit = workspaceManager.GetCompilationUnit(uri);
|
||||
if (compilationUnit == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var line = request.Position.Line;
|
||||
var character = request.Position.Character;
|
||||
|
||||
var node = compilationUnit.DeepestNodeAtPosition(line, character);
|
||||
|
||||
switch (node)
|
||||
{
|
||||
case VariableIdentifierNode variableIdentifierNode:
|
||||
{
|
||||
var function = compilationUnit.FunctionAtPosition(line, character);
|
||||
|
||||
var parameter = function?.Prototype.Parameters.FirstOrDefault(x => x.Name == variableIdentifierNode.Name);
|
||||
if (parameter != null)
|
||||
{
|
||||
return new LocationOrLocationLinks(parameter.ToLocation());
|
||||
}
|
||||
|
||||
var variable = function?.Body?
|
||||
.Descendants()
|
||||
.OfType<VariableDeclarationNode>()
|
||||
.FirstOrDefault(x => x.Name == variableIdentifierNode.Name);
|
||||
|
||||
if (variable != null)
|
||||
{
|
||||
return new LocationOrLocationLinks(variable.ToLocation());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
case FuncIdentifierNode funcIdentifierNode:
|
||||
{
|
||||
var prototype = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name);
|
||||
if (prototype != null)
|
||||
{
|
||||
return new LocationOrLocationLinks(prototype.ToLocation());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
|
||||
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
public class DiagnosticsPublisher
|
||||
{
|
||||
private readonly ILanguageServerFacade _server;
|
||||
|
||||
public DiagnosticsPublisher(ILanguageServerFacade server)
|
||||
{
|
||||
_server = server;
|
||||
}
|
||||
|
||||
public void Publish(DocumentUri uri, IEnumerable<Diagnostics.Diagnostic> diagnostics)
|
||||
{
|
||||
_server.TextDocument.PublishDiagnostics(new PublishDiagnosticsParams
|
||||
{
|
||||
Uri = uri,
|
||||
Diagnostics = new Container<Diagnostic>(diagnostics.Select(MapDiagnostic))
|
||||
});
|
||||
}
|
||||
|
||||
private static Diagnostic MapDiagnostic(Diagnostics.Diagnostic nubDiagnostic)
|
||||
{
|
||||
return new Diagnostic
|
||||
{
|
||||
Severity = nubDiagnostic.Severity switch
|
||||
{
|
||||
Diagnostics.DiagnosticSeverity.Info => DiagnosticSeverity.Information,
|
||||
Diagnostics.DiagnosticSeverity.Warning => DiagnosticSeverity.Warning,
|
||||
Diagnostics.DiagnosticSeverity.Error => DiagnosticSeverity.Error,
|
||||
_ => null
|
||||
},
|
||||
Message = $"{nubDiagnostic.Message}\n{(nubDiagnostic.Help == null ? "" : $"help: {nubDiagnostic.Help}")}",
|
||||
Range = nubDiagnostic.Span.HasValue
|
||||
? new Range(nubDiagnostic.Span.Value.Start.Line - 1, nubDiagnostic.Span.Value.Start.Column - 1, nubDiagnostic.Span.Value.End.Line - 1, nubDiagnostic.Span.Value.End.Column - 1)
|
||||
: new Range(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
using System.Globalization;
|
||||
using NubLang.Ast;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBase
|
||||
{
|
||||
protected override HoverRegistrationOptions CreateRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new HoverRegistrationOptions
|
||||
{
|
||||
DocumentSelector = TextDocumentSelector.ForLanguage("nub")
|
||||
};
|
||||
}
|
||||
|
||||
public override Task<Hover?> Handle(HoverParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(HandleSync(request, cancellationToken));
|
||||
}
|
||||
|
||||
private Hover? HandleSync(HoverParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
var compilationUnit = workspaceManager.GetCompilationUnit(request.TextDocument.Uri);
|
||||
if (compilationUnit == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var line = request.Position.Line;
|
||||
var character = request.Position.Character;
|
||||
|
||||
var hoveredNode = compilationUnit.DeepestNodeAtPosition(line, character);
|
||||
|
||||
if (hoveredNode == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var message = CreateMessage(hoveredNode, compilationUnit);
|
||||
if (message == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Hover
|
||||
{
|
||||
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
|
||||
{
|
||||
Value = message,
|
||||
Kind = MarkupKind.Markdown,
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
private static string? CreateMessage(Node hoveredNode, CompilationUnit compilationUnit)
|
||||
{
|
||||
return hoveredNode switch
|
||||
{
|
||||
FuncNode funcNode => CreateFuncPrototypeMessage(funcNode.Prototype),
|
||||
FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeMessage(funcPrototypeNode),
|
||||
FuncIdentifierNode funcIdentifierNode => CreateFuncIdentifierMessage(funcIdentifierNode, compilationUnit),
|
||||
FuncParameterNode funcParameterNode => CreateTypeNameMessage("Function parameter", funcParameterNode.Name, funcParameterNode.Type),
|
||||
VariableIdentifierNode variableIdentifierNode => CreateTypeNameMessage("Variable", variableIdentifierNode.Name, variableIdentifierNode.Type),
|
||||
VariableDeclarationNode variableDeclarationNode => CreateTypeNameMessage("Variable declaration", variableDeclarationNode.Name, variableDeclarationNode.Type),
|
||||
StructFieldAccessNode structFieldAccessNode => CreateTypeNameMessage("Struct field", $"{structFieldAccessNode.Target.Type}.{structFieldAccessNode.Field}", structFieldAccessNode.Type),
|
||||
CStringLiteralNode cStringLiteralNode => CreateLiteralMessage(cStringLiteralNode.Type, '"' + cStringLiteralNode.Value + '"'),
|
||||
StringLiteralNode stringLiteralNode => CreateLiteralMessage(stringLiteralNode.Type, '"' + stringLiteralNode.Value + '"'),
|
||||
BoolLiteralNode boolLiteralNode => CreateLiteralMessage(boolLiteralNode.Type, boolLiteralNode.Value.ToString()),
|
||||
Float32LiteralNode float32LiteralNode => CreateLiteralMessage(float32LiteralNode.Type, float32LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
|
||||
Float64LiteralNode float64LiteralNode => CreateLiteralMessage(float64LiteralNode.Type, float64LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
|
||||
I8LiteralNode i8LiteralNode => CreateLiteralMessage(i8LiteralNode.Type, i8LiteralNode.Value.ToString()),
|
||||
I16LiteralNode i16LiteralNode => CreateLiteralMessage(i16LiteralNode.Type, i16LiteralNode.Value.ToString()),
|
||||
I32LiteralNode i32LiteralNode => CreateLiteralMessage(i32LiteralNode.Type, i32LiteralNode.Value.ToString()),
|
||||
I64LiteralNode i64LiteralNode => CreateLiteralMessage(i64LiteralNode.Type, i64LiteralNode.Value.ToString()),
|
||||
U8LiteralNode u8LiteralNode => CreateLiteralMessage(u8LiteralNode.Type, u8LiteralNode.Value.ToString()),
|
||||
U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()),
|
||||
U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()),
|
||||
U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()),
|
||||
// Expressions can have a generic fallback showing the resulting type
|
||||
ExpressionNode expressionNode => $"""
|
||||
**Expression** `{expressionNode.GetType().Name}`
|
||||
```nub
|
||||
{expressionNode.Type}
|
||||
```
|
||||
""",
|
||||
BlockNode => null,
|
||||
_ => hoveredNode.GetType().Name
|
||||
};
|
||||
}
|
||||
|
||||
private static string CreateLiteralMessage(NubType type, string value)
|
||||
{
|
||||
return $"""
|
||||
**Literal** `{type}`
|
||||
```nub
|
||||
{value}: {type}
|
||||
```
|
||||
""";
|
||||
}
|
||||
|
||||
private static string CreateTypeNameMessage(string description, string name, NubType type)
|
||||
{
|
||||
return $"""
|
||||
**{description}** `{name}`
|
||||
```nub
|
||||
{name}: {type}
|
||||
```
|
||||
""";
|
||||
}
|
||||
|
||||
private static string CreateFuncIdentifierMessage(FuncIdentifierNode funcIdentifierNode, CompilationUnit compilationUnit)
|
||||
{
|
||||
var func = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name);
|
||||
if (func == null)
|
||||
{
|
||||
return $"""
|
||||
**Function** `{funcIdentifierNode.Module}::{funcIdentifierNode.Name}`
|
||||
```nub
|
||||
// Declaration not found
|
||||
```
|
||||
""";
|
||||
}
|
||||
|
||||
return CreateFuncPrototypeMessage(func);
|
||||
}
|
||||
|
||||
private static string CreateFuncPrototypeMessage(FuncPrototypeNode funcPrototypeNode)
|
||||
{
|
||||
var parameterText = string.Join(", ", funcPrototypeNode.Parameters.Select(x => $"{x.Name}: {x.Type}"));
|
||||
var externText = funcPrototypeNode.ExternSymbol != null ? $"extern \"{funcPrototypeNode.ExternSymbol}\" " : "";
|
||||
|
||||
return $"""
|
||||
**Function** `{funcPrototypeNode.Module}::{funcPrototypeNode.Name}`
|
||||
```nub
|
||||
{externText}func {funcPrototypeNode.Module}::{funcPrototypeNode.Name}({parameterText}): {funcPrototypeNode.ReturnType}
|
||||
```
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>nublsp</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="0.19.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NubLang\NubLang.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,34 +0,0 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NubLang.LSP;
|
||||
using OmniSharp.Extensions.LanguageServer.Server;
|
||||
|
||||
var server = await LanguageServer.From(options => options
|
||||
.WithInput(Console.OpenStandardInput())
|
||||
.WithOutput(Console.OpenStandardOutput())
|
||||
.WithServices(services =>
|
||||
{
|
||||
services.AddSingleton<DiagnosticsPublisher>();
|
||||
services.AddSingleton<WorkspaceManager>();
|
||||
})
|
||||
.ConfigureLogging(x => x
|
||||
.AddLanguageProtocolLogging()
|
||||
.SetMinimumLevel(LogLevel.Debug))
|
||||
.WithHandler<TextDocumentSyncHandler>()
|
||||
.WithHandler<HoverHandler>()
|
||||
.WithHandler<CompletionHandler>()
|
||||
.WithHandler<DefinitionHandler>()
|
||||
.OnInitialize((server, request, ct) =>
|
||||
{
|
||||
var workspaceManager = server.GetRequiredService<WorkspaceManager>();
|
||||
|
||||
if (request.RootPath != null)
|
||||
{
|
||||
workspaceManager.Init(request.RootPath);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
})
|
||||
);
|
||||
|
||||
await server.WaitForExit;
|
||||
@@ -1,44 +0,0 @@
|
||||
using MediatR;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
internal class TextDocumentSyncHandler(WorkspaceManager workspaceManager) : TextDocumentSyncHandlerBase
|
||||
{
|
||||
public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri)
|
||||
{
|
||||
return new TextDocumentAttributes(uri, "nub");
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
public override Task<Unit> Handle(DidCloseTextDocumentParams request, CancellationToken cancellationToken)
|
||||
{
|
||||
workspaceManager.UpdateFile(request.TextDocument.Uri.GetFileSystemPath());
|
||||
return Unit.Task;
|
||||
}
|
||||
|
||||
protected override TextDocumentSyncRegistrationOptions CreateRegistrationOptions(TextSynchronizationCapability capability, ClientCapabilities clientCapabilities)
|
||||
{
|
||||
return new TextDocumentSyncRegistrationOptions();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using NubLang.Ast;
|
||||
using NubLang.Syntax;
|
||||
using OmniSharp.Extensions.LanguageServer.Protocol;
|
||||
|
||||
namespace NubLang.LSP;
|
||||
|
||||
public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
|
||||
{
|
||||
private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new();
|
||||
private readonly Dictionary<string, CompilationUnit> _compilationUnits = new();
|
||||
|
||||
public void Init(string rootPath)
|
||||
{
|
||||
var files = Directory.GetFiles(rootPath, "*.nub", SearchOption.AllDirectories);
|
||||
foreach (var path in files)
|
||||
{
|
||||
var text = File.ReadAllText(path);
|
||||
var tokenizer = new Tokenizer(path, text);
|
||||
|
||||
tokenizer.Tokenize();
|
||||
diagnosticsPublisher.Publish(path, tokenizer.Diagnostics);
|
||||
|
||||
var parser = new Parser();
|
||||
var parseResult = parser.Parse(tokenizer.Tokens);
|
||||
diagnosticsPublisher.Publish(path, parser.Diagnostics);
|
||||
|
||||
_syntaxTrees[path] = parseResult;
|
||||
}
|
||||
|
||||
foreach (var (fsPath, syntaxTree) in _syntaxTrees)
|
||||
{
|
||||
var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
|
||||
|
||||
var typeChecker = new TypeChecker(syntaxTree, modules);
|
||||
var result = typeChecker.Check();
|
||||
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
|
||||
_compilationUnits[fsPath] = result;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateFile(DocumentUri path)
|
||||
{
|
||||
var fsPath = path.GetFileSystemPath();
|
||||
|
||||
var text = File.ReadAllText(fsPath);
|
||||
var tokenizer = new Tokenizer(fsPath, text);
|
||||
tokenizer.Tokenize();
|
||||
diagnosticsPublisher.Publish(path, tokenizer.Diagnostics);
|
||||
|
||||
var parser = new Parser();
|
||||
var syntaxTree = parser.Parse(tokenizer.Tokens);
|
||||
diagnosticsPublisher.Publish(path, parser.Diagnostics);
|
||||
_syntaxTrees[fsPath] = syntaxTree;
|
||||
|
||||
var modules = Module.Collect(_syntaxTrees.Select(x => x.Value).ToList());
|
||||
|
||||
var typeChecker = new TypeChecker(syntaxTree, modules);
|
||||
var result = typeChecker.Check();
|
||||
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
|
||||
_compilationUnits[fsPath] = result;
|
||||
}
|
||||
|
||||
public void RemoveFile(DocumentUri path)
|
||||
{
|
||||
var fsPath = path.GetFileSystemPath();
|
||||
_syntaxTrees.Remove(fsPath);
|
||||
_compilationUnits.Remove(fsPath);
|
||||
}
|
||||
|
||||
public CompilationUnit? GetCompilationUnit(DocumentUri path)
|
||||
{
|
||||
return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath());
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
@@ -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<List<DeferNode>> _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<string>();
|
||||
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<string>();
|
||||
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<string>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
compiler/NubLang/Sugar/DeSugar.cs
Normal file
6
compiler/NubLang/Sugar/DeSugar.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace NubLang.Sugar;
|
||||
|
||||
public class DeSugar
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user