...
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
using System.Diagnostics;
|
using NubLang.Ast;
|
||||||
using NubLang.Ast;
|
|
||||||
using NubLang.Diagnostics;
|
using NubLang.Diagnostics;
|
||||||
using NubLang.Generation;
|
using NubLang.Generation;
|
||||||
using NubLang.Syntax;
|
using NubLang.Syntax;
|
||||||
@@ -42,62 +41,22 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cPaths = new List<string>();
|
|
||||||
|
|
||||||
Directory.CreateDirectory(".build");
|
Directory.CreateDirectory(".build");
|
||||||
|
|
||||||
var typedModules = modules.Select(x => (x.Key, TypedModule.FromModule(x.Key, x.Value, modules))).ToDictionary();
|
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.Combine(".build", "modules"));
|
|
||||||
|
|
||||||
foreach (var typedModule in typedModules)
|
|
||||||
{
|
|
||||||
var header = HeaderGenerator.Generate(typedModule.Key, typedModule.Value);
|
|
||||||
var headerOut = Path.Combine(Path.Combine(".build", "modules"), typedModule.Key + ".h");
|
|
||||||
File.WriteAllText(headerOut, header);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < args.Length; i++)
|
for (var i = 0; i < args.Length; i++)
|
||||||
{
|
{
|
||||||
var file = args[i];
|
var file = args[i];
|
||||||
var compilationUnit = compilationUnits[i];
|
var compilationUnit = compilationUnits[i];
|
||||||
|
|
||||||
var generator = new Generator(compilationUnit);
|
var generator = new LlvmGenerator();
|
||||||
var directory = Path.GetDirectoryName(file);
|
var directory = Path.GetDirectoryName(file);
|
||||||
if (!string.IsNullOrWhiteSpace(directory))
|
if (!string.IsNullOrWhiteSpace(directory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.Combine(".build", directory));
|
Directory.CreateDirectory(Path.Combine(".build", directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = Path.Combine(".build", Path.ChangeExtension(file, "c"));
|
var path = Path.Combine(".build", Path.ChangeExtension(file, "ll"));
|
||||||
File.WriteAllText(path, generator.Emit());
|
File.WriteAllText(path, generator.Emit(compilationUnit));
|
||||||
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", [
|
|
||||||
"-I.build",
|
|
||||||
"-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;
|
||||||
@@ -8,6 +8,7 @@ public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
|
|||||||
{
|
{
|
||||||
private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new();
|
private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new();
|
||||||
private readonly Dictionary<string, List<TopLevelNode>> _compilationUnits = new();
|
private readonly Dictionary<string, List<TopLevelNode>> _compilationUnits = new();
|
||||||
|
private readonly Dictionary<string, TypedModule> _modules = new();
|
||||||
|
|
||||||
public void Init(string rootPath)
|
public void Init(string rootPath)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -307,7 +307,11 @@ public abstract class ExpressionNode(List<Token> tokens, NubType type) : Node(to
|
|||||||
public NubType Type { get; } = type;
|
public NubType Type { get; } = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StringLiteralNode(List<Token> tokens, string value) : ExpressionNode(tokens, new NubStringType())
|
public abstract class LValue(List<Token> tokens, NubType type) : ExpressionNode(tokens, type);
|
||||||
|
|
||||||
|
public abstract class RValue(List<Token> tokens, NubType type) : ExpressionNode(tokens, type);
|
||||||
|
|
||||||
|
public class StringLiteralNode(List<Token> tokens, string value) : RValue(tokens, new NubStringType())
|
||||||
{
|
{
|
||||||
public string Value { get; } = value;
|
public string Value { get; } = value;
|
||||||
|
|
||||||
@@ -317,7 +321,7 @@ public class StringLiteralNode(List<Token> tokens, string value) : ExpressionNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CStringLiteralNode(List<Token> tokens, string value) : ExpressionNode(tokens, new NubPointerType(new NubIntType(true, 8)))
|
public class CStringLiteralNode(List<Token> tokens, string value) : RValue(tokens, new NubPointerType(new NubIntType(true, 8)))
|
||||||
{
|
{
|
||||||
public string Value { get; } = value;
|
public string Value { get; } = value;
|
||||||
|
|
||||||
@@ -327,7 +331,7 @@ public class CStringLiteralNode(List<Token> tokens, string value) : ExpressionNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class I8LiteralNode(List<Token> tokens, sbyte value) : ExpressionNode(tokens, new NubIntType(true, 8))
|
public class I8LiteralNode(List<Token> tokens, sbyte value) : RValue(tokens, new NubIntType(true, 8))
|
||||||
{
|
{
|
||||||
public sbyte Value { get; } = value;
|
public sbyte Value { get; } = value;
|
||||||
|
|
||||||
@@ -337,7 +341,7 @@ public class I8LiteralNode(List<Token> tokens, sbyte value) : ExpressionNode(tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class I16LiteralNode(List<Token> tokens, short value) : ExpressionNode(tokens, new NubIntType(true, 16))
|
public class I16LiteralNode(List<Token> tokens, short value) : RValue(tokens, new NubIntType(true, 16))
|
||||||
{
|
{
|
||||||
public short Value { get; } = value;
|
public short Value { get; } = value;
|
||||||
|
|
||||||
@@ -347,7 +351,7 @@ public class I16LiteralNode(List<Token> tokens, short value) : ExpressionNode(to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class I32LiteralNode(List<Token> tokens, int value) : ExpressionNode(tokens, new NubIntType(true, 32))
|
public class I32LiteralNode(List<Token> tokens, int value) : RValue(tokens, new NubIntType(true, 32))
|
||||||
{
|
{
|
||||||
public int Value { get; } = value;
|
public int Value { get; } = value;
|
||||||
|
|
||||||
@@ -357,7 +361,7 @@ public class I32LiteralNode(List<Token> tokens, int value) : ExpressionNode(toke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class I64LiteralNode(List<Token> tokens, long value) : ExpressionNode(tokens, new NubIntType(true, 64))
|
public class I64LiteralNode(List<Token> tokens, long value) : RValue(tokens, new NubIntType(true, 64))
|
||||||
{
|
{
|
||||||
public long Value { get; } = value;
|
public long Value { get; } = value;
|
||||||
|
|
||||||
@@ -367,7 +371,7 @@ public class I64LiteralNode(List<Token> tokens, long value) : ExpressionNode(tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class U8LiteralNode(List<Token> tokens, byte value) : ExpressionNode(tokens, new NubIntType(false, 8))
|
public class U8LiteralNode(List<Token> tokens, byte value) : RValue(tokens, new NubIntType(false, 8))
|
||||||
{
|
{
|
||||||
public byte Value { get; } = value;
|
public byte Value { get; } = value;
|
||||||
|
|
||||||
@@ -377,7 +381,7 @@ public class U8LiteralNode(List<Token> tokens, byte value) : ExpressionNode(toke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class U16LiteralNode(List<Token> tokens, ushort value) : ExpressionNode(tokens, new NubIntType(false, 16))
|
public class U16LiteralNode(List<Token> tokens, ushort value) : RValue(tokens, new NubIntType(false, 16))
|
||||||
{
|
{
|
||||||
public ushort Value { get; } = value;
|
public ushort Value { get; } = value;
|
||||||
|
|
||||||
@@ -387,7 +391,7 @@ public class U16LiteralNode(List<Token> tokens, ushort value) : ExpressionNode(t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class U32LiteralNode(List<Token> tokens, uint value) : ExpressionNode(tokens, new NubIntType(false, 32))
|
public class U32LiteralNode(List<Token> tokens, uint value) : RValue(tokens, new NubIntType(false, 32))
|
||||||
{
|
{
|
||||||
public uint Value { get; } = value;
|
public uint Value { get; } = value;
|
||||||
|
|
||||||
@@ -397,7 +401,7 @@ public class U32LiteralNode(List<Token> tokens, uint value) : ExpressionNode(tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class U64LiteralNode(List<Token> tokens, ulong value) : ExpressionNode(tokens, new NubIntType(false, 64))
|
public class U64LiteralNode(List<Token> tokens, ulong value) : RValue(tokens, new NubIntType(false, 64))
|
||||||
{
|
{
|
||||||
public ulong Value { get; } = value;
|
public ulong Value { get; } = value;
|
||||||
|
|
||||||
@@ -407,7 +411,7 @@ public class U64LiteralNode(List<Token> tokens, ulong value) : ExpressionNode(to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Float32LiteralNode(List<Token> tokens, float value) : ExpressionNode(tokens, new NubFloatType(32))
|
public class Float32LiteralNode(List<Token> tokens, float value) : RValue(tokens, new NubFloatType(32))
|
||||||
{
|
{
|
||||||
public float Value { get; } = value;
|
public float Value { get; } = value;
|
||||||
|
|
||||||
@@ -417,7 +421,7 @@ public class Float32LiteralNode(List<Token> tokens, float value) : ExpressionNod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Float64LiteralNode(List<Token> tokens, double value) : ExpressionNode(tokens, new NubFloatType(64))
|
public class Float64LiteralNode(List<Token> tokens, double value) : RValue(tokens, new NubFloatType(64))
|
||||||
{
|
{
|
||||||
public double Value { get; } = value;
|
public double Value { get; } = value;
|
||||||
|
|
||||||
@@ -427,7 +431,7 @@ public class Float64LiteralNode(List<Token> tokens, double value) : ExpressionNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BoolLiteralNode(List<Token> tokens, bool value) : ExpressionNode(tokens, new NubBoolType())
|
public class BoolLiteralNode(List<Token> tokens, bool value) : RValue(tokens, new NubBoolType())
|
||||||
{
|
{
|
||||||
public bool Value { get; } = value;
|
public bool Value { get; } = value;
|
||||||
|
|
||||||
@@ -437,7 +441,7 @@ public class BoolLiteralNode(List<Token> tokens, bool value) : ExpressionNode(to
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BinaryExpressionNode(List<Token> tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : ExpressionNode(tokens, type)
|
public class BinaryExpressionNode(List<Token> tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Left { get; } = left;
|
public ExpressionNode Left { get; } = left;
|
||||||
public BinaryOperator Operator { get; } = @operator;
|
public BinaryOperator Operator { get; } = @operator;
|
||||||
@@ -450,7 +454,7 @@ public class BinaryExpressionNode(List<Token> tokens, NubType type, ExpressionNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UnaryExpressionNode(List<Token> tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : ExpressionNode(tokens, type)
|
public class UnaryExpressionNode(List<Token> tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public UnaryOperator Operator { get; } = @operator;
|
public UnaryOperator Operator { get; } = @operator;
|
||||||
public ExpressionNode Operand { get; } = operand;
|
public ExpressionNode Operand { get; } = operand;
|
||||||
@@ -461,7 +465,7 @@ public class UnaryExpressionNode(List<Token> tokens, NubType type, UnaryOperator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FuncCallNode(List<Token> tokens, NubType type, ExpressionNode expression, List<ExpressionNode> parameters) : ExpressionNode(tokens, type)
|
public class FuncCallNode(List<Token> tokens, NubType type, ExpressionNode expression, List<ExpressionNode> parameters) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Expression { get; } = expression;
|
public ExpressionNode Expression { get; } = expression;
|
||||||
public List<ExpressionNode> Parameters { get; } = parameters;
|
public List<ExpressionNode> Parameters { get; } = parameters;
|
||||||
@@ -476,7 +480,7 @@ public class FuncCallNode(List<Token> tokens, NubType type, ExpressionNode expre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VariableIdentifierNode(List<Token> tokens, NubType type, IdentifierToken nameToken) : ExpressionNode(tokens, type)
|
public class VariableIdentifierNode(List<Token> tokens, NubType type, IdentifierToken nameToken) : LValue(tokens, type)
|
||||||
{
|
{
|
||||||
public IdentifierToken NameToken { get; } = nameToken;
|
public IdentifierToken NameToken { get; } = nameToken;
|
||||||
|
|
||||||
@@ -486,7 +490,7 @@ public class VariableIdentifierNode(List<Token> tokens, NubType type, Identifier
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FuncIdentifierNode(List<Token> tokens, NubType type, IdentifierToken moduleToken, IdentifierToken nameToken, StringLiteralToken? externSymbolToken) : ExpressionNode(tokens, type)
|
public class FuncIdentifierNode(List<Token> tokens, NubType type, IdentifierToken moduleToken, IdentifierToken nameToken, StringLiteralToken? externSymbolToken) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public IdentifierToken ModuleToken { get; } = moduleToken;
|
public IdentifierToken ModuleToken { get; } = moduleToken;
|
||||||
public IdentifierToken NameToken { get; } = nameToken;
|
public IdentifierToken NameToken { get; } = nameToken;
|
||||||
@@ -498,27 +502,7 @@ public class FuncIdentifierNode(List<Token> tokens, NubType type, IdentifierToke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : ExpressionNode(tokens, type)
|
public class ArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValue(tokens, type)
|
||||||
{
|
|
||||||
public List<ExpressionNode> Values { get; } = values;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return Values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ConstArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : ExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public List<ExpressionNode> Values { get; } = values;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return Values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : ExpressionNode(tokens, type)
|
|
||||||
{
|
{
|
||||||
public ExpressionNode Target { get; } = target;
|
public ExpressionNode Target { get; } = target;
|
||||||
public ExpressionNode Index { get; } = index;
|
public ExpressionNode Index { get; } = index;
|
||||||
@@ -530,7 +514,7 @@ public class ArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConstArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : ExpressionNode(tokens, type)
|
public class ConstArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Target { get; } = target;
|
public ExpressionNode Target { get; } = target;
|
||||||
public ExpressionNode Index { get; } = index;
|
public ExpressionNode Index { get; } = index;
|
||||||
@@ -542,7 +526,7 @@ public class ConstArrayIndexAccessNode(List<Token> tokens, NubType type, Express
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SliceIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : ExpressionNode(tokens, type)
|
public class SliceIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Target { get; } = target;
|
public ExpressionNode Target { get; } = target;
|
||||||
public ExpressionNode Index { get; } = index;
|
public ExpressionNode Index { get; } = index;
|
||||||
@@ -554,7 +538,7 @@ public class SliceIndexAccessNode(List<Token> tokens, NubType type, ExpressionNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AddressOfNode(List<Token> tokens, NubType type, ExpressionNode target) : ExpressionNode(tokens, type)
|
public class AddressOfNode(List<Token> tokens, NubType type, ExpressionNode target) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Target { get; } = target;
|
public ExpressionNode Target { get; } = target;
|
||||||
|
|
||||||
@@ -564,7 +548,7 @@ public class AddressOfNode(List<Token> tokens, NubType type, ExpressionNode targ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionNode target, IdentifierToken fieldToken) : ExpressionNode(tokens, type)
|
public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionNode target, IdentifierToken fieldToken) : LValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Target { get; } = target;
|
public ExpressionNode Target { get; } = target;
|
||||||
public IdentifierToken FieldToken { get; } = fieldToken;
|
public IdentifierToken FieldToken { get; } = fieldToken;
|
||||||
@@ -575,7 +559,37 @@ public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : ExpressionNode(tokens, type)
|
public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValue(tokens, type)
|
||||||
|
{
|
||||||
|
public ExpressionNode Target { get; } = target;
|
||||||
|
|
||||||
|
public override IEnumerable<Node> Children()
|
||||||
|
{
|
||||||
|
yield return Target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SizeNode(List<Token> tokens, NubType targetType) : RValue(tokens, new NubIntType(false, 64))
|
||||||
|
{
|
||||||
|
public NubType TargetType { get; } = targetType;
|
||||||
|
|
||||||
|
public override IEnumerable<Node> Children()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) : RValue(tokens, type)
|
||||||
|
{
|
||||||
|
public ExpressionNode Value { get; } = value;
|
||||||
|
|
||||||
|
public override IEnumerable<Node> Children()
|
||||||
|
{
|
||||||
|
yield return Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public Dictionary<IdentifierToken, ExpressionNode> Initializers { get; } = initializers;
|
public Dictionary<IdentifierToken, ExpressionNode> Initializers { get; } = initializers;
|
||||||
|
|
||||||
@@ -588,33 +602,13 @@ public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : ExpressionNode(tokens, type)
|
public class ConstArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : RValue(tokens, type)
|
||||||
{
|
{
|
||||||
public ExpressionNode Target { get; } = target;
|
public List<ExpressionNode> Values { get; } = values;
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
public override IEnumerable<Node> Children()
|
||||||
{
|
{
|
||||||
yield return Target;
|
return Values;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SizeNode(List<Token> tokens, NubType TargetType) : ExpressionNode(tokens, new NubIntType(false, 64))
|
|
||||||
{
|
|
||||||
public NubType TargetType { get; } = TargetType;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) : ExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public ExpressionNode Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public abstract class NubType : IEquatable<NubType>
|
|||||||
{
|
{
|
||||||
public abstract ulong GetSize();
|
public abstract ulong GetSize();
|
||||||
public abstract ulong GetAlignment();
|
public abstract ulong GetAlignment();
|
||||||
|
public abstract bool IsAggregate();
|
||||||
|
|
||||||
public override bool Equals(object? obj) => obj is NubType other && Equals(other);
|
public override bool Equals(object? obj) => obj is NubType other && Equals(other);
|
||||||
public abstract bool Equals(NubType? other);
|
public abstract bool Equals(NubType? other);
|
||||||
@@ -22,6 +23,7 @@ public class NubVoidType : NubType
|
|||||||
{
|
{
|
||||||
public override ulong GetSize() => 8;
|
public override ulong GetSize() => 8;
|
||||||
public override ulong GetAlignment() => 8;
|
public override ulong GetAlignment() => 8;
|
||||||
|
public override bool IsAggregate() => false;
|
||||||
|
|
||||||
public override string ToString() => "void";
|
public override string ToString() => "void";
|
||||||
public override bool Equals(NubType? other) => other is NubVoidType;
|
public override bool Equals(NubType? other) => other is NubVoidType;
|
||||||
@@ -35,6 +37,7 @@ public sealed class NubIntType(bool signed, ulong width) : NubType
|
|||||||
|
|
||||||
public override ulong GetSize() => Width / 8;
|
public override ulong GetSize() => Width / 8;
|
||||||
public override ulong GetAlignment() => Width / 8;
|
public override ulong GetAlignment() => Width / 8;
|
||||||
|
public override bool IsAggregate() => false;
|
||||||
|
|
||||||
public override string ToString() => $"{(Signed ? "i" : "u")}{Width}";
|
public override string ToString() => $"{(Signed ? "i" : "u")}{Width}";
|
||||||
public override bool Equals(NubType? other) => other is NubIntType @int && @int.Width == Width && @int.Signed == Signed;
|
public override bool Equals(NubType? other) => other is NubIntType @int && @int.Width == Width && @int.Signed == Signed;
|
||||||
@@ -47,6 +50,7 @@ public sealed class NubFloatType(ulong width) : NubType
|
|||||||
|
|
||||||
public override ulong GetSize() => Width / 8;
|
public override ulong GetSize() => Width / 8;
|
||||||
public override ulong GetAlignment() => Width / 8;
|
public override ulong GetAlignment() => Width / 8;
|
||||||
|
public override bool IsAggregate() => false;
|
||||||
|
|
||||||
public override string ToString() => $"f{Width}";
|
public override string ToString() => $"f{Width}";
|
||||||
public override bool Equals(NubType? other) => other is NubFloatType @float && @float.Width == Width;
|
public override bool Equals(NubType? other) => other is NubFloatType @float && @float.Width == Width;
|
||||||
@@ -57,6 +61,7 @@ public class NubBoolType : NubType
|
|||||||
{
|
{
|
||||||
public override ulong GetSize() => 1;
|
public override ulong GetSize() => 1;
|
||||||
public override ulong GetAlignment() => 1;
|
public override ulong GetAlignment() => 1;
|
||||||
|
public override bool IsAggregate() => false;
|
||||||
|
|
||||||
public override string ToString() => "bool";
|
public override string ToString() => "bool";
|
||||||
public override bool Equals(NubType? other) => other is NubBoolType;
|
public override bool Equals(NubType? other) => other is NubBoolType;
|
||||||
@@ -69,6 +74,7 @@ public sealed class NubPointerType(NubType baseType) : NubType
|
|||||||
|
|
||||||
public override ulong GetSize() => 8;
|
public override ulong GetSize() => 8;
|
||||||
public override ulong GetAlignment() => 8;
|
public override ulong GetAlignment() => 8;
|
||||||
|
public override bool IsAggregate() => false;
|
||||||
|
|
||||||
public override string ToString() => "^" + BaseType;
|
public override string ToString() => "^" + BaseType;
|
||||||
public override bool Equals(NubType? other) => other is NubPointerType pointer && BaseType.Equals(pointer.BaseType);
|
public override bool Equals(NubType? other) => other is NubPointerType pointer && BaseType.Equals(pointer.BaseType);
|
||||||
@@ -82,6 +88,7 @@ public class NubFuncType(List<NubType> parameters, NubType returnType) : NubType
|
|||||||
|
|
||||||
public override ulong GetSize() => 8;
|
public override ulong GetSize() => 8;
|
||||||
public override ulong GetAlignment() => 8;
|
public override ulong GetAlignment() => 8;
|
||||||
|
public override bool IsAggregate() => false;
|
||||||
|
|
||||||
public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}";
|
public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}";
|
||||||
public override bool Equals(NubType? other) => other is NubFuncType func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
public override bool Equals(NubType? other) => other is NubFuncType func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
||||||
@@ -107,9 +114,14 @@ public class NubStructType(string module, string name, bool packed, List<NubStru
|
|||||||
public bool Packed { get; } = packed;
|
public bool Packed { get; } = packed;
|
||||||
public List<NubStructFieldType> Fields { get; set; } = fields;
|
public List<NubStructFieldType> Fields { get; set; } = fields;
|
||||||
|
|
||||||
public Dictionary<NubStructFieldType, ulong> GetFieldOffsets()
|
public int GetFieldIndex(string name)
|
||||||
{
|
{
|
||||||
var offsets = new Dictionary<NubStructFieldType, ulong>();
|
return Fields.FindIndex(x => x.Name == name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, ulong> GetFieldOffsets()
|
||||||
|
{
|
||||||
|
var offsets = new Dictionary<string, ulong>();
|
||||||
ulong offset = 0;
|
ulong offset = 0;
|
||||||
|
|
||||||
foreach (var field in Fields)
|
foreach (var field in Fields)
|
||||||
@@ -121,7 +133,7 @@ public class NubStructType(string module, string name, bool packed, List<NubStru
|
|||||||
offset += padding;
|
offset += padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
offsets[field] = offset;
|
offsets[field.Name] = offset;
|
||||||
offset += field.Type.GetSize();
|
offset += field.Type.GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +149,7 @@ public class NubStructType(string module, string name, bool packed, List<NubStru
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lastField = Fields.Last();
|
var lastField = Fields.Last();
|
||||||
var size = offsets[lastField] + lastField.Type.GetSize();
|
var size = offsets[lastField.Name] + lastField.Type.GetSize();
|
||||||
|
|
||||||
if (!Packed)
|
if (!Packed)
|
||||||
{
|
{
|
||||||
@@ -157,6 +169,8 @@ public class NubStructType(string module, string name, bool packed, List<NubStru
|
|||||||
return Packed ? 1 : Fields.Max(f => f.Type.GetAlignment());
|
return Packed ? 1 : Fields.Max(f => f.Type.GetAlignment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsAggregate() => true;
|
||||||
|
|
||||||
public override string ToString() => $"{Module}::{Name}";
|
public override string ToString() => $"{Module}::{Name}";
|
||||||
public override bool Equals(NubType? other) => other is NubStructType structType && Name == structType.Name && Module == structType.Module;
|
public override bool Equals(NubType? other) => other is NubStructType structType && Name == structType.Name && Module == structType.Module;
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubStructType), Module, Name);
|
public override int GetHashCode() => HashCode.Combine(typeof(NubStructType), Module, Name);
|
||||||
@@ -173,9 +187,9 @@ public class NubSliceType(NubType elementType) : NubType
|
|||||||
{
|
{
|
||||||
public NubType ElementType { get; } = elementType;
|
public NubType ElementType { get; } = elementType;
|
||||||
|
|
||||||
// note(nub31): Fat pointer
|
public override ulong GetSize() => 16; // note(nub31): Fat pointer
|
||||||
public override ulong GetSize() => 16;
|
|
||||||
public override ulong GetAlignment() => 8;
|
public override ulong GetAlignment() => 8;
|
||||||
|
public override bool IsAggregate() => true;
|
||||||
|
|
||||||
public override string ToString() => "[]" + ElementType;
|
public override string ToString() => "[]" + ElementType;
|
||||||
public override bool Equals(NubType? other) => other is NubSliceType slice && ElementType.Equals(slice.ElementType);
|
public override bool Equals(NubType? other) => other is NubSliceType slice && ElementType.Equals(slice.ElementType);
|
||||||
@@ -189,6 +203,7 @@ public class NubConstArrayType(NubType elementType, ulong size) : NubType
|
|||||||
|
|
||||||
public override ulong GetSize() => ElementType.GetSize() * Size;
|
public override ulong GetSize() => ElementType.GetSize() * Size;
|
||||||
public override ulong GetAlignment() => ElementType.GetAlignment();
|
public override ulong GetAlignment() => ElementType.GetAlignment();
|
||||||
|
public override bool IsAggregate() => true;
|
||||||
|
|
||||||
public override string ToString() => $"[{Size}]{ElementType}";
|
public override string ToString() => $"[{Size}]{ElementType}";
|
||||||
public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size;
|
public override bool Equals(NubType? other) => other is NubConstArrayType array && ElementType.Equals(array.ElementType) && Size == array.Size;
|
||||||
@@ -201,6 +216,7 @@ public class NubArrayType(NubType elementType) : NubType
|
|||||||
|
|
||||||
public override ulong GetSize() => 8;
|
public override ulong GetSize() => 8;
|
||||||
public override ulong GetAlignment() => 8;
|
public override ulong GetAlignment() => 8;
|
||||||
|
public override bool IsAggregate() => false; // note(nub31): Just a pointer
|
||||||
|
|
||||||
public override string ToString() => $"[?]{ElementType}";
|
public override string ToString() => $"[?]{ElementType}";
|
||||||
public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType);
|
public override bool Equals(NubType? other) => other is NubArrayType array && ElementType.Equals(array.ElementType);
|
||||||
@@ -209,9 +225,9 @@ public class NubArrayType(NubType elementType) : NubType
|
|||||||
|
|
||||||
public class NubStringType : NubType
|
public class NubStringType : NubType
|
||||||
{
|
{
|
||||||
// note(nub31): Fat pointer
|
public override ulong GetSize() => 16; // note(nub31): Fat pointer
|
||||||
public override ulong GetSize() => 16;
|
|
||||||
public override ulong GetAlignment() => 8;
|
public override ulong GetAlignment() => 8;
|
||||||
|
public override bool IsAggregate() => true;
|
||||||
|
|
||||||
public override string ToString() => "string";
|
public override string ToString() => "string";
|
||||||
public override bool Equals(NubType? other) => other is NubStringType;
|
public override bool Equals(NubType? other) => other is NubStringType;
|
||||||
|
|||||||
@@ -520,7 +520,7 @@ public sealed class TypeChecker
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType)
|
private ConstArrayInitializerNode CheckArrayInitializer(ArrayInitializerSyntax expression, NubType? expectedType)
|
||||||
{
|
{
|
||||||
var elementType = expectedType switch
|
var elementType = expectedType switch
|
||||||
{
|
{
|
||||||
@@ -563,12 +563,7 @@ public sealed class TypeChecker
|
|||||||
values.Add(value);
|
values.Add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return expectedType switch
|
return new ConstArrayInitializerNode(expression.Tokens, new NubConstArrayType(elementType, (ulong)expression.Values.Count), values);
|
||||||
{
|
|
||||||
NubArrayType => new ArrayInitializerNode(expression.Tokens, new NubArrayType(elementType), values),
|
|
||||||
NubConstArrayType constArrayType => new ConstArrayInitializerNode(expression.Tokens, constArrayType, values),
|
|
||||||
_ => new ConstArrayInitializerNode(expression.Tokens, new NubConstArrayType(elementType, (ulong)expression.Values.Count), values)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType)
|
private BinaryExpressionNode CheckBinaryExpression(BinaryExpressionSyntax expression, NubType? expectedType)
|
||||||
@@ -602,7 +597,7 @@ public sealed class TypeChecker
|
|||||||
case BinaryOperatorSyntax.NotEqual:
|
case BinaryOperatorSyntax.NotEqual:
|
||||||
{
|
{
|
||||||
var left = CheckExpression(expression.Left);
|
var left = CheckExpression(expression.Left);
|
||||||
if (left.Type is not NubIntType and not NubFloatType and not NubBoolType)
|
if (left.Type is not NubIntType and not NubFloatType and not NubBoolType and not NubPointerType)
|
||||||
{
|
{
|
||||||
throw new CompileException(Diagnostic
|
throw new CompileException(Diagnostic
|
||||||
.Error("Equal and not equal operators must must be used with int, float or bool types")
|
.Error("Equal and not equal operators must must be used with int, float or bool types")
|
||||||
@@ -718,6 +713,27 @@ public sealed class TypeChecker
|
|||||||
}
|
}
|
||||||
case BinaryOperatorSyntax.LeftShift:
|
case BinaryOperatorSyntax.LeftShift:
|
||||||
case BinaryOperatorSyntax.RightShift:
|
case BinaryOperatorSyntax.RightShift:
|
||||||
|
{
|
||||||
|
var left = CheckExpression(expression.Left, expectedType);
|
||||||
|
if (left.Type is not NubIntType)
|
||||||
|
{
|
||||||
|
throw new CompileException(Diagnostic
|
||||||
|
.Error("Bitwise operators must be used with int types")
|
||||||
|
.At(expression.Left)
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
var right = CheckExpression(expression.Right, left.Type);
|
||||||
|
if (right.Type is not NubIntType)
|
||||||
|
{
|
||||||
|
throw new CompileException(Diagnostic
|
||||||
|
.Error("Bitwise operators must be used with int types")
|
||||||
|
.At(expression.Right)
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BinaryExpressionNode(expression.Tokens, left.Type, left, op, right);
|
||||||
|
}
|
||||||
case BinaryOperatorSyntax.BitwiseAnd:
|
case BinaryOperatorSyntax.BitwiseAnd:
|
||||||
case BinaryOperatorSyntax.BitwiseXor:
|
case BinaryOperatorSyntax.BitwiseXor:
|
||||||
case BinaryOperatorSyntax.BitwiseOr:
|
case BinaryOperatorSyntax.BitwiseOr:
|
||||||
|
|||||||
@@ -1,98 +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 i => CreateIntType(i, variableName),
|
|
||||||
NubFloatType f => CreateFloatType(f, variableName),
|
|
||||||
NubPointerType p => CreatePointerType(p, variableName),
|
|
||||||
NubSliceType => "nub_slice" + (variableName != null ? $" {variableName}" : ""),
|
|
||||||
NubStringType => "nub_string" + (variableName != null ? $" {variableName}" : ""),
|
|
||||||
NubConstArrayType a => CreateConstArrayType(a, variableName, constArraysAsPointers),
|
|
||||||
NubArrayType a => CreateArrayType(a, variableName),
|
|
||||||
NubFuncType f => CreateFuncType(f, variableName),
|
|
||||||
// NubStructType s => $"{s.Module}_{s.Name}_{NameMangler.Mangle(s)}" + (variableName != null ? $" {variableName}" : ""),
|
|
||||||
NubStructType s => $"{s.Module}_{s.Name}" + (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 constArraysAsPointers)
|
|
||||||
{
|
|
||||||
var elementType = Create(arr.ElementType);
|
|
||||||
|
|
||||||
// Treat const arrays as pointers unless in a struct definition
|
|
||||||
if (constArraysAsPointers)
|
|
||||||
{
|
|
||||||
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,674 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
using NubLang.Ast;
|
|
||||||
using NubLang.Syntax;
|
|
||||||
|
|
||||||
namespace NubLang.Generation;
|
|
||||||
|
|
||||||
public class Generator
|
|
||||||
{
|
|
||||||
private readonly List<TopLevelNode> _compilationUnit;
|
|
||||||
private readonly IndentedTextWriter _writer;
|
|
||||||
private readonly Stack<Scope> _scopes = [];
|
|
||||||
private int _tmpIndex;
|
|
||||||
|
|
||||||
private Scope Scope => _scopes.Peek();
|
|
||||||
|
|
||||||
public Generator(List<TopLevelNode> 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}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetModuleName()
|
|
||||||
{
|
|
||||||
return _compilationUnit.OfType<ModuleNode>().First().NameToken.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Emit()
|
|
||||||
{
|
|
||||||
foreach (var importNode in _compilationUnit.OfType<ImportNode>())
|
|
||||||
{
|
|
||||||
_writer.WriteLine($"#include <modules/{importNode.NameToken.Value}.h>");
|
|
||||||
}
|
|
||||||
|
|
||||||
_writer.WriteLine($"#include <modules/{GetModuleName()}.h>");
|
|
||||||
|
|
||||||
_writer.WriteLine("""
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
unsigned long long length;
|
|
||||||
char *data;
|
|
||||||
} nub_string;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
unsigned long long length;
|
|
||||||
void *data;
|
|
||||||
} nub_slice;
|
|
||||||
|
|
||||||
""");
|
|
||||||
|
|
||||||
foreach (var structType in _compilationUnit.OfType<StructNode>())
|
|
||||||
{
|
|
||||||
_writer.WriteLine($"void {CType.Create(structType.StructType)}_create({CType.Create(structType.StructType)} *self)");
|
|
||||||
_writer.WriteLine("{");
|
|
||||||
using (_writer.Indent())
|
|
||||||
{
|
|
||||||
foreach (var field in structType.Fields)
|
|
||||||
{
|
|
||||||
if (field.Value != null)
|
|
||||||
{
|
|
||||||
var value = EmitExpression(field.Value);
|
|
||||||
_writer.WriteLine($"self->{field.NameToken.Value} = {value}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_writer.WriteLine("}");
|
|
||||||
_writer.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
// note(nub31): Normal functions
|
|
||||||
foreach (var funcNode in _compilationUnit.OfType<FuncNode>())
|
|
||||||
{
|
|
||||||
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.NameToken.Value)))
|
|
||||||
: "void";
|
|
||||||
|
|
||||||
var name = FuncName(GetModuleName(), funcNode.NameToken.Value, funcNode.Prototype.ExternSymbolToken?.Value);
|
|
||||||
_writer.WriteLine($"{CType.Create(funcNode.Prototype.ReturnType, name)}({parameters})");
|
|
||||||
_writer.WriteLine("{");
|
|
||||||
using (_writer.Indent())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
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())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
EmitBlock(blockNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_writer.WriteLine("}");
|
|
||||||
break;
|
|
||||||
case BreakNode breakNode:
|
|
||||||
EmitBreak(breakNode);
|
|
||||||
break;
|
|
||||||
case ContinueNode continueNode:
|
|
||||||
EmitContinue(continueNode);
|
|
||||||
break;
|
|
||||||
case DeferNode deferNode:
|
|
||||||
Scope.Defer(() => EmitStatement(deferNode.Statement));
|
|
||||||
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 EmitForSlice(ForSliceNode forSliceNode)
|
|
||||||
{
|
|
||||||
var targetType = (NubSliceType)forSliceNode.Target.Type;
|
|
||||||
var target = EmitExpression(forSliceNode.Target);
|
|
||||||
var indexName = forSliceNode.IndexNameToken?.Value ?? NewTmp();
|
|
||||||
|
|
||||||
_writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {target}.length; ++{indexName})");
|
|
||||||
_writer.WriteLine("{");
|
|
||||||
using (_writer.Indent())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
_writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementNameToken.Value)} = (({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.IndexNameToken?.Value ?? NewTmp();
|
|
||||||
|
|
||||||
_writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {targetType.Size}; ++{indexName})");
|
|
||||||
_writer.WriteLine("{");
|
|
||||||
using (_writer.Indent())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
_writer.WriteLine($"{CType.Create(targetType.ElementType, forConstArrayNode.ElementNameToken.Value)} = {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())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
EmitBlock(ifNode.Body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_writer.WriteLine("}");
|
|
||||||
ifNode.Else?.Match
|
|
||||||
(
|
|
||||||
elseIfNode => EmitIf(elseIfNode, true),
|
|
||||||
elseNode =>
|
|
||||||
{
|
|
||||||
_writer.WriteLine("else");
|
|
||||||
_writer.WriteLine("{");
|
|
||||||
using (_writer.Indent())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
EmitBlock(elseNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_writer.WriteLine("}");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitReturn(ReturnNode returnNode)
|
|
||||||
{
|
|
||||||
if (returnNode.Value == null)
|
|
||||||
{
|
|
||||||
EmitScopeCleanup();
|
|
||||||
_writer.WriteLine("return;");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var returnValue = EmitExpression(returnNode.Value);
|
|
||||||
var tmp = NewTmp();
|
|
||||||
_writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};");
|
|
||||||
|
|
||||||
EmitScopeCleanup();
|
|
||||||
EmitLine(returnNode.Tokens.FirstOrDefault());
|
|
||||||
_writer.WriteLine($"return {tmp};");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode)
|
|
||||||
{
|
|
||||||
EmitFuncCall(statementFuncCallNode.FuncCall);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode)
|
|
||||||
{
|
|
||||||
Scope.AddVariable(variableDeclarationNode.NameToken.Value, variableDeclarationNode.Type);
|
|
||||||
|
|
||||||
if (variableDeclarationNode.Assignment != null)
|
|
||||||
{
|
|
||||||
var value = EmitExpression(variableDeclarationNode.Assignment);
|
|
||||||
_writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.NameToken.Value)} = {value};");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.NameToken.Value)};");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitWhile(WhileNode whileNode)
|
|
||||||
{
|
|
||||||
var condition = EmitExpression(whileNode.Condition);
|
|
||||||
_writer.WriteLine($"while ({condition})");
|
|
||||||
_writer.WriteLine("{");
|
|
||||||
using (_writer.Indent())
|
|
||||||
{
|
|
||||||
using (BeginScope())
|
|
||||||
{
|
|
||||||
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.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value),
|
|
||||||
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.NameToken.Value,
|
|
||||||
_ => 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 arrayType = (NubConstArrayType)arrayInitializerNode.Type;
|
|
||||||
|
|
||||||
var tmp = NewTmp();
|
|
||||||
_writer.WriteLine($"{CType.Create(arrayType.ElementType)} {tmp}[{arrayType.Size}] = {{0}};");
|
|
||||||
|
|
||||||
for (var i = 0; i < arrayInitializerNode.Values.Count; i++)
|
|
||||||
{
|
|
||||||
var valueName = EmitExpression(arrayInitializerNode.Values[i]);
|
|
||||||
_writer.WriteLine($"{tmp}[{i}] = {valueName};");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = new List<string>();
|
|
||||||
foreach (var parameter in funcCallNode.Parameters)
|
|
||||||
{
|
|
||||||
parameterNames.Add(EmitExpression(parameter));
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmp = NewTmp();
|
|
||||||
|
|
||||||
_writer.WriteLine($"{CType.Create(funcCallNode.Type)} {tmp} = {name}({string.Join(", ", parameterNames)});");
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitAddressOf(AddressOfNode addressOfNode)
|
|
||||||
{
|
|
||||||
var value = EmitExpression(addressOfNode.Target);
|
|
||||||
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 $"({CType.Create(stringLiteralNode.Type)}){{.length = {length}, .data = \"{stringLiteralNode.Value}\"}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode)
|
|
||||||
{
|
|
||||||
var structExpr = EmitExpression(structFieldAccessNode.Target);
|
|
||||||
return $"{structExpr}.{structFieldAccessNode.FieldToken.Value}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitStructInitializer(StructInitializerNode structInitializerNode)
|
|
||||||
{
|
|
||||||
var structType = (NubStructType)structInitializerNode.Type;
|
|
||||||
|
|
||||||
var tmp = NewTmp();
|
|
||||||
_writer.WriteLine($"{CType.Create(structType)} {tmp} = ({CType.Create(structType)}){{0}};");
|
|
||||||
_writer.WriteLine($"{CType.Create(structType)}_create(&{tmp});");
|
|
||||||
|
|
||||||
foreach (var initializer in structInitializerNode.Initializers)
|
|
||||||
{
|
|
||||||
var value = EmitExpression(initializer.Value);
|
|
||||||
_writer.WriteLine($"{tmp}.{initializer.Key} = {value};");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
foreach (var statementNode in blockNode.Statements)
|
|
||||||
{
|
|
||||||
EmitStatement(statementNode);
|
|
||||||
|
|
||||||
if (statementNode != blockNode.Statements.Last())
|
|
||||||
{
|
|
||||||
_writer.WriteLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitScopeCleanup()
|
|
||||||
{
|
|
||||||
if (Scope.IsTerminated())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope.Terminate();
|
|
||||||
|
|
||||||
|
|
||||||
var deferredStack = Scope.GetDeferred();
|
|
||||||
while (deferredStack.TryPop(out var deferred))
|
|
||||||
{
|
|
||||||
deferred.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScopeDisposer BeginScope()
|
|
||||||
{
|
|
||||||
_scopes.Push(new Scope());
|
|
||||||
return new ScopeDisposer(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class ScopeDisposer(Generator owner) : IDisposable
|
|
||||||
{
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
owner.EmitScopeCleanup();
|
|
||||||
owner._scopes.Pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Variable(string ident, NubType type)
|
|
||||||
{
|
|
||||||
public string Ident { get; } = ident;
|
|
||||||
public NubType Type { get; } = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Scope
|
|
||||||
{
|
|
||||||
private bool _hasTerminated;
|
|
||||||
private readonly List<Action> _deferred = [];
|
|
||||||
private readonly List<Variable> _variables = [];
|
|
||||||
|
|
||||||
public void Terminate()
|
|
||||||
{
|
|
||||||
_hasTerminated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsTerminated() => _hasTerminated;
|
|
||||||
|
|
||||||
public void Defer(Action action)
|
|
||||||
{
|
|
||||||
_deferred.Add(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stack<Action> GetDeferred() => new(_deferred);
|
|
||||||
|
|
||||||
public void AddVariable(string ident, NubType type)
|
|
||||||
{
|
|
||||||
_variables.Add(new Variable(ident, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stack<Variable> GetVariables() => new(_variables);
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using NubLang.Syntax;
|
|
||||||
|
|
||||||
namespace NubLang.Generation;
|
|
||||||
|
|
||||||
public static class HeaderGenerator
|
|
||||||
{
|
|
||||||
private static string FuncName(string module, string name, string? externSymbol)
|
|
||||||
{
|
|
||||||
return externSymbol ?? $"{module}_{name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Generate(string name, TypedModule module)
|
|
||||||
{
|
|
||||||
var writer = new IndentedTextWriter();
|
|
||||||
|
|
||||||
foreach (var import in module.Imports)
|
|
||||||
{
|
|
||||||
writer.WriteLine($"#include \"{import}.h\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteLine();
|
|
||||||
|
|
||||||
foreach (var structType in module.StructTypes)
|
|
||||||
{
|
|
||||||
writer.WriteLine("typedef struct");
|
|
||||||
writer.WriteLine("{");
|
|
||||||
using (writer.Indent())
|
|
||||||
{
|
|
||||||
foreach (var field in structType.Fields)
|
|
||||||
{
|
|
||||||
writer.WriteLine($"{CType.Create(field.Type)} {field.Name};");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteLine($"}} {CType.Create(structType)};");
|
|
||||||
writer.WriteLine($"void {CType.Create(structType)}_create({CType.Create(structType)} *self);");
|
|
||||||
writer.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var prototype in module.FunctionPrototypes)
|
|
||||||
{
|
|
||||||
var parameters = prototype.Parameters.Count != 0
|
|
||||||
? string.Join(", ", prototype.Parameters.Select(x => CType.Create(x.Type, x.NameToken.Value)))
|
|
||||||
: "void";
|
|
||||||
|
|
||||||
var funcName = FuncName(name, prototype.NameToken.Value, prototype.ExternSymbolToken?.Value);
|
|
||||||
writer.WriteLine($"{CType.Create(prototype.ReturnType, funcName)}({parameters});");
|
|
||||||
}
|
|
||||||
|
|
||||||
return writer.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
979
compiler/NubLang/Generation/LlvmGenerator.cs
Normal file
979
compiler/NubLang/Generation/LlvmGenerator.cs
Normal file
@@ -0,0 +1,979 @@
|
|||||||
|
using System.Text;
|
||||||
|
using NubLang.Ast;
|
||||||
|
|
||||||
|
namespace NubLang.Generation;
|
||||||
|
|
||||||
|
public class LlvmGenerator
|
||||||
|
{
|
||||||
|
private string _module = string.Empty;
|
||||||
|
private int _tmpIndex;
|
||||||
|
private int _labelIndex;
|
||||||
|
private List<(string Name, int Size, string Text)> _stringLiterals = [];
|
||||||
|
private Stack<(string breakLabel, string continueLabel)> _loopStack = [];
|
||||||
|
|
||||||
|
public string Emit(List<TopLevelNode> topLevelNodes)
|
||||||
|
{
|
||||||
|
_stringLiterals = [];
|
||||||
|
_loopStack = [];
|
||||||
|
|
||||||
|
var writer = new IndentedTextWriter();
|
||||||
|
|
||||||
|
_module = topLevelNodes.OfType<ModuleNode>().First().NameToken.Value;
|
||||||
|
writer.WriteLine($"; Module {_module}");
|
||||||
|
writer.WriteLine();
|
||||||
|
|
||||||
|
writer.WriteLine("declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)");
|
||||||
|
writer.WriteLine();
|
||||||
|
|
||||||
|
foreach (var structNode in topLevelNodes.OfType<StructNode>())
|
||||||
|
{
|
||||||
|
var types = structNode.Fields.Select(x => MapType(x.Type));
|
||||||
|
writer.WriteLine($"%{StructName(structNode)} = type {{ {string.Join(", ", types)} }}");
|
||||||
|
writer.WriteLine();
|
||||||
|
|
||||||
|
_tmpIndex = 0;
|
||||||
|
_labelIndex = 0;
|
||||||
|
|
||||||
|
writer.WriteLine($"define void @{StructName(structNode)}.new(ptr %self) {{");
|
||||||
|
using (writer.Indent())
|
||||||
|
{
|
||||||
|
foreach (var field in structNode.Fields)
|
||||||
|
{
|
||||||
|
if (field.Value != null)
|
||||||
|
{
|
||||||
|
var index = structNode.StructType.GetFieldIndex(field.NameToken.Value);
|
||||||
|
var fieldTmp = NewTmp($"struct.field.{field.NameToken.Value}");
|
||||||
|
writer.WriteLine($"{fieldTmp} = getelementptr %{StructName(structNode)}, ptr %self, i32 0, i32 {index}");
|
||||||
|
|
||||||
|
EmitExpressionInto(writer, field.Value, fieldTmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine("ret void");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine("}");
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var funcNode in topLevelNodes.OfType<FuncNode>())
|
||||||
|
{
|
||||||
|
if (funcNode.Body != null) continue;
|
||||||
|
var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}");
|
||||||
|
|
||||||
|
writer.WriteLine($"declare {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)})");
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var funcNode in topLevelNodes.OfType<FuncNode>())
|
||||||
|
{
|
||||||
|
if (funcNode.Body == null) continue;
|
||||||
|
|
||||||
|
_tmpIndex = 0;
|
||||||
|
_labelIndex = 0;
|
||||||
|
|
||||||
|
var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}");
|
||||||
|
writer.WriteLine($"define {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)}) {{");
|
||||||
|
|
||||||
|
using (writer.Indent())
|
||||||
|
{
|
||||||
|
EmitBlock(writer, funcNode.Body);
|
||||||
|
|
||||||
|
// note(nub31): Implicit return for void functions
|
||||||
|
if (funcNode.Prototype.ReturnType is NubVoidType)
|
||||||
|
{
|
||||||
|
writer.WriteLine("ret void");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine("}");
|
||||||
|
writer.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var stringLiteral in _stringLiterals)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{stringLiteral.Name} = private unnamed_addr constant [{stringLiteral.Size} x i8] c\"{stringLiteral.Text}\\00\", align 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitStatement(IndentedTextWriter writer, StatementNode statementNode)
|
||||||
|
{
|
||||||
|
switch (statementNode)
|
||||||
|
{
|
||||||
|
case AssignmentNode assignmentNode:
|
||||||
|
EmitAssignment(writer, assignmentNode);
|
||||||
|
break;
|
||||||
|
case BlockNode blockNode:
|
||||||
|
EmitBlock(writer, blockNode);
|
||||||
|
break;
|
||||||
|
case BreakNode breakNode:
|
||||||
|
EmitBreak(writer, breakNode);
|
||||||
|
break;
|
||||||
|
case ContinueNode continueNode:
|
||||||
|
EmitContinue(writer, continueNode);
|
||||||
|
break;
|
||||||
|
case DeferNode deferNode:
|
||||||
|
EmitDefer(writer, deferNode);
|
||||||
|
break;
|
||||||
|
case ForConstArrayNode forConstArrayNode:
|
||||||
|
EmitForConstArray(writer, forConstArrayNode);
|
||||||
|
break;
|
||||||
|
case ForSliceNode forSliceNode:
|
||||||
|
EmitForSlice(writer, forSliceNode);
|
||||||
|
break;
|
||||||
|
case IfNode ifNode:
|
||||||
|
EmitIf(writer, ifNode);
|
||||||
|
break;
|
||||||
|
case ReturnNode returnNode:
|
||||||
|
EmitReturn(writer, returnNode);
|
||||||
|
break;
|
||||||
|
case StatementFuncCallNode statementFuncCallNode:
|
||||||
|
EmitStatementFuncCall(writer, statementFuncCallNode);
|
||||||
|
break;
|
||||||
|
case VariableDeclarationNode variableDeclarationNode:
|
||||||
|
EmitVariableDeclaration(writer, variableDeclarationNode);
|
||||||
|
break;
|
||||||
|
case WhileNode whileNode:
|
||||||
|
EmitWhile(writer, whileNode);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitAssignment(IndentedTextWriter writer, AssignmentNode assignmentNode)
|
||||||
|
{
|
||||||
|
var target = EmitExpression(writer, assignmentNode.Target);
|
||||||
|
var value = Unwrap(writer, EmitExpression(writer, assignmentNode.Value));
|
||||||
|
writer.WriteLine($"store {MapType(assignmentNode.Value.Type)} {value}, ptr {target.Ident}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitBlock(IndentedTextWriter writer, BlockNode blockNode)
|
||||||
|
{
|
||||||
|
foreach (var statementNode in blockNode.Statements)
|
||||||
|
{
|
||||||
|
EmitStatement(writer, statementNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitBreak(IndentedTextWriter writer, BreakNode breakNode)
|
||||||
|
{
|
||||||
|
var (breakLabel, _) = _loopStack.Peek();
|
||||||
|
writer.WriteLine($"br label %{breakLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitContinue(IndentedTextWriter writer, ContinueNode continueNode)
|
||||||
|
{
|
||||||
|
var (_, continueLabel) = _loopStack.Peek();
|
||||||
|
writer.WriteLine($"br label %{continueLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitDefer(IndentedTextWriter writer, DeferNode deferNode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitForConstArray(IndentedTextWriter writer, ForConstArrayNode forConstArrayNode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitForSlice(IndentedTextWriter writer, ForSliceNode forSliceNode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitIf(IndentedTextWriter writer, IfNode ifNode)
|
||||||
|
{
|
||||||
|
var endLabel = NewLabel("if.end");
|
||||||
|
EmitIf(writer, ifNode, endLabel);
|
||||||
|
writer.WriteLine($"{endLabel}:");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitIf(IndentedTextWriter writer, IfNode ifNode, string endLabel)
|
||||||
|
{
|
||||||
|
var condition = Unwrap(writer, EmitExpression(writer, ifNode.Condition));
|
||||||
|
var thenLabel = NewLabel("if.then");
|
||||||
|
var elseLabel = ifNode.Else.HasValue ? NewLabel("if.else") : endLabel;
|
||||||
|
|
||||||
|
writer.WriteLine($"br i1 {condition}, label %{thenLabel}, label %{elseLabel}");
|
||||||
|
|
||||||
|
writer.WriteLine($"{thenLabel}:");
|
||||||
|
using (writer.Indent())
|
||||||
|
{
|
||||||
|
EmitBlock(writer, ifNode.Body);
|
||||||
|
writer.WriteLine($"br label %{endLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ifNode.Else.HasValue) return;
|
||||||
|
|
||||||
|
writer.WriteLine($"{elseLabel}:");
|
||||||
|
using (writer.Indent())
|
||||||
|
{
|
||||||
|
ifNode.Else.Value.Match(
|
||||||
|
nestedElseIf => EmitIf(writer, nestedElseIf, endLabel),
|
||||||
|
finalElse =>
|
||||||
|
{
|
||||||
|
EmitBlock(writer, finalElse);
|
||||||
|
writer.WriteLine($"br label %{endLabel}");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitReturn(IndentedTextWriter writer, ReturnNode returnNode)
|
||||||
|
{
|
||||||
|
if (returnNode.Value != null)
|
||||||
|
{
|
||||||
|
var returnValue = Unwrap(writer, EmitExpression(writer, returnNode.Value));
|
||||||
|
writer.WriteLine($"ret {MapType(returnNode.Value.Type)} {returnValue}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine("ret void");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitStatementFuncCall(IndentedTextWriter writer, StatementFuncCallNode statementFuncCallNode)
|
||||||
|
{
|
||||||
|
EmitFuncCall(writer, statementFuncCallNode.FuncCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitVariableDeclaration(IndentedTextWriter writer, VariableDeclarationNode variableDeclarationNode)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"%{variableDeclarationNode.NameToken.Value} = alloca {MapType(variableDeclarationNode.Type)}");
|
||||||
|
if (variableDeclarationNode.Assignment != null)
|
||||||
|
{
|
||||||
|
EmitExpressionInto(writer, variableDeclarationNode.Assignment, $"%{variableDeclarationNode.NameToken.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitWhile(IndentedTextWriter writer, WhileNode whileNode)
|
||||||
|
{
|
||||||
|
var conditionLabel = NewLabel("while.condition");
|
||||||
|
var bodyLabel = NewLabel("while.body");
|
||||||
|
var endLabel = NewLabel("while.end");
|
||||||
|
|
||||||
|
_loopStack.Push((endLabel, conditionLabel));
|
||||||
|
|
||||||
|
writer.WriteLine($"br label %{conditionLabel}");
|
||||||
|
|
||||||
|
writer.WriteLine($"{conditionLabel}:");
|
||||||
|
using (writer.Indent())
|
||||||
|
{
|
||||||
|
var condition = Unwrap(writer, EmitExpression(writer, whileNode.Condition));
|
||||||
|
writer.WriteLine($"br i1 {condition}, label %{bodyLabel}, label %{endLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine($"{bodyLabel}:");
|
||||||
|
using (writer.Indent())
|
||||||
|
{
|
||||||
|
EmitBlock(writer, whileNode.Body);
|
||||||
|
writer.WriteLine($"br label %{conditionLabel}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_loopStack.Pop();
|
||||||
|
|
||||||
|
writer.WriteLine($"{endLabel}:");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitExpression(IndentedTextWriter writer, ExpressionNode expressionNode)
|
||||||
|
{
|
||||||
|
return expressionNode switch
|
||||||
|
{
|
||||||
|
RValue rValue => EmitRValue(writer, rValue),
|
||||||
|
LValue lValue => EmitLValue(writer, lValue),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(expressionNode))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitExpressionInto(IndentedTextWriter writer, ExpressionNode expressionNode, string destination)
|
||||||
|
{
|
||||||
|
switch (expressionNode)
|
||||||
|
{
|
||||||
|
case StructInitializerNode structInitializerNode:
|
||||||
|
{
|
||||||
|
EmitStructInitializer(writer, structInitializerNode, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConstArrayInitializerNode constArrayInitializerNode:
|
||||||
|
{
|
||||||
|
EmitConstArrayInitializer(writer, constArrayInitializerNode, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = Unwrap(writer, EmitExpression(writer, expressionNode));
|
||||||
|
|
||||||
|
if (expressionNode.Type.IsAggregate())
|
||||||
|
{
|
||||||
|
// note(nub31): Fall back to slow method creating a copy
|
||||||
|
writer.WriteLine("; Slow rvalue copy instead of direct memory write");
|
||||||
|
writer.WriteLine($"call void @llvm.memcpy.p0.p0.i64(ptr {destination}, ptr {value}, i64 {expressionNode.Type.GetSize()}, i1 false)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"store {MapType(expressionNode.Type)} {value}, ptr {destination}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitRValue(IndentedTextWriter writer, RValue rValue)
|
||||||
|
{
|
||||||
|
return rValue switch
|
||||||
|
{
|
||||||
|
AddressOfNode addressOfNode => EmitAddressOf(writer, addressOfNode),
|
||||||
|
BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(writer, binaryExpressionNode),
|
||||||
|
BoolLiteralNode boolLiteralNode => EmitBoolLiteral(writer, boolLiteralNode),
|
||||||
|
CastNode castNode => EmitCast(writer, castNode),
|
||||||
|
ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(writer, constArrayInitializerNode),
|
||||||
|
CStringLiteralNode cStringLiteralNode => EmitCStringLiteral(writer, cStringLiteralNode),
|
||||||
|
Float32LiteralNode float32LiteralNode => EmitFloat32Literal(writer, float32LiteralNode),
|
||||||
|
Float64LiteralNode float64LiteralNode => EmitFloat64Literal(writer, float64LiteralNode),
|
||||||
|
FuncCallNode funcCallNode => EmitFuncCall(writer, funcCallNode),
|
||||||
|
FuncIdentifierNode funcIdentifierNode => EmitFuncIdentifier(writer, funcIdentifierNode),
|
||||||
|
I16LiteralNode i16LiteralNode => EmitI16Literal(writer, i16LiteralNode),
|
||||||
|
I32LiteralNode i32LiteralNode => EmitI32Literal(writer, i32LiteralNode),
|
||||||
|
I64LiteralNode i64LiteralNode => EmitI64Literal(writer, i64LiteralNode),
|
||||||
|
I8LiteralNode i8LiteralNode => EmitI8Literal(writer, i8LiteralNode),
|
||||||
|
SizeNode sizeNode => EmitSize(writer, sizeNode),
|
||||||
|
StringLiteralNode stringLiteralNode => EmitStringLiteral(writer, stringLiteralNode),
|
||||||
|
StructInitializerNode structInitializerNode => EmitStructInitializer(writer, structInitializerNode),
|
||||||
|
U16LiteralNode u16LiteralNode => EmitU16Literal(writer, u16LiteralNode),
|
||||||
|
U32LiteralNode u32LiteralNode => EmitU32Literal(writer, u32LiteralNode),
|
||||||
|
U64LiteralNode u64LiteralNode => EmitU64Literal(writer, u64LiteralNode),
|
||||||
|
U8LiteralNode u8LiteralNode => EmitU8Literal(writer, u8LiteralNode),
|
||||||
|
UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(writer, unaryExpressionNode),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(rValue), rValue, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitLValue(IndentedTextWriter writer, LValue lValue)
|
||||||
|
{
|
||||||
|
return lValue switch
|
||||||
|
{
|
||||||
|
ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(writer, arrayIndexAccessNode),
|
||||||
|
ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(writer, constArrayIndexAccessNode),
|
||||||
|
DereferenceNode dereferenceNode => EmitDereference(writer, dereferenceNode),
|
||||||
|
SliceIndexAccessNode sliceIndexAccessNode => EmitSliceIndexAccess(writer, sliceIndexAccessNode),
|
||||||
|
StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(writer, structFieldAccessNode),
|
||||||
|
VariableIdentifierNode variableIdentifierNode => EmitVariableIdentifier(writer, variableIdentifierNode),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(lValue), lValue, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitAddressOf(IndentedTextWriter writer, AddressOfNode addressOfNode)
|
||||||
|
{
|
||||||
|
var target = EmitExpression(writer, addressOfNode.Target);
|
||||||
|
return new Tmp(target.Ident, addressOfNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitArrayIndexAccess(IndentedTextWriter writer, ArrayIndexAccessNode arrayIndexAccessNode)
|
||||||
|
{
|
||||||
|
var arrayPtr = Unwrap(writer, EmitExpression(writer, arrayIndexAccessNode.Target));
|
||||||
|
var index = Unwrap(writer, EmitExpression(writer, arrayIndexAccessNode.Index));
|
||||||
|
|
||||||
|
var elementType = ((NubArrayType)arrayIndexAccessNode.Target.Type).ElementType;
|
||||||
|
var ptrTmp = NewTmp("array.element");
|
||||||
|
writer.WriteLine($"{ptrTmp} = getelementptr {MapType(elementType)}, ptr {arrayPtr}, {MapType(arrayIndexAccessNode.Index.Type)} {index}");
|
||||||
|
|
||||||
|
return new Tmp(ptrTmp, arrayIndexAccessNode.Type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitBinaryExpression(IndentedTextWriter writer, BinaryExpressionNode binaryExpressionNode)
|
||||||
|
{
|
||||||
|
var left = Unwrap(writer, EmitExpression(writer, binaryExpressionNode.Left));
|
||||||
|
var right = Unwrap(writer, EmitExpression(writer, binaryExpressionNode.Right));
|
||||||
|
var result = NewTmp("binop");
|
||||||
|
|
||||||
|
var leftType = binaryExpressionNode.Left.Type;
|
||||||
|
var op = binaryExpressionNode.Operator;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case BinaryOperator.Equal:
|
||||||
|
case BinaryOperator.NotEqual:
|
||||||
|
case BinaryOperator.GreaterThan:
|
||||||
|
case BinaryOperator.GreaterThanOrEqual:
|
||||||
|
case BinaryOperator.LessThan:
|
||||||
|
case BinaryOperator.LessThanOrEqual:
|
||||||
|
{
|
||||||
|
var cmpOp = leftType switch
|
||||||
|
{
|
||||||
|
NubIntType intType => GenerateIntComparison(op, intType.Signed),
|
||||||
|
NubFloatType => GenerateFloatComparison(op),
|
||||||
|
NubBoolType => GenerateBoolComparison(op),
|
||||||
|
NubPointerType => GeneratePointerComparison(op),
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type for comparison: {leftType}")
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.WriteLine($"{result} = {cmpOp} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.LogicalAnd:
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = and i1 {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.LogicalOr:
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = or i1 {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.Plus:
|
||||||
|
{
|
||||||
|
var instruction = leftType switch
|
||||||
|
{
|
||||||
|
NubIntType => "add",
|
||||||
|
NubFloatType => "fadd",
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type for plus: {leftType}")
|
||||||
|
};
|
||||||
|
writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.Minus:
|
||||||
|
{
|
||||||
|
var instruction = leftType switch
|
||||||
|
{
|
||||||
|
NubIntType => "sub",
|
||||||
|
NubFloatType => "fsub",
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type for minus: {leftType}")
|
||||||
|
};
|
||||||
|
writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.Multiply:
|
||||||
|
{
|
||||||
|
var instruction = leftType switch
|
||||||
|
{
|
||||||
|
NubIntType => "mul",
|
||||||
|
NubFloatType => "fmul",
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type for multiply: {leftType}")
|
||||||
|
};
|
||||||
|
writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.Divide:
|
||||||
|
{
|
||||||
|
var instruction = leftType switch
|
||||||
|
{
|
||||||
|
NubIntType intType => intType.Signed ? "sdiv" : "udiv",
|
||||||
|
NubFloatType => "fdiv",
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type for divide: {leftType}")
|
||||||
|
};
|
||||||
|
writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.Modulo:
|
||||||
|
{
|
||||||
|
var instruction = leftType switch
|
||||||
|
{
|
||||||
|
NubIntType intType => intType.Signed ? "srem" : "urem",
|
||||||
|
NubFloatType => "frem",
|
||||||
|
_ => throw new InvalidOperationException($"Unexpected type for modulo: {leftType}")
|
||||||
|
};
|
||||||
|
writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.LeftShift:
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = shl {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.RightShift:
|
||||||
|
{
|
||||||
|
var intType = (NubIntType)leftType;
|
||||||
|
var instruction = intType.Signed ? "ashr" : "lshr";
|
||||||
|
writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.BitwiseAnd:
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = and {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.BitwiseXor:
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = xor {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator.BitwiseOr:
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = or {MapType(leftType)} {left}, {right}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(op), op, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(result, binaryExpressionNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateIntComparison(BinaryOperator op, bool signed)
|
||||||
|
{
|
||||||
|
return op switch
|
||||||
|
{
|
||||||
|
BinaryOperator.Equal => "icmp eq",
|
||||||
|
BinaryOperator.NotEqual => "icmp ne",
|
||||||
|
BinaryOperator.GreaterThan => signed ? "icmp sgt" : "icmp ugt",
|
||||||
|
BinaryOperator.GreaterThanOrEqual => signed ? "icmp sge" : "icmp uge",
|
||||||
|
BinaryOperator.LessThan => signed ? "icmp slt" : "icmp ult",
|
||||||
|
BinaryOperator.LessThanOrEqual => signed ? "icmp sle" : "icmp ule",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateFloatComparison(BinaryOperator op)
|
||||||
|
{
|
||||||
|
return op switch
|
||||||
|
{
|
||||||
|
BinaryOperator.Equal => "fcmp oeq",
|
||||||
|
BinaryOperator.NotEqual => "fcmp one",
|
||||||
|
BinaryOperator.GreaterThan => "fcmp ogt",
|
||||||
|
BinaryOperator.GreaterThanOrEqual => "fcmp oge",
|
||||||
|
BinaryOperator.LessThan => "fcmp olt",
|
||||||
|
BinaryOperator.LessThanOrEqual => "fcmp ole",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateBoolComparison(BinaryOperator op)
|
||||||
|
{
|
||||||
|
return op switch
|
||||||
|
{
|
||||||
|
BinaryOperator.Equal => "icmp eq",
|
||||||
|
BinaryOperator.NotEqual => "icmp ne",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GeneratePointerComparison(BinaryOperator op)
|
||||||
|
{
|
||||||
|
return op switch
|
||||||
|
{
|
||||||
|
BinaryOperator.Equal => "icmp eq",
|
||||||
|
BinaryOperator.NotEqual => "icmp ne",
|
||||||
|
BinaryOperator.GreaterThan => "icmp ugt",
|
||||||
|
BinaryOperator.GreaterThanOrEqual => "icmp uge",
|
||||||
|
BinaryOperator.LessThan => "icmp ult",
|
||||||
|
BinaryOperator.LessThanOrEqual => "icmp ule",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitBoolLiteral(IndentedTextWriter writer, BoolLiteralNode boolLiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(boolLiteralNode.Value ? "1" : "0", boolLiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitCast(IndentedTextWriter writer, CastNode castNode)
|
||||||
|
{
|
||||||
|
var source = Unwrap(writer, EmitExpression(writer, castNode.Value));
|
||||||
|
var sourceType = castNode.Value.Type;
|
||||||
|
var targetType = castNode.Type;
|
||||||
|
|
||||||
|
var result = NewTmp("cast");
|
||||||
|
|
||||||
|
switch (sourceType, targetType)
|
||||||
|
{
|
||||||
|
case (NubIntType sourceInt, NubIntType targetInt):
|
||||||
|
{
|
||||||
|
if (sourceInt.Width < targetInt.Width)
|
||||||
|
{
|
||||||
|
var op = sourceInt.Signed ? "sext" : "zext";
|
||||||
|
writer.WriteLine($"{result} = {op} {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
}
|
||||||
|
else if (sourceInt.Width > targetInt.Width)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = trunc {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = bitcast {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (NubFloatType sourceFloat, NubFloatType targetFloat):
|
||||||
|
{
|
||||||
|
if (sourceFloat.Width < targetFloat.Width)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = fpext {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = fptrunc {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (NubIntType intType, NubFloatType):
|
||||||
|
{
|
||||||
|
var intToFloatOp = intType.Signed ? "sitofp" : "uitofp";
|
||||||
|
writer.WriteLine($"{result} = {intToFloatOp} {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (NubFloatType, NubIntType targetInt):
|
||||||
|
{
|
||||||
|
var floatToIntOp = targetInt.Signed ? "fptosi" : "fptoui";
|
||||||
|
writer.WriteLine($"{result} = {floatToIntOp} {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (NubPointerType, NubPointerType):
|
||||||
|
case (NubPointerType, NubIntType):
|
||||||
|
case (NubIntType, NubPointerType):
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = inttoptr {MapType(sourceType)} {source} to {MapType(targetType)}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"Cast from {sourceType} to {targetType} not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(result, castNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Tmp EmitConstArrayIndexAccess(IndentedTextWriter writer, ConstArrayIndexAccessNode constArrayIndexAccessNode)
|
||||||
|
{
|
||||||
|
var arrayPtr = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Target));
|
||||||
|
var index = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Index));
|
||||||
|
|
||||||
|
var elementType = ((NubConstArrayType)constArrayIndexAccessNode.Target.Type).ElementType;
|
||||||
|
var ptrTmp = NewTmp("array.element");
|
||||||
|
writer.WriteLine($"{ptrTmp} = getelementptr {MapType(elementType)}, ptr {arrayPtr}, {MapType(constArrayIndexAccessNode.Index.Type)} {index}");
|
||||||
|
|
||||||
|
return new Tmp(ptrTmp, constArrayIndexAccessNode.Type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitConstArrayInitializer(IndentedTextWriter writer, ConstArrayInitializerNode constArrayInitializerNode, string? destination = null)
|
||||||
|
{
|
||||||
|
var arrayType = (NubConstArrayType)constArrayInitializerNode.Type;
|
||||||
|
|
||||||
|
if (destination == null)
|
||||||
|
{
|
||||||
|
destination = NewTmp("array");
|
||||||
|
writer.WriteLine($"{destination} = alloca {MapType(arrayType)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < constArrayInitializerNode.Values.Count; i++)
|
||||||
|
{
|
||||||
|
var value = constArrayInitializerNode.Values[i];
|
||||||
|
var indexTmp = NewTmp("array.element");
|
||||||
|
writer.WriteLine($"{indexTmp} = getelementptr {MapType(arrayType)}, ptr {destination}, i32 0, i32 {i}");
|
||||||
|
EmitExpressionInto(writer, value, indexTmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(destination, constArrayInitializerNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitCStringLiteral(IndentedTextWriter writer, CStringLiteralNode cStringLiteralNode)
|
||||||
|
{
|
||||||
|
var escaped = EscapeStringForLLVM(cStringLiteralNode.Value);
|
||||||
|
|
||||||
|
var stringWithNull = cStringLiteralNode.Value + "\0";
|
||||||
|
var length = stringWithNull.Length;
|
||||||
|
|
||||||
|
var globalName = $"@.str.{_stringLiterals.Count}";
|
||||||
|
|
||||||
|
_stringLiterals.Add((globalName, length, escaped));
|
||||||
|
|
||||||
|
var gepTmp = NewTmp("str.ptr");
|
||||||
|
writer.WriteLine($"{gepTmp} = getelementptr [{length} x i8], ptr {globalName}, i32 0, i32 0");
|
||||||
|
|
||||||
|
return new Tmp(gepTmp, cStringLiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string EscapeStringForLLVM(string input)
|
||||||
|
{
|
||||||
|
var result = new StringBuilder();
|
||||||
|
foreach (var c in input)
|
||||||
|
{
|
||||||
|
if (c == '\0')
|
||||||
|
result.Append("\\00");
|
||||||
|
else if (c == '\n')
|
||||||
|
result.Append("\\0A");
|
||||||
|
else if (c == '\r')
|
||||||
|
result.Append("\\0D");
|
||||||
|
else if (c == '\t')
|
||||||
|
result.Append("\\09");
|
||||||
|
else if (c == '\\')
|
||||||
|
result.Append("\\\\");
|
||||||
|
else if (c == '"')
|
||||||
|
result.Append("\\22");
|
||||||
|
else if (c < 32 || c > 126)
|
||||||
|
result.Append($"\\{(int)c:X2}");
|
||||||
|
else
|
||||||
|
result.Append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitDereference(IndentedTextWriter writer, DereferenceNode dereferenceNode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitFloat32Literal(IndentedTextWriter writer, Float32LiteralNode float32LiteralNode)
|
||||||
|
{
|
||||||
|
var literal = ((double)float32LiteralNode.Value).ToString("R", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
if (!literal.Contains('.'))
|
||||||
|
{
|
||||||
|
literal += ".0";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(literal, float32LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitFloat64Literal(IndentedTextWriter writer, Float64LiteralNode float64LiteralNode)
|
||||||
|
{
|
||||||
|
var literal = float64LiteralNode.Value.ToString("R", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
if (!literal.Contains('.'))
|
||||||
|
{
|
||||||
|
literal += ".0";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(literal, float64LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitFuncCall(IndentedTextWriter writer, FuncCallNode funcCallNode)
|
||||||
|
{
|
||||||
|
var result = NewTmp();
|
||||||
|
|
||||||
|
var parameterStrings = new List<string>();
|
||||||
|
|
||||||
|
foreach (var parameter in funcCallNode.Parameters)
|
||||||
|
{
|
||||||
|
var value = Unwrap(writer, EmitExpression(writer, parameter));
|
||||||
|
parameterStrings.Add($"{MapType(parameter.Type)} {value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var functionPtr = Unwrap(writer, EmitExpression(writer, funcCallNode.Expression));
|
||||||
|
|
||||||
|
if (funcCallNode.Type is NubVoidType)
|
||||||
|
{
|
||||||
|
writer.WriteLine($"call {MapType(funcCallNode.Type)} {functionPtr}({string.Join(", ", parameterStrings)})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteLine($"{result} = call {MapType(funcCallNode.Type)} {functionPtr}({string.Join(", ", parameterStrings)})");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(result, funcCallNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitFuncIdentifier(IndentedTextWriter writer, FuncIdentifierNode funcIdentifierNode)
|
||||||
|
{
|
||||||
|
var name = FuncName(funcIdentifierNode.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value);
|
||||||
|
return new Tmp($"@{name}", funcIdentifierNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitI16Literal(IndentedTextWriter writer, I16LiteralNode i16LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(i16LiteralNode.Value.ToString(), i16LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitI32Literal(IndentedTextWriter writer, I32LiteralNode i32LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(i32LiteralNode.Value.ToString(), i32LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitI64Literal(IndentedTextWriter writer, I64LiteralNode i64LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(i64LiteralNode.Value.ToString(), i64LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitI8Literal(IndentedTextWriter writer, I8LiteralNode i8LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(i8LiteralNode.Value.ToString(), i8LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitSize(IndentedTextWriter writer, SizeNode sizeNode)
|
||||||
|
{
|
||||||
|
return new Tmp(sizeNode.TargetType.GetSize().ToString(), sizeNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitSliceIndexAccess(IndentedTextWriter writer, SliceIndexAccessNode sliceIndexAccessNode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitStringLiteral(IndentedTextWriter writer, StringLiteralNode stringLiteralNode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitStructFieldAccess(IndentedTextWriter writer, StructFieldAccessNode structFieldAccessNode)
|
||||||
|
{
|
||||||
|
var target = Unwrap(writer, EmitExpression(writer, structFieldAccessNode.Target));
|
||||||
|
|
||||||
|
var structType = (NubStructType)structFieldAccessNode.Target.Type;
|
||||||
|
var index = structType.GetFieldIndex(structFieldAccessNode.FieldToken.Value);
|
||||||
|
|
||||||
|
var ptrTmp = NewTmp($"struct.field.{structFieldAccessNode.FieldToken.Value}");
|
||||||
|
writer.WriteLine($"{ptrTmp} = getelementptr %{StructName(structType.Module, structType.Name)}, ptr {target}, i32 0, i32 {index}");
|
||||||
|
|
||||||
|
return new Tmp(ptrTmp, structFieldAccessNode.Type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitStructInitializer(IndentedTextWriter writer, StructInitializerNode structInitializerNode, string? destination = null)
|
||||||
|
{
|
||||||
|
if (destination == null)
|
||||||
|
{
|
||||||
|
destination = NewTmp("struct");
|
||||||
|
writer.WriteLine($"{destination} = alloca {MapType(structInitializerNode.Type)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var structType = (NubStructType)structInitializerNode.Type;
|
||||||
|
|
||||||
|
writer.WriteLine($"call void @{StructName(structType.Module, structType.Name)}.new(ptr {destination})");
|
||||||
|
|
||||||
|
foreach (var (name, value) in structInitializerNode.Initializers)
|
||||||
|
{
|
||||||
|
var index = structType.GetFieldIndex(name.Value);
|
||||||
|
var fieldTmp = NewTmp($"struct.field.{name}");
|
||||||
|
writer.WriteLine($"{fieldTmp} = getelementptr %{StructName(structType.Module, structType.Name)}, ptr {destination}, i32 0, i32 {index}");
|
||||||
|
|
||||||
|
EmitExpressionInto(writer, value, fieldTmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(destination, structInitializerNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitU16Literal(IndentedTextWriter writer, U16LiteralNode u16LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(u16LiteralNode.Value.ToString(), u16LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitU32Literal(IndentedTextWriter writer, U32LiteralNode u32LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(u32LiteralNode.Value.ToString(), u32LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitU64Literal(IndentedTextWriter writer, U64LiteralNode u64LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(u64LiteralNode.Value.ToString(), u64LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitU8Literal(IndentedTextWriter writer, U8LiteralNode u8LiteralNode)
|
||||||
|
{
|
||||||
|
return new Tmp(u8LiteralNode.Value.ToString(), u8LiteralNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitUnaryExpression(IndentedTextWriter writer, UnaryExpressionNode unaryExpressionNode)
|
||||||
|
{
|
||||||
|
var operand = Unwrap(writer, EmitExpression(writer, unaryExpressionNode.Operand));
|
||||||
|
var result = NewTmp("unary");
|
||||||
|
|
||||||
|
switch (unaryExpressionNode.Operator)
|
||||||
|
{
|
||||||
|
case UnaryOperator.Negate:
|
||||||
|
switch (unaryExpressionNode.Operand.Type)
|
||||||
|
{
|
||||||
|
case NubIntType intType:
|
||||||
|
writer.WriteLine($"{result} = sub {MapType(intType)} 0, {operand}");
|
||||||
|
break;
|
||||||
|
case NubFloatType floatType:
|
||||||
|
writer.WriteLine($"{result} = fneg {MapType(floatType)} {operand}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case UnaryOperator.Invert:
|
||||||
|
writer.WriteLine($"{result} = xor i1 {operand}, true");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Tmp(result, unaryExpressionNode.Type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tmp EmitVariableIdentifier(IndentedTextWriter writer, VariableIdentifierNode variableIdentifierNode)
|
||||||
|
{
|
||||||
|
return new Tmp($"%{variableIdentifierNode.NameToken.Value}", variableIdentifierNode.Type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string StructName(StructNode structNode)
|
||||||
|
{
|
||||||
|
return StructName(_module, structNode.NameToken.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string StructName(string module, string name)
|
||||||
|
{
|
||||||
|
return $"struct.{module}.{name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FuncName(FuncPrototypeNode funcNodePrototype)
|
||||||
|
{
|
||||||
|
return FuncName(_module, funcNodePrototype.NameToken.Value, funcNodePrototype.ExternSymbolToken?.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FuncName(string module, string name, string? externSymbol)
|
||||||
|
{
|
||||||
|
if (externSymbol != null)
|
||||||
|
{
|
||||||
|
return externSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{module}.{name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapType(NubType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
NubArrayType arrayType => $"{MapType(arrayType.ElementType)}*",
|
||||||
|
NubBoolType => "i1",
|
||||||
|
NubConstArrayType constArrayType => $"[{constArrayType.Size} x {MapType(constArrayType.ElementType)}]",
|
||||||
|
NubFloatType floatType => floatType.Width == 32 ? "float" : "double",
|
||||||
|
NubFuncType funcType => MapFuncType(funcType),
|
||||||
|
NubIntType intType => $"i{intType.Width}",
|
||||||
|
NubPointerType pointerType => $"{MapType(pointerType.BaseType)}*",
|
||||||
|
NubSliceType sliceType => throw new NotImplementedException(),
|
||||||
|
NubStringType stringType => throw new NotImplementedException(),
|
||||||
|
NubStructType structType => $"%{StructName(structType.Module, structType.Name)}",
|
||||||
|
NubVoidType => "void",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapFuncType(NubFuncType funcType)
|
||||||
|
{
|
||||||
|
var paramTypes = string.Join(", ", funcType.Parameters.Select(MapType));
|
||||||
|
var returnType = MapType(funcType.ReturnType);
|
||||||
|
return $"{returnType} ({paramTypes})*";
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Tmp(string Ident, NubType Type, bool LValue);
|
||||||
|
|
||||||
|
private string Unwrap(IndentedTextWriter writer, Tmp tmp)
|
||||||
|
{
|
||||||
|
if (tmp.LValue && !tmp.Type.IsAggregate())
|
||||||
|
{
|
||||||
|
var newTmp = NewTmp("deref");
|
||||||
|
writer.WriteLine($"{newTmp} = load {MapType(tmp.Type)}, ptr {tmp.Ident}");
|
||||||
|
return newTmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp.Ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NewTmp(string name = "t")
|
||||||
|
{
|
||||||
|
return $"%{name}.{++_tmpIndex}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NewLabel(string name = "l")
|
||||||
|
{
|
||||||
|
return $"{name}.{++_labelIndex}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -423,7 +423,7 @@ public sealed class Parser
|
|||||||
case Symbol.Pipe:
|
case Symbol.Pipe:
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr;
|
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr;
|
||||||
return true;
|
return true;
|
||||||
case Symbol.Caret:
|
case Symbol.XOr:
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor;
|
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor;
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
@@ -445,6 +445,7 @@ public sealed class Parser
|
|||||||
IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
|
IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
|
||||||
SymbolToken symbolToken => symbolToken.Symbol switch
|
SymbolToken symbolToken => symbolToken.Symbol switch
|
||||||
{
|
{
|
||||||
|
Symbol.Ampersand => new AddressOfSyntax(GetTokens(startIndex), ParsePrimaryExpression()),
|
||||||
Symbol.OpenParen => ParseParenthesizedExpression(),
|
Symbol.OpenParen => ParseParenthesizedExpression(),
|
||||||
Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()),
|
Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()),
|
||||||
Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()),
|
Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()),
|
||||||
@@ -517,12 +518,6 @@ public sealed class Parser
|
|||||||
var startIndex = _tokenIndex;
|
var startIndex = _tokenIndex;
|
||||||
while (HasToken)
|
while (HasToken)
|
||||||
{
|
{
|
||||||
if (TryExpectSymbol(Symbol.Ampersand))
|
|
||||||
{
|
|
||||||
expr = new AddressOfSyntax(GetTokens(startIndex), expr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Caret))
|
if (TryExpectSymbol(Symbol.Caret))
|
||||||
{
|
{
|
||||||
expr = new DereferenceSyntax(GetTokens(startIndex), expr);
|
expr = new DereferenceSyntax(GetTokens(startIndex), expr);
|
||||||
|
|||||||
@@ -14,14 +14,29 @@ public record IdentifierToken(SourceSpan Span, string Value) : Token(Span)
|
|||||||
|
|
||||||
public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span)
|
public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span)
|
||||||
{
|
{
|
||||||
public ulong AsU64 => Convert.ToUInt64(Value, Base);
|
private string GetNumericValue()
|
||||||
public long AsI64 => Convert.ToInt64(Value, Base);
|
{
|
||||||
public uint AsU32 => Convert.ToUInt32(Value, Base);
|
// Strip base prefixes: 0b, 0o, 0x
|
||||||
public int AsI32 => Convert.ToInt32(Value, Base);
|
return Base switch
|
||||||
public ushort AsU16 => Convert.ToUInt16(Value, Base);
|
{
|
||||||
public short AsI16 => Convert.ToInt16(Value, Base);
|
2 when Value.StartsWith("0b", StringComparison.OrdinalIgnoreCase)
|
||||||
public byte AsU8 => Convert.ToByte(Value, Base);
|
=> Value.Substring(2),
|
||||||
public sbyte AsI8 => Convert.ToSByte(Value, Base);
|
8 when Value.StartsWith("0o", StringComparison.OrdinalIgnoreCase)
|
||||||
|
=> Value.Substring(2),
|
||||||
|
16 when Value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||||
|
=> Value.Substring(2),
|
||||||
|
_ => Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong AsU64 => Convert.ToUInt64(GetNumericValue(), Base);
|
||||||
|
public long AsI64 => Convert.ToInt64(GetNumericValue(), Base);
|
||||||
|
public uint AsU32 => Convert.ToUInt32(GetNumericValue(), Base);
|
||||||
|
public int AsI32 => Convert.ToInt32(GetNumericValue(), Base);
|
||||||
|
public ushort AsU16 => Convert.ToUInt16(GetNumericValue(), Base);
|
||||||
|
public short AsI16 => Convert.ToInt16(GetNumericValue(), Base);
|
||||||
|
public byte AsU8 => Convert.ToByte(GetNumericValue(), Base);
|
||||||
|
public sbyte AsI8 => Convert.ToSByte(GetNumericValue(), Base);
|
||||||
|
|
||||||
public float AsF32 => Convert.ToSingle(AsI32);
|
public float AsF32 => Convert.ToSingle(AsI32);
|
||||||
public double AsF64 => Convert.ToDouble(AsI64);
|
public double AsF64 => Convert.ToDouble(AsI64);
|
||||||
@@ -119,6 +134,7 @@ public enum Symbol
|
|||||||
Pipe,
|
Pipe,
|
||||||
And,
|
And,
|
||||||
Or,
|
Or,
|
||||||
|
XOr,
|
||||||
At,
|
At,
|
||||||
QuestionMark,
|
QuestionMark,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,19 +68,17 @@ public sealed class Tokenizer
|
|||||||
|
|
||||||
private Token ParseToken(char current, int lineStart, int columnStart)
|
private Token ParseToken(char current, int lineStart, int columnStart)
|
||||||
{
|
{
|
||||||
// Numbers
|
|
||||||
if (char.IsDigit(current))
|
if (char.IsDigit(current))
|
||||||
{
|
{
|
||||||
return ParseNumber(lineStart, columnStart);
|
return ParseNumber(lineStart, columnStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// String literals
|
|
||||||
if (current == '"')
|
if (current == '"')
|
||||||
{
|
{
|
||||||
return ParseString(lineStart, columnStart);
|
return ParseString(lineStart, columnStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try keywords and symbols by length (longest first)
|
// note(nub31): Look for keywords (longest first in case a keyword fits partially in a larger keyword)
|
||||||
for (var i = 8; i >= 1; i--)
|
for (var i = 8; i >= 1; i--)
|
||||||
{
|
{
|
||||||
if (TryMatchSymbol(i, lineStart, columnStart, out var token))
|
if (TryMatchSymbol(i, lineStart, columnStart, out var token))
|
||||||
@@ -89,7 +87,6 @@ public sealed class Tokenizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifiers
|
|
||||||
if (char.IsLetter(current) || current == '_')
|
if (char.IsLetter(current) || current == '_')
|
||||||
{
|
{
|
||||||
return ParseIdentifier(lineStart, columnStart);
|
return ParseIdentifier(lineStart, columnStart);
|
||||||
@@ -103,7 +100,7 @@ public sealed class Tokenizer
|
|||||||
var start = _index;
|
var start = _index;
|
||||||
var current = _content[_index];
|
var current = _content[_index];
|
||||||
|
|
||||||
// Hex literal
|
// note(nub31): 0xFFFFFF
|
||||||
if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'x')
|
if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'x')
|
||||||
{
|
{
|
||||||
Next(2);
|
Next(2);
|
||||||
@@ -128,7 +125,7 @@ public sealed class Tokenizer
|
|||||||
16);
|
16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binary literal
|
// note(nub31): 0b11001100
|
||||||
if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'b')
|
if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'b')
|
||||||
{
|
{
|
||||||
Next(2);
|
Next(2);
|
||||||
@@ -153,7 +150,7 @@ public sealed class Tokenizer
|
|||||||
2);
|
2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decimal or float
|
// note(nub31): 23/23.5
|
||||||
var isFloat = false;
|
var isFloat = false;
|
||||||
while (_index < _content.Length)
|
while (_index < _content.Length)
|
||||||
{
|
{
|
||||||
@@ -191,7 +188,7 @@ public sealed class Tokenizer
|
|||||||
|
|
||||||
private StringLiteralToken ParseString(int lineStart, int columnStart)
|
private StringLiteralToken ParseString(int lineStart, int columnStart)
|
||||||
{
|
{
|
||||||
Next(); // Skip opening quote
|
Next();
|
||||||
var start = _index;
|
var start = _index;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
@@ -236,6 +233,20 @@ public sealed class Tokenizer
|
|||||||
|
|
||||||
var span = _content.AsSpan(_index, length);
|
var span = _content.AsSpan(_index, length);
|
||||||
|
|
||||||
|
if (span is "true")
|
||||||
|
{
|
||||||
|
Next(4);
|
||||||
|
token = new BoolLiteralToken(CreateSpan(lineStart, columnStart), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (span is "false")
|
||||||
|
{
|
||||||
|
Next(5);
|
||||||
|
token = new BoolLiteralToken(CreateSpan(lineStart, columnStart), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var symbol = length switch
|
var symbol = length switch
|
||||||
{
|
{
|
||||||
8 => span switch
|
8 => span switch
|
||||||
@@ -287,6 +298,7 @@ public sealed class Tokenizer
|
|||||||
"&&" => Symbol.And,
|
"&&" => Symbol.And,
|
||||||
"||" => Symbol.Or,
|
"||" => Symbol.Or,
|
||||||
"::" => Symbol.DoubleColon,
|
"::" => Symbol.DoubleColon,
|
||||||
|
"x|" => Symbol.XOr,
|
||||||
_ => Symbol.None
|
_ => Symbol.None
|
||||||
},
|
},
|
||||||
1 => span[0] switch
|
1 => span[0] switch
|
||||||
@@ -361,9 +373,7 @@ public sealed class Tokenizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IdentifierToken(
|
return new IdentifierToken(CreateSpan(lineStart, columnStart), _content.Substring(start, _index - start));
|
||||||
CreateSpan(lineStart, columnStart),
|
|
||||||
_content.Substring(start, _index - start));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceSpan CreateSpan(int lineStart, int columnStart)
|
private SourceSpan CreateSpan(int lineStart, int columnStart)
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
obj=$(nubc main.nub)
|
nubc main.nub
|
||||||
clang $obj ../../runtime/.build/runtime.o -o .build/out
|
clang .build/main.ll -o .build/out
|
||||||
@@ -2,20 +2,6 @@ module main
|
|||||||
|
|
||||||
extern "puts" func puts(text: ^i8)
|
extern "puts" func puts(text: ^i8)
|
||||||
|
|
||||||
struct Name
|
extern "main" func main(argc: i64, argv: [?]^i8)
|
||||||
{
|
{
|
||||||
first: ^i8
|
|
||||||
last: ^i8
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Human
|
|
||||||
{
|
|
||||||
age: u64
|
|
||||||
name: Name
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "main" func main(argc: i64, argv: [?]^i8): i64
|
|
||||||
{
|
|
||||||
let x: [2]Human = [{}, {}]
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user