...
This commit is contained in:
@@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compiler", "Compiler\Compiler.csproj", "{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
EndGlobal
|
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>
|
||||||
216
compiler/Compiler/Generator.cs
Normal file
216
compiler/Compiler/Generator.cs
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
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("""
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
377
compiler/Compiler/Parser.cs
Normal file
377
compiler/Compiler/Parser.cs
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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<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 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<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 TokenIdent Name = name;
|
||||||
|
public List<Param> Parameters = parameters;
|
||||||
|
public NodeStatement Body = body;
|
||||||
|
public NodeType ReturnType = returnType;
|
||||||
|
|
||||||
|
public sealed class Param(List<Token> tokens, TokenIdent name, NodeType type) : Node(tokens)
|
||||||
|
{
|
||||||
|
public TokenIdent Name = name;
|
||||||
|
public 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 List<NodeStatement> Statements = statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeStatementFuncCall(List<Token> tokens, NodeExpression func, List<NodeExpression> parameters) : NodeStatement(tokens)
|
||||||
|
{
|
||||||
|
public NodeExpression Func = func;
|
||||||
|
public List<NodeExpression> Parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class NodeExpression(List<Token> tokens) : Node(tokens);
|
||||||
|
|
||||||
|
public sealed class NodeExpressionIntLiteral(List<Token> tokens, TokenIntLiteral value) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public TokenIntLiteral Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeExpressionFloatLiteral(List<Token> tokens, TokenFloatLiteral value) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public TokenFloatLiteral Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeExpressionStringLiteral(List<Token> tokens, TokenStringLiteral value) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public TokenStringLiteral Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeExpressionBoolLiteral(List<Token> tokens, TokenBoolLiteral value) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public TokenBoolLiteral Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeExpressionIdent(List<Token> tokens, TokenIdent value) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public TokenIdent Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class NodeType(List<Token> tokens) : Node(tokens);
|
||||||
|
|
||||||
|
public sealed class NubTypeVoid(List<Token> tokens) : NodeType(tokens);
|
||||||
|
|
||||||
|
public sealed class NodeTypeUInt(List<Token> tokens, int width) : NodeType(tokens)
|
||||||
|
{
|
||||||
|
public int Width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeTypeSInt(List<Token> tokens, int width) : NodeType(tokens)
|
||||||
|
{
|
||||||
|
public int Width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeTypeFloat(List<Token> tokens, int width) : NodeType(tokens)
|
||||||
|
{
|
||||||
|
public int Width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeTypeString(List<Token> tokens) : NodeType(tokens);
|
||||||
|
|
||||||
|
public sealed class NodeTypeCustom(List<Token> tokens, TokenIdent name) : NodeType(tokens)
|
||||||
|
{
|
||||||
|
public TokenIdent Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NodeTypePointer(List<Token> tokens, NodeType to) : NodeType(tokens)
|
||||||
|
{
|
||||||
|
public NodeType To = to;
|
||||||
|
}
|
||||||
16
compiler/Compiler/Program.cs
Normal file
16
compiler/Compiler/Program.cs
Normal file
@@ -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);
|
||||||
410
compiler/Compiler/Tokenizer.cs
Normal file
410
compiler/Compiler/Tokenizer.cs
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
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),
|
||||||
|
"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;
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using NubLang.Ast;
|
||||||
using NubLang.Ast;
|
|
||||||
using NubLang.Diagnostics;
|
using NubLang.Diagnostics;
|
||||||
using NubLang.Generation;
|
|
||||||
using NubLang.Syntax;
|
using NubLang.Syntax;
|
||||||
|
|
||||||
var diagnostics = new List<Diagnostic>();
|
var diagnostics = new List<Diagnostic>();
|
||||||
@@ -42,50 +40,4 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro
|
|||||||
return 1;
|
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;
|
return 0;
|
||||||
@@ -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