Compare commits

...

6 Commits

Author SHA1 Message Date
nub31
031b118a24 ... 2025-10-31 11:59:53 +01:00
nub31
c764857561 ... 2025-10-29 18:41:52 +01:00
nub31
4f724ddc0c remove refs 2025-10-29 17:45:25 +01:00
nub31
bf4c8838c6 ... 2025-10-29 15:14:13 +01:00
nub31
34a44f80a8 ... 2025-10-27 15:36:03 +01:00
nub31
640bd8c573 ... 2025-10-27 15:33:44 +01:00
21 changed files with 1275 additions and 1159 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.vscode

View File

@@ -1,5 +0,0 @@
{
"files.associations": {
"ref.h": "c"
}
}

View File

@@ -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,88 +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();
var moduleHeaders = new List<string>();
var commonHeaderOut = Path.Combine(".build", "runtime.h");
File.WriteAllText(commonHeaderOut, """
#include <stddef.h>
void *rc_alloc(size_t size, void (*destructor)(void *self));
void rc_retain(void *obj);
void rc_release(void *obj);
typedef struct
{
unsigned long long length;
char *data;
} nub_string;
typedef struct
{
unsigned long long length;
void *data;
} nub_slice;
""");
moduleHeaders.Add(commonHeaderOut);
foreach (var typedModule in typedModules)
{
var header = HeaderGenerator.Generate(typedModule.Key, typedModule.Value);
var headerOut = Path.Combine(".build", "modules", typedModule.Key + ".h");
Directory.CreateDirectory(Path.Combine(".build", "modules"));
File.WriteAllText(headerOut, header);
moduleHeaders.Add(headerOut);
}
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", [
..moduleHeaders.SelectMany(x => new[] { "-include", x }),
"-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;

View File

@@ -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)
{ {

View File

@@ -112,9 +112,10 @@ public class StructFieldNode(List<Token> tokens, IdentifierToken nameToken, NubT
} }
} }
public class StructNode(List<Token> tokens, IdentifierToken name, NubStructType structType, List<StructFieldNode> fields) : DefinitionNode(tokens, name) public class StructNode(List<Token> tokens, IdentifierToken name, NubStructType structType, bool packed, List<StructFieldNode> fields) : DefinitionNode(tokens, name)
{ {
public NubStructType StructType { get; } = structType; public NubStructType StructType { get; } = structType;
public bool Packed { get; } = packed;
public List<StructFieldNode> Fields { get; } = fields; public List<StructFieldNode> Fields { get; } = fields;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
@@ -132,8 +133,6 @@ public class StructNode(List<Token> tokens, IdentifierToken name, NubStructType
public abstract class StatementNode(List<Token> tokens) : Node(tokens); public abstract class StatementNode(List<Token> tokens) : Node(tokens);
public abstract class TerminalStatementNode(List<Token> tokens) : StatementNode(tokens);
public class BlockNode(List<Token> tokens, List<StatementNode> statements) : StatementNode(tokens) public class BlockNode(List<Token> tokens, List<StatementNode> statements) : StatementNode(tokens)
{ {
public List<StatementNode> Statements { get; } = statements; public List<StatementNode> Statements { get; } = statements;
@@ -154,7 +153,7 @@ public class StatementFuncCallNode(List<Token> tokens, FuncCallNode funcCall) :
} }
} }
public class ReturnNode(List<Token> tokens, ExpressionNode? value) : TerminalStatementNode(tokens) public class ReturnNode(List<Token> tokens, ExpressionNode? value) : StatementNode(tokens)
{ {
public ExpressionNode? Value { get; } = value; public ExpressionNode? Value { get; } = value;
@@ -164,9 +163,9 @@ public class ReturnNode(List<Token> tokens, ExpressionNode? value) : TerminalSta
} }
} }
public class AssignmentNode(List<Token> tokens, LValueExpressionNode target, ExpressionNode value) : StatementNode(tokens) public class AssignmentNode(List<Token> tokens, ExpressionNode target, ExpressionNode value) : StatementNode(tokens)
{ {
public LValueExpressionNode Target { get; } = target; public ExpressionNode Target { get; } = target;
public ExpressionNode Value { get; } = value; public ExpressionNode Value { get; } = value;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
@@ -205,7 +204,7 @@ public class VariableDeclarationNode(List<Token> tokens, IdentifierToken nameTok
} }
} }
public class ContinueNode(List<Token> tokens) : TerminalStatementNode(tokens) public class ContinueNode(List<Token> tokens) : StatementNode(tokens)
{ {
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -213,7 +212,7 @@ public class ContinueNode(List<Token> tokens) : TerminalStatementNode(tokens)
} }
} }
public class BreakNode(List<Token> tokens) : TerminalStatementNode(tokens) public class BreakNode(List<Token> tokens) : StatementNode(tokens)
{ {
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -308,13 +307,11 @@ public abstract class ExpressionNode(List<Token> tokens, NubType type) : Node(to
public NubType Type { get; } = type; public NubType Type { get; } = type;
} }
public abstract class LValueExpressionNode(List<Token> tokens, NubType type) : ExpressionNode(tokens, type); public abstract class LValue(List<Token> tokens, NubType type) : ExpressionNode(tokens, type);
public abstract class RValueExpressionNode(List<Token> tokens, NubType type) : ExpressionNode(tokens, type); public abstract class RValue(List<Token> tokens, NubType type) : ExpressionNode(tokens, type);
public abstract class IntermediateExpression(List<Token> tokens) : ExpressionNode(tokens, new NubVoidType()); public class StringLiteralNode(List<Token> tokens, string value) : RValue(tokens, new NubStringType())
public class StringLiteralNode(List<Token> tokens, string value) : RValueExpressionNode(tokens, new NubStringType())
{ {
public string Value { get; } = value; public string Value { get; } = value;
@@ -324,7 +321,7 @@ public class StringLiteralNode(List<Token> tokens, string value) : RValueExpress
} }
} }
public class CStringLiteralNode(List<Token> tokens, string value) : RValueExpressionNode(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;
@@ -334,7 +331,7 @@ public class CStringLiteralNode(List<Token> tokens, string value) : RValueExpres
} }
} }
public class I8LiteralNode(List<Token> tokens, sbyte value) : RValueExpressionNode(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;
@@ -344,7 +341,7 @@ public class I8LiteralNode(List<Token> tokens, sbyte value) : RValueExpressionNo
} }
} }
public class I16LiteralNode(List<Token> tokens, short value) : RValueExpressionNode(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;
@@ -354,7 +351,7 @@ public class I16LiteralNode(List<Token> tokens, short value) : RValueExpressionN
} }
} }
public class I32LiteralNode(List<Token> tokens, int value) : RValueExpressionNode(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;
@@ -364,7 +361,7 @@ public class I32LiteralNode(List<Token> tokens, int value) : RValueExpressionNod
} }
} }
public class I64LiteralNode(List<Token> tokens, long value) : RValueExpressionNode(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;
@@ -374,7 +371,7 @@ public class I64LiteralNode(List<Token> tokens, long value) : RValueExpressionNo
} }
} }
public class U8LiteralNode(List<Token> tokens, byte value) : RValueExpressionNode(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;
@@ -384,7 +381,7 @@ public class U8LiteralNode(List<Token> tokens, byte value) : RValueExpressionNod
} }
} }
public class U16LiteralNode(List<Token> tokens, ushort value) : RValueExpressionNode(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;
@@ -394,7 +391,7 @@ public class U16LiteralNode(List<Token> tokens, ushort value) : RValueExpression
} }
} }
public class U32LiteralNode(List<Token> tokens, uint value) : RValueExpressionNode(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;
@@ -404,7 +401,7 @@ public class U32LiteralNode(List<Token> tokens, uint value) : RValueExpressionNo
} }
} }
public class U64LiteralNode(List<Token> tokens, ulong value) : RValueExpressionNode(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;
@@ -414,7 +411,7 @@ public class U64LiteralNode(List<Token> tokens, ulong value) : RValueExpressionN
} }
} }
public class Float32LiteralNode(List<Token> tokens, float value) : RValueExpressionNode(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;
@@ -424,7 +421,7 @@ public class Float32LiteralNode(List<Token> tokens, float value) : RValueExpress
} }
} }
public class Float64LiteralNode(List<Token> tokens, double value) : RValueExpressionNode(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;
@@ -434,7 +431,7 @@ public class Float64LiteralNode(List<Token> tokens, double value) : RValueExpres
} }
} }
public class BoolLiteralNode(List<Token> tokens, bool value) : RValueExpressionNode(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;
@@ -444,7 +441,7 @@ public class BoolLiteralNode(List<Token> tokens, bool value) : RValueExpressionN
} }
} }
public class BinaryExpressionNode(List<Token> tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : RValueExpressionNode(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;
@@ -457,7 +454,7 @@ public class BinaryExpressionNode(List<Token> tokens, NubType type, ExpressionNo
} }
} }
public class UnaryExpressionNode(List<Token> tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : RValueExpressionNode(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;
@@ -468,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) : RValueExpressionNode(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;
@@ -483,7 +480,7 @@ public class FuncCallNode(List<Token> tokens, NubType type, ExpressionNode expre
} }
} }
public class VariableIdentifierNode(List<Token> tokens, NubType type, IdentifierToken nameToken) : LValueExpressionNode(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;
@@ -493,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) : RValueExpressionNode(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;
@@ -505,27 +502,7 @@ public class FuncIdentifierNode(List<Token> tokens, NubType type, IdentifierToke
} }
} }
public class ArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : RValueExpressionNode(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) : RValueExpressionNode(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) : LValueExpressionNode(tokens, type)
{ {
public ExpressionNode Target { get; } = target; public ExpressionNode Target { get; } = target;
public ExpressionNode Index { get; } = index; public ExpressionNode Index { get; } = index;
@@ -537,7 +514,7 @@ public class ArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNo
} }
} }
public class ConstArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(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;
@@ -549,7 +526,7 @@ public class ConstArrayIndexAccessNode(List<Token> tokens, NubType type, Express
} }
} }
public class SliceIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(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;
@@ -561,17 +538,17 @@ public class SliceIndexAccessNode(List<Token> tokens, NubType type, ExpressionNo
} }
} }
public class AddressOfNode(List<Token> tokens, NubType type, LValueExpressionNode lValue) : RValueExpressionNode(tokens, type) public class AddressOfNode(List<Token> tokens, NubType type, ExpressionNode target) : RValue(tokens, type)
{ {
public LValueExpressionNode LValue { get; } = lValue; public ExpressionNode Target { get; } = target;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
yield return LValue; yield return Target;
} }
} }
public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionNode target, IdentifierToken fieldToken) : LValueExpressionNode(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;
@@ -582,20 +559,7 @@ public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionN
} }
} }
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : RValueExpressionNode(tokens, type) public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValue(tokens, type)
{
public Dictionary<IdentifierToken, ExpressionNode> Initializers { get; } = initializers;
public override IEnumerable<Node> Children()
{
foreach (var initializer in Initializers)
{
yield return initializer.Value;
}
}
}
public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type)
{ {
public ExpressionNode Target { get; } = target; public ExpressionNode Target { get; } = target;
@@ -605,19 +569,9 @@ public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode ta
} }
} }
public class RefDereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type) public class SizeNode(List<Token> tokens, NubType targetType) : RValue(tokens, new NubIntType(false, 64))
{ {
public ExpressionNode Target { get; } = target; public NubType TargetType { get; } = targetType;
public override IEnumerable<Node> Children()
{
yield return Target;
}
}
public class SizeNode(List<Token> tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64))
{
public NubType TargetType { get; } = TargetType;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -625,7 +579,7 @@ public class SizeNode(List<Token> tokens, NubType TargetType) : RValueExpression
} }
} }
public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) : RValueExpressionNode(tokens, type) public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) : RValue(tokens, type)
{ {
public ExpressionNode Value { get; } = value; public ExpressionNode Value { get; } = value;
@@ -635,7 +589,7 @@ public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) :
} }
} }
public class RefStructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : RValueExpressionNode(tokens, type) 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;
@@ -648,6 +602,18 @@ public class RefStructInitializerNode(List<Token> tokens, NubType type, Dictiona
} }
} }
public class ConstArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : RValue(tokens, type)
{
public List<ExpressionNode> Values { get; } = values;
public override IEnumerable<Node> Children()
{
return Values;
}
}
public abstract class IntermediateExpression(List<Token> tokens) : ExpressionNode(tokens, new NubVoidType());
public class EnumReferenceIntermediateNode(List<Token> tokens, IdentifierToken moduleToken, IdentifierToken nameToken) : IntermediateExpression(tokens) public class EnumReferenceIntermediateNode(List<Token> tokens, IdentifierToken moduleToken, IdentifierToken nameToken) : IntermediateExpression(tokens)
{ {
public IdentifierToken ModuleToken { get; } = moduleToken; public IdentifierToken ModuleToken { get; } = moduleToken;

View File

@@ -5,6 +5,10 @@ namespace NubLang.Ast;
public abstract class NubType : IEquatable<NubType> public abstract class NubType : IEquatable<NubType>
{ {
public abstract ulong GetSize();
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);
@@ -17,24 +21,36 @@ public abstract class NubType : IEquatable<NubType>
public class NubVoidType : NubType public class NubVoidType : NubType
{ {
public override ulong GetSize() => 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;
public override int GetHashCode() => HashCode.Combine(typeof(NubVoidType)); public override int GetHashCode() => HashCode.Combine(typeof(NubVoidType));
} }
public sealed class NubIntType(bool signed, int width) : NubType public sealed class NubIntType(bool signed, ulong width) : NubType
{ {
public bool Signed { get; } = signed; public bool Signed { get; } = signed;
public int Width { get; } = width; public ulong Width { get; } = width;
public override ulong GetSize() => 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;
public override int GetHashCode() => HashCode.Combine(typeof(NubIntType), Signed, Width); public override int GetHashCode() => HashCode.Combine(typeof(NubIntType), Signed, Width);
} }
public sealed class NubFloatType(int width) : NubType public sealed class NubFloatType(ulong width) : NubType
{ {
public int Width { get; } = width; public ulong Width { get; } = width;
public override ulong GetSize() => 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;
@@ -43,6 +59,10 @@ public sealed class NubFloatType(int width) : NubType
public class NubBoolType : NubType public class NubBoolType : NubType
{ {
public override ulong GetSize() => 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;
public override int GetHashCode() => HashCode.Combine(typeof(NubBoolType)); public override int GetHashCode() => HashCode.Combine(typeof(NubBoolType));
@@ -52,25 +72,24 @@ public sealed class NubPointerType(NubType baseType) : NubType
{ {
public NubType BaseType { get; } = baseType; public NubType BaseType { get; } = baseType;
public override ulong GetSize() => 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);
public override int GetHashCode() => HashCode.Combine(typeof(NubPointerType), BaseType); public override int GetHashCode() => HashCode.Combine(typeof(NubPointerType), BaseType);
} }
public class NubRefType(NubType baseType) : NubType
{
public NubType BaseType { get; } = baseType;
public override string ToString() => "&" + BaseType;
public override bool Equals(NubType? other) => other is NubRefType;
public override int GetHashCode() => HashCode.Combine(typeof(NubRefType));
}
public class NubFuncType(List<NubType> parameters, NubType returnType) : NubType public class NubFuncType(List<NubType> parameters, NubType returnType) : NubType
{ {
public List<NubType> Parameters { get; } = parameters; public List<NubType> Parameters { get; } = parameters;
public NubType ReturnType { get; } = returnType; public NubType ReturnType { get; } = returnType;
public override ulong GetSize() => 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);
@@ -88,12 +107,70 @@ public class NubFuncType(List<NubType> parameters, NubType returnType) : NubType
} }
} }
public class NubStructType(string module, string name, List<NubStructFieldType> fields) : NubType public class NubStructType(string module, string name, bool packed, List<NubStructFieldType> fields) : NubType
{ {
public string Module { get; } = module; public string Module { get; } = module;
public string Name { get; } = name; public string Name { get; } = name;
public bool Packed { get; } = packed;
public List<NubStructFieldType> Fields { get; set; } = fields; public List<NubStructFieldType> Fields { get; set; } = fields;
public int GetFieldIndex(string name)
{
return Fields.FindIndex(x => x.Name == name);
}
public Dictionary<string, ulong> GetFieldOffsets()
{
var offsets = new Dictionary<string, ulong>();
ulong offset = 0;
foreach (var field in Fields)
{
var alignment = Packed ? 1 : field.Type.GetAlignment();
if (!Packed)
{
var padding = (alignment - offset % alignment) % alignment;
offset += padding;
}
offsets[field.Name] = offset;
offset += field.Type.GetSize();
}
return offsets;
}
public override ulong GetSize()
{
var offsets = GetFieldOffsets();
if (Fields.Count == 0)
{
return 0;
}
var lastField = Fields.Last();
var size = offsets[lastField.Name] + lastField.Type.GetSize();
if (!Packed)
{
var structAlignment = GetAlignment();
var padding = (structAlignment - size % structAlignment) % structAlignment;
size += padding;
}
return size;
}
public override ulong GetAlignment()
{
if (Fields.Count == 0)
return 1;
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);
@@ -110,6 +187,10 @@ public class NubSliceType(NubType elementType) : NubType
{ {
public NubType ElementType { get; } = elementType; public NubType ElementType { get; } = elementType;
public override ulong GetSize() => 16; // note(nub31): Fat pointer
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);
public override int GetHashCode() => HashCode.Combine(typeof(NubSliceType), ElementType); public override int GetHashCode() => HashCode.Combine(typeof(NubSliceType), ElementType);
@@ -120,6 +201,10 @@ public class NubConstArrayType(NubType elementType, ulong size) : NubType
public NubType ElementType { get; } = elementType; public NubType ElementType { get; } = elementType;
public ulong Size { get; } = size; public ulong Size { get; } = size;
public override ulong GetSize() => ElementType.GetSize() * Size;
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;
public override int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size); public override int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size);
@@ -129,6 +214,10 @@ public class NubArrayType(NubType elementType) : NubType
{ {
public NubType ElementType { get; } = elementType; public NubType ElementType { get; } = elementType;
public override ulong GetSize() => 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);
public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType); public override int GetHashCode() => HashCode.Combine(typeof(NubArrayType), ElementType);
@@ -136,6 +225,10 @@ public class NubArrayType(NubType elementType) : NubType
public class NubStringType : NubType public class NubStringType : NubType
{ {
public override ulong GetSize() => 16; // note(nub31): Fat pointer
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;
public override int GetHashCode() => HashCode.Combine(typeof(NubStringType)); public override int GetHashCode() => HashCode.Combine(typeof(NubStringType));

View File

@@ -221,30 +221,25 @@ public sealed class TypeChecker
} }
var currentModule = GetCurrentModule(); var currentModule = GetCurrentModule();
var type = new NubStructType(currentModule.Name.Value, structSyntax.NameToken.Value, fields.Select(x => new NubStructFieldType(x.NameToken.Value, x.Type, x.Value != null)).ToList()); var type = new NubStructType(currentModule.Name.Value, structSyntax.NameToken.Value, structSyntax.Packed, fields.Select(x => new NubStructFieldType(x.NameToken.Value, x.Type, x.Value != null)).ToList());
return new StructNode(structSyntax.Tokens, structSyntax.NameToken, type, fields); return new StructNode(structSyntax.Tokens, structSyntax.NameToken, type, structSyntax.Packed, fields);
} }
private AssignmentNode CheckAssignment(AssignmentSyntax statement) private AssignmentNode CheckAssignment(AssignmentSyntax statement)
{ {
var target = CheckExpression(statement.Target); var target = CheckExpression(statement.Target);
if (target is not LValueExpressionNode lValue) var value = CheckExpression(statement.Value, target.Type);
{
throw new CompileException(Diagnostic.Error("Cannot assign to an rvalue").At(statement).Build());
}
var value = CheckExpression(statement.Value, lValue.Type); if (value.Type != target.Type)
if (value.Type != lValue.Type)
{ {
throw new CompileException(Diagnostic throw new CompileException(Diagnostic
.Error($"Cannot assign {value.Type} to {lValue.Type}") .Error($"Cannot assign {value.Type} to {target.Type}")
.At(statement.Value) .At(statement.Value)
.Build()); .Build());
} }
return new AssignmentNode(statement.Tokens, lValue, value); return new AssignmentNode(statement.Tokens, target, value);
} }
private IfNode CheckIf(IfSyntax statement) private IfNode CheckIf(IfSyntax statement)
@@ -499,13 +494,8 @@ public sealed class TypeChecker
private AddressOfNode CheckAddressOf(AddressOfSyntax expression, NubType? expectedType) private AddressOfNode CheckAddressOf(AddressOfSyntax expression, NubType? expectedType)
{ {
var target = CheckExpression(expression.Target, (expectedType as NubPointerType)?.BaseType); var target = CheckExpression(expression.Target, (expectedType as NubPointerType)?.BaseType);
if (target is not LValueExpressionNode lvalue)
{
throw new CompileException(Diagnostic.Error("Cannot take address of an rvalue").At(expression).Build());
}
var type = new NubPointerType(target.Type); var type = new NubPointerType(target.Type);
return new AddressOfNode(expression.Tokens, type, lvalue); return new AddressOfNode(expression.Tokens, type, target);
} }
private ExpressionNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression, NubType? _) private ExpressionNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression, NubType? _)
@@ -530,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
{ {
@@ -573,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)
@@ -612,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")
@@ -728,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:
@@ -796,16 +802,18 @@ public sealed class TypeChecker
} }
} }
private ExpressionNode CheckDereference(DereferenceSyntax expression, NubType? _) private DereferenceNode CheckDereference(DereferenceSyntax expression, NubType? _)
{ {
var target = CheckExpression(expression.Target); var target = CheckExpression(expression.Target);
if (target.Type is not NubPointerType pointerType)
return target.Type switch
{ {
NubPointerType pointerType => new DereferenceNode(expression.Tokens, pointerType.BaseType, target), throw new CompileException(Diagnostic
NubRefType refType => new RefDereferenceNode(expression.Tokens, refType.BaseType, target), .Error($"Cannot dereference non-pointer type {target.Type}")
_ => throw new CompileException(Diagnostic.Error($"Cannot dereference non-pointer type {target.Type}").At(expression).Build()) .At(expression)
}; .Build());
}
return new DereferenceNode(expression.Tokens, pointerType.BaseType, target);
} }
private FuncCallNode CheckFuncCall(FuncCallSyntax expression, NubType? _) private FuncCallNode CheckFuncCall(FuncCallSyntax expression, NubType? _)
@@ -1090,7 +1098,7 @@ public sealed class TypeChecker
throw new UnreachableException(); throw new UnreachableException();
} }
private ExpressionNode CheckStructInitializer(StructInitializerSyntax expression, NubType? expectedType) private StructInitializerNode CheckStructInitializer(StructInitializerSyntax expression, NubType? expectedType)
{ {
NubStructType? structType = null; NubStructType? structType = null;
@@ -1104,14 +1112,10 @@ public sealed class TypeChecker
structType = checkedStructType; structType = checkedStructType;
} }
else
if (expectedType is NubStructType expectedStructType)
{ {
structType = expectedType switch structType ??= expectedStructType;
{
NubStructType expectedStructType => expectedStructType,
NubRefType { BaseType: NubStructType expectedStructType } => expectedStructType,
_ => structType
};
} }
if (structType == null) if (structType == null)
@@ -1154,14 +1158,7 @@ public sealed class TypeChecker
.Build()); .Build());
} }
if (expectedType is NubRefType refType && refType.BaseType == structType) return new StructInitializerNode(expression.Tokens, structType, initializers);
{
return new RefStructInitializerNode(expression.Tokens, refType, initializers);
}
else
{
return new StructInitializerNode(expression.Tokens, structType, initializers);
}
} }
private BlockNode CheckBlock(BlockSyntax node) private BlockNode CheckBlock(BlockSyntax node)

View File

@@ -26,7 +26,6 @@ public class TypeResolver
SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType, currentModule)), SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType, currentModule)),
ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType, currentModule), arr.Size), ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType, currentModule), arr.Size),
PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType, currentModule)), PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType, currentModule)),
RefTypeSyntax r => new NubRefType(ResolveType(r.BaseType, currentModule)),
StringTypeSyntax => new NubStringType(), StringTypeSyntax => new NubStringType(),
CustomTypeSyntax c => ResolveCustomType(c, currentModule), CustomTypeSyntax c => ResolveCustomType(c, currentModule),
VoidTypeSyntax => new NubVoidType(), VoidTypeSyntax => new NubVoidType(),
@@ -56,14 +55,14 @@ public class TypeResolver
if (!_resolvingTypes.Add(key)) if (!_resolvingTypes.Add(key))
{ {
var placeholder = new NubStructType(customType.ModuleToken?.Value ?? currentModule, customType.NameToken.Value, []); var placeholder = new NubStructType(customType.ModuleToken?.Value ?? currentModule, customType.NameToken.Value, structDef.Packed, []);
_typeCache[key] = placeholder; _typeCache[key] = placeholder;
return placeholder; return placeholder;
} }
try try
{ {
var result = new NubStructType(customType.ModuleToken?.Value ?? currentModule, structDef.NameToken.Value, []); var result = new NubStructType(customType.ModuleToken?.Value ?? currentModule, structDef.NameToken.Value, structDef.Packed, []);
_typeCache[key] = result; _typeCache[key] = result;
var fields = structDef.Fields var fields = structDef.Fields

View File

@@ -1,104 +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),
NubRefType r => CreateRefType(r, 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}" : ""),
_ => 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 CreateRefType(NubRefType ptr, string? varName)
{
var baseType = Create(ptr.BaseType);
return baseType + "*" + (varName != null ? $" {varName}" : "");
}
private static string CreatePointerType(NubPointerType ptr, string? varName)
{
var baseType = Create(ptr.BaseType);
return baseType + "*" + (varName != null ? $" {varName}" : "");
}
private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool inStructDef)
{
var elementType = Create(arr.ElementType);
// Treat const arrays as pointers unless in a struct definition
if (!inStructDef)
{
return elementType + "*" + (varName != null ? $" {varName}" : "");
}
if (varName != null)
{
return $"{elementType} {varName}[{arr.Size}]";
}
return $"{elementType}[{arr.Size}]";
}
private static string CreateArrayType(NubArrayType arr, string? varName)
{
var elementType = Create(arr.ElementType);
return elementType + "*" + (varName != null ? $" {varName}" : "");
}
private static string CreateFuncType(NubFuncType fn, string? varName)
{
var returnType = Create(fn.ReturnType);
var parameters = string.Join(", ", fn.Parameters.Select(p => Create(p)));
if (string.IsNullOrEmpty(parameters))
{
parameters = "void";
}
if (varName != null)
{
return $"{returnType} (*{varName})({parameters})";
}
return $"{returnType} (*)({parameters})";
}
}

View File

@@ -1,691 +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 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();
_writer.WriteLine($"void {CType.Create(structType.StructType)}_destroy({CType.Create(structType.StructType)} *self)");
_writer.WriteLine("{");
using (_writer.Indent())
{
foreach (var field in structType.Fields)
{
if (field.Type is NubRefType)
{
_writer.WriteLine($"rc_release(self->{field.NameToken.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())
{
foreach (var parameter in funcNode.Prototype.Parameters)
{
if (parameter.Type is NubRefType)
{
Scope.Defer(() => _writer.WriteLine($"rc_release({parameter.NameToken.Value});"));
}
}
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);
if (assignmentNode.Target.Type is NubRefType)
{
_writer.WriteLine($"rc_retain({value});");
Scope.Defer(() => _writer.WriteLine($"rc_release({value});"));
_writer.WriteLine($"rc_release({target});");
}
_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};");
if (returnNode.Value.Type is NubRefType)
{
_writer.WriteLine($"rc_retain({tmp});");
}
EmitScopeCleanup();
EmitLine(returnNode.Tokens.FirstOrDefault());
_writer.WriteLine($"return {tmp};");
}
}
private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode)
{
EmitFuncCall(statementFuncCallNode.FuncCall);
}
private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode)
{
if (variableDeclarationNode.Assignment != null)
{
var value = EmitExpression(variableDeclarationNode.Assignment);
if (variableDeclarationNode.Type is NubRefType)
{
_writer.WriteLine($"rc_retain({value});");
Scope.Defer(() => _writer.WriteLine($"rc_release({value});"));
}
_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),
RefDereferenceNode refDereferenceNode => EmitRefDereference(refDereferenceNode),
RefStructInitializerNode refStructInitializerNode => EmitRefStructInitializer(refStructInitializerNode),
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 values = new List<string>();
foreach (var value in arrayInitializerNode.Values)
{
values.Add(EmitExpression(value));
}
var arrayType = (NubConstArrayType)arrayInitializerNode.Type;
return $"({CType.Create(arrayType.ElementType)}[{arrayType.Size}]){{{string.Join(", ", values)}}}";
}
private string EmitDereference(DereferenceNode dereferenceNode)
{
var pointer = EmitExpression(dereferenceNode.Target);
return $"*{pointer}";
}
private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode)
{
var str = float32LiteralNode.Value.ToString("G9", System.Globalization.CultureInfo.InvariantCulture);
if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E'))
{
str += ".0";
}
return str + "f";
}
private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode)
{
var str = float64LiteralNode.Value.ToString("G17", System.Globalization.CultureInfo.InvariantCulture);
if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E'))
{
str += ".0";
}
return str;
}
private string EmitCast(CastNode castNode)
{
var value = EmitExpression(castNode.Value);
if (castNode is { Type: NubSliceType sliceType, Value.Type: NubConstArrayType arrayType })
{
return $"({CType.Create(sliceType)}){{.length = {arrayType.Size}, .data = (void*){value}}}";
}
return $"({CType.Create(castNode.Type)}){value}";
}
private string EmitFuncCall(FuncCallNode funcCallNode)
{
var name = EmitExpression(funcCallNode.Expression);
var parameterNames = new List<string>();
foreach (var parameter in funcCallNode.Parameters)
{
var result = EmitExpression(parameter);
if (parameter.Type is NubRefType)
{
_writer.WriteLine($"rc_retain({result});");
}
parameterNames.Add(result);
}
var tmp = NewTmp();
_writer.WriteLine($"{CType.Create(funcCallNode.Type)} {tmp} = {name}({string.Join(", ", parameterNames)});");
if (funcCallNode.Type is NubRefType)
{
Scope.Defer(() => _writer.WriteLine($"rc_release({tmp});"));
}
return tmp;
}
private string EmitAddressOf(AddressOfNode addressOfNode)
{
var value = EmitExpression(addressOfNode.LValue);
return $"&{value}";
}
private string EmitRefDereference(RefDereferenceNode refDereferenceNode)
{
var pointer = EmitExpression(refDereferenceNode.Target);
return $"*{pointer}";
}
private string EmitRefStructInitializer(RefStructInitializerNode refStructInitializerNode)
{
var type = (NubRefType)refStructInitializerNode.Type;
var structType = (NubStructType)type.BaseType;
var tmp = NewTmp();
_writer.WriteLine($"{CType.Create(type)} {tmp} = ({CType.Create(type)})rc_alloc(sizeof({CType.Create(structType)}), (void (*)(void *)){CType.Create(structType)}_destroy);");
Scope.Defer(() => _writer.WriteLine($"rc_release({tmp});"));
_writer.WriteLine($"*{tmp} = ({CType.Create(structType)}){{{0}}};");
_writer.WriteLine($"{CType.Create(structType)}_create({tmp});");
foreach (var initializer in refStructInitializerNode.Initializers)
{
var value = EmitExpression(initializer.Value);
_writer.WriteLine($"{tmp}->{initializer.Key} = {value};");
}
return tmp;
}
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);
}
}
private void EmitScopeCleanup()
{
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 Scope
{
private readonly List<Action> _deferred = [];
public void Defer(Action action)
{
_deferred.Add(action);
}
public Stack<Action> GetDeferred() => new(_deferred);
}

View File

@@ -1,49 +0,0 @@
using NubLang.Ast;
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();
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($"void {CType.Create(structType)}_destroy({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();
}
}

View 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}";
}
}

View File

@@ -48,4 +48,13 @@ public sealed class Module
.Where(x => x.Exported || includePrivate) .Where(x => x.Exported || includePrivate)
.ToList(); .ToList();
} }
public List<string> Imports()
{
return _definitions
.OfType<ImportSyntax>()
.Select(x => x.NameToken.Value)
.Distinct()
.ToList();
}
} }

View File

@@ -28,6 +28,7 @@ public sealed class Parser
var startIndex = _tokenIndex; var startIndex = _tokenIndex;
var exported = TryExpectSymbol(Symbol.Export); var exported = TryExpectSymbol(Symbol.Export);
var packed = TryExpectSymbol(Symbol.Packed);
if (TryExpectSymbol(Symbol.Extern)) if (TryExpectSymbol(Symbol.Extern))
{ {
@@ -43,7 +44,7 @@ public sealed class Parser
Symbol.Module => ParseModule(startIndex), Symbol.Module => ParseModule(startIndex),
Symbol.Import => ParseImport(startIndex), Symbol.Import => ParseImport(startIndex),
Symbol.Func => ParseFunc(startIndex, exported, null), Symbol.Func => ParseFunc(startIndex, exported, null),
Symbol.Struct => ParseStruct(startIndex, exported), Symbol.Struct => ParseStruct(startIndex, exported, packed),
Symbol.Enum => ParseEnum(startIndex, exported), Symbol.Enum => ParseEnum(startIndex, exported),
_ => throw new CompileException(Diagnostic _ => throw new CompileException(Diagnostic
.Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'") .Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'")
@@ -126,7 +127,7 @@ public sealed class Parser
return new FuncSyntax(GetTokens(startIndex), prototype, body); return new FuncSyntax(GetTokens(startIndex), prototype, body);
} }
private StructSyntax ParseStruct(int startIndex, bool exported) private StructSyntax ParseStruct(int startIndex, bool exported, bool packed)
{ {
var name = ExpectIdentifier(); var name = ExpectIdentifier();
@@ -152,7 +153,7 @@ public sealed class Parser
fields.Add(new StructFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldType, fieldValue)); fields.Add(new StructFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldType, fieldValue));
} }
return new StructSyntax(GetTokens(startIndex), name, exported, fields); return new StructSyntax(GetTokens(startIndex), name, exported, packed, fields);
} }
private EnumSyntax ParseEnum(int startIndex, bool exported) private EnumSyntax ParseEnum(int startIndex, bool exported)
@@ -422,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:
@@ -444,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()),
@@ -516,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);
@@ -650,7 +646,7 @@ public sealed class Parser
var startIndex = _tokenIndex; var startIndex = _tokenIndex;
if (TryExpectIdentifier(out var name)) if (TryExpectIdentifier(out var name))
{ {
if (name.Value[0] == 'u' && int.TryParse(name.Value[1..], out var size)) if (name.Value[0] == 'u' && ulong.TryParse(name.Value[1..], out var size))
{ {
if (size is not 8 and not 16 and not 32 and not 64) if (size is not 8 and not 16 and not 32 and not 64)
{ {
@@ -664,7 +660,7 @@ public sealed class Parser
return new IntTypeSyntax(GetTokens(startIndex), false, size); return new IntTypeSyntax(GetTokens(startIndex), false, size);
} }
if (name.Value[0] == 'i' && int.TryParse(name.Value[1..], out size)) if (name.Value[0] == 'i' && ulong.TryParse(name.Value[1..], out size))
{ {
if (size is not 8 and not 16 and not 32 and not 64) if (size is not 8 and not 16 and not 32 and not 64)
{ {
@@ -678,7 +674,7 @@ public sealed class Parser
return new IntTypeSyntax(GetTokens(startIndex), true, size); return new IntTypeSyntax(GetTokens(startIndex), true, size);
} }
if (name.Value[0] == 'f' && int.TryParse(name.Value[1..], out size)) if (name.Value[0] == 'f' && ulong.TryParse(name.Value[1..], out size))
{ {
if (size is not 32 and not 64) if (size is not 32 and not 64)
{ {
@@ -716,12 +712,6 @@ public sealed class Parser
} }
} }
if (TryExpectSymbol(Symbol.Ampersand))
{
var baseType = ParseType();
return new RefTypeSyntax(GetTokens(startIndex), baseType);
}
if (TryExpectSymbol(Symbol.Caret)) if (TryExpectSymbol(Symbol.Caret))
{ {
var baseType = ParseType(); var baseType = ParseType();

View File

@@ -20,7 +20,7 @@ public record FuncSyntax(List<Token> Tokens, FuncPrototypeSyntax Prototype, Bloc
public record StructFieldSyntax(List<Token> Tokens, IdentifierToken NameToken, TypeSyntax Type, ExpressionSyntax? Value) : SyntaxNode(Tokens); public record StructFieldSyntax(List<Token> Tokens, IdentifierToken NameToken, TypeSyntax Type, ExpressionSyntax? Value) : SyntaxNode(Tokens);
public record StructSyntax(List<Token> Tokens, IdentifierToken NameToken, bool Exported, List<StructFieldSyntax> Fields) : DefinitionSyntax(Tokens, NameToken, Exported); public record StructSyntax(List<Token> Tokens, IdentifierToken NameToken, bool Exported, bool Packed, List<StructFieldSyntax> Fields) : DefinitionSyntax(Tokens, NameToken, Exported);
public record EnumFieldSyntax(List<Token> Tokens, IdentifierToken NameToken, IntLiteralToken? ValueToken) : SyntaxNode(Tokens); public record EnumFieldSyntax(List<Token> Tokens, IdentifierToken NameToken, IntLiteralToken? ValueToken) : SyntaxNode(Tokens);
@@ -134,9 +134,9 @@ public record PointerTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeS
public record VoidTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens); public record VoidTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens);
public record IntTypeSyntax(List<Token> Tokens, bool Signed, int Width) : TypeSyntax(Tokens); public record IntTypeSyntax(List<Token> Tokens, bool Signed, ulong Width) : TypeSyntax(Tokens);
public record FloatTypeSyntax(List<Token> Tokens, int Width) : TypeSyntax(Tokens); public record FloatTypeSyntax(List<Token> Tokens, ulong Width) : TypeSyntax(Tokens);
public record BoolTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens); public record BoolTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens);
@@ -150,6 +150,4 @@ public record ConstArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType, ulon
public record CustomTypeSyntax(List<Token> Tokens, IdentifierToken? ModuleToken, IdentifierToken NameToken) : TypeSyntax(Tokens); public record CustomTypeSyntax(List<Token> Tokens, IdentifierToken? ModuleToken, IdentifierToken NameToken) : TypeSyntax(Tokens);
public record RefTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens);
#endregion #endregion

View File

@@ -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);
@@ -85,6 +100,7 @@ public enum Symbol
// Modifier // Modifier
Extern, Extern,
Packed,
Export, Export,
Colon, Colon,
@@ -118,6 +134,7 @@ public enum Symbol
Pipe, Pipe,
And, And,
Or, Or,
XOr,
At, At,
QuestionMark, QuestionMark,
} }

View File

@@ -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
@@ -248,6 +259,7 @@ public sealed class Tokenizer
"return" => Symbol.Return, "return" => Symbol.Return,
"struct" => Symbol.Struct, "struct" => Symbol.Struct,
"extern" => Symbol.Extern, "extern" => Symbol.Extern,
"packed" => Symbol.Packed,
"module" => Symbol.Module, "module" => Symbol.Module,
"export" => Symbol.Export, "export" => Symbol.Export,
"import" => Symbol.Import, "import" => Symbol.Import,
@@ -286,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
@@ -360,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)

View File

@@ -33,18 +33,20 @@ public sealed class TypedModule
fields.Add(new NubStructFieldType(field.NameToken.Value, typeResolver.ResolveType(field.Type, name), field.Value != null)); fields.Add(new NubStructFieldType(field.NameToken.Value, typeResolver.ResolveType(field.Type, name), field.Value != null));
} }
structTypes.Add(new NubStructType(name, structSyntax.NameToken.Value, fields)); structTypes.Add(new NubStructType(name, structSyntax.NameToken.Value, structSyntax.Packed, fields));
} }
return new TypedModule(functionPrototypes, structTypes); return new TypedModule(functionPrototypes, structTypes, module.Imports());
} }
public TypedModule(List<FuncPrototypeNode> functionPrototypes, List<NubStructType> structTypes) public TypedModule(List<FuncPrototypeNode> functionPrototypes, List<NubStructType> structTypes, List<string> imports)
{ {
FunctionPrototypes = functionPrototypes; FunctionPrototypes = functionPrototypes;
StructTypes = structTypes; StructTypes = structTypes;
Imports = imports;
} }
public List<FuncPrototypeNode> FunctionPrototypes { get; set; } public List<FuncPrototypeNode> FunctionPrototypes { get; }
public List<NubStructType> StructTypes { get; set; } public List<NubStructType> StructTypes { get; }
public List<string> Imports { get; }
} }

View File

@@ -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

View File

@@ -2,44 +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: &Human = {
age = 23
name = {
first = "oliver"
last = "stene"
}
}
let z: Human = {
age = 23
name = {
first = "oliver"
last = "stene"
}
}
test(x)
let y = x
return 0
}
func test(x: &Human): &Human
{
return x
} }

View File

@@ -7,6 +7,7 @@ void *rc_alloc(size_t size, void (*destructor)(void *self))
{ {
printf("rc_alloc %zu bytes\n", size); printf("rc_alloc %zu bytes\n", size);
ref_header *header = malloc(sizeof(ref_header) + size); ref_header *header = malloc(sizeof(ref_header) + size);
memset(header, 0, size);
if (!header) if (!header)
{ {
exit(69); exit(69);
@@ -20,6 +21,9 @@ void *rc_alloc(size_t size, void (*destructor)(void *self))
void rc_retain(void *obj) void rc_retain(void *obj)
{ {
if (!obj)
return;
printf("rc_retain\n"); printf("rc_retain\n");
ref_header *header = ((ref_header *)obj) - 1; ref_header *header = ((ref_header *)obj) - 1;
header->ref_count++; header->ref_count++;
@@ -27,6 +31,9 @@ void rc_retain(void *obj)
void rc_release(void *obj) void rc_release(void *obj)
{ {
if (!obj)
return;
ref_header *header = ((ref_header *)obj) - 1; ref_header *header = ((ref_header *)obj) - 1;
printf("rc_release\n"); printf("rc_release\n");
if (--header->ref_count == 0) if (--header->ref_count == 0)