Compare commits

...

12 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
nub31
560e6428ff ... 2025-10-26 22:28:48 +01:00
nub31
27bc4da4fd ... 2025-10-26 20:04:57 +01:00
nub31
d11df414ad Refs working? 2025-10-26 17:05:56 +01:00
nub31
3febaaea81 ref 2025-10-26 16:07:49 +01:00
nub31
828e20ddb6 ... 2025-10-26 14:15:25 +01:00
nub31
396ddf93a2 ... 2025-10-25 18:07:34 +02:00
33 changed files with 2632 additions and 1711 deletions

1
.gitignore vendored Normal file
View File

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

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;
@@ -21,7 +20,7 @@ foreach (var file in args)
} }
var modules = Module.Collect(syntaxTrees); var modules = Module.Collect(syntaxTrees);
var compilationUnits = new List<CompilationUnit>(); var compilationUnits = new List<List<TopLevelNode>>();
for (var i = 0; i < args.Length; i++) for (var i = 0; i < args.Length; i++)
{ {
@@ -42,8 +41,6 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro
return 1; return 1;
} }
var cPaths = new List<string>();
Directory.CreateDirectory(".build"); Directory.CreateDirectory(".build");
for (var i = 0; i < args.Length; i++) for (var i = 0; i < args.Length; i++)
@@ -51,41 +48,15 @@ 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", [
"-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

@@ -1,5 +1,4 @@
using NubLang.Ast; using NubLang.Ast;
using NubLang.Syntax;
using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
@@ -58,16 +57,16 @@ public static class AstExtensions
return false; return false;
} }
public static FuncNode? FunctionAtPosition(this CompilationUnit compilationUnit, int line, int character) public static FuncNode? FunctionAtPosition(this List<TopLevelNode> compilationUnit, int line, int character)
{ {
return compilationUnit return compilationUnit
.Functions .OfType<FuncNode>()
.FirstOrDefault(x => x.ContainsPosition(line, character)); .FirstOrDefault(x => x.ContainsPosition(line, character));
} }
public static Node? DeepestNodeAtPosition(this CompilationUnit compilationUnit, int line, int character) public static Node? DeepestNodeAtPosition(this List<TopLevelNode> compilationUnit, int line, int character)
{ {
return compilationUnit.Functions return compilationUnit
.SelectMany(x => x.DescendantsAndSelf()) .SelectMany(x => x.DescendantsAndSelf())
.Where(n => n.ContainsPosition(line, character)) .Where(n => n.ContainsPosition(line, character))
.OrderBy(n => n.Tokens.First().Span.Start.Line) .OrderBy(n => n.Tokens.First().Span.Start.Line)

View File

@@ -118,35 +118,38 @@ internal class CompletionHandler(WorkspaceManager workspaceManager) : Completion
var compilationUnit = workspaceManager.GetCompilationUnit(uri); var compilationUnit = workspaceManager.GetCompilationUnit(uri);
if (compilationUnit != null) if (compilationUnit != null)
{ {
var function = compilationUnit.Functions.FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character)); var function = compilationUnit.OfType<FuncNode>().FirstOrDefault(x => x.Body != null && x.Body.ContainsPosition(position.Line, position.Character));
if (function != null) if (function != null)
{ {
completions.AddRange(_statementSnippets); completions.AddRange(_statementSnippets);
foreach (var prototype in compilationUnit.ImportedFunctions) // foreach (var (module, prototypes) in compilationUnit.ImportedFunctions)
{ // {
var parameterStrings = new List<string>(); // foreach (var prototype in prototypes)
foreach (var (index, parameter) in prototype.Parameters.Index()) // {
{ // var parameterStrings = new List<string>();
parameterStrings.AddRange($"${{{index + 1}:{parameter.Name}}}"); // foreach (var (index, parameter) in prototype.Parameters.Index())
} // {
// parameterStrings.AddRange($"${{{index + 1}:{parameter.NameToken.Value}}}");
completions.Add(new CompletionItem // }
{ //
Kind = CompletionItemKind.Function, // completions.Add(new CompletionItem
Label = $"{prototype.Module}::{prototype.Name}", // {
InsertTextFormat = InsertTextFormat.Snippet, // Kind = CompletionItemKind.Function,
InsertText = $"{prototype.Module}::{prototype.Name}({string.Join(", ", parameterStrings)})", // Label = $"{module.Value}::{prototype.NameToken.Value}",
}); // InsertTextFormat = InsertTextFormat.Snippet,
} // InsertText = $"{module.Value}::{prototype.NameToken.Value}({string.Join(", ", parameterStrings)})",
// });
// }
// }
foreach (var parameter in function.Prototype.Parameters) foreach (var parameter in function.Prototype.Parameters)
{ {
completions.Add(new CompletionItem completions.Add(new CompletionItem
{ {
Kind = CompletionItemKind.Variable, Kind = CompletionItemKind.Variable,
Label = parameter.Name, Label = parameter.NameToken.Value,
InsertText = parameter.Name, InsertText = parameter.NameToken.Value,
}); });
} }
@@ -159,8 +162,8 @@ internal class CompletionHandler(WorkspaceManager workspaceManager) : Completion
completions.Add(new CompletionItem completions.Add(new CompletionItem
{ {
Kind = CompletionItemKind.Variable, Kind = CompletionItemKind.Variable,
Label = variable.Name, Label = variable.NameToken.Value,
InsertText = variable.Name, InsertText = variable.NameToken.Value,
}); });
} }
} }

View File

@@ -37,7 +37,7 @@ internal class DefinitionHandler(WorkspaceManager workspaceManager) : Definition
{ {
var function = compilationUnit.FunctionAtPosition(line, character); var function = compilationUnit.FunctionAtPosition(line, character);
var parameter = function?.Prototype.Parameters.FirstOrDefault(x => x.Name == variableIdentifierNode.Name); var parameter = function?.Prototype.Parameters.FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value);
if (parameter != null) if (parameter != null)
{ {
return new LocationOrLocationLinks(parameter.ToLocation()); return new LocationOrLocationLinks(parameter.ToLocation());
@@ -46,7 +46,7 @@ internal class DefinitionHandler(WorkspaceManager workspaceManager) : Definition
var variable = function?.Body? var variable = function?.Body?
.Descendants() .Descendants()
.OfType<VariableDeclarationNode>() .OfType<VariableDeclarationNode>()
.FirstOrDefault(x => x.Name == variableIdentifierNode.Name); .FirstOrDefault(x => x.NameToken.Value == variableIdentifierNode.NameToken.Value);
if (variable != null) if (variable != null)
{ {
@@ -57,11 +57,16 @@ internal class DefinitionHandler(WorkspaceManager workspaceManager) : Definition
} }
case FuncIdentifierNode funcIdentifierNode: case FuncIdentifierNode funcIdentifierNode:
{ {
var prototype = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name); // var prototype = compilationUnit
if (prototype != null) // .ImportedFunctions
{ // .Where(x => x.Key.Value == funcIdentifierNode.ModuleToken.Value)
return new LocationOrLocationLinks(prototype.ToLocation()); // .SelectMany(x => x.Value)
} // .FirstOrDefault(x => x.NameToken.Value == funcIdentifierNode.NameToken.Value);
//
// if (prototype != null)
// {
// return new LocationOrLocationLinks(prototype.ToLocation());
// }
return null; return null;
} }

View File

@@ -39,104 +39,110 @@ internal class HoverHandler(WorkspaceManager workspaceManager) : HoverHandlerBas
return null; return null;
} }
var message = CreateMessage(hoveredNode, compilationUnit); // var message = CreateMessage(hoveredNode, compilationUnit);
if (message == null) // if (message == null)
{ // {
return null; // return null;
} // }
//
// return new Hover
// {
// Contents = new MarkedStringsOrMarkupContent(new MarkupContent
// {
// Value = message,
// Kind = MarkupKind.Markdown,
// })
// };
return new Hover return null;
{
Contents = new MarkedStringsOrMarkupContent(new MarkupContent
{
Value = message,
Kind = MarkupKind.Markdown,
})
};
} }
private static string? CreateMessage(Node hoveredNode, CompilationUnit compilationUnit) // private static string? CreateMessage(Node hoveredNode, CompilationUnit compilationUnit)
{ // {
return hoveredNode switch // return hoveredNode switch
{ // {
FuncNode funcNode => CreateFuncPrototypeMessage(funcNode.Prototype), // FuncNode funcNode => CreateFuncPrototypeMessage(funcNode.Prototype),
FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeMessage(funcPrototypeNode), // FuncPrototypeNode funcPrototypeNode => CreateFuncPrototypeMessage(funcPrototypeNode),
FuncIdentifierNode funcIdentifierNode => CreateFuncIdentifierMessage(funcIdentifierNode, compilationUnit), // FuncIdentifierNode funcIdentifierNode => CreateFuncIdentifierMessage(funcIdentifierNode, compilationUnit),
FuncParameterNode funcParameterNode => CreateTypeNameMessage("Function parameter", funcParameterNode.Name, funcParameterNode.Type), // FuncParameterNode funcParameterNode => CreateTypeNameMessage("Function parameter", funcParameterNode.NameToken.Value, funcParameterNode.Type),
VariableIdentifierNode variableIdentifierNode => CreateTypeNameMessage("Variable", variableIdentifierNode.Name, variableIdentifierNode.Type), // VariableIdentifierNode variableIdentifierNode => CreateTypeNameMessage("Variable", variableIdentifierNode.NameToken.Value, variableIdentifierNode.Type),
VariableDeclarationNode variableDeclarationNode => CreateTypeNameMessage("Variable declaration", variableDeclarationNode.Name, variableDeclarationNode.Type), // VariableDeclarationNode variableDeclarationNode => CreateTypeNameMessage("Variable declaration", variableDeclarationNode.NameToken.Value, variableDeclarationNode.Type),
StructFieldAccessNode structFieldAccessNode => CreateTypeNameMessage("Struct field", $"{structFieldAccessNode.Target.Type}.{structFieldAccessNode.Field}", structFieldAccessNode.Type), // StructFieldAccessNode structFieldAccessNode => CreateTypeNameMessage("Struct field", $"{structFieldAccessNode.Target.Type}.{structFieldAccessNode.FieldToken.Value}", structFieldAccessNode.Type),
CStringLiteralNode cStringLiteralNode => CreateLiteralMessage(cStringLiteralNode.Type, '"' + cStringLiteralNode.Value + '"'), // CStringLiteralNode cStringLiteralNode => CreateLiteralMessage(cStringLiteralNode.Type, '"' + cStringLiteralNode.Value + '"'),
StringLiteralNode stringLiteralNode => CreateLiteralMessage(stringLiteralNode.Type, '"' + stringLiteralNode.Value + '"'), // StringLiteralNode stringLiteralNode => CreateLiteralMessage(stringLiteralNode.Type, '"' + stringLiteralNode.Value + '"'),
BoolLiteralNode boolLiteralNode => CreateLiteralMessage(boolLiteralNode.Type, boolLiteralNode.Value.ToString()), // BoolLiteralNode boolLiteralNode => CreateLiteralMessage(boolLiteralNode.Type, boolLiteralNode.Value.ToString()),
Float32LiteralNode float32LiteralNode => CreateLiteralMessage(float32LiteralNode.Type, float32LiteralNode.Value.ToString(CultureInfo.InvariantCulture)), // Float32LiteralNode float32LiteralNode => CreateLiteralMessage(float32LiteralNode.Type, float32LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
Float64LiteralNode float64LiteralNode => CreateLiteralMessage(float64LiteralNode.Type, float64LiteralNode.Value.ToString(CultureInfo.InvariantCulture)), // Float64LiteralNode float64LiteralNode => CreateLiteralMessage(float64LiteralNode.Type, float64LiteralNode.Value.ToString(CultureInfo.InvariantCulture)),
I8LiteralNode i8LiteralNode => CreateLiteralMessage(i8LiteralNode.Type, i8LiteralNode.Value.ToString()), // I8LiteralNode i8LiteralNode => CreateLiteralMessage(i8LiteralNode.Type, i8LiteralNode.Value.ToString()),
I16LiteralNode i16LiteralNode => CreateLiteralMessage(i16LiteralNode.Type, i16LiteralNode.Value.ToString()), // I16LiteralNode i16LiteralNode => CreateLiteralMessage(i16LiteralNode.Type, i16LiteralNode.Value.ToString()),
I32LiteralNode i32LiteralNode => CreateLiteralMessage(i32LiteralNode.Type, i32LiteralNode.Value.ToString()), // I32LiteralNode i32LiteralNode => CreateLiteralMessage(i32LiteralNode.Type, i32LiteralNode.Value.ToString()),
I64LiteralNode i64LiteralNode => CreateLiteralMessage(i64LiteralNode.Type, i64LiteralNode.Value.ToString()), // I64LiteralNode i64LiteralNode => CreateLiteralMessage(i64LiteralNode.Type, i64LiteralNode.Value.ToString()),
U8LiteralNode u8LiteralNode => CreateLiteralMessage(u8LiteralNode.Type, u8LiteralNode.Value.ToString()), // U8LiteralNode u8LiteralNode => CreateLiteralMessage(u8LiteralNode.Type, u8LiteralNode.Value.ToString()),
U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()), // U16LiteralNode u16LiteralNode => CreateLiteralMessage(u16LiteralNode.Type, u16LiteralNode.Value.ToString()),
U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()), // U32LiteralNode u32LiteralNode => CreateLiteralMessage(u32LiteralNode.Type, u32LiteralNode.Value.ToString()),
U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()), // U64LiteralNode u64LiteralNode => CreateLiteralMessage(u64LiteralNode.Type, u64LiteralNode.Value.ToString()),
// Expressions can have a generic fallback showing the resulting type // // Expressions can have a generic fallback showing the resulting type
ExpressionNode expressionNode => $""" // ExpressionNode expressionNode => $"""
**Expression** `{expressionNode.GetType().Name}` // **Expression** `{expressionNode.GetType().Name}`
```nub // ```nub
{expressionNode.Type} // {expressionNode.Type}
``` // ```
""", // """,
BlockNode => null, // BlockNode => null,
_ => hoveredNode.GetType().Name // _ => hoveredNode.GetType().Name
}; // };
} // }
//
private static string CreateLiteralMessage(NubType type, string value) // private static string CreateLiteralMessage(NubType type, string value)
{ // {
return $""" // return $"""
**Literal** `{type}` // **Literal** `{type}`
```nub // ```nub
{value}: {type} // {value}: {type}
``` // ```
"""; // """;
} // }
//
private static string CreateTypeNameMessage(string description, string name, NubType type) // private static string CreateTypeNameMessage(string description, string name, NubType type)
{ // {
return $""" // return $"""
**{description}** `{name}` // **{description}** `{name}`
```nub // ```nub
{name}: {type} // {name}: {type}
``` // ```
"""; // """;
} // }
//
private static string CreateFuncIdentifierMessage(FuncIdentifierNode funcIdentifierNode, CompilationUnit compilationUnit) // private static string CreateFuncIdentifierMessage(FuncIdentifierNode funcIdentifierNode, CompilationUnit compilationUnit)
{ // {
var func = compilationUnit.ImportedFunctions.FirstOrDefault(x => x.Module == funcIdentifierNode.Module && x.Name == funcIdentifierNode.Name); // var func = compilationUnit.ImportedFunctions
if (func == null) // .Where(x => x.Key.Value == funcIdentifierNode.ModuleToken.Value)
{ // .SelectMany(x => x.Value)
return $""" // .FirstOrDefault(x => x.NameToken.Value == funcIdentifierNode.NameToken.Value);
**Function** `{funcIdentifierNode.Module}::{funcIdentifierNode.Name}` //
```nub // if (func == null)
// Declaration not found // {
``` // return $"""
"""; // **Function** `{funcIdentifierNode.ModuleToken.Value}::{funcIdentifierNode.NameToken.Value}`
} // ```nub
// // Declaration not found
return CreateFuncPrototypeMessage(func); // ```
} // """;
// }
private static string CreateFuncPrototypeMessage(FuncPrototypeNode funcPrototypeNode) //
{ // return CreateFuncPrototypeMessage(func);
var parameterText = string.Join(", ", funcPrototypeNode.Parameters.Select(x => $"{x.Name}: {x.Type}")); // }
var externText = funcPrototypeNode.ExternSymbol != null ? $"extern \"{funcPrototypeNode.ExternSymbol}\" " : ""; //
// private static string CreateFuncPrototypeMessage(FuncPrototypeNode funcPrototypeNode)
return $""" // {
**Function** `{funcPrototypeNode.Module}::{funcPrototypeNode.Name}` // var parameterText = string.Join(", ", funcPrototypeNode.Parameters.Select(x => $"{x.NameToken.Value}: {x.Type}"));
```nub // var externText = funcPrototypeNode.ExternSymbolToken != null ? $"extern \"{funcPrototypeNode.ExternSymbolToken.Value}\" " : "";
{externText}func {funcPrototypeNode.Module}::{funcPrototypeNode.Name}({parameterText}): {funcPrototypeNode.ReturnType} //
``` // return $"""
"""; // **Function** `{funcPrototypeNode.NameToken.Value}`
} // ```nub
// {externText}func {funcPrototypeNode.NameToken.Value}({parameterText}): {funcPrototypeNode.ReturnType}
// ```
// """;
// }
} }

View File

@@ -7,7 +7,8 @@ namespace NubLang.LSP;
public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher) public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
{ {
private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new(); private readonly Dictionary<string, SyntaxTree> _syntaxTrees = new();
private readonly Dictionary<string, CompilationUnit> _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)
{ {
@@ -34,6 +35,7 @@ public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
var typeChecker = new TypeChecker(syntaxTree, modules); var typeChecker = new TypeChecker(syntaxTree, modules);
var result = typeChecker.Check(); var result = typeChecker.Check();
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics); diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
_compilationUnits[fsPath] = result; _compilationUnits[fsPath] = result;
} }
} }
@@ -57,6 +59,7 @@ public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
var typeChecker = new TypeChecker(syntaxTree, modules); var typeChecker = new TypeChecker(syntaxTree, modules);
var result = typeChecker.Check(); var result = typeChecker.Check();
diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics); diagnosticsPublisher.Publish(fsPath, typeChecker.Diagnostics);
_compilationUnits[fsPath] = result; _compilationUnits[fsPath] = result;
} }
@@ -67,7 +70,7 @@ public class WorkspaceManager(DiagnosticsPublisher diagnosticsPublisher)
_compilationUnits.Remove(fsPath); _compilationUnits.Remove(fsPath);
} }
public CompilationUnit? GetCompilationUnit(DocumentUri path) public List<TopLevelNode>? GetCompilationUnit(DocumentUri path)
{ {
return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath()); return _compilationUnits.GetValueOrDefault(path.GetFileSystemPath());
} }

View File

@@ -1,15 +1,12 @@
using NubLang.Syntax;
namespace NubLang.Ast; namespace NubLang.Ast;
public sealed class CompilationUnit // public sealed class CompilationUnit(IdentifierToken module, List<FuncNode> functions, List<StructNode> structTypes, Dictionary<IdentifierToken, List<NubStructType>> importedStructTypes, Dictionary<IdentifierToken, List<FuncPrototypeNode>> importedFunctions)
{ // {
public CompilationUnit(List<FuncNode> functions, List<NubStructType> importedStructTypes, List<FuncPrototypeNode> importedFunctions) // public IdentifierToken Module { get; } = module;
{ // public List<FuncNode> Functions { get; } = functions;
Functions = functions; // public List<StructNode> Structs { get; } = structTypes;
ImportedStructTypes = importedStructTypes; // public Dictionary<IdentifierToken, List<NubStructType>> ImportedStructTypes { get; } = importedStructTypes;
ImportedFunctions = importedFunctions; // public Dictionary<IdentifierToken, List<FuncPrototypeNode>> ImportedFunctions { get; } = importedFunctions;
} // }
public List<FuncNode> Functions { get; }
public List<NubStructType> ImportedStructTypes { get; }
public List<FuncPrototypeNode> ImportedFunctions { get; }
}

View File

@@ -29,17 +29,38 @@ public abstract class Node(List<Token> tokens)
} }
} }
#region Definitions public abstract class TopLevelNode(List<Token> tokens) : Node(tokens);
public abstract class DefinitionNode(List<Token> tokens, string module, string name) : Node(tokens) public class ImportNode(List<Token> tokens, IdentifierToken nameToken) : TopLevelNode(tokens)
{ {
public string Module { get; } = module; public IdentifierToken NameToken { get; } = nameToken;
public string Name { get; } = name;
public override IEnumerable<Node> Children()
{
return [];
}
} }
public class FuncParameterNode(List<Token> tokens, string name, NubType type) : Node(tokens) public class ModuleNode(List<Token> tokens, IdentifierToken nameToken) : TopLevelNode(tokens)
{ {
public string Name { get; } = name; public IdentifierToken NameToken { get; } = nameToken;
public override IEnumerable<Node> Children()
{
return [];
}
}
#region Definitions
public abstract class DefinitionNode(List<Token> tokens, IdentifierToken nameToken) : TopLevelNode(tokens)
{
public IdentifierToken NameToken { get; } = nameToken;
}
public class FuncParameterNode(List<Token> tokens, IdentifierToken nameToken, NubType type) : Node(tokens)
{
public IdentifierToken NameToken { get; } = nameToken;
public NubType Type { get; } = type; public NubType Type { get; } = type;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
@@ -48,11 +69,10 @@ public class FuncParameterNode(List<Token> tokens, string name, NubType type) :
} }
} }
public class FuncPrototypeNode(List<Token> tokens, string module, string name, string? externSymbol, List<FuncParameterNode> parameters, NubType returnType) : Node(tokens) public class FuncPrototypeNode(List<Token> tokens, IdentifierToken nameToken, StringLiteralToken? externSymbolToken, List<FuncParameterNode> parameters, NubType returnType) : Node(tokens)
{ {
public string Module { get; } = module; public IdentifierToken NameToken { get; } = nameToken;
public string Name { get; } = name; public StringLiteralToken? ExternSymbolToken { get; } = externSymbolToken;
public string? ExternSymbol { get; } = externSymbol;
public List<FuncParameterNode> Parameters { get; } = parameters; public List<FuncParameterNode> Parameters { get; } = parameters;
public NubType ReturnType { get; } = returnType; public NubType ReturnType { get; } = returnType;
@@ -62,7 +82,7 @@ public class FuncPrototypeNode(List<Token> tokens, string module, string name, s
} }
} }
public class FuncNode(List<Token> tokens, FuncPrototypeNode prototype, BlockNode? body) : DefinitionNode(tokens, prototype.Module, prototype.Name) public class FuncNode(List<Token> tokens, FuncPrototypeNode prototype, BlockNode? body) : DefinitionNode(tokens, prototype.NameToken)
{ {
public FuncPrototypeNode Prototype { get; } = prototype; public FuncPrototypeNode Prototype { get; } = prototype;
public BlockNode? Body { get; } = body; public BlockNode? Body { get; } = body;
@@ -77,14 +97,42 @@ public class FuncNode(List<Token> tokens, FuncPrototypeNode prototype, BlockNode
} }
} }
public class StructFieldNode(List<Token> tokens, IdentifierToken nameToken, NubType type, ExpressionNode? value) : Node(tokens)
{
public IdentifierToken NameToken { get; } = nameToken;
public NubType Type { get; } = type;
public ExpressionNode? Value { get; } = value;
public override IEnumerable<Node> Children()
{
if (Value != null)
{
yield return Value;
}
}
}
public class StructNode(List<Token> tokens, IdentifierToken name, NubStructType structType, bool packed, List<StructFieldNode> fields) : DefinitionNode(tokens, name)
{
public NubStructType StructType { get; } = structType;
public bool Packed { get; } = packed;
public List<StructFieldNode> Fields { get; } = fields;
public override IEnumerable<Node> Children()
{
foreach (var field in Fields)
{
yield return field;
}
}
}
#endregion #endregion
#region Statements #region Statements
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;
@@ -105,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;
@@ -115,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()
@@ -144,9 +192,9 @@ public class IfNode(List<Token> tokens, ExpressionNode condition, BlockNode body
} }
} }
public class VariableDeclarationNode(List<Token> tokens, string name, ExpressionNode? assignment, NubType type) : StatementNode(tokens) public class VariableDeclarationNode(List<Token> tokens, IdentifierToken nameToken, ExpressionNode? assignment, NubType type) : StatementNode(tokens)
{ {
public string Name { get; } = name; public IdentifierToken NameToken { get; } = nameToken;
public ExpressionNode? Assignment { get; } = assignment; public ExpressionNode? Assignment { get; } = assignment;
public NubType Type { get; } = type; public NubType Type { get; } = type;
@@ -156,7 +204,7 @@ public class VariableDeclarationNode(List<Token> tokens, string name, Expression
} }
} }
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()
{ {
@@ -164,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()
{ {
@@ -184,10 +232,10 @@ public class WhileNode(List<Token> tokens, ExpressionNode condition, BlockNode b
} }
} }
public class ForSliceNode(List<Token> tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens) public class ForSliceNode(List<Token> tokens, IdentifierToken elementNameToken, IdentifierToken? indexNameToken, ExpressionNode target, BlockNode body) : StatementNode(tokens)
{ {
public string ElementName { get; } = elementName; public IdentifierToken ElementNameToken { get; } = elementNameToken;
public string? IndexName { get; } = indexName; public IdentifierToken? IndexNameToken { get; } = indexNameToken;
public ExpressionNode Target { get; } = target; public ExpressionNode Target { get; } = target;
public BlockNode Body { get; } = body; public BlockNode Body { get; } = body;
@@ -198,10 +246,10 @@ public class ForSliceNode(List<Token> tokens, string elementName, string? indexN
} }
} }
public class ForConstArrayNode(List<Token> tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens) public class ForConstArrayNode(List<Token> tokens, IdentifierToken elementNameToken, IdentifierToken? indexNameToken, ExpressionNode target, BlockNode body) : StatementNode(tokens)
{ {
public string ElementName { get; } = elementName; public IdentifierToken ElementNameToken { get; } = elementNameToken;
public string? IndexName { get; } = indexName; public IdentifierToken? IndexNameToken { get; } = indexNameToken;
public ExpressionNode Target { get; } = target; public ExpressionNode Target { get; } = target;
public BlockNode Body { get; } = body; public BlockNode Body { get; } = body;
@@ -259,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;
@@ -275,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;
@@ -285,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;
@@ -295,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;
@@ -305,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;
@@ -315,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;
@@ -325,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;
@@ -335,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;
@@ -345,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;
@@ -355,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;
@@ -365,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;
@@ -375,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;
@@ -385,7 +431,7 @@ public class Float64LiteralNode(List<Token> tokens, double value) : RValueExpres
} }
} }
public class BoolLiteralNode(List<Token> tokens, NubType type, bool value) : RValueExpressionNode(tokens, type) public class BoolLiteralNode(List<Token> tokens, bool value) : RValue(tokens, new NubBoolType())
{ {
public bool Value { get; } = value; public bool Value { get; } = value;
@@ -395,7 +441,7 @@ public class BoolLiteralNode(List<Token> tokens, NubType type, bool value) : RVa
} }
} }
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;
@@ -408,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;
@@ -419,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;
@@ -434,9 +480,9 @@ public class FuncCallNode(List<Token> tokens, NubType type, ExpressionNode expre
} }
} }
public class VariableIdentifierNode(List<Token> tokens, NubType type, string name) : LValueExpressionNode(tokens, type) public class VariableIdentifierNode(List<Token> tokens, NubType type, IdentifierToken nameToken) : LValue(tokens, type)
{ {
public string Name { get; } = name; public IdentifierToken NameToken { get; } = nameToken;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -444,11 +490,11 @@ public class VariableIdentifierNode(List<Token> tokens, NubType type, string nam
} }
} }
public class FuncIdentifierNode(List<Token> tokens, NubType type, string module, string name, string? externSymbol) : RValueExpressionNode(tokens, type) public class FuncIdentifierNode(List<Token> tokens, NubType type, IdentifierToken moduleToken, IdentifierToken nameToken, StringLiteralToken? externSymbolToken) : RValue(tokens, type)
{ {
public string Module { get; } = module; public IdentifierToken ModuleToken { get; } = moduleToken;
public string Name { get; } = name; public IdentifierToken NameToken { get; } = nameToken;
public string? ExternSymbol { get; } = externSymbol; public StringLiteralToken? ExternSymbolToken { get; } = externSymbolToken;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -456,27 +502,7 @@ public class FuncIdentifierNode(List<Token> tokens, NubType type, string module,
} }
} }
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;
@@ -488,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;
@@ -500,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;
@@ -512,20 +538,9 @@ 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 override IEnumerable<Node> Children()
{
yield return LValue;
}
}
public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionNode target, string field) : LValueExpressionNode(tokens, type)
{ {
public ExpressionNode Target { get; } = target; public ExpressionNode Target { get; } = target;
public string Field { get; } = field;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -533,9 +548,50 @@ public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionN
} }
} }
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<string, ExpressionNode> initializers) : RValueExpressionNode(tokens, type) public class StructFieldAccessNode(List<Token> tokens, NubType type, ExpressionNode target, IdentifierToken fieldToken) : LValue(tokens, type)
{ {
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers; public ExpressionNode Target { get; } = target;
public IdentifierToken FieldToken { get; } = fieldToken;
public override IEnumerable<Node> Children()
{
yield return Target;
}
}
public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValue(tokens, type)
{
public ExpressionNode Target { get; } = target;
public override IEnumerable<Node> Children()
{
yield return Target;
}
}
public class SizeNode(List<Token> tokens, NubType targetType) : RValue(tokens, new NubIntType(false, 64))
{
public NubType TargetType { get; } = targetType;
public override IEnumerable<Node> Children()
{
return [];
}
}
public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) : RValue(tokens, type)
{
public ExpressionNode Value { get; } = value;
public override IEnumerable<Node> Children()
{
yield return Value;
}
}
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : RValue(tokens, type)
{
public Dictionary<IdentifierToken, ExpressionNode> Initializers { get; } = initializers;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
@@ -546,40 +602,22 @@ public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<
} }
} }
public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type) public class ConstArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : RValue(tokens, type)
{ {
public ExpressionNode Target { get; } = target; public List<ExpressionNode> Values { get; } = values;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {
yield return Target; return Values;
} }
} }
public class SizeNode(List<Token> tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64)) public abstract class IntermediateExpression(List<Token> tokens) : ExpressionNode(tokens, new NubVoidType());
public class EnumReferenceIntermediateNode(List<Token> tokens, IdentifierToken moduleToken, IdentifierToken nameToken) : IntermediateExpression(tokens)
{ {
public NubType TargetType { get; } = TargetType; public IdentifierToken ModuleToken { get; } = moduleToken;
public IdentifierToken NameToken { get; } = nameToken;
public override IEnumerable<Node> Children()
{
return [];
}
}
public class CastNode(List<Token> tokens, NubType type, ExpressionNode value) : RValueExpressionNode(tokens, type)
{
public ExpressionNode Value { get; } = value;
public override IEnumerable<Node> Children()
{
yield return Value;
}
}
public class EnumReferenceIntermediateNode(List<Token> tokens, string module, string name) : IntermediateExpression(tokens)
{
public string Module { get; } = module;
public string Name { get; } = name;
public override IEnumerable<Node> Children() public override IEnumerable<Node> Children()
{ {

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,6 +72,10 @@ 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);
@@ -62,6 +86,10 @@ 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);
@@ -79,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);
@@ -101,15 +187,23 @@ 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);
} }
public class NubConstArrayType(NubType elementType, long size) : NubType public class NubConstArrayType(NubType elementType, ulong size) : NubType
{ {
public NubType ElementType { get; } = elementType; public NubType ElementType { get; } = elementType;
public long 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;
@@ -120,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);
@@ -127,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));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
using NubLang.Diagnostics;
using NubLang.Syntax;
namespace NubLang.Ast;
public class TypeResolver
{
private readonly Dictionary<string, Module> _modules;
private readonly Dictionary<(string Module, string Name), NubType> _typeCache = new();
private readonly HashSet<(string Module, string Name)> _resolvingTypes = [];
public TypeResolver(Dictionary<string, Module> modules)
{
_modules = modules;
}
public NubType ResolveType(TypeSyntax type, string currentModule)
{
return type switch
{
ArrayTypeSyntax arr => new NubArrayType(ResolveType(arr.BaseType, currentModule)),
BoolTypeSyntax => new NubBoolType(),
IntTypeSyntax i => new NubIntType(i.Signed, i.Width),
FloatTypeSyntax f => new NubFloatType(f.Width),
FuncTypeSyntax func => new NubFuncType(func.Parameters.Select(x => ResolveType(x, currentModule)).ToList(), ResolveType(func.ReturnType, currentModule)),
SliceTypeSyntax slice => new NubSliceType(ResolveType(slice.BaseType, currentModule)),
ConstArrayTypeSyntax arr => new NubConstArrayType(ResolveType(arr.BaseType, currentModule), arr.Size),
PointerTypeSyntax ptr => new NubPointerType(ResolveType(ptr.BaseType, currentModule)),
StringTypeSyntax => new NubStringType(),
CustomTypeSyntax c => ResolveCustomType(c, currentModule),
VoidTypeSyntax => new NubVoidType(),
_ => throw new NotSupportedException($"Unknown type syntax: {type}")
};
}
private NubType ResolveCustomType(CustomTypeSyntax customType, string currentModule)
{
var module = _modules[customType.ModuleToken?.Value ?? currentModule];
var enumDef = module.Enums(true).FirstOrDefault(x => x.NameToken.Value == customType.NameToken.Value);
if (enumDef != null)
{
return enumDef.Type != null ? ResolveType(enumDef.Type, currentModule) : new NubIntType(false, 64);
}
var structDef = module.Structs(true).FirstOrDefault(x => x.NameToken.Value == customType.NameToken.Value);
if (structDef != null)
{
var key = (customType.ModuleToken?.Value ?? currentModule, customType.NameToken.Value);
if (_typeCache.TryGetValue(key, out var cachedType))
{
return cachedType;
}
if (!_resolvingTypes.Add(key))
{
var placeholder = new NubStructType(customType.ModuleToken?.Value ?? currentModule, customType.NameToken.Value, structDef.Packed, []);
_typeCache[key] = placeholder;
return placeholder;
}
try
{
var result = new NubStructType(customType.ModuleToken?.Value ?? currentModule, structDef.NameToken.Value, structDef.Packed, []);
_typeCache[key] = result;
var fields = structDef.Fields
.Select(x => new NubStructFieldType(x.NameToken.Value, ResolveType(x.Type, currentModule), x.Value != null))
.ToList();
result.Fields.AddRange(fields);
return result;
}
finally
{
_resolvingTypes.Remove(key);
}
}
throw new TypeResolverException(Diagnostic
.Error($"Type {customType.NameToken.Value} not found in module {customType.ModuleToken?.Value ?? currentModule}")
.At(customType)
.Build());
}
}
public class TypeResolverException : Exception
{
public Diagnostic Diagnostic { get; }
public TypeResolverException(Diagnostic diagnostic) : base(diagnostic.Message)
{
Diagnostic = diagnostic;
}
}

View File

@@ -0,0 +1,11 @@
namespace NubLang.Diagnostics;
public class CompileException : Exception
{
public Diagnostic Diagnostic { get; }
public CompileException(Diagnostic diagnostic) : base(diagnostic.Message)
{
Diagnostic = diagnostic;
}
}

View File

@@ -1,97 +0,0 @@
using NubLang.Ast;
namespace NubLang.Generation;
public static class CType
{
public static string Create(NubType type, string? variableName = null, bool constArraysAsPointers = true)
{
return type switch
{
NubVoidType => "void" + (variableName != null ? $" {variableName}" : ""),
NubBoolType => "bool" + (variableName != null ? $" {variableName}" : ""),
NubIntType intType => CreateIntType(intType, variableName),
NubFloatType floatType => CreateFloatType(floatType, variableName),
NubPointerType ptr => CreatePointerType(ptr, variableName),
NubSliceType => "struct nub_slice" + (variableName != null ? $" {variableName}" : ""),
NubStringType => "struct nub_string" + (variableName != null ? $" {variableName}" : ""),
NubConstArrayType arr => CreateConstArrayType(arr, variableName, constArraysAsPointers),
NubArrayType arr => CreateArrayType(arr, variableName),
NubFuncType fn => CreateFuncType(fn, variableName),
NubStructType st => $"struct {st.Module}_{st.Name}_{NameMangler.Mangle(st)}" + (variableName != null ? $" {variableName}" : ""),
_ => throw new NotSupportedException($"C type generation not supported for: {type}")
};
}
private static string CreateIntType(NubIntType intType, string? varName)
{
var cType = intType.Width switch
{
8 => intType.Signed ? "char" : "unsigned char",
16 => intType.Signed ? "short" : "unsigned short",
32 => intType.Signed ? "int" : "unsigned int",
64 => intType.Signed ? "long long" : "unsigned long long",
_ => throw new NotSupportedException($"Unsupported integer width: {intType.Width}")
};
return cType + (varName != null ? $" {varName}" : "");
}
private static string CreateFloatType(NubFloatType floatType, string? varName)
{
var cType = floatType.Width switch
{
32 => "float",
64 => "double",
_ => throw new NotSupportedException($"Unsupported float width: {floatType.Width}")
};
return cType + (varName != null ? $" {varName}" : "");
}
private static string CreatePointerType(NubPointerType ptr, string? varName)
{
var baseType = Create(ptr.BaseType);
return baseType + "*" + (varName != null ? $" {varName}" : "");
}
private static string CreateConstArrayType(NubConstArrayType arr, string? varName, bool inStructDef)
{
var elementType = Create(arr.ElementType);
// Treat const arrays as pointers unless in a struct definition
if (!inStructDef)
{
return elementType + "*" + (varName != null ? $" {varName}" : "");
}
if (varName != null)
{
return $"{elementType} {varName}[{arr.Size}]";
}
return $"{elementType}[{arr.Size}]";
}
private static string CreateArrayType(NubArrayType arr, string? varName)
{
var elementType = Create(arr.ElementType);
return elementType + "*" + (varName != null ? $" {varName}" : "");
}
private static string CreateFuncType(NubFuncType fn, string? varName)
{
var returnType = Create(fn.ReturnType);
var parameters = string.Join(", ", fn.Parameters.Select(p => Create(p)));
if (string.IsNullOrEmpty(parameters))
{
parameters = "void";
}
if (varName != null)
{
return $"{returnType} (*{varName})({parameters})";
}
return $"{returnType} (*)({parameters})";
}
}

View File

@@ -1,590 +0,0 @@
using System.Diagnostics;
using System.Text;
using NubLang.Ast;
using NubLang.Syntax;
namespace NubLang.Generation;
public class Generator
{
private readonly CompilationUnit _compilationUnit;
private readonly IndentedTextWriter _writer;
private readonly Stack<List<DeferNode>> _deferStack = [];
private int _tmpIndex;
public Generator(CompilationUnit compilationUnit)
{
_compilationUnit = compilationUnit;
_writer = new IndentedTextWriter();
}
// todo(nub31): Handle name collisions
private string NewTmp()
{
return $"_t{++_tmpIndex}";
}
private static string FuncName(string module, string name, string? externSymbol)
{
return externSymbol ?? $"{module}_{name}";
}
public string Emit()
{
_writer.WriteLine("""
struct nub_string
{
unsigned long long length;
char *data;
};
struct nub_slice
{
unsigned long long length;
void *data;
};
""");
foreach (var structType in _compilationUnit.ImportedStructTypes)
{
_writer.WriteLine(CType.Create(structType));
_writer.WriteLine("{");
using (_writer.Indent())
{
foreach (var field in structType.Fields)
{
_writer.WriteLine($"{CType.Create(field.Type, field.Name, constArraysAsPointers: false)};");
}
}
_writer.WriteLine("};");
_writer.WriteLine();
}
// note(nub31): Forward declarations
foreach (var prototype in _compilationUnit.ImportedFunctions)
{
EmitLine(prototype.Tokens.FirstOrDefault());
var parameters = prototype.Parameters.Count != 0
? string.Join(", ", prototype.Parameters.Select(x => CType.Create(x.Type, x.Name)))
: "void";
var name = FuncName(prototype.Module, prototype.Name, prototype.ExternSymbol);
_writer.WriteLine($"{CType.Create(prototype.ReturnType, name)}({parameters});");
_writer.WriteLine();
}
// note(nub31): Normal functions
foreach (var funcNode in _compilationUnit.Functions)
{
if (funcNode.Body == null) continue;
EmitLine(funcNode.Tokens.FirstOrDefault());
var parameters = funcNode.Prototype.Parameters.Count != 0
? string.Join(", ", funcNode.Prototype.Parameters.Select(x => CType.Create(x.Type, x.Name)))
: "void";
var name = FuncName(funcNode.Module, funcNode.Name, funcNode.Prototype.ExternSymbol);
_writer.WriteLine($"{CType.Create(funcNode.Prototype.ReturnType, name)}({parameters})");
_writer.WriteLine("{");
using (_writer.Indent())
{
EmitBlock(funcNode.Body);
}
_writer.WriteLine("}");
_writer.WriteLine();
}
return _writer.ToString();
}
private void EmitStatement(StatementNode statementNode)
{
EmitLine(statementNode.Tokens.FirstOrDefault());
switch (statementNode)
{
case AssignmentNode assignmentNode:
EmitAssignment(assignmentNode);
break;
case BlockNode blockNode:
_writer.WriteLine("{");
using (_writer.Indent())
{
EmitBlock(blockNode);
}
_writer.WriteLine("}");
break;
case BreakNode breakNode:
EmitBreak(breakNode);
break;
case ContinueNode continueNode:
EmitContinue(continueNode);
break;
case DeferNode deferNode:
EmitDefer(deferNode);
break;
case ForConstArrayNode forConstArrayNode:
EmitForConstArray(forConstArrayNode);
break;
case ForSliceNode forSliceNode:
EmitForSlice(forSliceNode);
break;
case IfNode ifNode:
EmitIf(ifNode);
break;
case ReturnNode returnNode:
EmitReturn(returnNode);
break;
case StatementFuncCallNode statementFuncCallNode:
EmitStatementFuncCall(statementFuncCallNode);
break;
case VariableDeclarationNode variableDeclarationNode:
EmitVariableDeclaration(variableDeclarationNode);
break;
case WhileNode whileNode:
EmitWhile(whileNode);
break;
default:
throw new ArgumentOutOfRangeException(nameof(statementNode));
}
}
private void EmitLine(Token? token)
{
if (token == null) return;
var file = token.Span.FilePath;
var line = token.Span.Start.Line;
_writer.WriteLine($"#line {line} \"{file}\"");
}
private void EmitAssignment(AssignmentNode assignmentNode)
{
var target = EmitExpression(assignmentNode.Target);
var value = EmitExpression(assignmentNode.Value);
_writer.WriteLine($"{target} = {value};");
}
private void EmitBreak(BreakNode _)
{
// todo(nub31): Emit deferred statements
_writer.WriteLine("break;");
}
private void EmitContinue(ContinueNode _)
{
// todo(nub31): Emit deferred statements
_writer.WriteLine("continue;");
}
private void EmitDefer(DeferNode deferNode)
{
_deferStack.Peek().Add(deferNode);
}
private void EmitForSlice(ForSliceNode forSliceNode)
{
var targetType = (NubSliceType)forSliceNode.Target.Type;
var target = EmitExpression(forSliceNode.Target);
var indexName = forSliceNode.IndexName ?? NewTmp();
_writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {target}.length; ++{indexName})");
_writer.WriteLine("{");
using (_writer.Indent())
{
_writer.WriteLine($"{CType.Create(targetType.ElementType, forSliceNode.ElementName)} = (({CType.Create(targetType.ElementType)}*){target}.data)[{indexName}];");
EmitBlock(forSliceNode.Body);
}
_writer.WriteLine("}");
}
private void EmitForConstArray(ForConstArrayNode forConstArrayNode)
{
var targetType = (NubConstArrayType)forConstArrayNode.Target.Type;
var target = EmitExpression(forConstArrayNode.Target);
var indexName = forConstArrayNode.IndexName ?? NewTmp();
_writer.WriteLine($"for (unsigned long long {indexName} = 0; {indexName} < {targetType.Size}; ++{indexName})");
_writer.WriteLine("{");
using (_writer.Indent())
{
_writer.WriteLine($"{CType.Create(targetType.ElementType, forConstArrayNode.ElementName)} = {target}[{indexName}];");
EmitBlock(forConstArrayNode.Body);
}
_writer.WriteLine("}");
}
private void EmitIf(IfNode ifNode, bool elseIf = false)
{
var condition = EmitExpression(ifNode.Condition);
_writer.WriteLine($"{(elseIf ? "else " : "")}if ({condition})");
_writer.WriteLine("{");
using (_writer.Indent())
{
EmitBlock(ifNode.Body);
}
_writer.WriteLine("}");
ifNode.Else?.Match
(
elseIfNode => EmitIf(elseIfNode, true),
elseNode =>
{
_writer.WriteLine("else");
_writer.WriteLine("{");
using (_writer.Indent())
{
EmitBlock(elseNode);
}
_writer.WriteLine("}");
}
);
}
private void EmitReturn(ReturnNode returnNode)
{
if (returnNode.Value == null)
{
var blockDefers = _deferStack.Peek();
for (var i = blockDefers.Count - 1; i >= 0; i--)
{
EmitStatement(blockDefers[i].Statement);
}
_writer.WriteLine("return;");
}
else
{
var returnValue = EmitExpression(returnNode.Value);
if (_deferStack.Peek().Count != 0)
{
var tmp = NewTmp();
_writer.WriteLine($"{CType.Create(returnNode.Value.Type, tmp)} = {returnValue};");
var blockDefers = _deferStack.Peek();
for (var i = blockDefers.Count - 1; i >= 0; i--)
{
EmitStatement(blockDefers[i].Statement);
}
EmitLine(returnNode.Tokens.FirstOrDefault());
_writer.WriteLine($"return {tmp};");
}
else
{
EmitLine(returnNode.Tokens.FirstOrDefault());
_writer.WriteLine($"return {returnValue};");
}
}
}
private void EmitStatementFuncCall(StatementFuncCallNode statementFuncCallNode)
{
var funcCall = EmitFuncCall(statementFuncCallNode.FuncCall);
_writer.WriteLine($"{funcCall};");
}
private void EmitVariableDeclaration(VariableDeclarationNode variableDeclarationNode)
{
if (variableDeclarationNode.Assignment != null)
{
var value = EmitExpression(variableDeclarationNode.Assignment);
_writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.Name)} = {value};");
}
else
{
_writer.WriteLine($"{CType.Create(variableDeclarationNode.Type, variableDeclarationNode.Name)};");
}
}
private void EmitWhile(WhileNode whileNode)
{
var condition = EmitExpression(whileNode.Condition);
_writer.WriteLine($"while ({condition})");
_writer.WriteLine("{");
using (_writer.Indent())
{
EmitBlock(whileNode.Body);
}
_writer.WriteLine("}");
}
private string EmitExpression(ExpressionNode expressionNode)
{
if (expressionNode is IntermediateExpression)
{
throw new UnreachableException("Type checker fucked up");
}
var expr = expressionNode switch
{
ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode),
ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode),
BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(binaryExpressionNode),
BoolLiteralNode boolLiteralNode => boolLiteralNode.Value ? "true" : "false",
ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(constArrayIndexAccessNode),
ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode),
CStringLiteralNode cStringLiteralNode => $"\"{cStringLiteralNode.Value}\"",
DereferenceNode dereferenceNode => EmitDereference(dereferenceNode),
Float32LiteralNode float32LiteralNode => EmitFloat32Literal(float32LiteralNode),
Float64LiteralNode float64LiteralNode => EmitFloat64Literal(float64LiteralNode),
CastNode castNode => EmitCast(castNode),
FuncCallNode funcCallNode => EmitFuncCall(funcCallNode),
FuncIdentifierNode funcIdentifierNode => FuncName(funcIdentifierNode.Module, funcIdentifierNode.Name, funcIdentifierNode.ExternSymbol),
AddressOfNode addressOfNode => EmitAddressOf(addressOfNode),
SizeNode sizeBuiltinNode => $"sizeof({CType.Create(sizeBuiltinNode.TargetType)})",
SliceIndexAccessNode sliceIndexAccessNode => EmitSliceArrayIndexAccess(sliceIndexAccessNode),
StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode),
StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(structFieldAccessNode),
StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode),
I8LiteralNode i8LiteralNode => EmitI8Literal(i8LiteralNode),
I16LiteralNode i16LiteralNode => EmitI16Literal(i16LiteralNode),
I32LiteralNode i32LiteralNode => EmitI32Literal(i32LiteralNode),
I64LiteralNode i64LiteralNode => EmitI64Literal(i64LiteralNode),
U8LiteralNode u8LiteralNode => EmitU8Literal(u8LiteralNode),
U16LiteralNode u16LiteralNode => EmitU16Literal(u16LiteralNode),
U32LiteralNode u32LiteralNode => EmitU32Literal(u32LiteralNode),
U64LiteralNode u64LiteralNode => EmitU64Literal(u64LiteralNode),
UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(unaryExpressionNode),
VariableIdentifierNode variableIdentifierNode => variableIdentifierNode.Name,
_ => throw new ArgumentOutOfRangeException(nameof(expressionNode))
};
return $"({expr})";
}
private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode)
{
var target = EmitExpression(arrayIndexAccessNode.Target);
var index = EmitExpression(arrayIndexAccessNode.Index);
return $"{target}[{index}]";
}
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode)
{
var values = new List<string>();
foreach (var value in arrayInitializerNode.Values)
{
values.Add(EmitExpression(value));
}
var arrayType = (NubArrayType)arrayInitializerNode.Type;
return $"({CType.Create(arrayType.ElementType)}[]){{{string.Join(", ", values)}}}";
}
private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode)
{
var left = EmitExpression(binaryExpressionNode.Left);
var right = EmitExpression(binaryExpressionNode.Right);
var op = binaryExpressionNode.Operator switch
{
BinaryOperator.Plus => "+",
BinaryOperator.Minus => "-",
BinaryOperator.Multiply => "*",
BinaryOperator.Divide => "/",
BinaryOperator.Modulo => "%",
BinaryOperator.Equal => "==",
BinaryOperator.NotEqual => "!=",
BinaryOperator.LessThan => "<",
BinaryOperator.LessThanOrEqual => "<=",
BinaryOperator.GreaterThan => ">",
BinaryOperator.GreaterThanOrEqual => ">=",
BinaryOperator.LogicalAnd => "&&",
BinaryOperator.LogicalOr => "||",
BinaryOperator.BitwiseAnd => "&",
BinaryOperator.BitwiseOr => "|",
BinaryOperator.BitwiseXor => "^",
BinaryOperator.LeftShift => "<<",
BinaryOperator.RightShift => ">>",
_ => throw new ArgumentOutOfRangeException()
};
return $"{left} {op} {right}";
}
private string EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode)
{
var target = EmitExpression(constArrayIndexAccessNode.Target);
var index = EmitExpression(constArrayIndexAccessNode.Index);
// todo(nub31): We can emit bounds checking here
return $"{target}[{index}]";
}
private string EmitConstArrayInitializer(ConstArrayInitializerNode arrayInitializerNode)
{
var values = new List<string>();
foreach (var value in arrayInitializerNode.Values)
{
values.Add(EmitExpression(value));
}
var arrayType = (NubConstArrayType)arrayInitializerNode.Type;
return $"({CType.Create(arrayType.ElementType)}[{arrayType.Size}]){{{string.Join(", ", values)}}}";
}
private string EmitDereference(DereferenceNode dereferenceNode)
{
var pointer = EmitExpression(dereferenceNode.Target);
return $"*{pointer}";
}
private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode)
{
var str = float32LiteralNode.Value.ToString("G9", System.Globalization.CultureInfo.InvariantCulture);
if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E'))
{
str += ".0";
}
return str + "f";
}
private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode)
{
var str = float64LiteralNode.Value.ToString("G17", System.Globalization.CultureInfo.InvariantCulture);
if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E'))
{
str += ".0";
}
return str;
}
private string EmitCast(CastNode castNode)
{
var value = EmitExpression(castNode.Value);
if (castNode is { Type: NubSliceType sliceType, Value.Type: NubConstArrayType arrayType })
{
return $"({CType.Create(sliceType)}){{.length = {arrayType.Size}, .data = (void*){value}}}";
}
return $"({CType.Create(castNode.Type)}){value}";
}
private string EmitFuncCall(FuncCallNode funcCallNode)
{
var name = EmitExpression(funcCallNode.Expression);
var parameterNames = funcCallNode.Parameters.Select(EmitExpression).ToList();
return $"{name}({string.Join(", ", parameterNames)})";
}
private string EmitAddressOf(AddressOfNode addressOfNode)
{
var value = EmitExpression(addressOfNode.LValue);
return $"&{value}";
}
private string EmitSliceArrayIndexAccess(SliceIndexAccessNode sliceIndexAccessNode)
{
var targetType = (NubSliceType)sliceIndexAccessNode.Target.Type;
var target = EmitExpression(sliceIndexAccessNode.Target);
var index = EmitExpression(sliceIndexAccessNode.Index);
// todo(nub31): We can emit bounds checking here
return $"(({CType.Create(targetType.ElementType)}*){target}.data)[{index}]";
}
private string EmitStringLiteral(StringLiteralNode stringLiteralNode)
{
var length = Encoding.UTF8.GetByteCount(stringLiteralNode.Value);
return $"(nub_string){{.length = {length}, .data = \"{stringLiteralNode.Value}\"}}";
}
private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode)
{
var structExpr = EmitExpression(structFieldAccessNode.Target);
return $"{structExpr}.{structFieldAccessNode.Field}";
}
private string EmitStructInitializer(StructInitializerNode structInitializerNode)
{
var initValues = new List<string>();
foreach (var initializer in structInitializerNode.Initializers)
{
var value = EmitExpression(initializer.Value);
initValues.Add($".{initializer.Key} = {value}");
}
var initString = initValues.Count == 0
? "0"
: string.Join(", ", initValues);
return $"({CType.Create(structInitializerNode.Type)}){{{initString}}}";
}
private string EmitI8Literal(I8LiteralNode i8LiteralNode)
{
return i8LiteralNode.Value.ToString();
}
private string EmitI16Literal(I16LiteralNode i16LiteralNode)
{
return i16LiteralNode.Value.ToString();
}
private string EmitI32Literal(I32LiteralNode i32LiteralNode)
{
return i32LiteralNode.Value.ToString();
}
private string EmitI64Literal(I64LiteralNode i64LiteralNode)
{
return i64LiteralNode.Value + "LL";
}
private string EmitU8Literal(U8LiteralNode u8LiteralNode)
{
return u8LiteralNode.Value.ToString();
}
private string EmitU16Literal(U16LiteralNode u16LiteralNode)
{
return u16LiteralNode.Value.ToString();
}
private string EmitU32Literal(U32LiteralNode u32LiteralNode)
{
return u32LiteralNode.Value.ToString();
}
private string EmitU64Literal(U64LiteralNode u64LiteralNode)
{
return u64LiteralNode.Value + "ULL";
}
private string EmitUnaryExpression(UnaryExpressionNode unaryExpressionNode)
{
var value = EmitExpression(unaryExpressionNode.Operand);
return unaryExpressionNode.Operator switch
{
UnaryOperator.Negate => $"-{value}",
UnaryOperator.Invert => $"!{value}",
_ => throw new ArgumentOutOfRangeException()
};
}
private void EmitBlock(BlockNode blockNode)
{
_deferStack.Push([]);
foreach (var statementNode in blockNode.Statements)
{
EmitStatement(statementNode);
}
var blockDefers = _deferStack.Pop();
for (var i = blockDefers.Count - 1; i >= 0; i--)
{
EmitStatement(blockDefers[i].Statement);
}
}
}

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

@@ -7,19 +7,23 @@ public sealed class Module
var modules = new Dictionary<string, Module>(); var modules = new Dictionary<string, Module>();
foreach (var syntaxTree in syntaxTrees) foreach (var syntaxTree in syntaxTrees)
{ {
if (!modules.TryGetValue(syntaxTree.ModuleName, out var module)) var moduleDeclaration = syntaxTree.TopLevelSyntaxNodes.OfType<ModuleSyntax>().FirstOrDefault();
if (moduleDeclaration != null)
{ {
module = new Module(); if (!modules.TryGetValue(moduleDeclaration.NameToken.Value, out var module))
modules.Add(syntaxTree.ModuleName, module); {
} module = new Module();
modules.Add(moduleDeclaration.NameToken.Value, module);
}
module._definitions.AddRange(syntaxTree.Definitions); module._definitions.AddRange(syntaxTree.TopLevelSyntaxNodes);
}
} }
return modules; return modules;
} }
private readonly List<DefinitionSyntax> _definitions = []; private readonly List<TopLevelSyntaxNode> _definitions = [];
public List<StructSyntax> Structs(bool includePrivate) public List<StructSyntax> Structs(bool includePrivate)
{ {
@@ -44,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

@@ -19,9 +19,7 @@ public sealed class Parser
_tokens = tokens; _tokens = tokens;
_tokenIndex = 0; _tokenIndex = 0;
string? moduleName = null; var topLevelSyntaxNodes = new List<TopLevelSyntaxNode>();
var imports = new List<string>();
var definitions = new List<DefinitionSyntax>();
while (HasToken) while (HasToken)
{ {
@@ -29,66 +27,35 @@ public sealed class Parser
{ {
var startIndex = _tokenIndex; var startIndex = _tokenIndex;
if (TryExpectSymbol(Symbol.Import))
{
var name = ExpectStringLiteral();
if (imports.Contains(name.Value))
{
Diagnostics.Add(Diagnostic
.Warning($"Module {name.Value} is imported twice")
.At(name)
.WithHelp($"Remove duplicate import \"{name.Value}\"")
.Build());
}
else
{
imports.Add(name.Value);
}
continue;
}
if (TryExpectSymbol(Symbol.Module))
{
if (moduleName != null)
{
throw new ParseException(Diagnostic
.Error("Module is declared more than once")
.At(CurrentToken)
.WithHelp("Remove duplicate module declaration")
.Build());
}
moduleName = ExpectStringLiteral().Value;
continue;
}
var exported = TryExpectSymbol(Symbol.Export); var exported = TryExpectSymbol(Symbol.Export);
var packed = TryExpectSymbol(Symbol.Packed);
if (TryExpectSymbol(Symbol.Extern)) if (TryExpectSymbol(Symbol.Extern))
{ {
var externSymbol = ExpectStringLiteral(); var externSymbol = ExpectStringLiteral();
ExpectSymbol(Symbol.Func); ExpectSymbol(Symbol.Func);
definitions.Add(ParseFunc(startIndex, exported, externSymbol.Value)); topLevelSyntaxNodes.Add(ParseFunc(startIndex, exported, externSymbol));
continue; continue;
} }
var keyword = ExpectSymbol(); var keyword = ExpectSymbol();
DefinitionSyntax definition = keyword.Symbol switch TopLevelSyntaxNode definition = keyword.Symbol switch
{ {
Symbol.Module => ParseModule(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 ParseException(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}'")
.WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'") .WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'")
.At(keyword) .At(keyword)
.Build()) .Build())
}; };
definitions.Add(definition); topLevelSyntaxNodes.Add(definition);
} }
catch (ParseException e) catch (CompileException e)
{ {
Diagnostics.Add(e.Diagnostic); Diagnostics.Add(e.Diagnostic);
while (HasToken) while (HasToken)
@@ -103,7 +70,19 @@ public sealed class Parser
} }
} }
return new SyntaxTree(definitions, moduleName ?? "default", imports); return new SyntaxTree(topLevelSyntaxNodes);
}
private ImportSyntax ParseImport(int startIndex)
{
var name = ExpectIdentifier();
return new ImportSyntax(GetTokens(startIndex), name);
}
private ModuleSyntax ParseModule(int startIndex)
{
var name = ExpectIdentifier();
return new ModuleSyntax(GetTokens(startIndex), name);
} }
private FuncParameterSyntax ParseFuncParameter() private FuncParameterSyntax ParseFuncParameter()
@@ -113,10 +92,10 @@ public sealed class Parser
ExpectSymbol(Symbol.Colon); ExpectSymbol(Symbol.Colon);
var type = ParseType(); var type = ParseType();
return new FuncParameterSyntax(GetTokens(startIndex), name.Value, type); return new FuncParameterSyntax(GetTokens(startIndex), name, type);
} }
private FuncSyntax ParseFunc(int startIndex, bool exported, string? externSymbol) private FuncSyntax ParseFunc(int startIndex, bool exported, StringLiteralToken? externSymbol)
{ {
var name = ExpectIdentifier(); var name = ExpectIdentifier();
List<FuncParameterSyntax> parameters = []; List<FuncParameterSyntax> parameters = [];
@@ -136,7 +115,7 @@ public sealed class Parser
var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new VoidTypeSyntax([]); var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new VoidTypeSyntax([]);
var prototype = new FuncPrototypeSyntax(GetTokens(startIndex), name.Value, exported, externSymbol, parameters, returnType); var prototype = new FuncPrototypeSyntax(GetTokens(startIndex), name, exported, externSymbol, parameters, returnType);
BlockSyntax? body = null; BlockSyntax? body = null;
var bodyStartIndex = _tokenIndex; var bodyStartIndex = _tokenIndex;
@@ -148,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();
@@ -160,7 +139,7 @@ public sealed class Parser
{ {
var memberStartIndex = _tokenIndex; var memberStartIndex = _tokenIndex;
var fieldName = ExpectIdentifier().Value; var fieldName = ExpectIdentifier();
ExpectSymbol(Symbol.Colon); ExpectSymbol(Symbol.Colon);
var fieldType = ParseType(); var fieldType = ParseType();
@@ -174,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.Value, 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)
@@ -192,37 +171,29 @@ public sealed class Parser
ExpectSymbol(Symbol.OpenBrace); ExpectSymbol(Symbol.OpenBrace);
long value = -1;
while (!TryExpectSymbol(Symbol.CloseBrace)) while (!TryExpectSymbol(Symbol.CloseBrace))
{ {
var memberStartIndex = _tokenIndex; var memberStartIndex = _tokenIndex;
var fieldName = ExpectIdentifier().Value; var fieldName = ExpectIdentifier();
long fieldValue; IntLiteralToken? value = null;
if (TryExpectSymbol(Symbol.Assign)) if (TryExpectSymbol(Symbol.Assign))
{ {
if (!TryExpectIntLiteral(out var intLiteralToken)) if (!TryExpectIntLiteral(out var intLiteralToken))
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error("Value of enum field must be an integer literal") .Error("Value of enum field must be an integer literal")
.At(CurrentToken) .At(CurrentToken)
.Build()); .Build());
} }
fieldValue = Convert.ToInt64(intLiteralToken.Value, intLiteralToken.Base); value = intLiteralToken;
value = fieldValue;
}
else
{
fieldValue = value + 1;
value = fieldValue;
} }
fields.Add(new EnumFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldValue)); fields.Add(new EnumFieldSyntax(GetTokens(memberStartIndex), fieldName, value));
} }
return new EnumSyntax(GetTokens(startIndex), name.Value, exported, type, fields); return new EnumSyntax(GetTokens(startIndex), name, exported, type, fields);
} }
private StatementSyntax ParseStatement() private StatementSyntax ParseStatement()
@@ -267,7 +238,7 @@ public sealed class Parser
private VariableDeclarationSyntax ParseVariableDeclaration(int startIndex) private VariableDeclarationSyntax ParseVariableDeclaration(int startIndex)
{ {
var name = ExpectIdentifier().Value; var name = ExpectIdentifier();
TypeSyntax? explicitType = null; TypeSyntax? explicitType = null;
if (TryExpectSymbol(Symbol.Colon)) if (TryExpectSymbol(Symbol.Colon))
@@ -334,12 +305,12 @@ public sealed class Parser
private ForSyntax ParseFor(int startIndex) private ForSyntax ParseFor(int startIndex)
{ {
var itemName = ExpectIdentifier().Value; var itemName = ExpectIdentifier();
string? indexName = null; IdentifierToken? indexName = null;
if (TryExpectSymbol(Symbol.Comma)) if (TryExpectSymbol(Symbol.Comma))
{ {
indexName = ExpectIdentifier().Value; indexName = ExpectIdentifier();
} }
ExpectSymbol(Symbol.In); ExpectSymbol(Symbol.In);
@@ -452,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:
@@ -467,13 +438,14 @@ public sealed class Parser
var token = ExpectToken(); var token = ExpectToken();
var expr = token switch var expr = token switch
{ {
BoolLiteralToken boolLiteral => new BoolLiteralSyntax(GetTokens(startIndex), boolLiteral.Value), BoolLiteralToken boolLiteral => new BoolLiteralSyntax(GetTokens(startIndex), boolLiteral),
StringLiteralToken stringLiteral => new StringLiteralSyntax(GetTokens(startIndex), stringLiteral.Value), StringLiteralToken stringLiteral => new StringLiteralSyntax(GetTokens(startIndex), stringLiteral),
FloatLiteralToken floatLiteral => new FloatLiteralSyntax(GetTokens(startIndex), floatLiteral.Value), FloatLiteralToken floatLiteral => new FloatLiteralSyntax(GetTokens(startIndex), floatLiteral),
IntLiteralToken intLiteral => new IntLiteralSyntax(GetTokens(startIndex), intLiteral.Value, intLiteral.Base), IntLiteralToken intLiteral => new IntLiteralSyntax(GetTokens(startIndex), intLiteral),
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()),
@@ -481,13 +453,13 @@ public sealed class Parser
Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()), Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()),
Symbol.Struct => ParseStructInitializer(startIndex), Symbol.Struct => ParseStructInitializer(startIndex),
Symbol.At => ParseBuiltinFunction(startIndex), Symbol.At => ParseBuiltinFunction(startIndex),
_ => throw new ParseException(Diagnostic _ => throw new CompileException(Diagnostic
.Error($"Unexpected symbol '{symbolToken.Symbol}' in expression") .Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
.WithHelp("Expected '(', '-', '!', '[' or '{'") .WithHelp("Expected '(', '-', '!', '[' or '{'")
.At(symbolToken) .At(symbolToken)
.Build()) .Build())
}, },
_ => throw new ParseException(Diagnostic _ => throw new CompileException(Diagnostic
.Error($"Unexpected token '{token.GetType().Name}' in expression") .Error($"Unexpected token '{token.GetType().Name}' in expression")
.WithHelp("Expected literal, identifier, or parenthesized expression") .WithHelp("Expected literal, identifier, or parenthesized expression")
.At(token) .At(token)
@@ -518,7 +490,7 @@ public sealed class Parser
} }
default: default:
{ {
throw new ParseException(Diagnostic.Error($"Unknown builtin {name.Value}").At(name).Build()); throw new CompileException(Diagnostic.Error($"Unknown builtin {name.Value}").At(name).Build());
} }
} }
} }
@@ -528,10 +500,10 @@ public sealed class Parser
if (TryExpectSymbol(Symbol.DoubleColon)) if (TryExpectSymbol(Symbol.DoubleColon))
{ {
var name = ExpectIdentifier(); var name = ExpectIdentifier();
return new ModuleIdentifierSyntax(GetTokens(startIndex), identifier.Value, name.Value); return new ModuleIdentifierSyntax(GetTokens(startIndex), identifier, name);
} }
return new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value); return new LocalIdentifierSyntax(GetTokens(startIndex), identifier);
} }
private ExpressionSyntax ParseParenthesizedExpression() private ExpressionSyntax ParseParenthesizedExpression()
@@ -546,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);
@@ -560,7 +526,7 @@ public sealed class Parser
if (TryExpectSymbol(Symbol.Period)) if (TryExpectSymbol(Symbol.Period))
{ {
var member = ExpectIdentifier().Value; var member = ExpectIdentifier();
expr = new MemberAccessSyntax(GetTokens(startIndex), expr, member); expr = new MemberAccessSyntax(GetTokens(startIndex), expr, member);
continue; continue;
} }
@@ -627,12 +593,12 @@ public sealed class Parser
return new StructInitializerSyntax(GetTokens(startIndex), type, initializers); return new StructInitializerSyntax(GetTokens(startIndex), type, initializers);
} }
private Dictionary<string, ExpressionSyntax> ParseStructInitializerBody() private Dictionary<IdentifierToken, ExpressionSyntax> ParseStructInitializerBody()
{ {
Dictionary<string, ExpressionSyntax> initializers = []; Dictionary<IdentifierToken, ExpressionSyntax> initializers = [];
while (!TryExpectSymbol(Symbol.CloseBrace)) while (!TryExpectSymbol(Symbol.CloseBrace))
{ {
var name = ExpectIdentifier().Value; var name = ExpectIdentifier();
ExpectSymbol(Symbol.Assign); ExpectSymbol(Symbol.Assign);
var value = ParseExpression(); var value = ParseExpression();
initializers.Add(name, value); initializers.Add(name, value);
@@ -658,7 +624,7 @@ public sealed class Parser
{ {
statements.Add(ParseStatement()); statements.Add(ParseStatement());
} }
catch (ParseException ex) catch (CompileException ex)
{ {
Diagnostics.Add(ex.Diagnostic); Diagnostics.Add(ex.Diagnostic);
if (HasToken) if (HasToken)
@@ -680,11 +646,11 @@ 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)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error("Arbitrary uint size is not supported") .Error("Arbitrary uint size is not supported")
.WithHelp("Use u8, u16, u32 or u64") .WithHelp("Use u8, u16, u32 or u64")
.At(name) .At(name)
@@ -694,11 +660,11 @@ 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)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error("Arbitrary int size is not supported") .Error("Arbitrary int size is not supported")
.WithHelp("Use i8, i16, i32 or i64") .WithHelp("Use i8, i16, i32 or i64")
.At(name) .At(name)
@@ -708,11 +674,11 @@ 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)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error("Arbitrary float size is not supported") .Error("Arbitrary float size is not supported")
.WithHelp("Use f32 or f64") .WithHelp("Use f32 or f64")
.At(name) .At(name)
@@ -732,16 +698,16 @@ public sealed class Parser
return new BoolTypeSyntax(GetTokens(startIndex)); return new BoolTypeSyntax(GetTokens(startIndex));
default: default:
{ {
string? module = null; IdentifierToken? module = null;
if (TryExpectSymbol(Symbol.DoubleColon)) if (TryExpectSymbol(Symbol.DoubleColon))
{ {
var customTypeName = ExpectIdentifier(); var customTypeName = ExpectIdentifier();
module = name.Value; module = name;
name = customTypeName; name = customTypeName;
} }
return new CustomTypeSyntax(GetTokens(startIndex), module, name.Value); return new CustomTypeSyntax(GetTokens(startIndex), module, name);
} }
} }
} }
@@ -780,7 +746,7 @@ public sealed class Parser
{ {
ExpectSymbol(Symbol.CloseBracket); ExpectSymbol(Symbol.CloseBracket);
var baseType = ParseType(); var baseType = ParseType();
return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, Convert.ToInt64(intLiteral.Value, intLiteral.Base)); return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, intLiteral.AsU64);
} }
else if (TryExpectSymbol(Symbol.QuestionMark)) else if (TryExpectSymbol(Symbol.QuestionMark))
{ {
@@ -796,7 +762,7 @@ public sealed class Parser
} }
} }
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error("Invalid type syntax") .Error("Invalid type syntax")
.WithHelp("Expected type name, '^' for pointer, or '[]' for array") .WithHelp("Expected type name, '^' for pointer, or '[]' for array")
.At(CurrentToken) .At(CurrentToken)
@@ -807,7 +773,7 @@ public sealed class Parser
{ {
if (!HasToken) if (!HasToken)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error("Unexpected end of file") .Error("Unexpected end of file")
.WithHelp("Expected more tokens to complete the syntax") .WithHelp("Expected more tokens to complete the syntax")
.At(_tokens[^1]) .At(_tokens[^1])
@@ -824,7 +790,7 @@ public sealed class Parser
var token = ExpectToken(); var token = ExpectToken();
if (token is not SymbolToken symbol) if (token is not SymbolToken symbol)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error($"Expected symbol, but found {token.GetType().Name}") .Error($"Expected symbol, but found {token.GetType().Name}")
.WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.") .WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.")
.At(token) .At(token)
@@ -839,7 +805,7 @@ public sealed class Parser
var token = ExpectSymbol(); var token = ExpectSymbol();
if (token.Symbol != expectedSymbol) if (token.Symbol != expectedSymbol)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'") .Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'")
.WithHelp($"Insert '{expectedSymbol}' here") .WithHelp($"Insert '{expectedSymbol}' here")
.At(token) .At(token)
@@ -889,7 +855,7 @@ public sealed class Parser
var token = ExpectToken(); var token = ExpectToken();
if (token is not IdentifierToken identifier) if (token is not IdentifierToken identifier)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error($"Expected identifier, but found {token.GetType().Name}") .Error($"Expected identifier, but found {token.GetType().Name}")
.WithHelp("Provide a valid identifier name here") .WithHelp("Provide a valid identifier name here")
.At(token) .At(token)
@@ -917,7 +883,7 @@ public sealed class Parser
var token = ExpectToken(); var token = ExpectToken();
if (token is not StringLiteralToken identifier) if (token is not StringLiteralToken identifier)
{ {
throw new ParseException(Diagnostic throw new CompileException(Diagnostic
.Error($"Expected string literal, but found {token.GetType().Name}") .Error($"Expected string literal, but found {token.GetType().Name}")
.WithHelp("Provide a valid string literal") .WithHelp("Provide a valid string literal")
.At(token) .At(token)
@@ -938,14 +904,4 @@ public sealed class Parser
} }
} }
public record SyntaxTree(List<DefinitionSyntax> Definitions, string ModuleName, List<string> Imports); public record SyntaxTree(List<TopLevelSyntaxNode> TopLevelSyntaxNodes);
public class ParseException : Exception
{
public Diagnostic Diagnostic { get; }
public ParseException(Diagnostic diagnostic) : base(diagnostic.Message)
{
Diagnostic = diagnostic;
}
}

View File

@@ -4,21 +4,27 @@ public abstract record SyntaxNode(List<Token> Tokens);
#region Definitions #region Definitions
public abstract record DefinitionSyntax(List<Token> Tokens, string Name, bool Exported) : SyntaxNode(Tokens); public record TopLevelSyntaxNode(List<Token> Tokens) : SyntaxNode(Tokens);
public record FuncParameterSyntax(List<Token> Tokens, string Name, TypeSyntax Type) : SyntaxNode(Tokens); public record ModuleSyntax(List<Token> Tokens, IdentifierToken NameToken) : TopLevelSyntaxNode(Tokens);
public record FuncPrototypeSyntax(List<Token> Tokens, string Name, bool Exported, string? ExternSymbol, List<FuncParameterSyntax> Parameters, TypeSyntax ReturnType) : SyntaxNode(Tokens); public record ImportSyntax(List<Token> Tokens, IdentifierToken NameToken) : TopLevelSyntaxNode(Tokens);
public record FuncSyntax(List<Token> Tokens, FuncPrototypeSyntax Prototype, BlockSyntax? Body) : DefinitionSyntax(Tokens, Prototype.Name, Prototype.Exported); public abstract record DefinitionSyntax(List<Token> Tokens, IdentifierToken NameToken, bool Exported) : TopLevelSyntaxNode(Tokens);
public record StructFieldSyntax(List<Token> Tokens, string Name, TypeSyntax Type, ExpressionSyntax? Value) : SyntaxNode(Tokens); public record FuncParameterSyntax(List<Token> Tokens, IdentifierToken NameToken, TypeSyntax Type) : SyntaxNode(Tokens);
public record StructSyntax(List<Token> Tokens, string Name, bool Exported, List<StructFieldSyntax> Fields) : DefinitionSyntax(Tokens, Name, Exported); public record FuncPrototypeSyntax(List<Token> Tokens, IdentifierToken NameToken, bool Exported, StringLiteralToken? ExternSymbolToken, List<FuncParameterSyntax> Parameters, TypeSyntax ReturnType) : SyntaxNode(Tokens);
public record EnumFieldSyntax(List<Token> Tokens, string Name, long Value) : SyntaxNode(Tokens); public record FuncSyntax(List<Token> Tokens, FuncPrototypeSyntax Prototype, BlockSyntax? Body) : DefinitionSyntax(Tokens, Prototype.NameToken, Prototype.Exported);
public record EnumSyntax(List<Token> Tokens, string Name, bool Exported, TypeSyntax? Type, List<EnumFieldSyntax> Fields) : DefinitionSyntax(Tokens, Name, Exported); public record StructFieldSyntax(List<Token> Tokens, IdentifierToken NameToken, TypeSyntax Type, ExpressionSyntax? Value) : SyntaxNode(Tokens);
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 EnumSyntax(List<Token> Tokens, IdentifierToken NameToken, bool Exported, TypeSyntax? Type, List<EnumFieldSyntax> Fields) : DefinitionSyntax(Tokens, NameToken, Exported);
public enum UnaryOperatorSyntax public enum UnaryOperatorSyntax
{ {
@@ -64,7 +70,7 @@ public record AssignmentSyntax(List<Token> Tokens, ExpressionSyntax Target, Expr
public record IfSyntax(List<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body, Variant<IfSyntax, BlockSyntax>? Else) : StatementSyntax(Tokens); public record IfSyntax(List<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body, Variant<IfSyntax, BlockSyntax>? Else) : StatementSyntax(Tokens);
public record VariableDeclarationSyntax(List<Token> Tokens, string Name, TypeSyntax? ExplicitType, ExpressionSyntax? Assignment) : StatementSyntax(Tokens); public record VariableDeclarationSyntax(List<Token> Tokens, IdentifierToken NameToken, TypeSyntax? ExplicitType, ExpressionSyntax? Assignment) : StatementSyntax(Tokens);
public record ContinueSyntax(List<Token> Tokens) : StatementSyntax(Tokens); public record ContinueSyntax(List<Token> Tokens) : StatementSyntax(Tokens);
@@ -74,7 +80,7 @@ public record DeferSyntax(List<Token> Tokens, StatementSyntax Statement) : State
public record WhileSyntax(List<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens); public record WhileSyntax(List<Token> Tokens, ExpressionSyntax Condition, BlockSyntax Body) : StatementSyntax(Tokens);
public record ForSyntax(List<Token> Tokens, string ElementName, string? IndexName, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens); public record ForSyntax(List<Token> Tokens, IdentifierToken ElementNameToken, IdentifierToken? IndexNameToken, ExpressionSyntax Target, BlockSyntax Body) : StatementSyntax(Tokens);
#endregion #endregion
@@ -88,9 +94,9 @@ public record UnaryExpressionSyntax(List<Token> Tokens, UnaryOperatorSyntax Oper
public record FuncCallSyntax(List<Token> Tokens, ExpressionSyntax Expression, List<ExpressionSyntax> Parameters) : ExpressionSyntax(Tokens); public record FuncCallSyntax(List<Token> Tokens, ExpressionSyntax Expression, List<ExpressionSyntax> Parameters) : ExpressionSyntax(Tokens);
public record LocalIdentifierSyntax(List<Token> Tokens, string Name) : ExpressionSyntax(Tokens); public record LocalIdentifierSyntax(List<Token> Tokens, IdentifierToken NameToken) : ExpressionSyntax(Tokens);
public record ModuleIdentifierSyntax(List<Token> Tokens, string Module, string Name) : ExpressionSyntax(Tokens); public record ModuleIdentifierSyntax(List<Token> Tokens, IdentifierToken ModuleToken, IdentifierToken NameToken) : ExpressionSyntax(Tokens);
public record ArrayInitializerSyntax(List<Token> Tokens, List<ExpressionSyntax> Values) : ExpressionSyntax(Tokens); public record ArrayInitializerSyntax(List<Token> Tokens, List<ExpressionSyntax> Values) : ExpressionSyntax(Tokens);
@@ -98,17 +104,17 @@ public record ArrayIndexAccessSyntax(List<Token> Tokens, ExpressionSyntax Target
public record AddressOfSyntax(List<Token> Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens); public record AddressOfSyntax(List<Token> Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens);
public record IntLiteralSyntax(List<Token> Tokens, string Value, int Base) : ExpressionSyntax(Tokens); public record IntLiteralSyntax(List<Token> Tokens, IntLiteralToken Token) : ExpressionSyntax(Tokens);
public record StringLiteralSyntax(List<Token> Tokens, string Value) : ExpressionSyntax(Tokens); public record StringLiteralSyntax(List<Token> Tokens, StringLiteralToken Token) : ExpressionSyntax(Tokens);
public record BoolLiteralSyntax(List<Token> Tokens, bool Value) : ExpressionSyntax(Tokens); public record BoolLiteralSyntax(List<Token> Tokens, BoolLiteralToken Token) : ExpressionSyntax(Tokens);
public record FloatLiteralSyntax(List<Token> Tokens, string Value) : ExpressionSyntax(Tokens); public record FloatLiteralSyntax(List<Token> Tokens, FloatLiteralToken Token) : ExpressionSyntax(Tokens);
public record MemberAccessSyntax(List<Token> Tokens, ExpressionSyntax Target, string Member) : ExpressionSyntax(Tokens); public record MemberAccessSyntax(List<Token> Tokens, ExpressionSyntax Target, IdentifierToken MemberToken) : ExpressionSyntax(Tokens);
public record StructInitializerSyntax(List<Token> Tokens, TypeSyntax? StructType, Dictionary<string, ExpressionSyntax> Initializers) : ExpressionSyntax(Tokens); public record StructInitializerSyntax(List<Token> Tokens, TypeSyntax? StructType, Dictionary<IdentifierToken, ExpressionSyntax> Initializers) : ExpressionSyntax(Tokens);
public record DereferenceSyntax(List<Token> Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens); public record DereferenceSyntax(List<Token> Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens);
@@ -128,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);
@@ -140,8 +146,8 @@ public record SliceTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyn
public record ArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens); public record ArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens);
public record ConstArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType, long Size) : TypeSyntax(Tokens); public record ConstArrayTypeSyntax(List<Token> Tokens, TypeSyntax BaseType, ulong Size) : TypeSyntax(Tokens);
public record CustomTypeSyntax(List<Token> Tokens, string? Module, string Name) : TypeSyntax(Tokens); public record CustomTypeSyntax(List<Token> Tokens, IdentifierToken? ModuleToken, IdentifierToken NameToken) : TypeSyntax(Tokens);
#endregion #endregion

View File

@@ -2,8 +2,83 @@
namespace NubLang.Syntax; namespace NubLang.Syntax;
public abstract record Token(SourceSpan Span);
public record IdentifierToken(SourceSpan Span, string Value) : Token(Span)
{
public override string ToString()
{
return Value;
}
}
public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span)
{
private string GetNumericValue()
{
// Strip base prefixes: 0b, 0o, 0x
return Base switch
{
2 when Value.StartsWith("0b", StringComparison.OrdinalIgnoreCase)
=> Value.Substring(2),
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 double AsF64 => Convert.ToDouble(AsI64);
public override string ToString()
{
return Value;
}
}
public record StringLiteralToken(SourceSpan Span, string Value) : Token(Span)
{
public override string ToString()
{
return $"\"{Value}\"";
}
}
public record BoolLiteralToken(SourceSpan Span, bool Value) : Token(Span)
{
public override string ToString()
{
return Value ? "true" : "false";
}
}
public record FloatLiteralToken(SourceSpan Span, string Value) : Token(Span)
{
public float AsF32 => Convert.ToSingle(Value);
public double AsF64 => Convert.ToDouble(Value);
public override string ToString()
{
return Value;
}
}
public enum Symbol public enum Symbol
{ {
// Default
None,
// Control // Control
If, If,
Else, Else,
@@ -25,6 +100,7 @@ public enum Symbol
// Modifier // Modifier
Extern, Extern,
Packed,
Export, Export,
Colon, Colon,
@@ -58,20 +134,68 @@ public enum Symbol
Pipe, Pipe,
And, And,
Or, Or,
XOr,
At, At,
QuestionMark, QuestionMark,
} }
public abstract record Token(SourceSpan Span); public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span)
{
public record IdentifierToken(SourceSpan Span, string Value) : Token(Span); public override string ToString()
{
public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span); return Symbol switch
{
public record StringLiteralToken(SourceSpan Span, string Value) : Token(Span); Symbol.Func => "func",
Symbol.If => "if",
public record BoolLiteralToken(SourceSpan Span, bool Value) : Token(Span); Symbol.Else => "else",
Symbol.While => "while",
public record FloatLiteralToken(SourceSpan Span, string Value) : Token(Span); Symbol.For => "for",
Symbol.In => "in",
public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span); Symbol.Break => "break",
Symbol.Continue => "continue",
Symbol.Return => "return",
Symbol.Struct => "struct",
Symbol.Let => "let",
Symbol.Extern => "extern",
Symbol.Module => "module",
Symbol.Export => "export",
Symbol.Import => "import",
Symbol.Defer => "defer",
Symbol.Enum => "enum",
Symbol.Equal => "==",
Symbol.NotEqual => "!=",
Symbol.LessThanOrEqual => "<=",
Symbol.GreaterThanOrEqual => ">=",
Symbol.LeftShift => "<<",
Symbol.RightShift => ">>",
Symbol.And => "&&",
Symbol.Or => "||",
Symbol.DoubleColon => "::",
Symbol.Colon => ":",
Symbol.OpenParen => "(",
Symbol.CloseParen => ")",
Symbol.OpenBrace => "{",
Symbol.CloseBrace => "}",
Symbol.OpenBracket => "[",
Symbol.CloseBracket => "]",
Symbol.Comma => ",",
Symbol.Period => ".",
Symbol.Assign => "=",
Symbol.LessThan => "<",
Symbol.GreaterThan => ">",
Symbol.Plus => "+",
Symbol.Minus => "-",
Symbol.Star => "*",
Symbol.ForwardSlash => "/",
Symbol.Bang => "!",
Symbol.Caret => "^",
Symbol.Ampersand => "&",
Symbol.Semi => ";",
Symbol.Percent => "%",
Symbol.Pipe => "|",
Symbol.At => "@",
Symbol.QuestionMark => "?",
_ => "none",
};
}
}

View File

@@ -4,72 +4,9 @@ namespace NubLang.Syntax;
public sealed class Tokenizer public sealed class Tokenizer
{ {
private static readonly Dictionary<string, Symbol> Keywords = new()
{
["func"] = Symbol.Func,
["if"] = Symbol.If,
["else"] = Symbol.Else,
["while"] = Symbol.While,
["for"] = Symbol.For,
["in"] = Symbol.In,
["break"] = Symbol.Break,
["continue"] = Symbol.Continue,
["return"] = Symbol.Return,
["struct"] = Symbol.Struct,
["let"] = Symbol.Let,
["extern"] = Symbol.Extern,
["module"] = Symbol.Module,
["export"] = Symbol.Export,
["import"] = Symbol.Import,
["defer"] = Symbol.Defer,
["enum"] = Symbol.Enum,
};
private static readonly Dictionary<char[], Symbol> Symbols = new()
{
[['=', '=']] = Symbol.Equal,
[['!', '=']] = Symbol.NotEqual,
[['<', '=']] = Symbol.LessThanOrEqual,
[['>', '=']] = Symbol.GreaterThanOrEqual,
[['<', '<']] = Symbol.LeftShift,
[['>', '>']] = Symbol.RightShift,
[['&', '&']] = Symbol.And,
[['|', '|']] = Symbol.Or,
[[':', ':']] = Symbol.DoubleColon,
[[':']] = Symbol.Colon,
[['(']] = Symbol.OpenParen,
[[')']] = Symbol.CloseParen,
[['{']] = Symbol.OpenBrace,
[['}']] = Symbol.CloseBrace,
[['[']] = Symbol.OpenBracket,
[[']']] = Symbol.CloseBracket,
[[',']] = Symbol.Comma,
[['.']] = Symbol.Period,
[['=']] = Symbol.Assign,
[['<']] = Symbol.LessThan,
[['>']] = Symbol.GreaterThan,
[['+']] = Symbol.Plus,
[['-']] = Symbol.Minus,
[['*']] = Symbol.Star,
[['/']] = Symbol.ForwardSlash,
[['!']] = Symbol.Bang,
[['^']] = Symbol.Caret,
[['&']] = Symbol.Ampersand,
[[';']] = Symbol.Semi,
[['%']] = Symbol.Percent,
[['|']] = Symbol.Pipe,
[['@']] = Symbol.At,
[['?']] = Symbol.QuestionMark,
};
private static readonly (char[] Pattern, Symbol Symbol)[] OrderedSymbols = Symbols
.OrderByDescending(kvp => kvp.Key.Length)
.Select(kvp => (kvp.Key, kvp.Value))
.ToArray();
private readonly string _fileName; private readonly string _fileName;
private readonly string _content; private readonly string _content;
private int _index = 0; private int _index;
private int _line = 1; private int _line = 1;
private int _column = 1; private int _column = 1;
@@ -79,8 +16,8 @@ public sealed class Tokenizer
_content = content; _content = content;
} }
public List<Diagnostic> Diagnostics { get; } = []; public List<Diagnostic> Diagnostics { get; } = new(16);
public List<Token> Tokens { get; } = []; public List<Token> Tokens { get; } = new(256);
public void Tokenize() public void Tokenize()
{ {
@@ -90,17 +27,17 @@ public sealed class Tokenizer
_line = 1; _line = 1;
_column = 1; _column = 1;
while (Peek().HasValue) while (_index < _content.Length)
{ {
try try
{ {
var current = Peek()!.Value; var current = _content[_index];
if (char.IsWhiteSpace(current)) if (char.IsWhiteSpace(current))
{ {
if (current is '\n') if (current == '\n')
{ {
_line += 1; _line += 1;
// note(nub31): Next increments the column, so 0 is correct here
_column = 0; _column = 0;
} }
@@ -108,10 +45,10 @@ public sealed class Tokenizer
continue; continue;
} }
if (current == '/' && Peek(1) == '/') if (current == '/' && _index + 1 < _content.Length && _content[_index + 1] == '/')
{ {
// note(nub31): Keep newline so next iteration increments the line counter Next(2);
while (Peek() is not '\n') while (_index < _content.Length && _content[_index] != '\n')
{ {
Next(); Next();
} }
@@ -121,7 +58,7 @@ public sealed class Tokenizer
Tokens.Add(ParseToken(current, _line, _column)); Tokens.Add(ParseToken(current, _line, _column));
} }
catch (TokenizerException e) catch (CompileException e)
{ {
Diagnostics.Add(e.Diagnostic); Diagnostics.Add(e.Diagnostic);
Next(); Next();
@@ -131,171 +68,312 @@ public sealed class Tokenizer
private Token ParseToken(char current, int lineStart, int columnStart) private Token ParseToken(char current, int lineStart, int columnStart)
{ {
if (char.IsLetter(current) || current == '_')
{
var buffer = string.Empty;
while (Peek() != null && (char.IsLetterOrDigit(Peek()!.Value) || Peek() == '_'))
{
buffer += Peek();
Next();
}
if (Keywords.TryGetValue(buffer, out var keywordSymbol))
{
return new SymbolToken(CreateSpan(lineStart, columnStart), keywordSymbol);
}
if (buffer is "true" or "false")
{
return new BoolLiteralToken(CreateSpan(lineStart, columnStart), Convert.ToBoolean(buffer));
}
return new IdentifierToken(CreateSpan(lineStart, columnStart), buffer);
}
if (char.IsDigit(current)) if (char.IsDigit(current))
{ {
var buffer = string.Empty; return ParseNumber(lineStart, columnStart);
if (current == '0' && Peek(1) is 'x')
{
buffer += "0x";
Next();
Next();
while (Peek() != null && Uri.IsHexDigit(Peek()!.Value))
{
buffer += Peek()!.Value;
Next();
}
if (buffer.Length <= 2)
{
throw new TokenizerException(Diagnostic
.Error("Invalid hex literal, no digits found")
.At(_fileName, _line, _column)
.Build());
}
return new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 16);
}
if (current == '0' && Peek(1) is 'b')
{
buffer += "0b";
Next();
Next();
while (Peek() != null && (Peek() == '0' || Peek() == '1'))
{
buffer += Peek()!.Value;
Next();
}
if (buffer.Length <= 2)
{
throw new TokenizerException(Diagnostic
.Error("Invalid binary literal, no digits found")
.At(_fileName, _line, _column)
.Build());
}
return new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 2);
}
var isFloat = false;
while (Peek() != null)
{
var next = Peek()!.Value;
if (next == '.')
{
if (isFloat)
{
throw new TokenizerException(Diagnostic
.Error("More than one period found in float literal")
.At(_fileName, _line, _column)
.Build());
}
isFloat = true;
buffer += next;
Next();
}
else if (char.IsDigit(next))
{
buffer += next;
Next();
}
else
{
break;
}
}
if (isFloat)
{
return new FloatLiteralToken(CreateSpan(lineStart, columnStart), buffer);
}
else
{
return new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 10);
}
} }
if (current == '"') if (current == '"')
{ {
Next(); return ParseString(lineStart, columnStart);
var buffer = string.Empty; }
while (true) // note(nub31): Look for keywords (longest first in case a keyword fits partially in a larger keyword)
for (var i = 8; i >= 1; i--)
{
if (TryMatchSymbol(i, lineStart, columnStart, out var token))
{ {
var next = Peek(); return token;
if (!next.HasValue) }
}
if (char.IsLetter(current) || current == '_')
{
return ParseIdentifier(lineStart, columnStart);
}
throw new CompileException(Diagnostic.Error($"Unknown token '{current}'").Build());
}
private Token ParseNumber(int lineStart, int columnStart)
{
var start = _index;
var current = _content[_index];
// note(nub31): 0xFFFFFF
if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'x')
{
Next(2);
var digitStart = _index;
while (_index < _content.Length && Uri.IsHexDigit(_content[_index]))
{
Next();
}
if (_index == digitStart)
{
throw new CompileException(Diagnostic
.Error("Invalid hex literal, no digits found")
.At(_fileName, _line, _column)
.Build());
}
return new IntLiteralToken(
CreateSpan(lineStart, columnStart),
_content.Substring(start, _index - start),
16);
}
// note(nub31): 0b11001100
if (current == '0' && _index + 1 < _content.Length && _content[_index + 1] == 'b')
{
Next(2);
var digitStart = _index;
while (_index < _content.Length && (_content[_index] == '0' || _content[_index] == '1'))
{
Next();
}
if (_index == digitStart)
{
throw new CompileException(Diagnostic
.Error("Invalid binary literal, no digits found")
.At(_fileName, _line, _column)
.Build());
}
return new IntLiteralToken(
CreateSpan(lineStart, columnStart),
_content.Substring(start, _index - start),
2);
}
// note(nub31): 23/23.5
var isFloat = false;
while (_index < _content.Length)
{
var next = _content[_index];
if (next == '.')
{
if (isFloat)
{ {
throw new TokenizerException(Diagnostic throw new CompileException(Diagnostic
.Error("Unclosed string literal") .Error("More than one period found in float literal")
.At(_fileName, _line, _column) .At(_fileName, _line, _column)
.Build()); .Build());
} }
if (next is '\n') isFloat = true;
{
_line += 1;
break;
}
if (next is '"')
{
Next();
break;
}
buffer += next;
Next(); Next();
} }
else if (char.IsDigit(next))
return new StringLiteralToken(CreateSpan(lineStart, columnStart), buffer);
}
foreach (var (pattern, symbol) in OrderedSymbols)
{
for (var i = 0; i < pattern.Length; i++)
{ {
var c = Peek(i); Next();
if (!c.HasValue || c.Value != pattern[i]) break; }
else
if (i == pattern.Length - 1) {
{ break;
for (var j = 0; j <= i; j++)
{
Next();
}
return new SymbolToken(CreateSpan(lineStart, columnStart), symbol);
}
} }
} }
throw new TokenizerException(Diagnostic.Error($"Unknown token '{current}'").Build()); var buffer = _content.Substring(start, _index - start);
return isFloat
? new FloatLiteralToken(CreateSpan(lineStart, columnStart), buffer)
: new IntLiteralToken(CreateSpan(lineStart, columnStart), buffer, 10);
}
private StringLiteralToken ParseString(int lineStart, int columnStart)
{
Next();
var start = _index;
while (true)
{
if (_index >= _content.Length)
{
throw new CompileException(Diagnostic
.Error("Unclosed string literal")
.At(_fileName, _line, _column)
.Build());
}
var next = _content[_index];
if (next == '\n')
{
throw new CompileException(Diagnostic
.Error("Unclosed string literal (newline found)")
.At(_fileName, _line, _column)
.Build());
}
if (next == '"')
{
var buffer = _content.Substring(start, _index - start);
Next();
return new StringLiteralToken(CreateSpan(lineStart, columnStart), buffer);
}
Next();
}
}
private bool TryMatchSymbol(int length, int lineStart, int columnStart, out Token token)
{
token = null!;
if (_index + length > _content.Length)
{
return false;
}
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
{
8 => span switch
{
"continue" => Symbol.Continue,
_ => Symbol.None
},
6 => span switch
{
"return" => Symbol.Return,
"struct" => Symbol.Struct,
"extern" => Symbol.Extern,
"packed" => Symbol.Packed,
"module" => Symbol.Module,
"export" => Symbol.Export,
"import" => Symbol.Import,
_ => Symbol.None
},
5 => span switch
{
"break" => Symbol.Break,
"while" => Symbol.While,
"defer" => Symbol.Defer,
_ => Symbol.None
},
4 => span switch
{
"func" => Symbol.Func,
"else" => Symbol.Else,
"enum" => Symbol.Enum,
_ => Symbol.None
},
3 => span switch
{
"for" => Symbol.For,
"let" => Symbol.Let,
_ => Symbol.None
},
2 => span switch
{
"if" => Symbol.If,
"in" => Symbol.In,
"==" => Symbol.Equal,
"!=" => Symbol.NotEqual,
"<=" => Symbol.LessThanOrEqual,
">=" => Symbol.GreaterThanOrEqual,
"<<" => Symbol.LeftShift,
">>" => Symbol.RightShift,
"&&" => Symbol.And,
"||" => Symbol.Or,
"::" => Symbol.DoubleColon,
"x|" => Symbol.XOr,
_ => Symbol.None
},
1 => span[0] switch
{
':' => Symbol.Colon,
'(' => Symbol.OpenParen,
')' => Symbol.CloseParen,
'{' => Symbol.OpenBrace,
'}' => Symbol.CloseBrace,
'[' => Symbol.OpenBracket,
']' => Symbol.CloseBracket,
',' => Symbol.Comma,
'.' => Symbol.Period,
'=' => Symbol.Assign,
'<' => Symbol.LessThan,
'>' => Symbol.GreaterThan,
'+' => Symbol.Plus,
'-' => Symbol.Minus,
'*' => Symbol.Star,
'/' => Symbol.ForwardSlash,
'!' => Symbol.Bang,
'^' => Symbol.Caret,
'&' => Symbol.Ampersand,
';' => Symbol.Semi,
'%' => Symbol.Percent,
'|' => Symbol.Pipe,
'@' => Symbol.At,
'?' => Symbol.QuestionMark,
_ => Symbol.None
},
_ => Symbol.None
};
if (symbol != Symbol.None)
{
var isAlphaKeyword = char.IsLetter(span[0]);
if (isAlphaKeyword)
{
var nextIdx = _index + length;
if (nextIdx < _content.Length)
{
var nextChar = _content[nextIdx];
if (char.IsLetterOrDigit(nextChar) || nextChar == '_')
{
return false;
}
}
}
Next(length);
token = new SymbolToken(CreateSpan(lineStart, columnStart), symbol);
return true;
}
return false;
}
private IdentifierToken ParseIdentifier(int lineStart, int columnStart)
{
var start = _index;
while (_index < _content.Length)
{
var ch = _content[_index];
if (char.IsLetterOrDigit(ch) || ch == '_')
{
Next();
}
else
{
break;
}
}
return new IdentifierToken(CreateSpan(lineStart, columnStart), _content.Substring(start, _index - start));
} }
private SourceSpan CreateSpan(int lineStart, int columnStart) private SourceSpan CreateSpan(int lineStart, int columnStart)
@@ -303,29 +381,9 @@ public sealed class Tokenizer
return new SourceSpan(_fileName, new SourceLocation(lineStart, columnStart), new SourceLocation(_line, _column)); return new SourceSpan(_fileName, new SourceLocation(lineStart, columnStart), new SourceLocation(_line, _column));
} }
private char? Peek(int offset = 0) private void Next(int count = 1)
{ {
if (_index + offset < _content.Length) _index += count;
{ _column += count;
return _content[_index + offset];
}
return null;
}
private void Next()
{
_index += 1;
_column += 1;
}
}
public class TokenizerException : Exception
{
public Diagnostic Diagnostic { get; }
public TokenizerException(Diagnostic diagnostic) : base(diagnostic.Message)
{
Diagnostic = diagnostic;
} }
} }

View File

@@ -0,0 +1,52 @@
using NubLang.Ast;
namespace NubLang.Syntax;
public sealed class TypedModule
{
public static TypedModule FromModule(string name, Module module, Dictionary<string, Module> modules)
{
var typeResolver = new TypeResolver(modules);
var functionPrototypes = new List<FuncPrototypeNode>();
foreach (var funcSyntax in module.Functions(true))
{
var parameters = new List<FuncParameterNode>();
foreach (var parameter in funcSyntax.Prototype.Parameters)
{
parameters.Add(new FuncParameterNode(parameter.Tokens, parameter.NameToken, typeResolver.ResolveType(parameter.Type, name)));
}
var returnType = typeResolver.ResolveType(funcSyntax.Prototype.ReturnType, name);
functionPrototypes.Add(new FuncPrototypeNode(funcSyntax.Tokens, funcSyntax.Prototype.NameToken, funcSyntax.Prototype.ExternSymbolToken, parameters, returnType));
}
var structTypes = new List<NubStructType>();
foreach (var structSyntax in module.Structs(true))
{
var fields = new List<NubStructFieldType>();
foreach (var field in structSyntax.Fields)
{
fields.Add(new NubStructFieldType(field.NameToken.Value, typeResolver.ResolveType(field.Type, name), field.Value != null));
}
structTypes.Add(new NubStructType(name, structSyntax.NameToken.Value, structSyntax.Packed, fields));
}
return new TypedModule(functionPrototypes, structTypes, module.Imports());
}
public TypedModule(List<FuncPrototypeNode> functionPrototypes, List<NubStructType> structTypes, List<string> imports)
{
FunctionPrototypes = functionPrototypes;
StructTypes = structTypes;
Imports = imports;
}
public List<FuncPrototypeNode> FunctionPrototypes { get; }
public List<NubStructType> StructTypes { get; }
public List<string> Imports { get; }
}

31
compiler/NubLang/Utils.cs Normal file
View File

@@ -0,0 +1,31 @@
namespace NubLang;
public static class Utils
{
public static int LevenshteinDistance(string a, string b)
{
if (a == b) return 0;
if (a.Length == 0) return b.Length;
if (b.Length == 0) return a.Length;
var costs = new int[b.Length + 1];
for (int j = 0; j <= b.Length; j++) costs[j] = j;
for (int i = 1; i <= a.Length; i++)
{
costs[0] = i;
int prevCost = i - 1;
for (int j = 1; j <= b.Length; j++)
{
int currentCost = costs[j];
costs[j] = Math.Min(
Math.Min(costs[j - 1] + 1, costs[j] + 1),
prevCost + (a[i - 1] == b[j - 1] ? 0 : 1)
);
prevCost = currentCost;
}
}
return costs[b.Length];
}
}

View File

@@ -1,4 +1,4 @@
module "main" module main
extern "puts" func puts(text: ^i8) extern "puts" func puts(text: ^i8)

6
examples/playgroud/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
nubc main.nub
clang .build/main.ll -o .build/out

View File

@@ -0,0 +1,7 @@
module main
extern "puts" func puts(text: ^i8)
extern "main" func main(argc: i64, argv: [?]^i8)
{
}

View File

@@ -1,4 +1,4 @@
module "raylib" module raylib
export struct Vector2 export struct Vector2
{ {

View File

@@ -1,11 +1,9 @@
import "raylib" import raylib
module "main" module main
extern "main" func main(argc: i64, argv: [?]^i8): i64 extern "main" func main(argc: i64, argv: [?]^i8): i64
{ {
let uwu: []i32 = [1, 2]
raylib::SetConfigFlags(raylib::ConfigFlags.FLAG_VSYNC_HINT | raylib::ConfigFlags.FLAG_WINDOW_RESIZABLE) raylib::SetConfigFlags(raylib::ConfigFlags.FLAG_VSYNC_HINT | raylib::ConfigFlags.FLAG_WINDOW_RESIZABLE)
raylib::InitWindow(1600, 900, "Hi from nub-lang") raylib::InitWindow(1600, 900, "Hi from nub-lang")

1
runtime/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.build

6
runtime/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
mkdir -p .build
clang -c runtime.c -o .build/runtime.o

49
runtime/ref.c Normal file
View File

@@ -0,0 +1,49 @@
#include "ref.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void *rc_alloc(size_t size, void (*destructor)(void *self))
{
printf("rc_alloc %zu bytes\n", size);
ref_header *header = malloc(sizeof(ref_header) + size);
memset(header, 0, size);
if (!header)
{
exit(69);
}
header->ref_count = 1;
header->destructor = destructor;
return (void *)(header + 1);
}
void rc_retain(void *obj)
{
if (!obj)
return;
printf("rc_retain\n");
ref_header *header = ((ref_header *)obj) - 1;
header->ref_count++;
}
void rc_release(void *obj)
{
if (!obj)
return;
ref_header *header = ((ref_header *)obj) - 1;
printf("rc_release\n");
if (--header->ref_count == 0)
{
if (header->destructor)
{
header->destructor(obj);
}
free(header);
printf("rc_free\n");
}
}

13
runtime/ref.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <stddef.h>
typedef struct
{
int ref_count;
void (*destructor)(void *self);
} ref_header;
void *rc_alloc(size_t size, void (*destructor)(void *self));
void rc_retain(void *obj);
void rc_release(void *obj);

1
runtime/runtime.c Normal file
View File

@@ -0,0 +1 @@
#include "ref.c"