Compare commits
13 Commits
3b89b3d9bb
...
d409bb4d92
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d409bb4d92 | ||
|
|
00a172b922 | ||
|
|
ea3d374831 | ||
|
|
ab9bd6fd05 | ||
|
|
96670b1201 | ||
|
|
9fb9c50a0b | ||
|
|
89a827fdef | ||
|
|
f035499ba7 | ||
|
|
3e3757a402 | ||
|
|
1d63bd6389 | ||
|
|
1a3cf3c4fd | ||
|
|
5105a689df | ||
|
|
bd7e3fc931 |
@@ -1,166 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import clang.cindex
|
|
||||||
from clang.cindex import CursorKind, TypeKind, Type
|
|
||||||
|
|
||||||
|
|
||||||
def map_type(clang_type: Type):
|
|
||||||
canonical = clang_type.get_canonical()
|
|
||||||
kind = canonical.kind
|
|
||||||
spelling = (
|
|
||||||
canonical.spelling.replace("const ", "")
|
|
||||||
.replace("volatile ", "")
|
|
||||||
.replace("restrict ", "")
|
|
||||||
.replace("struct ", "")
|
|
||||||
.replace("union ", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
if kind == TypeKind.POINTER:
|
|
||||||
pointee = canonical.get_pointee()
|
|
||||||
if pointee.kind == TypeKind.RECORD:
|
|
||||||
decl = pointee.get_declaration()
|
|
||||||
if not decl.is_definition():
|
|
||||||
return "^void"
|
|
||||||
|
|
||||||
if pointee.kind == TypeKind.FUNCTIONPROTO:
|
|
||||||
arg_types = []
|
|
||||||
|
|
||||||
for arg in pointee.get_canonical().argument_types():
|
|
||||||
arg_types.append(map_type(arg))
|
|
||||||
|
|
||||||
mapped_return = map_type(pointee.get_canonical().get_result())
|
|
||||||
args_str = ", ".join(arg_types)
|
|
||||||
|
|
||||||
return f"func({args_str}): {mapped_return}"
|
|
||||||
|
|
||||||
return f"^{map_type(pointee)}"
|
|
||||||
|
|
||||||
if kind == TypeKind.CONSTANTARRAY:
|
|
||||||
element_type = canonical.get_array_element_type()
|
|
||||||
size = canonical.get_array_size()
|
|
||||||
return f"[{size}]{map_type(element_type)}"
|
|
||||||
|
|
||||||
if kind == TypeKind.INCOMPLETEARRAY:
|
|
||||||
element_type = canonical.get_array_element_type()
|
|
||||||
return f"[?]{map_type(element_type)}"
|
|
||||||
|
|
||||||
if kind == TypeKind.FUNCTIONPROTO or kind == TypeKind.FUNCTIONNOPROTO:
|
|
||||||
arg_types = []
|
|
||||||
|
|
||||||
for arg in canonical.argument_types():
|
|
||||||
arg_types.append(map_type(arg))
|
|
||||||
|
|
||||||
mapped_return = map_type(canonical.get_result())
|
|
||||||
args_str = ", ".join(arg_types)
|
|
||||||
|
|
||||||
return f"func({args_str}): {mapped_return}"
|
|
||||||
|
|
||||||
if kind == TypeKind.VOID:
|
|
||||||
return "void"
|
|
||||||
|
|
||||||
if kind == TypeKind.BOOL:
|
|
||||||
return "bool"
|
|
||||||
|
|
||||||
if kind in [TypeKind.CHAR_S, TypeKind.SCHAR]:
|
|
||||||
return "i8"
|
|
||||||
if kind == TypeKind.CHAR_U or kind == TypeKind.UCHAR:
|
|
||||||
return "u8"
|
|
||||||
if kind == TypeKind.SHORT:
|
|
||||||
return "i16"
|
|
||||||
if kind == TypeKind.USHORT:
|
|
||||||
return "u16"
|
|
||||||
if kind == TypeKind.INT:
|
|
||||||
return "i32"
|
|
||||||
if kind == TypeKind.UINT:
|
|
||||||
return "u32"
|
|
||||||
if kind in [TypeKind.LONG, TypeKind.LONGLONG]:
|
|
||||||
return "i64"
|
|
||||||
if kind in [TypeKind.ULONG, TypeKind.ULONGLONG]:
|
|
||||||
return "u64"
|
|
||||||
|
|
||||||
if kind == TypeKind.FLOAT:
|
|
||||||
return "f32"
|
|
||||||
if kind == TypeKind.DOUBLE or kind == TypeKind.LONGDOUBLE:
|
|
||||||
return "f64"
|
|
||||||
|
|
||||||
if kind == TypeKind.RECORD:
|
|
||||||
return spelling
|
|
||||||
|
|
||||||
raise Exception(f"Unresolved type: {spelling}")
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print("Usage: python3 generate.py [path to header]", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
filename = sys.argv[1]
|
|
||||||
|
|
||||||
index = clang.cindex.Index.create()
|
|
||||||
|
|
||||||
tu = index.parse(filename, ["-x", "c", "-std=c23", "-I/usr/include"])
|
|
||||||
|
|
||||||
if tu.diagnostics:
|
|
||||||
for diag in tu.diagnostics:
|
|
||||||
if diag.severity >= clang.cindex.Diagnostic.Error:
|
|
||||||
print(f"Error: {diag.spelling}", file=sys.stderr)
|
|
||||||
|
|
||||||
print(f'module "{os.path.basename(filename).split(".")[0]}"')
|
|
||||||
print()
|
|
||||||
|
|
||||||
seen_structs = []
|
|
||||||
|
|
||||||
for cursor in tu.cursor.walk_preorder():
|
|
||||||
if cursor.location.file and cursor.location.file.name != filename:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if cursor.kind == CursorKind.FUNCTION_DECL:
|
|
||||||
name = cursor.spelling
|
|
||||||
return_type = map_type(cursor.result_type)
|
|
||||||
|
|
||||||
params = []
|
|
||||||
for arg in cursor.get_arguments():
|
|
||||||
param_name = arg.spelling
|
|
||||||
param_type = map_type(arg.type)
|
|
||||||
params.append(f"{param_name}: {param_type}")
|
|
||||||
|
|
||||||
params_str = ", ".join(params)
|
|
||||||
|
|
||||||
print(f'export extern "{name}" func {name}({params_str}): {return_type}')
|
|
||||||
|
|
||||||
elif cursor.kind == CursorKind.STRUCT_DECL:
|
|
||||||
if cursor.get_usr() in seen_structs:
|
|
||||||
continue
|
|
||||||
|
|
||||||
seen_structs.append(cursor.get_usr())
|
|
||||||
|
|
||||||
if cursor.is_definition():
|
|
||||||
name = cursor.spelling
|
|
||||||
print(f"export struct {name}")
|
|
||||||
print("{")
|
|
||||||
for field in cursor.get_children():
|
|
||||||
if field.kind == CursorKind.FIELD_DECL:
|
|
||||||
field_name = field.spelling
|
|
||||||
field_type = map_type(field.type)
|
|
||||||
print(f" {field_name}: {field_type}")
|
|
||||||
else:
|
|
||||||
raise Exception(
|
|
||||||
f"Unsupported child of struct: {field.spelling}: {field.kind}"
|
|
||||||
)
|
|
||||||
print("}")
|
|
||||||
|
|
||||||
elif cursor.kind == CursorKind.ENUM_DECL:
|
|
||||||
name = cursor.spelling
|
|
||||||
print(f"export enum {name} : u32")
|
|
||||||
print("{")
|
|
||||||
for field in cursor.get_children():
|
|
||||||
if field.kind == CursorKind.ENUM_CONSTANT_DECL:
|
|
||||||
field_name = field.spelling
|
|
||||||
field_value = field.enum_value
|
|
||||||
print(f" {field_name} = {field_value}")
|
|
||||||
else:
|
|
||||||
raise Exception(
|
|
||||||
f"Unsupported child of enum: {field.spelling}: {field.kind}"
|
|
||||||
)
|
|
||||||
print("}")
|
|
||||||
38
compiler/.gitignore
vendored
38
compiler/.gitignore
vendored
@@ -1,34 +1,4 @@
|
|||||||
# Common IntelliJ Platform excludes
|
.idea
|
||||||
|
bin
|
||||||
# User specific
|
obj
|
||||||
**/.idea/**/workspace.xml
|
.build
|
||||||
**/.idea/**/tasks.xml
|
|
||||||
**/.idea/shelf/*
|
|
||||||
**/.idea/dictionaries
|
|
||||||
**/.idea/httpRequests/
|
|
||||||
|
|
||||||
# Sensitive or high-churn files
|
|
||||||
**/.idea/**/dataSources/
|
|
||||||
**/.idea/**/dataSources.ids
|
|
||||||
**/.idea/**/dataSources.xml
|
|
||||||
**/.idea/**/dataSources.local.xml
|
|
||||||
**/.idea/**/sqlDataSources.xml
|
|
||||||
**/.idea/**/dynamic.xml
|
|
||||||
|
|
||||||
# Rider
|
|
||||||
# Rider auto-generates .iml files, and contentModel.xml
|
|
||||||
**/.idea/**/*.iml
|
|
||||||
**/.idea/**/contentModel.xml
|
|
||||||
**/.idea/**/modules.xml
|
|
||||||
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
.vs/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
[Pp]ackages/
|
|
||||||
|
|
||||||
Thumbs.db
|
|
||||||
Desktop.ini
|
|
||||||
.DS_Store
|
|
||||||
13
compiler/.idea/.idea.Compiler/.idea/.gitignore
generated
vendored
13
compiler/.idea/.idea.Compiler/.idea/.gitignore
generated
vendored
@@ -1,13 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Rider ignored files
|
|
||||||
/modules.xml
|
|
||||||
/projectSettingsUpdater.xml
|
|
||||||
/contentModel.xml
|
|
||||||
/.idea.Compiler.iml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
1
compiler/.idea/.idea.Compiler/.idea/.name
generated
1
compiler/.idea/.idea.Compiler/.idea/.name
generated
@@ -1 +0,0 @@
|
|||||||
Compiler
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
|
||||||
</project>
|
|
||||||
10
compiler/.idea/.idea.Compiler/.idea/indexLayout.xml
generated
10
compiler/.idea/.idea.Compiler/.idea/indexLayout.xml
generated
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="UserContentModel">
|
|
||||||
<attachedFolders>
|
|
||||||
<Path>Runtime</Path>
|
|
||||||
</attachedFolders>
|
|
||||||
<explicitIncludes />
|
|
||||||
<explicitExcludes />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
compiler/.idea/.idea.Compiler/.idea/vcs.xml
generated
6
compiler/.idea/.idea.Compiler/.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang", "NubLang\NubLang.csproj", "{5047E21F-590D-4CB3-AFF3-064316485009}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NubLang.CLI", "NubLang.CLI\NubLang.CLI.csproj", "{A22F17ED-FA17-45AB-92BA-CD02C28B3524}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compiler", "Compiler\Compiler.csproj", "{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{5047E21F-590D-4CB3-AFF3-064316485009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{5047E21F-590D-4CB3-AFF3-064316485009}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{5047E21F-590D-4CB3-AFF3-064316485009}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{5047E21F-590D-4CB3-AFF3-064316485009}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A22F17ED-FA17-45AB-92BA-CD02C28B3524}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C7D2F2D1-2012-4624-8D62-2EAB34A9F714}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
1
compiler/Compiler/.gitignore
vendored
1
compiler/Compiler/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
out.c
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Compiler;
|
|
||||||
|
|
||||||
const string fileName = "test.nub";
|
|
||||||
|
|
||||||
var file = File.ReadAllText(fileName);
|
|
||||||
|
|
||||||
var tokens = Tokenizer.Tokenize(fileName, file, out var tokenizerDiagnostics);
|
|
||||||
|
|
||||||
foreach (var diagnostic in tokenizerDiagnostics)
|
|
||||||
{
|
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenizerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ast = Parser.Parse(fileName, tokens, out var parserDiagnostics);
|
|
||||||
|
|
||||||
foreach (var diagnostic in parserDiagnostics)
|
|
||||||
{
|
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parserDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var typedAst = TypeChecker.Check(fileName, ast, out var typeCheckerDiagnostics);
|
|
||||||
|
|
||||||
foreach (var diagnostic in typeCheckerDiagnostics)
|
|
||||||
{
|
|
||||||
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeCheckerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var output = Generator.Emit(typedAst);
|
|
||||||
|
|
||||||
File.WriteAllText("C:/Users/oliste/repos/nub-lang/compiler/Compiler/out.c", output);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class Generator(TypedAst ast)
|
public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGraph moduleGraph)
|
||||||
{
|
{
|
||||||
public static string Emit(TypedAst ast)
|
public static string Emit(List<TypedNodeDefinitionFunc> functions, ModuleGraph moduleGraph)
|
||||||
{
|
{
|
||||||
return new Generator(ast).Emit();
|
return new Generator(functions, moduleGraph).Emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndentedTextWriter writer = new();
|
private readonly IndentedTextWriter writer = new();
|
||||||
private Dictionary<NubTypeStruct, string> structTypeNames = new();
|
private readonly Dictionary<NubTypeStruct, string> structTypeNames = new();
|
||||||
|
|
||||||
private string Emit()
|
private string Emit()
|
||||||
{
|
{
|
||||||
@@ -28,23 +28,22 @@ public sealed class Generator(TypedAst ast)
|
|||||||
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
for (var i = 0; i < ast.StructTypes.Count; i++)
|
foreach (var (i, structType) in moduleGraph.GetModules().SelectMany(x => x.GetCustomTypes().OfType<NubTypeStruct>().Index()))
|
||||||
{
|
|
||||||
var structType = ast.StructTypes[i];
|
|
||||||
structTypeNames[structType] = $"s{i}";
|
structTypeNames[structType] = $"s{i}";
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var structType in ast.StructTypes)
|
foreach (var typeName in structTypeNames)
|
||||||
|
writer.WriteLine($"struct {typeName.Value};");
|
||||||
|
|
||||||
|
writer.WriteLine();
|
||||||
|
|
||||||
|
foreach (var typeName in structTypeNames)
|
||||||
{
|
{
|
||||||
var name = structTypeNames[structType];
|
writer.WriteLine($"struct {typeName.Value}");
|
||||||
writer.WriteLine($"struct {name}");
|
|
||||||
writer.WriteLine("{");
|
writer.WriteLine("{");
|
||||||
using (writer.Indent())
|
using (writer.Indent())
|
||||||
{
|
{
|
||||||
foreach (var field in structType.Fields)
|
foreach (var field in typeName.Key.Fields)
|
||||||
{
|
|
||||||
writer.WriteLine($"{CType(field.Type, field.Name)};");
|
writer.WriteLine($"{CType(field.Type, field.Name)};");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteLine("};");
|
writer.WriteLine("};");
|
||||||
@@ -52,7 +51,7 @@ public sealed class Generator(TypedAst ast)
|
|||||||
|
|
||||||
writer.WriteLine();
|
writer.WriteLine();
|
||||||
|
|
||||||
foreach (var node in ast.Functions)
|
foreach (var node in functions)
|
||||||
{
|
{
|
||||||
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
||||||
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});");
|
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});");
|
||||||
@@ -60,7 +59,7 @@ public sealed class Generator(TypedAst ast)
|
|||||||
|
|
||||||
writer.WriteLine();
|
writer.WriteLine();
|
||||||
|
|
||||||
foreach (var node in ast.Functions)
|
foreach (var node in functions)
|
||||||
{
|
{
|
||||||
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
|
||||||
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)})");
|
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)})");
|
||||||
@@ -199,7 +198,8 @@ public sealed class Generator(TypedAst ast)
|
|||||||
TypedNodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
|
TypedNodeExpressionStringLiteral expression => $"(struct string){{ \"{expression.Value.Value}\", {expression.Value.Value.Length} }}",
|
||||||
TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
|
TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
|
||||||
TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
|
TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
|
||||||
TypedNodeExpressionIdent expression => expression.Value.Ident,
|
TypedNodeExpressionLocalIdent expression => expression.Value.Ident,
|
||||||
|
TypedNodeExpressionModuleIdent expression => expression.Value.Ident,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
167
compiler/ModuleGraph.cs
Normal file
167
compiler/ModuleGraph.cs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Compiler;
|
||||||
|
|
||||||
|
public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
|
||||||
|
{
|
||||||
|
public static Builder Create() => new();
|
||||||
|
|
||||||
|
public List<Module> GetModules()
|
||||||
|
{
|
||||||
|
return modules.Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryResolveModule(string moduleName, [NotNullWhen(true)] out Module? module)
|
||||||
|
{
|
||||||
|
module = modules.GetValueOrDefault(moduleName);
|
||||||
|
return module != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Module(string name)
|
||||||
|
{
|
||||||
|
public string Name { get; } = name;
|
||||||
|
private readonly Dictionary<string, NubType> customTypes = new();
|
||||||
|
private readonly Dictionary<string, NubType> identifierTypes = new();
|
||||||
|
|
||||||
|
public List<NubType> GetCustomTypes()
|
||||||
|
{
|
||||||
|
return customTypes.Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryResolveCustomType(string name, [NotNullWhen(true)] out NubType? customType)
|
||||||
|
{
|
||||||
|
customType = customTypes.GetValueOrDefault(name);
|
||||||
|
return customType != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryResolveIdentifierType(string name, [NotNullWhen(true)] out NubType? identifier)
|
||||||
|
{
|
||||||
|
identifier = identifierTypes.GetValueOrDefault(name);
|
||||||
|
return identifier != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCustomType(string name, NubType type)
|
||||||
|
{
|
||||||
|
customTypes.Add(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddIdentifier(string name, NubType identifier)
|
||||||
|
{
|
||||||
|
identifierTypes.Add(name, identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Builder
|
||||||
|
{
|
||||||
|
private readonly List<Ast> asts = [];
|
||||||
|
|
||||||
|
public void AddAst(Ast ast)
|
||||||
|
{
|
||||||
|
asts.Add(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModuleGraph Build(out List<Diagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
diagnostics = [];
|
||||||
|
|
||||||
|
var astModuleCache = new Dictionary<Ast, Module>();
|
||||||
|
var modules = new Dictionary<string, Module>();
|
||||||
|
|
||||||
|
// First pass: Register modules
|
||||||
|
foreach (var ast in asts)
|
||||||
|
{
|
||||||
|
var moduleDefinitions = ast.Definitions.OfType<NodeDefinitionModule>().ToList();
|
||||||
|
|
||||||
|
if (moduleDefinitions.Count == 0)
|
||||||
|
diagnostics.Add(Diagnostic.Error("Missing module declaration").At(ast.FileName, 1, 1, 1).Build());
|
||||||
|
|
||||||
|
foreach (var extraModuleDefinition in moduleDefinitions.Skip(1))
|
||||||
|
diagnostics.Add(Diagnostic.Warning("Duplicate module declaration will be ignored").At(ast.FileName, extraModuleDefinition).Build());
|
||||||
|
|
||||||
|
if (moduleDefinitions.Count >= 1)
|
||||||
|
{
|
||||||
|
var currentModule = moduleDefinitions[0].Name.Ident;
|
||||||
|
|
||||||
|
if (!modules.ContainsKey(currentModule))
|
||||||
|
{
|
||||||
|
var module = new Module(currentModule);
|
||||||
|
modules.Add(currentModule, module);
|
||||||
|
astModuleCache[ast] = module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: Register struct types without fields
|
||||||
|
foreach (var ast in asts)
|
||||||
|
{
|
||||||
|
var module = astModuleCache.GetValueOrDefault(ast);
|
||||||
|
if (module == null) continue;
|
||||||
|
|
||||||
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
||||||
|
module.AddCustomType(structDef.Name.Ident, new NubTypeStruct(module.Name, structDef.Name.Ident));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third pass: Resolve struct fields
|
||||||
|
foreach (var ast in asts)
|
||||||
|
{
|
||||||
|
var module = astModuleCache.GetValueOrDefault(ast);
|
||||||
|
if (module == null) continue;
|
||||||
|
|
||||||
|
foreach (var structDef in ast.Definitions.OfType<NodeDefinitionStruct>())
|
||||||
|
{
|
||||||
|
if (!module.TryResolveCustomType(structDef.Name.Ident, out var customType))
|
||||||
|
throw new UnreachableException($"{nameof(customType)} should always be registered");
|
||||||
|
|
||||||
|
var fields = structDef.Fields.Select(f => new NubTypeStruct.Field(f.Name.Ident, ResolveType(f.Type))).ToList();
|
||||||
|
((NubTypeStruct)customType).ResolveFields(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fourth pass: Register identifiers
|
||||||
|
foreach (var ast in asts)
|
||||||
|
{
|
||||||
|
var module = astModuleCache.GetValueOrDefault(ast);
|
||||||
|
if (module == null) continue;
|
||||||
|
|
||||||
|
foreach (var funcDef in ast.Definitions.OfType<NodeDefinitionFunc>())
|
||||||
|
{
|
||||||
|
var parameters = funcDef.Parameters.Select(x => ResolveType(x.Type)).ToList();
|
||||||
|
var returnType = ResolveType(funcDef.ReturnType);
|
||||||
|
var funcType = NubTypeFunc.Get(parameters, returnType);
|
||||||
|
module.AddIdentifier(funcDef.Name.Ident, funcType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ModuleGraph(modules);
|
||||||
|
|
||||||
|
NubType ResolveType(NodeType node)
|
||||||
|
{
|
||||||
|
return node switch
|
||||||
|
{
|
||||||
|
NodeTypeBool => NubTypeBool.Instance,
|
||||||
|
NodeTypeCustom type => ResolveCustomType(type),
|
||||||
|
NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(ResolveType).ToList(), ResolveType(type.ReturnType)),
|
||||||
|
NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To)),
|
||||||
|
NodeTypeSInt type => NubTypeSInt.Get(type.Width),
|
||||||
|
NodeTypeUInt type => NubTypeUInt.Get(type.Width),
|
||||||
|
NodeTypeString => NubTypeString.Instance,
|
||||||
|
NodeTypeVoid => NubTypeVoid.Instance,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
NubType ResolveCustomType(NodeTypeCustom type)
|
||||||
|
{
|
||||||
|
var module = modules.GetValueOrDefault(type.Module.Ident);
|
||||||
|
if (module == null)
|
||||||
|
throw new CompileException(Diagnostic.Error($"Unknown module: {type.Module.Ident}").Build());
|
||||||
|
|
||||||
|
if (!module.TryResolveCustomType(type.Name.Ident, out var customType))
|
||||||
|
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type.Module.Ident}::{type.Name.Ident}").Build());
|
||||||
|
|
||||||
|
return customType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<AssemblyName>nubc</AssemblyName>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<PublishAot>true</PublishAot>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\NubLang\NubLang.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using NubLang.Ast;
|
|
||||||
using NubLang.Diagnostics;
|
|
||||||
using NubLang.Syntax;
|
|
||||||
|
|
||||||
var diagnostics = new List<Diagnostic>();
|
|
||||||
var syntaxTrees = new List<SyntaxTree>();
|
|
||||||
|
|
||||||
foreach (var file in args)
|
|
||||||
{
|
|
||||||
var tokenizer = new Tokenizer(file, File.ReadAllText(file));
|
|
||||||
tokenizer.Tokenize();
|
|
||||||
diagnostics.AddRange(tokenizer.Diagnostics);
|
|
||||||
|
|
||||||
var parser = new Parser();
|
|
||||||
var syntaxTree = parser.Parse(tokenizer.Tokens);
|
|
||||||
diagnostics.AddRange(parser.Diagnostics);
|
|
||||||
|
|
||||||
syntaxTrees.Add(syntaxTree);
|
|
||||||
}
|
|
||||||
|
|
||||||
var modules = Module.Collect(syntaxTrees);
|
|
||||||
var compilationUnits = new List<CompilationUnit>();
|
|
||||||
|
|
||||||
for (var i = 0; i < args.Length; i++)
|
|
||||||
{
|
|
||||||
var typeChecker = new TypeChecker(syntaxTrees[i], modules);
|
|
||||||
var compilationUnit = typeChecker.Check();
|
|
||||||
|
|
||||||
compilationUnits.Add(compilationUnit);
|
|
||||||
diagnostics.AddRange(typeChecker.Diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var diagnostic in diagnostics)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine(diagnostic.FormatANSI());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace NubLang.Ast;
|
|
||||||
|
|
||||||
public sealed class CompilationUnit
|
|
||||||
{
|
|
||||||
public CompilationUnit(List<FuncNode> functions, List<NubStructType> importedStructTypes, List<FuncPrototypeNode> importedFunctions)
|
|
||||||
{
|
|
||||||
Functions = functions;
|
|
||||||
ImportedStructTypes = importedStructTypes;
|
|
||||||
ImportedFunctions = importedFunctions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FuncNode> Functions { get; }
|
|
||||||
public List<NubStructType> ImportedStructTypes { get; }
|
|
||||||
public List<FuncPrototypeNode> ImportedFunctions { get; }
|
|
||||||
}
|
|
||||||
@@ -1,590 +0,0 @@
|
|||||||
using NubLang.Syntax;
|
|
||||||
|
|
||||||
namespace NubLang.Ast;
|
|
||||||
|
|
||||||
public abstract class Node(List<Token> tokens)
|
|
||||||
{
|
|
||||||
public List<Token> Tokens { get; } = tokens;
|
|
||||||
|
|
||||||
public abstract IEnumerable<Node> Children();
|
|
||||||
|
|
||||||
public IEnumerable<Node> Descendants()
|
|
||||||
{
|
|
||||||
foreach (var child in Children())
|
|
||||||
{
|
|
||||||
foreach (var descendant in child.DescendantsAndSelf())
|
|
||||||
{
|
|
||||||
yield return descendant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Node> DescendantsAndSelf()
|
|
||||||
{
|
|
||||||
yield return this;
|
|
||||||
foreach (var descendant in Descendants())
|
|
||||||
{
|
|
||||||
yield return descendant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Definitions
|
|
||||||
|
|
||||||
public abstract class DefinitionNode(List<Token> tokens, string module, string name) : Node(tokens)
|
|
||||||
{
|
|
||||||
public string Module { get; } = module;
|
|
||||||
public string Name { get; } = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FuncParameterNode(List<Token> tokens, string name, NubType type) : Node(tokens)
|
|
||||||
{
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public NubType Type { get; } = type;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FuncPrototypeNode(List<Token> tokens, string module, string name, string? externSymbol, List<FuncParameterNode> parameters, NubType returnType) : Node(tokens)
|
|
||||||
{
|
|
||||||
public string Module { get; } = module;
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public string? ExternSymbol { get; } = externSymbol;
|
|
||||||
public List<FuncParameterNode> Parameters { get; } = parameters;
|
|
||||||
public NubType ReturnType { get; } = returnType;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return Parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FuncNode(List<Token> tokens, FuncPrototypeNode prototype, BlockNode? body) : DefinitionNode(tokens, prototype.Module, prototype.Name)
|
|
||||||
{
|
|
||||||
public FuncPrototypeNode Prototype { get; } = prototype;
|
|
||||||
public BlockNode? Body { get; } = body;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Prototype;
|
|
||||||
if (Body != null)
|
|
||||||
{
|
|
||||||
yield return Body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Statements
|
|
||||||
|
|
||||||
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 List<StatementNode> Statements { get; } = statements;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return Statements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StatementFuncCallNode(List<Token> tokens, FuncCallNode funcCall) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public FuncCallNode FuncCall { get; } = funcCall;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return FuncCall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ReturnNode(List<Token> tokens, ExpressionNode? value) : TerminalStatementNode(tokens)
|
|
||||||
{
|
|
||||||
public ExpressionNode? Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
if (Value != null) yield return Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AssignmentNode(List<Token> tokens, LValueExpressionNode target, ExpressionNode value) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public LValueExpressionNode Target { get; } = target;
|
|
||||||
public ExpressionNode Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
yield return Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IfNode(List<Token> tokens, ExpressionNode condition, BlockNode body, Variant<IfNode, BlockNode>? @else) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public ExpressionNode Condition { get; } = condition;
|
|
||||||
public BlockNode Body { get; } = body;
|
|
||||||
public Variant<IfNode, BlockNode>? Else { get; } = @else;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Condition;
|
|
||||||
yield return Body;
|
|
||||||
if (Else.HasValue)
|
|
||||||
{
|
|
||||||
yield return Else.Value.Match<Node>(x => x, x => x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VariableDeclarationNode(List<Token> tokens, string name, ExpressionNode? assignment, NubType type) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public ExpressionNode? Assignment { get; } = assignment;
|
|
||||||
public NubType Type { get; } = type;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
if (Assignment != null) yield return Assignment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContinueNode(List<Token> tokens) : TerminalStatementNode(tokens)
|
|
||||||
{
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BreakNode(List<Token> tokens) : TerminalStatementNode(tokens)
|
|
||||||
{
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WhileNode(List<Token> tokens, ExpressionNode condition, BlockNode body) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public ExpressionNode Condition { get; } = condition;
|
|
||||||
public BlockNode Body { get; } = body;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Condition;
|
|
||||||
yield return Body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ForSliceNode(List<Token> tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public string ElementName { get; } = elementName;
|
|
||||||
public string? IndexName { get; } = indexName;
|
|
||||||
public ExpressionNode Target { get; } = target;
|
|
||||||
public BlockNode Body { get; } = body;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
yield return Body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ForConstArrayNode(List<Token> tokens, string elementName, string? indexName, ExpressionNode target, BlockNode body) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public string ElementName { get; } = elementName;
|
|
||||||
public string? IndexName { get; } = indexName;
|
|
||||||
public ExpressionNode Target { get; } = target;
|
|
||||||
public BlockNode Body { get; } = body;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
yield return Body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DeferNode(List<Token> tokens, StatementNode statement) : StatementNode(tokens)
|
|
||||||
{
|
|
||||||
public StatementNode Statement { get; } = statement;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Statement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Expressions
|
|
||||||
|
|
||||||
public enum UnaryOperator
|
|
||||||
{
|
|
||||||
Negate,
|
|
||||||
Invert
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum BinaryOperator
|
|
||||||
{
|
|
||||||
Equal,
|
|
||||||
NotEqual,
|
|
||||||
GreaterThan,
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
LessThan,
|
|
||||||
LessThanOrEqual,
|
|
||||||
LogicalAnd,
|
|
||||||
LogicalOr,
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Multiply,
|
|
||||||
Divide,
|
|
||||||
Modulo,
|
|
||||||
LeftShift,
|
|
||||||
RightShift,
|
|
||||||
BitwiseAnd,
|
|
||||||
BitwiseXor,
|
|
||||||
BitwiseOr
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ExpressionNode(List<Token> tokens, NubType type) : Node(tokens)
|
|
||||||
{
|
|
||||||
public NubType Type { get; } = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class LValueExpressionNode(List<Token> tokens, NubType type) : ExpressionNode(tokens, type);
|
|
||||||
|
|
||||||
public abstract class RValueExpressionNode(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) : RValueExpressionNode(tokens, new NubStringType())
|
|
||||||
{
|
|
||||||
public string Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CStringLiteralNode(List<Token> tokens, string value) : RValueExpressionNode(tokens, new NubPointerType(new NubIntType(true, 8)))
|
|
||||||
{
|
|
||||||
public string Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class I8LiteralNode(List<Token> tokens, sbyte value) : RValueExpressionNode(tokens, new NubIntType(true, 8))
|
|
||||||
{
|
|
||||||
public sbyte Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class I16LiteralNode(List<Token> tokens, short value) : RValueExpressionNode(tokens, new NubIntType(true, 16))
|
|
||||||
{
|
|
||||||
public short Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class I32LiteralNode(List<Token> tokens, int value) : RValueExpressionNode(tokens, new NubIntType(true, 32))
|
|
||||||
{
|
|
||||||
public int Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class I64LiteralNode(List<Token> tokens, long value) : RValueExpressionNode(tokens, new NubIntType(true, 64))
|
|
||||||
{
|
|
||||||
public long Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class U8LiteralNode(List<Token> tokens, byte value) : RValueExpressionNode(tokens, new NubIntType(false, 8))
|
|
||||||
{
|
|
||||||
public byte Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class U16LiteralNode(List<Token> tokens, ushort value) : RValueExpressionNode(tokens, new NubIntType(false, 16))
|
|
||||||
{
|
|
||||||
public ushort Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class U32LiteralNode(List<Token> tokens, uint value) : RValueExpressionNode(tokens, new NubIntType(false, 32))
|
|
||||||
{
|
|
||||||
public uint Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class U64LiteralNode(List<Token> tokens, ulong value) : RValueExpressionNode(tokens, new NubIntType(false, 64))
|
|
||||||
{
|
|
||||||
public ulong Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Float32LiteralNode(List<Token> tokens, float value) : RValueExpressionNode(tokens, new NubFloatType(32))
|
|
||||||
{
|
|
||||||
public float Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Float64LiteralNode(List<Token> tokens, double value) : RValueExpressionNode(tokens, new NubFloatType(64))
|
|
||||||
{
|
|
||||||
public double Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoolLiteralNode(List<Token> tokens, NubType type, bool value) : RValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public bool Value { get; } = value;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BinaryExpressionNode(List<Token> tokens, NubType type, ExpressionNode left, BinaryOperator @operator, ExpressionNode right) : RValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public ExpressionNode Left { get; } = left;
|
|
||||||
public BinaryOperator Operator { get; } = @operator;
|
|
||||||
public ExpressionNode Right { get; } = right;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Left;
|
|
||||||
yield return Right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UnaryExpressionNode(List<Token> tokens, NubType type, UnaryOperator @operator, ExpressionNode operand) : RValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public UnaryOperator Operator { get; } = @operator;
|
|
||||||
public ExpressionNode Operand { get; } = operand;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Operand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FuncCallNode(List<Token> tokens, NubType type, ExpressionNode expression, List<ExpressionNode> parameters) : RValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public ExpressionNode Expression { get; } = expression;
|
|
||||||
public List<ExpressionNode> Parameters { get; } = parameters;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Expression;
|
|
||||||
foreach (var expressionNode in Parameters)
|
|
||||||
{
|
|
||||||
yield return expressionNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VariableIdentifierNode(List<Token> tokens, NubType type, string name) : LValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public string Name { get; } = name;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FuncIdentifierNode(List<Token> tokens, NubType type, string module, string name, string? externSymbol) : RValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public string Module { get; } = module;
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public string? ExternSymbol { get; } = externSymbol;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ArrayInitializerNode(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 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 Index { get; } = index;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
yield return Index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ConstArrayIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public ExpressionNode Target { get; } = target;
|
|
||||||
public ExpressionNode Index { get; } = index;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
yield return Index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SliceIndexAccessNode(List<Token> tokens, NubType type, ExpressionNode target, ExpressionNode index) : LValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public ExpressionNode Target { get; } = target;
|
|
||||||
public ExpressionNode Index { get; } = index;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
yield return Index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddressOfNode(List<Token> tokens, NubType type, LValueExpressionNode lValue) : RValueExpressionNode(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 string Field { get; } = field;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<string, ExpressionNode> initializers) : RValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
foreach (var initializer in Initializers)
|
|
||||||
{
|
|
||||||
yield return initializer.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DereferenceNode(List<Token> tokens, NubType type, ExpressionNode target) : LValueExpressionNode(tokens, type)
|
|
||||||
{
|
|
||||||
public ExpressionNode Target { get; } = target;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
yield return Target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SizeNode(List<Token> tokens, NubType TargetType) : RValueExpressionNode(tokens, new NubIntType(false, 64))
|
|
||||||
{
|
|
||||||
public NubType TargetType { get; } = TargetType;
|
|
||||||
|
|
||||||
public override IEnumerable<Node> Children()
|
|
||||||
{
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace NubLang.Ast;
|
|
||||||
|
|
||||||
public abstract class NubType : IEquatable<NubType>
|
|
||||||
{
|
|
||||||
public override bool Equals(object? obj) => obj is NubType other && Equals(other);
|
|
||||||
public abstract bool Equals(NubType? other);
|
|
||||||
|
|
||||||
public abstract override int GetHashCode();
|
|
||||||
public abstract override string ToString();
|
|
||||||
|
|
||||||
public static bool operator ==(NubType? left, NubType? right) => Equals(left, right);
|
|
||||||
public static bool operator !=(NubType? left, NubType? right) => !Equals(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NubVoidType : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "void";
|
|
||||||
public override bool Equals(NubType? other) => other is NubVoidType;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubVoidType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubIntType(bool signed, int width) : NubType
|
|
||||||
{
|
|
||||||
public bool Signed { get; } = signed;
|
|
||||||
public int Width { get; } = 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 int GetHashCode() => HashCode.Combine(typeof(NubIntType), Signed, Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubFloatType(int width) : NubType
|
|
||||||
{
|
|
||||||
public int Width { get; } = width;
|
|
||||||
|
|
||||||
public override string ToString() => $"f{Width}";
|
|
||||||
public override bool Equals(NubType? other) => other is NubFloatType @float && @float.Width == Width;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubFloatType), Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NubBoolType : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "bool";
|
|
||||||
public override bool Equals(NubType? other) => other is NubBoolType;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubBoolType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubPointerType(NubType baseType) : NubType
|
|
||||||
{
|
|
||||||
public NubType BaseType { get; } = baseType;
|
|
||||||
|
|
||||||
public override string ToString() => "^" + 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 class NubFuncType(List<NubType> parameters, NubType returnType) : NubType
|
|
||||||
{
|
|
||||||
public List<NubType> Parameters { get; } = parameters;
|
|
||||||
public NubType ReturnType { get; } = 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 int GetHashCode()
|
|
||||||
{
|
|
||||||
var hash = new HashCode();
|
|
||||||
hash.Add(typeof(NubFuncType));
|
|
||||||
hash.Add(ReturnType);
|
|
||||||
foreach (var param in Parameters)
|
|
||||||
{
|
|
||||||
hash.Add(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash.ToHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NubStructType(string module, string name, List<NubStructFieldType> fields) : NubType
|
|
||||||
{
|
|
||||||
public string Module { get; } = module;
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public List<NubStructFieldType> Fields { get; set; } = fields;
|
|
||||||
|
|
||||||
public override string ToString() => $"{Module}::{Name}";
|
|
||||||
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 class NubStructFieldType(string name, NubType type, bool hasDefaultValue)
|
|
||||||
{
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public NubType Type { get; } = type;
|
|
||||||
public bool HasDefaultValue { get; } = hasDefaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NubSliceType(NubType elementType) : NubType
|
|
||||||
{
|
|
||||||
public NubType ElementType { get; } = elementType;
|
|
||||||
|
|
||||||
public override string ToString() => "[]" + 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 class NubConstArrayType(NubType elementType, long size) : NubType
|
|
||||||
{
|
|
||||||
public NubType ElementType { get; } = elementType;
|
|
||||||
public long Size { get; } = size;
|
|
||||||
|
|
||||||
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 int GetHashCode() => HashCode.Combine(typeof(NubConstArrayType), ElementType, Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NubArrayType(NubType elementType) : NubType
|
|
||||||
{
|
|
||||||
public NubType ElementType { get; } = elementType;
|
|
||||||
|
|
||||||
public override string ToString() => $"[?]{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 class NubStringType : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "string";
|
|
||||||
public override bool Equals(NubType? other) => other is NubStringType;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubStringType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NameMangler
|
|
||||||
{
|
|
||||||
public static string Mangle(params IEnumerable<NubType> types)
|
|
||||||
{
|
|
||||||
var readable = string.Join(":", types.Select(EncodeType));
|
|
||||||
return ComputeShortHash(readable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string EncodeType(NubType node) => node switch
|
|
||||||
{
|
|
||||||
NubVoidType => "V",
|
|
||||||
NubBoolType => "B",
|
|
||||||
NubIntType i => (i.Signed ? "I" : "U") + i.Width,
|
|
||||||
NubFloatType f => "F" + f.Width,
|
|
||||||
NubStringType => "S",
|
|
||||||
NubArrayType a => $"A({EncodeType(a.ElementType)})",
|
|
||||||
NubConstArrayType ca => $"CA({EncodeType(ca.ElementType)})",
|
|
||||||
NubSliceType a => $"SL{EncodeType(a.ElementType)}()",
|
|
||||||
NubPointerType p => $"P({EncodeType(p.BaseType)})",
|
|
||||||
NubFuncType fn => $"FN({string.Join(":", fn.Parameters.Select(EncodeType))}:{EncodeType(fn.ReturnType)})",
|
|
||||||
NubStructType st => $"ST({st.Module}:{st.Name})",
|
|
||||||
_ => throw new NotSupportedException($"Cannot encode type: {node}")
|
|
||||||
};
|
|
||||||
|
|
||||||
private static string ComputeShortHash(string input)
|
|
||||||
{
|
|
||||||
var bytes = Encoding.UTF8.GetBytes(input);
|
|
||||||
var hash = SHA256.HashData(bytes);
|
|
||||||
return Convert.ToHexString(hash[..8]).ToLower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,398 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using NubLang.Syntax;
|
|
||||||
|
|
||||||
namespace NubLang.Diagnostics;
|
|
||||||
|
|
||||||
public class Diagnostic
|
|
||||||
{
|
|
||||||
public class DiagnosticBuilder
|
|
||||||
{
|
|
||||||
private readonly DiagnosticSeverity _severity;
|
|
||||||
private readonly string _message;
|
|
||||||
private SourceSpan? _span;
|
|
||||||
private string? _help;
|
|
||||||
|
|
||||||
public DiagnosticBuilder(DiagnosticSeverity severity, string message)
|
|
||||||
{
|
|
||||||
_severity = severity;
|
|
||||||
_message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiagnosticBuilder At(SyntaxNode? node)
|
|
||||||
{
|
|
||||||
if (node != null)
|
|
||||||
{
|
|
||||||
_span = SourceSpan.Merge(node.Tokens.Select(x => x.Span));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiagnosticBuilder At(Token? token)
|
|
||||||
{
|
|
||||||
if (token != null)
|
|
||||||
{
|
|
||||||
At(token.Span);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiagnosticBuilder At(SourceSpan? span)
|
|
||||||
{
|
|
||||||
if (span != null)
|
|
||||||
{
|
|
||||||
_span = span;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiagnosticBuilder At(string filePath, int line, int column)
|
|
||||||
{
|
|
||||||
_span = new SourceSpan(filePath, new SourceLocation(line, column), new SourceLocation(line, column));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiagnosticBuilder WithHelp(string help)
|
|
||||||
{
|
|
||||||
_help = help;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Diagnostic Build() => new(_severity, _message, _help, _span);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DiagnosticBuilder Error(string message) => new(DiagnosticSeverity.Error, message);
|
|
||||||
public static DiagnosticBuilder Warning(string message) => new(DiagnosticSeverity.Warning, message);
|
|
||||||
public static DiagnosticBuilder Info(string message) => new(DiagnosticSeverity.Info, message);
|
|
||||||
|
|
||||||
public DiagnosticSeverity Severity { get; }
|
|
||||||
public string Message { get; }
|
|
||||||
public string? Help { get; }
|
|
||||||
public SourceSpan? Span { get; }
|
|
||||||
|
|
||||||
private Diagnostic(DiagnosticSeverity severity, string message, string? help, SourceSpan? span)
|
|
||||||
{
|
|
||||||
Severity = severity;
|
|
||||||
Message = message;
|
|
||||||
Help = help;
|
|
||||||
Span = span;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FormatANSI()
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.Append(Severity switch
|
|
||||||
{
|
|
||||||
DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red),
|
|
||||||
DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow),
|
|
||||||
DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue),
|
|
||||||
_ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Span.HasValue)
|
|
||||||
{
|
|
||||||
sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(": ");
|
|
||||||
sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite));
|
|
||||||
|
|
||||||
if (Span.HasValue)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
var text = File.ReadAllText(Span.Value.FilePath);
|
|
||||||
|
|
||||||
var tokenizer = new Tokenizer(Span.Value.FilePath, text);
|
|
||||||
tokenizer.Tokenize();
|
|
||||||
|
|
||||||
var lines = text.Split('\n');
|
|
||||||
|
|
||||||
var startLine = Span.Value.Start.Line;
|
|
||||||
var endLine = Span.Value.End.Line;
|
|
||||||
|
|
||||||
const int CONTEXT_LINES = 3;
|
|
||||||
|
|
||||||
var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES);
|
|
||||||
var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES);
|
|
||||||
|
|
||||||
var numberPadding = contextEndLine.ToString().Length;
|
|
||||||
|
|
||||||
var codePadding = 0;
|
|
||||||
for (var i = contextStartLine - 1; i < contextEndLine && i < lines.Length; i++)
|
|
||||||
{
|
|
||||||
var lineLength = lines[i].Length;
|
|
||||||
if (lineLength > codePadding)
|
|
||||||
{
|
|
||||||
codePadding = lineLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('╭');
|
|
||||||
sb.Append(new string('─', numberPadding + 2));
|
|
||||||
sb.Append('┬');
|
|
||||||
sb.Append(new string('─', codePadding + 2));
|
|
||||||
sb.Append('╮');
|
|
||||||
sb.AppendLine();
|
|
||||||
|
|
||||||
for (var i = contextStartLine; i <= contextEndLine; i++)
|
|
||||||
{
|
|
||||||
var line = lines[i - 1];
|
|
||||||
|
|
||||||
sb.Append("│ ");
|
|
||||||
sb.Append(i.ToString().PadRight(numberPadding));
|
|
||||||
sb.Append(" │ ");
|
|
||||||
sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens));
|
|
||||||
// sb.Append(line.PadRight(codePadding));
|
|
||||||
sb.Append(" │");
|
|
||||||
sb.AppendLine();
|
|
||||||
|
|
||||||
if (i >= startLine && i <= endLine)
|
|
||||||
{
|
|
||||||
var markerStartColumn = 1;
|
|
||||||
var markerEndColumn = line.Length;
|
|
||||||
|
|
||||||
if (i == startLine)
|
|
||||||
{
|
|
||||||
markerStartColumn = Span.Value.Start.Column;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == endLine)
|
|
||||||
{
|
|
||||||
markerEndColumn = Span.Value.End.Column;
|
|
||||||
}
|
|
||||||
|
|
||||||
var markerLength = markerEndColumn - markerStartColumn;
|
|
||||||
var marker = new string('^', markerLength);
|
|
||||||
|
|
||||||
var markerColor = Severity switch
|
|
||||||
{
|
|
||||||
DiagnosticSeverity.Info => ConsoleColors.Blue,
|
|
||||||
DiagnosticSeverity.Warning => ConsoleColors.Yellow,
|
|
||||||
DiagnosticSeverity.Error => ConsoleColors.Red,
|
|
||||||
_ => ConsoleColors.White
|
|
||||||
};
|
|
||||||
|
|
||||||
sb.Append("│ ");
|
|
||||||
sb.Append(new string(' ', numberPadding));
|
|
||||||
sb.Append(" │ ");
|
|
||||||
sb.Append(new string(' ', markerStartColumn - 1));
|
|
||||||
sb.Append(ConsoleColors.Colorize(marker, markerColor));
|
|
||||||
sb.Append(new string(' ', codePadding - (markerStartColumn - 1) - markerLength));
|
|
||||||
sb.Append(" │");
|
|
||||||
sb.AppendLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('╰');
|
|
||||||
sb.Append(new string('─', numberPadding + 2));
|
|
||||||
sb.Append('┴');
|
|
||||||
sb.Append(new string('─', codePadding + 2));
|
|
||||||
sb.Append('╯');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Help != null)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ApplySyntaxHighlighting(string line, int lineNumber, List<Token> tokens)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
var lineTokens = tokens
|
|
||||||
.Where(t => t.Span.Start.Line == lineNumber)
|
|
||||||
.OrderBy(t => t.Span.Start.Column)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (lineTokens.Count == 0)
|
|
||||||
{
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentColumn = 1;
|
|
||||||
|
|
||||||
foreach (var token in lineTokens)
|
|
||||||
{
|
|
||||||
var tokenStart = token.Span.Start.Column;
|
|
||||||
var tokenEnd = token.Span.End.Column;
|
|
||||||
|
|
||||||
if (tokenStart > currentColumn && currentColumn - 1 < line.Length)
|
|
||||||
{
|
|
||||||
var beforeLength = Math.Min(tokenStart - currentColumn, line.Length - (currentColumn - 1));
|
|
||||||
if (beforeLength > 0)
|
|
||||||
{
|
|
||||||
var beforeToken = line.Substring(currentColumn - 1, beforeLength);
|
|
||||||
sb.Append(beforeToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenLength = tokenEnd - tokenStart;
|
|
||||||
if (tokenStart >= 1 && tokenStart - 1 < line.Length && tokenLength > 0)
|
|
||||||
{
|
|
||||||
var availableLength = line.Length - (tokenStart - 1);
|
|
||||||
var actualLength = Math.Min(tokenLength, availableLength);
|
|
||||||
|
|
||||||
if (actualLength > 0)
|
|
||||||
{
|
|
||||||
var tokenText = line.Substring(tokenStart - 1, actualLength);
|
|
||||||
var coloredToken = ColorizeToken(token, tokenText);
|
|
||||||
sb.Append(coloredToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentColumn = tokenEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentColumn - 1 < line.Length)
|
|
||||||
{
|
|
||||||
var remaining = line[(currentColumn - 1)..];
|
|
||||||
sb.Append(remaining);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ColorizeToken(Token token, string tokenText)
|
|
||||||
{
|
|
||||||
switch (token)
|
|
||||||
{
|
|
||||||
case IdentifierToken:
|
|
||||||
{
|
|
||||||
return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightWhite);
|
|
||||||
}
|
|
||||||
case StringLiteralToken:
|
|
||||||
{
|
|
||||||
return ConsoleColors.Colorize(tokenText, ConsoleColors.Green);
|
|
||||||
}
|
|
||||||
case IntLiteralToken:
|
|
||||||
case FloatLiteralToken:
|
|
||||||
case BoolLiteralToken:
|
|
||||||
{
|
|
||||||
return ConsoleColors.Colorize(tokenText, ConsoleColors.Magenta);
|
|
||||||
}
|
|
||||||
case SymbolToken symbolToken:
|
|
||||||
{
|
|
||||||
switch (symbolToken.Symbol)
|
|
||||||
{
|
|
||||||
case Symbol.Func:
|
|
||||||
case Symbol.Return:
|
|
||||||
case Symbol.If:
|
|
||||||
case Symbol.Else:
|
|
||||||
case Symbol.While:
|
|
||||||
case Symbol.Break:
|
|
||||||
case Symbol.Continue:
|
|
||||||
case Symbol.Struct:
|
|
||||||
case Symbol.Let:
|
|
||||||
case Symbol.Extern:
|
|
||||||
case Symbol.For:
|
|
||||||
case Symbol.In:
|
|
||||||
{
|
|
||||||
return ConsoleColors.Colorize(tokenText, ConsoleColors.Bold + ConsoleColors.Blue);
|
|
||||||
}
|
|
||||||
case Symbol.Assign:
|
|
||||||
case Symbol.Bang:
|
|
||||||
case Symbol.Equal:
|
|
||||||
case Symbol.NotEqual:
|
|
||||||
case Symbol.LessThan:
|
|
||||||
case Symbol.LessThanOrEqual:
|
|
||||||
case Symbol.GreaterThan:
|
|
||||||
case Symbol.GreaterThanOrEqual:
|
|
||||||
case Symbol.Plus:
|
|
||||||
case Symbol.Minus:
|
|
||||||
case Symbol.Star:
|
|
||||||
case Symbol.ForwardSlash:
|
|
||||||
case Symbol.Caret:
|
|
||||||
case Symbol.Ampersand:
|
|
||||||
{
|
|
||||||
return ConsoleColors.Colorize(tokenText, ConsoleColors.Yellow);
|
|
||||||
}
|
|
||||||
case Symbol.Colon:
|
|
||||||
case Symbol.OpenParen:
|
|
||||||
case Symbol.CloseParen:
|
|
||||||
case Symbol.OpenBrace:
|
|
||||||
case Symbol.CloseBrace:
|
|
||||||
case Symbol.OpenBracket:
|
|
||||||
case Symbol.CloseBracket:
|
|
||||||
case Symbol.Comma:
|
|
||||||
case Symbol.Period:
|
|
||||||
case Symbol.Semi:
|
|
||||||
{
|
|
||||||
return ConsoleColors.Colorize(tokenText, ConsoleColors.BrightBlack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum DiagnosticSeverity
|
|
||||||
{
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConsoleColors
|
|
||||||
{
|
|
||||||
public const string Reset = "\e[0m";
|
|
||||||
public const string Bold = "\e[1m";
|
|
||||||
public const string Faint = "\e[2m";
|
|
||||||
public const string Italic = "\e[3m";
|
|
||||||
public const string Underline = "\e[4m";
|
|
||||||
public const string SlowBlink = "\e[5m";
|
|
||||||
public const string RapidBlink = "\e[6m";
|
|
||||||
public const string SwapBgAndFg = "\e[7m";
|
|
||||||
public const string Conceal = "\e[8m";
|
|
||||||
public const string CrossedOut = "\e[9m";
|
|
||||||
|
|
||||||
public const string DefaultFont = "\e[10m";
|
|
||||||
public const string AltFont1 = "\e[11m";
|
|
||||||
public const string AltFont2 = "\e[12m";
|
|
||||||
public const string AltFont3 = "\e[13m";
|
|
||||||
public const string AltFont4 = "\e[14m";
|
|
||||||
public const string AltFont5 = "\e[15m";
|
|
||||||
public const string AltFont6 = "\e[16m";
|
|
||||||
public const string AltFont7 = "\e[17m";
|
|
||||||
public const string AltFont8 = "\e[18m";
|
|
||||||
public const string AltFont9 = "\e[19m";
|
|
||||||
|
|
||||||
public const string Black = "\e[30m";
|
|
||||||
public const string Red = "\e[31m";
|
|
||||||
public const string Green = "\e[32m";
|
|
||||||
public const string Yellow = "\e[33m";
|
|
||||||
public const string Blue = "\e[34m";
|
|
||||||
public const string Magenta = "\e[35m";
|
|
||||||
public const string Cyan = "\e[36m";
|
|
||||||
public const string White = "\e[37m";
|
|
||||||
|
|
||||||
public const string BrightBlack = "\e[90m";
|
|
||||||
public const string BrightRed = "\e[91m";
|
|
||||||
public const string BrightGreen = "\e[92m";
|
|
||||||
public const string BrightYellow = "\e[93m";
|
|
||||||
public const string BrightBlue = "\e[94m";
|
|
||||||
public const string BrightMagenta = "\e[95m";
|
|
||||||
public const string BrightCyan = "\e[96m";
|
|
||||||
public const string BrightWhite = "\e[97m";
|
|
||||||
|
|
||||||
private static bool IsColorSupported()
|
|
||||||
{
|
|
||||||
var term = Environment.GetEnvironmentVariable("TERM");
|
|
||||||
var colorTerm = Environment.GetEnvironmentVariable("COLORTERM");
|
|
||||||
return !string.IsNullOrEmpty(term) || !string.IsNullOrEmpty(colorTerm) || !Console.IsOutputRedirected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Colorize(string text, string color)
|
|
||||||
{
|
|
||||||
return IsColorSupported() ? $"{color}{text}{Reset}" : text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
namespace NubLang.Diagnostics;
|
|
||||||
|
|
||||||
public readonly struct SourceSpan : IEquatable<SourceSpan>, IComparable<SourceSpan>
|
|
||||||
{
|
|
||||||
public static SourceSpan Merge(params IEnumerable<SourceSpan> spans)
|
|
||||||
{
|
|
||||||
var spanArray = spans as SourceSpan[] ?? spans.ToArray();
|
|
||||||
if (spanArray.Length == 0)
|
|
||||||
{
|
|
||||||
return new SourceSpan(string.Empty, new SourceLocation(0, 0), new SourceLocation(0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
var minStart = spanArray.Min(s => s.Start);
|
|
||||||
var maxEnd = spanArray.Max(s => s.End);
|
|
||||||
|
|
||||||
return new SourceSpan(spanArray[0].FilePath, minStart, maxEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceSpan(string filePath, SourceLocation start, SourceLocation end)
|
|
||||||
{
|
|
||||||
if (start > end)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Start location cannot be after end location");
|
|
||||||
}
|
|
||||||
|
|
||||||
FilePath = filePath;
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FilePath { get; }
|
|
||||||
public SourceLocation Start { get; }
|
|
||||||
public SourceLocation End { get; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if (Start == End)
|
|
||||||
{
|
|
||||||
return $"{FilePath}:{Start}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Start.Line == End.Line)
|
|
||||||
{
|
|
||||||
return Start.Column == End.Column ? $"{FilePath}:{Start}" : $"{FilePath}:{Start.Line}:{Start.Column}-{End.Column}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{FilePath}:{Start}-{End}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(SourceSpan other) => Start == other.Start && End == other.End;
|
|
||||||
public override bool Equals(object? obj) => obj is SourceSpan other && Equals(other);
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(SourceSpan), Start, End);
|
|
||||||
|
|
||||||
public static bool operator ==(SourceSpan left, SourceSpan right) => Equals(left, right);
|
|
||||||
public static bool operator !=(SourceSpan left, SourceSpan right) => !Equals(left, right);
|
|
||||||
|
|
||||||
public static bool operator <(SourceSpan left, SourceSpan right) => left.CompareTo(right) < 0;
|
|
||||||
public static bool operator <=(SourceSpan left, SourceSpan right) => left.CompareTo(right) <= 0;
|
|
||||||
public static bool operator >(SourceSpan left, SourceSpan right) => left.CompareTo(right) > 0;
|
|
||||||
public static bool operator >=(SourceSpan left, SourceSpan right) => left.CompareTo(right) >= 0;
|
|
||||||
|
|
||||||
public int CompareTo(SourceSpan other)
|
|
||||||
{
|
|
||||||
var startComparison = Start.CompareTo(other.Start);
|
|
||||||
return startComparison != 0 ? startComparison : End.CompareTo(other.End);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct SourceLocation : IEquatable<SourceLocation>, IComparable<SourceLocation>
|
|
||||||
{
|
|
||||||
public SourceLocation(int line, int column)
|
|
||||||
{
|
|
||||||
Line = line;
|
|
||||||
Column = column;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Line { get; }
|
|
||||||
public int Column { get; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{Line}:{Column}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
return obj is SourceLocation other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(SourceLocation other)
|
|
||||||
{
|
|
||||||
return Line == other.Line && Column == other.Column;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(typeof(SourceLocation), Line, Column);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(SourceLocation left, SourceLocation right) => Equals(left, right);
|
|
||||||
public static bool operator !=(SourceLocation left, SourceLocation right) => !Equals(left, right);
|
|
||||||
public static bool operator <(SourceLocation left, SourceLocation right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column);
|
|
||||||
public static bool operator >(SourceLocation left, SourceLocation right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column);
|
|
||||||
public static bool operator <=(SourceLocation left, SourceLocation right) => left.Line <= right.Line || (left.Line == right.Line && left.Column <= right.Column);
|
|
||||||
public static bool operator >=(SourceLocation left, SourceLocation right) => left.Line >= right.Line || (left.Line == right.Line && left.Column >= right.Column);
|
|
||||||
|
|
||||||
public int CompareTo(SourceLocation other)
|
|
||||||
{
|
|
||||||
var lineComparison = Line.CompareTo(other.Line);
|
|
||||||
return lineComparison != 0 ? lineComparison : Column.CompareTo(other.Column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<IsAotCompatible>true</IsAotCompatible>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace NubLang.Sugar;
|
|
||||||
|
|
||||||
public class DeSugar
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
namespace NubLang.Syntax;
|
|
||||||
|
|
||||||
public sealed class Module
|
|
||||||
{
|
|
||||||
public static Dictionary<string, Module> Collect(List<SyntaxTree> syntaxTrees)
|
|
||||||
{
|
|
||||||
var modules = new Dictionary<string, Module>();
|
|
||||||
foreach (var syntaxTree in syntaxTrees)
|
|
||||||
{
|
|
||||||
if (!modules.TryGetValue(syntaxTree.ModuleName, out var module))
|
|
||||||
{
|
|
||||||
module = new Module();
|
|
||||||
modules.Add(syntaxTree.ModuleName, module);
|
|
||||||
}
|
|
||||||
|
|
||||||
module._definitions.AddRange(syntaxTree.Definitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return modules;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly List<DefinitionSyntax> _definitions = [];
|
|
||||||
|
|
||||||
public List<StructSyntax> Structs(bool includePrivate)
|
|
||||||
{
|
|
||||||
return _definitions
|
|
||||||
.OfType<StructSyntax>()
|
|
||||||
.Where(x => x.Exported || includePrivate)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FuncSyntax> Functions(bool includePrivate)
|
|
||||||
{
|
|
||||||
return _definitions
|
|
||||||
.OfType<FuncSyntax>()
|
|
||||||
.Where(x => x.Exported || includePrivate)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EnumSyntax> Enums(bool includePrivate)
|
|
||||||
{
|
|
||||||
return _definitions
|
|
||||||
.OfType<EnumSyntax>()
|
|
||||||
.Where(x => x.Exported || includePrivate)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,951 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using NubLang.Diagnostics;
|
|
||||||
|
|
||||||
namespace NubLang.Syntax;
|
|
||||||
|
|
||||||
public sealed class Parser
|
|
||||||
{
|
|
||||||
private List<Token> _tokens = [];
|
|
||||||
private int _tokenIndex;
|
|
||||||
|
|
||||||
private Token? CurrentToken => _tokenIndex < _tokens.Count ? _tokens[_tokenIndex] : null;
|
|
||||||
private bool HasToken => CurrentToken != null;
|
|
||||||
|
|
||||||
public List<Diagnostic> Diagnostics { get; } = [];
|
|
||||||
|
|
||||||
public SyntaxTree Parse(List<Token> tokens)
|
|
||||||
{
|
|
||||||
Diagnostics.Clear();
|
|
||||||
_tokens = tokens;
|
|
||||||
_tokenIndex = 0;
|
|
||||||
|
|
||||||
string? moduleName = null;
|
|
||||||
var imports = new List<string>();
|
|
||||||
var definitions = new List<DefinitionSyntax>();
|
|
||||||
|
|
||||||
while (HasToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Extern))
|
|
||||||
{
|
|
||||||
var externSymbol = ExpectStringLiteral();
|
|
||||||
ExpectSymbol(Symbol.Func);
|
|
||||||
definitions.Add(ParseFunc(startIndex, exported, externSymbol.Value));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyword = ExpectSymbol();
|
|
||||||
DefinitionSyntax definition = keyword.Symbol switch
|
|
||||||
{
|
|
||||||
Symbol.Func => ParseFunc(startIndex, exported, null),
|
|
||||||
Symbol.Struct => ParseStruct(startIndex, exported),
|
|
||||||
Symbol.Enum => ParseEnum(startIndex, exported),
|
|
||||||
_ => throw new ParseException(Diagnostic
|
|
||||||
.Error($"Expected 'func', 'struct', 'enum', 'import' or 'module' but found '{keyword.Symbol}'")
|
|
||||||
.WithHelp("Valid top level statements are 'func', 'struct', 'enum', 'import' and 'module'")
|
|
||||||
.At(keyword)
|
|
||||||
.Build())
|
|
||||||
};
|
|
||||||
|
|
||||||
definitions.Add(definition);
|
|
||||||
}
|
|
||||||
catch (ParseException e)
|
|
||||||
{
|
|
||||||
Diagnostics.Add(e.Diagnostic);
|
|
||||||
while (HasToken)
|
|
||||||
{
|
|
||||||
if (CurrentToken is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct })
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SyntaxTree(definitions, moduleName ?? "default", imports);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FuncParameterSyntax ParseFuncParameter()
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
ExpectSymbol(Symbol.Colon);
|
|
||||||
var type = ParseType();
|
|
||||||
|
|
||||||
return new FuncParameterSyntax(GetTokens(startIndex), name.Value, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FuncSyntax ParseFunc(int startIndex, bool exported, string? externSymbol)
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
List<FuncParameterSyntax> parameters = [];
|
|
||||||
|
|
||||||
ExpectSymbol(Symbol.OpenParen);
|
|
||||||
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
||||||
{
|
|
||||||
parameters.Add(ParseFuncParameter());
|
|
||||||
|
|
||||||
if (!TryExpectSymbol(Symbol.Comma))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseParen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new VoidTypeSyntax([]);
|
|
||||||
|
|
||||||
var prototype = new FuncPrototypeSyntax(GetTokens(startIndex), name.Value, exported, externSymbol, parameters, returnType);
|
|
||||||
|
|
||||||
BlockSyntax? body = null;
|
|
||||||
var bodyStartIndex = _tokenIndex;
|
|
||||||
if (TryExpectSymbol(Symbol.OpenBrace))
|
|
||||||
{
|
|
||||||
body = ParseBlock(bodyStartIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FuncSyntax(GetTokens(startIndex), prototype, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StructSyntax ParseStruct(int startIndex, bool exported)
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
|
|
||||||
ExpectSymbol(Symbol.OpenBrace);
|
|
||||||
|
|
||||||
List<StructFieldSyntax> fields = [];
|
|
||||||
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
||||||
{
|
|
||||||
var memberStartIndex = _tokenIndex;
|
|
||||||
|
|
||||||
var fieldName = ExpectIdentifier().Value;
|
|
||||||
ExpectSymbol(Symbol.Colon);
|
|
||||||
var fieldType = ParseType();
|
|
||||||
|
|
||||||
ExpressionSyntax? fieldValue = null;
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Assign))
|
|
||||||
{
|
|
||||||
fieldValue = ParseExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.Add(new StructFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldType, fieldValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StructSyntax(GetTokens(startIndex), name.Value, exported, fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EnumSyntax ParseEnum(int startIndex, bool exported)
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
|
|
||||||
TypeSyntax? type = null;
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Colon))
|
|
||||||
{
|
|
||||||
type = ParseType();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<EnumFieldSyntax> fields = [];
|
|
||||||
|
|
||||||
ExpectSymbol(Symbol.OpenBrace);
|
|
||||||
|
|
||||||
long value = -1;
|
|
||||||
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
||||||
{
|
|
||||||
var memberStartIndex = _tokenIndex;
|
|
||||||
var fieldName = ExpectIdentifier().Value;
|
|
||||||
long fieldValue;
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Assign))
|
|
||||||
{
|
|
||||||
if (!TryExpectIntLiteral(out var intLiteralToken))
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error("Value of enum field must be an integer literal")
|
|
||||||
.At(CurrentToken)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldValue = Convert.ToInt64(intLiteralToken.Value, intLiteralToken.Base);
|
|
||||||
value = fieldValue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fieldValue = value + 1;
|
|
||||||
value = fieldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.Add(new EnumFieldSyntax(GetTokens(memberStartIndex), fieldName, fieldValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new EnumSyntax(GetTokens(startIndex), name.Value, exported, type, fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StatementSyntax ParseStatement()
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
|
|
||||||
if (TryExpectSymbol(out var symbol))
|
|
||||||
{
|
|
||||||
switch (symbol)
|
|
||||||
{
|
|
||||||
case Symbol.OpenBrace:
|
|
||||||
return ParseBlock(startIndex);
|
|
||||||
case Symbol.Return:
|
|
||||||
return ParseReturn(startIndex);
|
|
||||||
case Symbol.If:
|
|
||||||
return ParseIf(startIndex);
|
|
||||||
case Symbol.While:
|
|
||||||
return ParseWhile(startIndex);
|
|
||||||
case Symbol.For:
|
|
||||||
return ParseFor(startIndex);
|
|
||||||
case Symbol.Let:
|
|
||||||
return ParseVariableDeclaration(startIndex);
|
|
||||||
case Symbol.Defer:
|
|
||||||
return ParseDefer(startIndex);
|
|
||||||
case Symbol.Break:
|
|
||||||
return new BreakSyntax(GetTokens(startIndex));
|
|
||||||
case Symbol.Continue:
|
|
||||||
return new ContinueSyntax(GetTokens(startIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var expr = ParseExpression();
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Assign))
|
|
||||||
{
|
|
||||||
var value = ParseExpression();
|
|
||||||
return new AssignmentSyntax(GetTokens(startIndex), expr, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StatementExpressionSyntax(GetTokens(startIndex), expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private VariableDeclarationSyntax ParseVariableDeclaration(int startIndex)
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier().Value;
|
|
||||||
|
|
||||||
TypeSyntax? explicitType = null;
|
|
||||||
if (TryExpectSymbol(Symbol.Colon))
|
|
||||||
{
|
|
||||||
explicitType = ParseType();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpressionSyntax? assignment = null;
|
|
||||||
if (TryExpectSymbol(Symbol.Assign))
|
|
||||||
{
|
|
||||||
assignment = ParseExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new VariableDeclarationSyntax(GetTokens(startIndex), name, explicitType, assignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeferSyntax ParseDefer(int startIndex)
|
|
||||||
{
|
|
||||||
var statement = ParseStatement();
|
|
||||||
return new DeferSyntax(GetTokens(startIndex), statement);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReturnSyntax ParseReturn(int startIndex)
|
|
||||||
{
|
|
||||||
ExpressionSyntax? value = null;
|
|
||||||
|
|
||||||
if (!TryExpectSymbol(Symbol.Semi))
|
|
||||||
{
|
|
||||||
value = ParseExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ReturnSyntax(GetTokens(startIndex), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IfSyntax ParseIf(int startIndex)
|
|
||||||
{
|
|
||||||
var condition = ParseExpression();
|
|
||||||
var body = ParseBlock();
|
|
||||||
|
|
||||||
Variant<IfSyntax, BlockSyntax>? elseStatement = null;
|
|
||||||
|
|
||||||
var elseStartIndex = _tokenIndex;
|
|
||||||
if (TryExpectSymbol(Symbol.Else))
|
|
||||||
{
|
|
||||||
if (TryExpectSymbol(Symbol.If))
|
|
||||||
{
|
|
||||||
elseStatement = (Variant<IfSyntax, BlockSyntax>)ParseIf(elseStartIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elseStatement = (Variant<IfSyntax, BlockSyntax>)ParseBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new IfSyntax(GetTokens(startIndex), condition, body, elseStatement);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WhileSyntax ParseWhile(int startIndex)
|
|
||||||
{
|
|
||||||
var condition = ParseExpression();
|
|
||||||
var body = ParseBlock();
|
|
||||||
return new WhileSyntax(GetTokens(startIndex), condition, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ForSyntax ParseFor(int startIndex)
|
|
||||||
{
|
|
||||||
var itemName = ExpectIdentifier().Value;
|
|
||||||
string? indexName = null;
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Comma))
|
|
||||||
{
|
|
||||||
indexName = ExpectIdentifier().Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpectSymbol(Symbol.In);
|
|
||||||
var target = ParseExpression();
|
|
||||||
var body = ParseBlock();
|
|
||||||
|
|
||||||
return new ForSyntax(GetTokens(startIndex), itemName, indexName, target, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParseExpression(int precedence = 0)
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
var left = ParsePrimaryExpression();
|
|
||||||
|
|
||||||
while (CurrentToken is SymbolToken symbolToken && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence)
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1);
|
|
||||||
left = new BinaryExpressionSyntax(GetTokens(startIndex), left, op.Value, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetBinaryOperatorPrecedence(BinaryOperatorSyntax operatorSyntax)
|
|
||||||
{
|
|
||||||
return operatorSyntax switch
|
|
||||||
{
|
|
||||||
BinaryOperatorSyntax.Multiply => 10,
|
|
||||||
BinaryOperatorSyntax.Divide => 10,
|
|
||||||
BinaryOperatorSyntax.Modulo => 10,
|
|
||||||
|
|
||||||
BinaryOperatorSyntax.Plus => 9,
|
|
||||||
BinaryOperatorSyntax.Minus => 9,
|
|
||||||
|
|
||||||
BinaryOperatorSyntax.LeftShift => 8,
|
|
||||||
BinaryOperatorSyntax.RightShift => 8,
|
|
||||||
|
|
||||||
BinaryOperatorSyntax.GreaterThan => 7,
|
|
||||||
BinaryOperatorSyntax.GreaterThanOrEqual => 7,
|
|
||||||
BinaryOperatorSyntax.LessThan => 7,
|
|
||||||
BinaryOperatorSyntax.LessThanOrEqual => 7,
|
|
||||||
|
|
||||||
BinaryOperatorSyntax.Equal => 7,
|
|
||||||
BinaryOperatorSyntax.NotEqual => 7,
|
|
||||||
|
|
||||||
BinaryOperatorSyntax.BitwiseAnd => 6,
|
|
||||||
BinaryOperatorSyntax.BitwiseXor => 5,
|
|
||||||
BinaryOperatorSyntax.BitwiseOr => 4,
|
|
||||||
|
|
||||||
BinaryOperatorSyntax.LogicalAnd => 3,
|
|
||||||
BinaryOperatorSyntax.LogicalOr => 2,
|
|
||||||
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(operatorSyntax), operatorSyntax, null)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryOperatorSyntax? binaryExpressionOperator)
|
|
||||||
{
|
|
||||||
switch (symbol)
|
|
||||||
{
|
|
||||||
case Symbol.Equal:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.Equal;
|
|
||||||
return true;
|
|
||||||
case Symbol.NotEqual:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.NotEqual;
|
|
||||||
return true;
|
|
||||||
case Symbol.LessThan:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.LessThan;
|
|
||||||
return true;
|
|
||||||
case Symbol.LessThanOrEqual:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.LessThanOrEqual;
|
|
||||||
return true;
|
|
||||||
case Symbol.GreaterThan:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.GreaterThan;
|
|
||||||
return true;
|
|
||||||
case Symbol.GreaterThanOrEqual:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.GreaterThanOrEqual;
|
|
||||||
return true;
|
|
||||||
case Symbol.And:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.LogicalAnd;
|
|
||||||
return true;
|
|
||||||
case Symbol.Or:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.LogicalOr;
|
|
||||||
return true;
|
|
||||||
case Symbol.Plus:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.Plus;
|
|
||||||
return true;
|
|
||||||
case Symbol.Minus:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.Minus;
|
|
||||||
return true;
|
|
||||||
case Symbol.Star:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.Multiply;
|
|
||||||
return true;
|
|
||||||
case Symbol.ForwardSlash:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.Divide;
|
|
||||||
return true;
|
|
||||||
case Symbol.Percent:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.Modulo;
|
|
||||||
return true;
|
|
||||||
case Symbol.LeftShift:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.LeftShift;
|
|
||||||
return true;
|
|
||||||
case Symbol.RightShift:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.RightShift;
|
|
||||||
return true;
|
|
||||||
case Symbol.Ampersand:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseAnd;
|
|
||||||
return true;
|
|
||||||
case Symbol.Pipe:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr;
|
|
||||||
return true;
|
|
||||||
case Symbol.Caret:
|
|
||||||
binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor;
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
binaryExpressionOperator = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParsePrimaryExpression()
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
var token = ExpectToken();
|
|
||||||
var expr = token switch
|
|
||||||
{
|
|
||||||
BoolLiteralToken boolLiteral => new BoolLiteralSyntax(GetTokens(startIndex), boolLiteral.Value),
|
|
||||||
StringLiteralToken stringLiteral => new StringLiteralSyntax(GetTokens(startIndex), stringLiteral.Value),
|
|
||||||
FloatLiteralToken floatLiteral => new FloatLiteralSyntax(GetTokens(startIndex), floatLiteral.Value),
|
|
||||||
IntLiteralToken intLiteral => new IntLiteralSyntax(GetTokens(startIndex), intLiteral.Value, intLiteral.Base),
|
|
||||||
IdentifierToken identifier => ParseIdentifier(startIndex, identifier),
|
|
||||||
SymbolToken symbolToken => symbolToken.Symbol switch
|
|
||||||
{
|
|
||||||
Symbol.OpenParen => ParseParenthesizedExpression(),
|
|
||||||
Symbol.Minus => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Negate, ParsePrimaryExpression()),
|
|
||||||
Symbol.Bang => new UnaryExpressionSyntax(GetTokens(startIndex), UnaryOperatorSyntax.Invert, ParsePrimaryExpression()),
|
|
||||||
Symbol.OpenBracket => ParseArrayInitializer(startIndex),
|
|
||||||
Symbol.OpenBrace => new StructInitializerSyntax(GetTokens(startIndex), null, ParseStructInitializerBody()),
|
|
||||||
Symbol.Struct => ParseStructInitializer(startIndex),
|
|
||||||
Symbol.At => ParseBuiltinFunction(startIndex),
|
|
||||||
_ => throw new ParseException(Diagnostic
|
|
||||||
.Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
|
|
||||||
.WithHelp("Expected '(', '-', '!', '[' or '{'")
|
|
||||||
.At(symbolToken)
|
|
||||||
.Build())
|
|
||||||
},
|
|
||||||
_ => throw new ParseException(Diagnostic
|
|
||||||
.Error($"Unexpected token '{token.GetType().Name}' in expression")
|
|
||||||
.WithHelp("Expected literal, identifier, or parenthesized expression")
|
|
||||||
.At(token)
|
|
||||||
.Build())
|
|
||||||
};
|
|
||||||
|
|
||||||
return ParsePostfixOperators(expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParseBuiltinFunction(int startIndex)
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
ExpectSymbol(Symbol.OpenParen);
|
|
||||||
|
|
||||||
switch (name.Value)
|
|
||||||
{
|
|
||||||
case "size":
|
|
||||||
{
|
|
||||||
var type = ParseType();
|
|
||||||
ExpectSymbol(Symbol.CloseParen);
|
|
||||||
return new SizeSyntax(GetTokens(startIndex), type);
|
|
||||||
}
|
|
||||||
case "cast":
|
|
||||||
{
|
|
||||||
var expression = ParseExpression();
|
|
||||||
ExpectSymbol(Symbol.CloseParen);
|
|
||||||
return new CastSyntax(GetTokens(startIndex), expression);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic.Error($"Unknown builtin {name.Value}").At(name).Build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParseIdentifier(int startIndex, IdentifierToken identifier)
|
|
||||||
{
|
|
||||||
if (TryExpectSymbol(Symbol.DoubleColon))
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier();
|
|
||||||
return new ModuleIdentifierSyntax(GetTokens(startIndex), identifier.Value, name.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new LocalIdentifierSyntax(GetTokens(startIndex), identifier.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParseParenthesizedExpression()
|
|
||||||
{
|
|
||||||
var expression = ParseExpression();
|
|
||||||
ExpectSymbol(Symbol.CloseParen);
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr)
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
while (HasToken)
|
|
||||||
{
|
|
||||||
if (TryExpectSymbol(Symbol.Ampersand))
|
|
||||||
{
|
|
||||||
expr = new AddressOfSyntax(GetTokens(startIndex), expr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Caret))
|
|
||||||
{
|
|
||||||
expr = new DereferenceSyntax(GetTokens(startIndex), expr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Period))
|
|
||||||
{
|
|
||||||
var member = ExpectIdentifier().Value;
|
|
||||||
expr = new MemberAccessSyntax(GetTokens(startIndex), expr, member);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.OpenBracket))
|
|
||||||
{
|
|
||||||
var index = ParseExpression();
|
|
||||||
ExpectSymbol(Symbol.CloseBracket);
|
|
||||||
expr = new ArrayIndexAccessSyntax(GetTokens(startIndex), expr, index);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.OpenParen))
|
|
||||||
{
|
|
||||||
var parameters = new List<ExpressionSyntax>();
|
|
||||||
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
||||||
{
|
|
||||||
parameters.Add(ParseExpression());
|
|
||||||
if (!TryExpectSymbol(Symbol.Comma))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseParen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expr = new FuncCallSyntax(GetTokens(startIndex), expr, parameters);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExpressionSyntax ParseArrayInitializer(int startIndex)
|
|
||||||
{
|
|
||||||
var values = new List<ExpressionSyntax>();
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseBracket))
|
|
||||||
{
|
|
||||||
values.Add(ParseExpression());
|
|
||||||
if (!TryExpectSymbol(Symbol.Comma))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseBracket);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ArrayInitializerSyntax(GetTokens(startIndex), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StructInitializerSyntax ParseStructInitializer(int startIndex)
|
|
||||||
{
|
|
||||||
TypeSyntax? type = null;
|
|
||||||
if (!TryExpectSymbol(Symbol.OpenBrace))
|
|
||||||
{
|
|
||||||
type = ParseType();
|
|
||||||
ExpectSymbol(Symbol.OpenBrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
var initializers = ParseStructInitializerBody();
|
|
||||||
|
|
||||||
return new StructInitializerSyntax(GetTokens(startIndex), type, initializers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, ExpressionSyntax> ParseStructInitializerBody()
|
|
||||||
{
|
|
||||||
Dictionary<string, ExpressionSyntax> initializers = [];
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
||||||
{
|
|
||||||
var name = ExpectIdentifier().Value;
|
|
||||||
ExpectSymbol(Symbol.Assign);
|
|
||||||
var value = ParseExpression();
|
|
||||||
initializers.Add(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return initializers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlockSyntax ParseBlock()
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
ExpectSymbol(Symbol.OpenBrace);
|
|
||||||
return ParseBlock(startIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BlockSyntax ParseBlock(int startIndex)
|
|
||||||
{
|
|
||||||
List<StatementSyntax> statements = [];
|
|
||||||
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
statements.Add(ParseStatement());
|
|
||||||
}
|
|
||||||
catch (ParseException ex)
|
|
||||||
{
|
|
||||||
Diagnostics.Add(ex.Diagnostic);
|
|
||||||
if (HasToken)
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BlockSyntax(GetTokens(startIndex), statements);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeSyntax ParseType()
|
|
||||||
{
|
|
||||||
var startIndex = _tokenIndex;
|
|
||||||
if (TryExpectIdentifier(out var name))
|
|
||||||
{
|
|
||||||
if (name.Value[0] == 'u' && int.TryParse(name.Value[1..], out var size))
|
|
||||||
{
|
|
||||||
if (size is not 8 and not 16 and not 32 and not 64)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error("Arbitrary uint size is not supported")
|
|
||||||
.WithHelp("Use u8, u16, u32 or u64")
|
|
||||||
.At(name)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new IntTypeSyntax(GetTokens(startIndex), false, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Value[0] == 'i' && int.TryParse(name.Value[1..], out size))
|
|
||||||
{
|
|
||||||
if (size is not 8 and not 16 and not 32 and not 64)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error("Arbitrary int size is not supported")
|
|
||||||
.WithHelp("Use i8, i16, i32 or i64")
|
|
||||||
.At(name)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new IntTypeSyntax(GetTokens(startIndex), true, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.Value[0] == 'f' && int.TryParse(name.Value[1..], out size))
|
|
||||||
{
|
|
||||||
if (size is not 32 and not 64)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error("Arbitrary float size is not supported")
|
|
||||||
.WithHelp("Use f32 or f64")
|
|
||||||
.At(name)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FloatTypeSyntax(GetTokens(startIndex), size);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (name.Value)
|
|
||||||
{
|
|
||||||
case "void":
|
|
||||||
return new VoidTypeSyntax(GetTokens(startIndex));
|
|
||||||
case "string":
|
|
||||||
return new StringTypeSyntax(GetTokens(startIndex));
|
|
||||||
case "bool":
|
|
||||||
return new BoolTypeSyntax(GetTokens(startIndex));
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
string? module = null;
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.DoubleColon))
|
|
||||||
{
|
|
||||||
var customTypeName = ExpectIdentifier();
|
|
||||||
module = name.Value;
|
|
||||||
name = customTypeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CustomTypeSyntax(GetTokens(startIndex), module, name.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Caret))
|
|
||||||
{
|
|
||||||
var baseType = ParseType();
|
|
||||||
return new PointerTypeSyntax(GetTokens(startIndex), baseType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.Func))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.OpenParen);
|
|
||||||
|
|
||||||
List<TypeSyntax> parameters = [];
|
|
||||||
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
||||||
{
|
|
||||||
parameters.Add(ParseType());
|
|
||||||
if (!TryExpectSymbol(Symbol.Comma))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseParen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var returnType = TryExpectSymbol(Symbol.Colon)
|
|
||||||
? ParseType()
|
|
||||||
: new VoidTypeSyntax([]);
|
|
||||||
|
|
||||||
return new FuncTypeSyntax(GetTokens(startIndex), parameters, returnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryExpectSymbol(Symbol.OpenBracket))
|
|
||||||
{
|
|
||||||
if (TryExpectIntLiteral(out var intLiteral))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseBracket);
|
|
||||||
var baseType = ParseType();
|
|
||||||
return new ConstArrayTypeSyntax(GetTokens(startIndex), baseType, Convert.ToInt64(intLiteral.Value, intLiteral.Base));
|
|
||||||
}
|
|
||||||
else if (TryExpectSymbol(Symbol.QuestionMark))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseBracket);
|
|
||||||
var baseType = ParseType();
|
|
||||||
return new ArrayTypeSyntax(GetTokens(startIndex), baseType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.CloseBracket);
|
|
||||||
var baseType = ParseType();
|
|
||||||
return new SliceTypeSyntax(GetTokens(startIndex), baseType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error("Invalid type syntax")
|
|
||||||
.WithHelp("Expected type name, '^' for pointer, or '[]' for array")
|
|
||||||
.At(CurrentToken)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Token ExpectToken()
|
|
||||||
{
|
|
||||||
if (!HasToken)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error("Unexpected end of file")
|
|
||||||
.WithHelp("Expected more tokens to complete the syntax")
|
|
||||||
.At(_tokens[^1])
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
var token = CurrentToken!;
|
|
||||||
Next();
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SymbolToken ExpectSymbol()
|
|
||||||
{
|
|
||||||
var token = ExpectToken();
|
|
||||||
if (token is not SymbolToken symbol)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error($"Expected symbol, but found {token.GetType().Name}")
|
|
||||||
.WithHelp("This position requires a symbol like '(', ')', '{', '}', etc.")
|
|
||||||
.At(token)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExpectSymbol(Symbol expectedSymbol)
|
|
||||||
{
|
|
||||||
var token = ExpectSymbol();
|
|
||||||
if (token.Symbol != expectedSymbol)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error($"Expected '{expectedSymbol}', but found '{token.Symbol}'")
|
|
||||||
.WithHelp($"Insert '{expectedSymbol}' here")
|
|
||||||
.At(token)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryExpectSymbol(out Symbol symbol)
|
|
||||||
{
|
|
||||||
if (CurrentToken is SymbolToken symbolToken)
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
symbol = symbolToken.Symbol;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryExpectSymbol(Symbol symbol)
|
|
||||||
{
|
|
||||||
if (CurrentToken is SymbolToken symbolToken && symbolToken.Symbol == symbol)
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier)
|
|
||||||
{
|
|
||||||
if (CurrentToken is IdentifierToken identifierToken)
|
|
||||||
{
|
|
||||||
identifier = identifierToken;
|
|
||||||
Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
identifier = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IdentifierToken ExpectIdentifier()
|
|
||||||
{
|
|
||||||
var token = ExpectToken();
|
|
||||||
if (token is not IdentifierToken identifier)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error($"Expected identifier, but found {token.GetType().Name}")
|
|
||||||
.WithHelp("Provide a valid identifier name here")
|
|
||||||
.At(token)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryExpectIntLiteral([NotNullWhen(true)] out IntLiteralToken? stringLiteral)
|
|
||||||
{
|
|
||||||
if (CurrentToken is IntLiteralToken token)
|
|
||||||
{
|
|
||||||
stringLiteral = token;
|
|
||||||
Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
stringLiteral = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private StringLiteralToken ExpectStringLiteral()
|
|
||||||
{
|
|
||||||
var token = ExpectToken();
|
|
||||||
if (token is not StringLiteralToken identifier)
|
|
||||||
{
|
|
||||||
throw new ParseException(Diagnostic
|
|
||||||
.Error($"Expected string literal, but found {token.GetType().Name}")
|
|
||||||
.WithHelp("Provide a valid string literal")
|
|
||||||
.At(token)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Next()
|
|
||||||
{
|
|
||||||
_tokenIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Token> GetTokens(int tokenStartIndex)
|
|
||||||
{
|
|
||||||
return _tokens.Skip(tokenStartIndex).Take(_tokenIndex - tokenStartIndex).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record SyntaxTree(List<DefinitionSyntax> Definitions, string ModuleName, List<string> Imports);
|
|
||||||
|
|
||||||
public class ParseException : Exception
|
|
||||||
{
|
|
||||||
public Diagnostic Diagnostic { get; }
|
|
||||||
|
|
||||||
public ParseException(Diagnostic diagnostic) : base(diagnostic.Message)
|
|
||||||
{
|
|
||||||
Diagnostic = diagnostic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
namespace NubLang.Syntax;
|
|
||||||
|
|
||||||
public abstract record SyntaxNode(List<Token> Tokens);
|
|
||||||
|
|
||||||
#region Definitions
|
|
||||||
|
|
||||||
public abstract record DefinitionSyntax(List<Token> Tokens, string Name, bool Exported) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record FuncParameterSyntax(List<Token> Tokens, string Name, TypeSyntax Type) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record FuncPrototypeSyntax(List<Token> Tokens, string Name, bool Exported, string? ExternSymbol, List<FuncParameterSyntax> Parameters, TypeSyntax ReturnType) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record FuncSyntax(List<Token> Tokens, FuncPrototypeSyntax Prototype, BlockSyntax? Body) : DefinitionSyntax(Tokens, Prototype.Name, Prototype.Exported);
|
|
||||||
|
|
||||||
public record StructFieldSyntax(List<Token> Tokens, string Name, TypeSyntax Type, ExpressionSyntax? Value) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record StructSyntax(List<Token> Tokens, string Name, bool Exported, List<StructFieldSyntax> Fields) : DefinitionSyntax(Tokens, Name, Exported);
|
|
||||||
|
|
||||||
public record EnumFieldSyntax(List<Token> Tokens, string Name, long Value) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record EnumSyntax(List<Token> Tokens, string Name, bool Exported, TypeSyntax? Type, List<EnumFieldSyntax> Fields) : DefinitionSyntax(Tokens, Name, Exported);
|
|
||||||
|
|
||||||
public enum UnaryOperatorSyntax
|
|
||||||
{
|
|
||||||
Negate,
|
|
||||||
Invert
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum BinaryOperatorSyntax
|
|
||||||
{
|
|
||||||
Equal,
|
|
||||||
NotEqual,
|
|
||||||
GreaterThan,
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
LessThan,
|
|
||||||
LessThanOrEqual,
|
|
||||||
LogicalAnd,
|
|
||||||
LogicalOr,
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Multiply,
|
|
||||||
Divide,
|
|
||||||
Modulo,
|
|
||||||
LeftShift,
|
|
||||||
RightShift,
|
|
||||||
BitwiseAnd,
|
|
||||||
BitwiseXor,
|
|
||||||
BitwiseOr,
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Statements
|
|
||||||
|
|
||||||
public abstract record StatementSyntax(List<Token> Tokens) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record BlockSyntax(List<Token> Tokens, List<StatementSyntax> Statements) : StatementSyntax(Tokens);
|
|
||||||
|
|
||||||
public record StatementExpressionSyntax(List<Token> Tokens, ExpressionSyntax Expression) : StatementSyntax(Tokens);
|
|
||||||
|
|
||||||
public record ReturnSyntax(List<Token> Tokens, ExpressionSyntax? Value) : StatementSyntax(Tokens);
|
|
||||||
|
|
||||||
public record AssignmentSyntax(List<Token> Tokens, ExpressionSyntax Target, ExpressionSyntax Value) : 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 ContinueSyntax(List<Token> Tokens) : StatementSyntax(Tokens);
|
|
||||||
|
|
||||||
public record BreakSyntax(List<Token> Tokens) : StatementSyntax(Tokens);
|
|
||||||
|
|
||||||
public record DeferSyntax(List<Token> Tokens, StatementSyntax Statement) : 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);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Expressions
|
|
||||||
|
|
||||||
public abstract record ExpressionSyntax(List<Token> Tokens) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record BinaryExpressionSyntax(List<Token> Tokens, ExpressionSyntax Left, BinaryOperatorSyntax Operator, ExpressionSyntax Right) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record UnaryExpressionSyntax(List<Token> Tokens, UnaryOperatorSyntax Operator, ExpressionSyntax Operand) : 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 ModuleIdentifierSyntax(List<Token> Tokens, string Module, string Name) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record ArrayInitializerSyntax(List<Token> Tokens, List<ExpressionSyntax> Values) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record ArrayIndexAccessSyntax(List<Token> Tokens, ExpressionSyntax Target, ExpressionSyntax Index) : 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 StringLiteralSyntax(List<Token> Tokens, string Value) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record BoolLiteralSyntax(List<Token> Tokens, bool Value) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record FloatLiteralSyntax(List<Token> Tokens, string Value) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record MemberAccessSyntax(List<Token> Tokens, ExpressionSyntax Target, string Member) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record StructInitializerSyntax(List<Token> Tokens, TypeSyntax? StructType, Dictionary<string, ExpressionSyntax> Initializers) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record DereferenceSyntax(List<Token> Tokens, ExpressionSyntax Target) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record SizeSyntax(List<Token> Tokens, TypeSyntax Type) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
public record CastSyntax(List<Token> Tokens, ExpressionSyntax Value) : ExpressionSyntax(Tokens);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Types
|
|
||||||
|
|
||||||
public abstract record TypeSyntax(List<Token> Tokens) : SyntaxNode(Tokens);
|
|
||||||
|
|
||||||
public record FuncTypeSyntax(List<Token> Tokens, List<TypeSyntax> Parameters, TypeSyntax ReturnType) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record PointerTypeSyntax(List<Token> Tokens, TypeSyntax BaseType) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record VoidTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record IntTypeSyntax(List<Token> Tokens, bool Signed, int Width) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record FloatTypeSyntax(List<Token> Tokens, int Width) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record BoolTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record StringTypeSyntax(List<Token> Tokens) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
public record SliceTypeSyntax(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 CustomTypeSyntax(List<Token> Tokens, string? Module, string Name) : TypeSyntax(Tokens);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
using NubLang.Diagnostics;
|
|
||||||
|
|
||||||
namespace NubLang.Syntax;
|
|
||||||
|
|
||||||
public enum Symbol
|
|
||||||
{
|
|
||||||
// Control
|
|
||||||
If,
|
|
||||||
Else,
|
|
||||||
While,
|
|
||||||
For,
|
|
||||||
In,
|
|
||||||
Break,
|
|
||||||
Continue,
|
|
||||||
Return,
|
|
||||||
Let,
|
|
||||||
Defer,
|
|
||||||
|
|
||||||
// Declaration
|
|
||||||
Func,
|
|
||||||
Struct,
|
|
||||||
Enum,
|
|
||||||
Import,
|
|
||||||
Module,
|
|
||||||
|
|
||||||
// Modifier
|
|
||||||
Extern,
|
|
||||||
Export,
|
|
||||||
|
|
||||||
Colon,
|
|
||||||
DoubleColon,
|
|
||||||
OpenParen,
|
|
||||||
CloseParen,
|
|
||||||
OpenBrace,
|
|
||||||
CloseBrace,
|
|
||||||
OpenBracket,
|
|
||||||
CloseBracket,
|
|
||||||
Comma,
|
|
||||||
Period,
|
|
||||||
Assign,
|
|
||||||
Bang,
|
|
||||||
Equal,
|
|
||||||
NotEqual,
|
|
||||||
LessThan,
|
|
||||||
LessThanOrEqual,
|
|
||||||
GreaterThan,
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
Plus,
|
|
||||||
Minus,
|
|
||||||
Star,
|
|
||||||
ForwardSlash,
|
|
||||||
Caret,
|
|
||||||
Ampersand,
|
|
||||||
Semi,
|
|
||||||
Percent,
|
|
||||||
LeftShift,
|
|
||||||
RightShift,
|
|
||||||
Pipe,
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
At,
|
|
||||||
QuestionMark,
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract record Token(SourceSpan Span);
|
|
||||||
|
|
||||||
public record IdentifierToken(SourceSpan Span, string Value) : Token(Span);
|
|
||||||
|
|
||||||
public record IntLiteralToken(SourceSpan Span, string Value, int Base) : Token(Span);
|
|
||||||
|
|
||||||
public record StringLiteralToken(SourceSpan Span, string Value) : Token(Span);
|
|
||||||
|
|
||||||
public record BoolLiteralToken(SourceSpan Span, bool Value) : Token(Span);
|
|
||||||
|
|
||||||
public record FloatLiteralToken(SourceSpan Span, string Value) : Token(Span);
|
|
||||||
|
|
||||||
public record SymbolToken(SourceSpan Span, Symbol Symbol) : Token(Span);
|
|
||||||
@@ -1,331 +0,0 @@
|
|||||||
using NubLang.Diagnostics;
|
|
||||||
|
|
||||||
namespace NubLang.Syntax;
|
|
||||||
|
|
||||||
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 _content;
|
|
||||||
private int _index = 0;
|
|
||||||
private int _line = 1;
|
|
||||||
private int _column = 1;
|
|
||||||
|
|
||||||
public Tokenizer(string fileName, string content)
|
|
||||||
{
|
|
||||||
_fileName = fileName;
|
|
||||||
_content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Diagnostic> Diagnostics { get; } = [];
|
|
||||||
public List<Token> Tokens { get; } = [];
|
|
||||||
|
|
||||||
public void Tokenize()
|
|
||||||
{
|
|
||||||
Diagnostics.Clear();
|
|
||||||
Tokens.Clear();
|
|
||||||
_index = 0;
|
|
||||||
_line = 1;
|
|
||||||
_column = 1;
|
|
||||||
|
|
||||||
while (Peek().HasValue)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var current = Peek()!.Value;
|
|
||||||
if (char.IsWhiteSpace(current))
|
|
||||||
{
|
|
||||||
if (current is '\n')
|
|
||||||
{
|
|
||||||
_line += 1;
|
|
||||||
// note(nub31): Next increments the column, so 0 is correct here
|
|
||||||
_column = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Next();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current == '/' && Peek(1) == '/')
|
|
||||||
{
|
|
||||||
// note(nub31): Keep newline so next iteration increments the line counter
|
|
||||||
while (Peek() is not '\n')
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tokens.Add(ParseToken(current, _line, _column));
|
|
||||||
}
|
|
||||||
catch (TokenizerException e)
|
|
||||||
{
|
|
||||||
Diagnostics.Add(e.Diagnostic);
|
|
||||||
Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
var buffer = string.Empty;
|
|
||||||
|
|
||||||
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 == '"')
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
var buffer = string.Empty;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var next = Peek();
|
|
||||||
if (!next.HasValue)
|
|
||||||
{
|
|
||||||
throw new TokenizerException(Diagnostic
|
|
||||||
.Error("Unclosed string literal")
|
|
||||||
.At(_fileName, _line, _column)
|
|
||||||
.Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next is '\n')
|
|
||||||
{
|
|
||||||
_line += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next is '"')
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer += next;
|
|
||||||
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);
|
|
||||||
if (!c.HasValue || c.Value != pattern[i]) break;
|
|
||||||
|
|
||||||
if (i == pattern.Length - 1)
|
|
||||||
{
|
|
||||||
for (var j = 0; j <= i; j++)
|
|
||||||
{
|
|
||||||
Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SymbolToken(CreateSpan(lineStart, columnStart), symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TokenizerException(Diagnostic.Error($"Unknown token '{current}'").Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private SourceSpan CreateSpan(int lineStart, int columnStart)
|
|
||||||
{
|
|
||||||
return new SourceSpan(_fileName, new SourceLocation(lineStart, columnStart), new SourceLocation(_line, _column));
|
|
||||||
}
|
|
||||||
|
|
||||||
private char? Peek(int offset = 0)
|
|
||||||
{
|
|
||||||
if (_index + offset < _content.Length)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace NubLang;
|
|
||||||
|
|
||||||
public readonly struct Variant<T1, T2> where T1 : notnull where T2 : notnull
|
|
||||||
{
|
|
||||||
public Variant()
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Variant must be initialized with a value");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Variant(T1 value)
|
|
||||||
{
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Variant(T2 value)
|
|
||||||
{
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly object _value;
|
|
||||||
|
|
||||||
public void Match(Action<T1> on1, Action<T2> on2)
|
|
||||||
{
|
|
||||||
switch (_value)
|
|
||||||
{
|
|
||||||
case T1 v1:
|
|
||||||
on1(v1);
|
|
||||||
break;
|
|
||||||
case T2 v2:
|
|
||||||
on2(v2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidCastException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Match<T>(Func<T1, T> on1, Func<T2, T> on2)
|
|
||||||
{
|
|
||||||
return _value switch
|
|
||||||
{
|
|
||||||
T1 v1 => on1(v1),
|
|
||||||
T2 v2 => on2(v2),
|
|
||||||
_ => throw new InvalidCastException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCase1([NotNullWhen(true)] out T1? value)
|
|
||||||
{
|
|
||||||
if (_value is T1 converted)
|
|
||||||
{
|
|
||||||
value = converted;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCase2([NotNullWhen(true)] out T2? value)
|
|
||||||
{
|
|
||||||
if (_value is T2 converted)
|
|
||||||
{
|
|
||||||
value = converted;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator Variant<T1, T2>(T1 value) => new(value);
|
|
||||||
public static implicit operator Variant<T1, T2>(T2 value) => new(value);
|
|
||||||
}
|
|
||||||
165
compiler/NubType.cs
Normal file
165
compiler/NubType.cs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
namespace Compiler;
|
||||||
|
|
||||||
|
public abstract class NubType
|
||||||
|
{
|
||||||
|
public abstract override string ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeVoid : NubType
|
||||||
|
{
|
||||||
|
public static readonly NubTypeVoid Instance = new();
|
||||||
|
|
||||||
|
private NubTypeVoid()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => "void";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeUInt : NubType
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<int, NubTypeUInt> Cache = new();
|
||||||
|
|
||||||
|
public static NubTypeUInt Get(int width)
|
||||||
|
{
|
||||||
|
if (!Cache.TryGetValue(width, out var type))
|
||||||
|
Cache[width] = type = new NubTypeUInt(width);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Width { get; }
|
||||||
|
|
||||||
|
private NubTypeUInt(int width)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"u{Width}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeSInt : NubType
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<int, NubTypeSInt> Cache = new();
|
||||||
|
|
||||||
|
public static NubTypeSInt Get(int width)
|
||||||
|
{
|
||||||
|
if (!Cache.TryGetValue(width, out var type))
|
||||||
|
Cache[width] = type = new NubTypeSInt(width);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Width { get; }
|
||||||
|
|
||||||
|
private NubTypeSInt(int width)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"i{Width}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeBool : NubType
|
||||||
|
{
|
||||||
|
public static readonly NubTypeBool Instance = new();
|
||||||
|
|
||||||
|
private NubTypeBool()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => "bool";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeString : NubType
|
||||||
|
{
|
||||||
|
public static readonly NubTypeString Instance = new();
|
||||||
|
|
||||||
|
private NubTypeString()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeStruct : NubType
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
public string Module { get; }
|
||||||
|
|
||||||
|
private IReadOnlyList<Field>? _resolvedFields;
|
||||||
|
|
||||||
|
public IReadOnlyList<Field> Fields => _resolvedFields ?? throw new InvalidOperationException();
|
||||||
|
|
||||||
|
public NubTypeStruct(string module, string name)
|
||||||
|
{
|
||||||
|
Module = module;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResolveFields(IReadOnlyList<Field> fields)
|
||||||
|
{
|
||||||
|
if (_resolvedFields != null)
|
||||||
|
throw new InvalidOperationException($"{Name} already resolved");
|
||||||
|
|
||||||
|
_resolvedFields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => _resolvedFields == null ? $"struct {Module}::{Name} <unresolved>" : $"struct {Module}::{Name} {{ {string.Join(' ', Fields.Select(f => $"{f.Name}: {f.Type}"))} }}";
|
||||||
|
|
||||||
|
public sealed class Field(string name, NubType type)
|
||||||
|
{
|
||||||
|
public string Name { get; } = name;
|
||||||
|
public NubType Type { get; } = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypePointer : NubType
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<NubType, NubTypePointer> Cache = new();
|
||||||
|
|
||||||
|
public static NubTypePointer Get(NubType to)
|
||||||
|
{
|
||||||
|
if (!Cache.TryGetValue(to, out var ptr))
|
||||||
|
Cache[to] = ptr = new NubTypePointer(to);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NubType To { get; }
|
||||||
|
|
||||||
|
private NubTypePointer(NubType to)
|
||||||
|
{
|
||||||
|
To = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"^{To}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class NubTypeFunc : NubType
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<Signature, NubTypeFunc> Cache = new();
|
||||||
|
|
||||||
|
public static NubTypeFunc Get(List<NubType> parameters, NubType returnType)
|
||||||
|
{
|
||||||
|
var sig = new Signature(parameters, returnType);
|
||||||
|
|
||||||
|
if (!Cache.TryGetValue(sig, out var func))
|
||||||
|
Cache[sig] = func = new NubTypeFunc(parameters, returnType);
|
||||||
|
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<NubType> Parameters { get; }
|
||||||
|
public NubType ReturnType { get; }
|
||||||
|
|
||||||
|
private NubTypeFunc(List<NubType> parameters, NubType returnType)
|
||||||
|
{
|
||||||
|
Parameters = parameters;
|
||||||
|
ReturnType = returnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}";
|
||||||
|
|
||||||
|
private readonly record struct Signature(IReadOnlyList<NubType> Parameters, NubType ReturnType);
|
||||||
|
}
|
||||||
@@ -13,26 +13,14 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
|
|
||||||
private Ast Parse(out List<Diagnostic> diagnostics)
|
private Ast Parse(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var functionDefinitions = new List<NodeDefinitionFunc>();
|
var definitions = new List<NodeDefinition>();
|
||||||
var structDefinitions = new List<NodeDefinitionStruct>();
|
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (Peek() != null)
|
while (Peek() != null)
|
||||||
{
|
{
|
||||||
var definition = ParseDefinition();
|
definitions.Add(ParseDefinition());
|
||||||
switch (definition)
|
|
||||||
{
|
|
||||||
case NodeDefinitionFunc def:
|
|
||||||
functionDefinitions.Add(def);
|
|
||||||
break;
|
|
||||||
case NodeDefinitionStruct def:
|
|
||||||
structDefinitions.Add(def);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(definition));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (CompileException e)
|
catch (CompileException e)
|
||||||
@@ -40,7 +28,7 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
diagnostics.Add(e.Diagnostic);
|
diagnostics.Add(e.Diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Ast(structDefinitions, functionDefinitions);
|
return new Ast(definitions, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeDefinition ParseDefinition()
|
private NodeDefinition ParseDefinition()
|
||||||
@@ -88,6 +76,12 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
|
return new NodeDefinitionStruct(TokensFrom(startIndex), name, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TryExpectKeyword(Keyword.Module))
|
||||||
|
{
|
||||||
|
var name = ExpectIdent();
|
||||||
|
return new NodeDefinitionModule(TokensFrom(startIndex), name);
|
||||||
|
}
|
||||||
|
|
||||||
throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build());
|
throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,11 +238,22 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
}
|
}
|
||||||
else if (TryExpectIdent(out var ident))
|
else if (TryExpectIdent(out var ident))
|
||||||
{
|
{
|
||||||
expr = new NodeExpressionIdent(TokensFrom(startIndex), ident);
|
if (TryExpectSymbol(Symbol.ColonColon))
|
||||||
|
{
|
||||||
|
var name = ExpectIdent();
|
||||||
|
expr = new NodeExpressionModuleIdent(TokensFrom(startIndex), ident, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
expr = new NodeExpressionLocalIdent(TokensFrom(startIndex), ident);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (TryExpectKeyword(Keyword.Struct))
|
else if (TryExpectKeyword(Keyword.Struct))
|
||||||
{
|
{
|
||||||
|
var module = ExpectIdent();
|
||||||
|
ExpectSymbol(Symbol.ColonColon);
|
||||||
var name = ExpectIdent();
|
var name = ExpectIdent();
|
||||||
|
|
||||||
var initializers = new List<NodeExpressionStructLiteral.Initializer>();
|
var initializers = new List<NodeExpressionStructLiteral.Initializer>();
|
||||||
|
|
||||||
ExpectSymbol(Symbol.OpenCurly);
|
ExpectSymbol(Symbol.OpenCurly);
|
||||||
@@ -261,7 +266,7 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue));
|
initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), name, initializers);
|
expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), module, name, initializers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -330,7 +335,9 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
case "u64":
|
case "u64":
|
||||||
return new NodeTypeUInt(TokensFrom(startIndex), 64);
|
return new NodeTypeUInt(TokensFrom(startIndex), 64);
|
||||||
default:
|
default:
|
||||||
return new NodeTypeCustom(TokensFrom(startIndex), ident);
|
ExpectSymbol(Symbol.ColonColon);
|
||||||
|
var name = ExpectIdent();
|
||||||
|
return new NodeTypeCustom(TokensFrom(startIndex), ident, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +349,17 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
return tokens.GetRange(startIndex, index - startIndex);
|
return tokens.GetRange(startIndex, index - startIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExpectKeyword(Keyword keyword)
|
||||||
|
{
|
||||||
|
if (Peek() is TokenKeyword token && token.Keyword == keyword)
|
||||||
|
{
|
||||||
|
Consume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CompileException(Diagnostic.Error($"Expected '{keyword.AsString()}'").At(fileName, Peek()).Build());
|
||||||
|
}
|
||||||
|
|
||||||
private bool TryExpectKeyword(Keyword keyword)
|
private bool TryExpectKeyword(Keyword keyword)
|
||||||
{
|
{
|
||||||
if (Peek() is TokenKeyword token && token.Keyword == keyword)
|
if (Peek() is TokenKeyword token && token.Keyword == keyword)
|
||||||
@@ -516,10 +534,10 @@ public sealed class Parser(string fileName, List<Token> tokens)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Ast(List<NodeDefinitionStruct> structs, List<NodeDefinitionFunc> functions)
|
public sealed class Ast(List<NodeDefinition> definitions, string fileName)
|
||||||
{
|
{
|
||||||
public readonly List<NodeDefinitionStruct> Structs = structs;
|
public string FileName = fileName;
|
||||||
public readonly List<NodeDefinitionFunc> Functions = functions;
|
public readonly List<NodeDefinition> Definitions = definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class Node(List<Token> tokens)
|
public abstract class Node(List<Token> tokens)
|
||||||
@@ -529,6 +547,11 @@ public abstract class Node(List<Token> tokens)
|
|||||||
|
|
||||||
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
|
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
|
||||||
|
|
||||||
|
public sealed class NodeDefinitionModule(List<Token> tokens, TokenIdent name) : NodeDefinition(tokens)
|
||||||
|
{
|
||||||
|
public readonly TokenIdent Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
|
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
|
||||||
{
|
{
|
||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
@@ -616,8 +639,9 @@ public sealed class NodeExpressionBoolLiteral(List<Token> tokens, TokenBoolLiter
|
|||||||
public readonly TokenBoolLiteral Value = value;
|
public readonly TokenBoolLiteral Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class NodeExpressionStructLiteral(List<Token> tokens, TokenIdent name, List<NodeExpressionStructLiteral.Initializer> initializers) : NodeExpression(tokens)
|
public sealed class NodeExpressionStructLiteral(List<Token> tokens, TokenIdent module, TokenIdent name, List<NodeExpressionStructLiteral.Initializer> initializers) : NodeExpression(tokens)
|
||||||
{
|
{
|
||||||
|
public readonly TokenIdent Module = module;
|
||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
public readonly List<Initializer> Initializers = initializers;
|
public readonly List<Initializer> Initializers = initializers;
|
||||||
|
|
||||||
@@ -634,11 +658,17 @@ public sealed class NodeExpressionMemberAccess(List<Token> tokens, NodeExpressio
|
|||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class NodeExpressionIdent(List<Token> tokens, TokenIdent value) : NodeExpression(tokens)
|
public sealed class NodeExpressionLocalIdent(List<Token> tokens, TokenIdent value) : NodeExpression(tokens)
|
||||||
{
|
{
|
||||||
public readonly TokenIdent Value = value;
|
public readonly TokenIdent Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class NodeExpressionModuleIdent(List<Token> tokens, TokenIdent module, TokenIdent value) : NodeExpression(tokens)
|
||||||
|
{
|
||||||
|
public readonly TokenIdent Module = module;
|
||||||
|
public readonly TokenIdent Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class NodeExpressionBinary(List<Token> tokens, NodeExpression left, NodeExpressionBinary.Op operation, NodeExpression right) : NodeExpression(tokens)
|
public sealed class NodeExpressionBinary(List<Token> tokens, NodeExpression left, NodeExpressionBinary.Op operation, NodeExpression right) : NodeExpression(tokens)
|
||||||
{
|
{
|
||||||
public readonly NodeExpression Left = left;
|
public readonly NodeExpression Left = left;
|
||||||
@@ -702,8 +732,9 @@ public sealed class NodeTypeBool(List<Token> tokens) : NodeType(tokens);
|
|||||||
|
|
||||||
public sealed class NodeTypeString(List<Token> tokens) : NodeType(tokens);
|
public sealed class NodeTypeString(List<Token> tokens) : NodeType(tokens);
|
||||||
|
|
||||||
public sealed class NodeTypeCustom(List<Token> tokens, TokenIdent name) : NodeType(tokens)
|
public sealed class NodeTypeCustom(List<Token> tokens, TokenIdent module, TokenIdent name) : NodeType(tokens)
|
||||||
{
|
{
|
||||||
|
public readonly TokenIdent Module = module;
|
||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
66
compiler/Program.cs
Normal file
66
compiler/Program.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Compiler;
|
||||||
|
|
||||||
|
var moduleGraphBuilder = ModuleGraph.Create();
|
||||||
|
var asts = new List<Ast>();
|
||||||
|
|
||||||
|
foreach (var fileName in args)
|
||||||
|
{
|
||||||
|
var file = File.ReadAllText(fileName);
|
||||||
|
|
||||||
|
var tokens = Tokenizer.Tokenize(fileName, file, out var tokenizerDiagnostics);
|
||||||
|
|
||||||
|
foreach (var diagnostic in tokenizerDiagnostics)
|
||||||
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
|
||||||
|
if (tokenizerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
var ast = Parser.Parse(fileName, tokens, out var parserDiagnostics);
|
||||||
|
|
||||||
|
foreach (var diagnostic in parserDiagnostics)
|
||||||
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
|
||||||
|
if (parserDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
moduleGraphBuilder.AddAst(ast);
|
||||||
|
asts.Add(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
var moduleGraph = moduleGraphBuilder.Build(out var moduleGraphDiagnostics);
|
||||||
|
|
||||||
|
foreach (var diagnostic in moduleGraphDiagnostics)
|
||||||
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
|
||||||
|
if (moduleGraphDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
var functions = new List<TypedNodeDefinitionFunc>();
|
||||||
|
|
||||||
|
foreach (var ast in asts)
|
||||||
|
{
|
||||||
|
foreach (var func in ast.Definitions.OfType<NodeDefinitionFunc>())
|
||||||
|
{
|
||||||
|
var typedFunction = TypeChecker.CheckFunction(ast.FileName, func, moduleGraph, out var typeCheckerDiagnostics);
|
||||||
|
|
||||||
|
foreach (var diagnostic in typeCheckerDiagnostics)
|
||||||
|
DiagnosticFormatter.Print(diagnostic, Console.Error);
|
||||||
|
|
||||||
|
if (typedFunction == null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
functions.Add(typedFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = Generator.Emit(functions, moduleGraph);
|
||||||
|
|
||||||
|
Directory.Delete(".build", recursive: true);
|
||||||
|
Directory.CreateDirectory(".build");
|
||||||
|
|
||||||
|
File.WriteAllText(".build/out.c", output);
|
||||||
|
|
||||||
|
Process.Start("gcc", ["-Og", "-g", "-o", ".build/out", ".build/out.c"]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
@@ -177,6 +177,12 @@ public sealed class Tokenizer(string fileName, string contents)
|
|||||||
Consume();
|
Consume();
|
||||||
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period);
|
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.Period);
|
||||||
}
|
}
|
||||||
|
case ':' when Peek(1) is ':':
|
||||||
|
{
|
||||||
|
Consume();
|
||||||
|
Consume();
|
||||||
|
return new TokenSymbol(line, startColumn, column - startColumn, Symbol.ColonColon);
|
||||||
|
}
|
||||||
case ':':
|
case ':':
|
||||||
{
|
{
|
||||||
Consume();
|
Consume();
|
||||||
@@ -340,6 +346,7 @@ public sealed class Tokenizer(string fileName, string contents)
|
|||||||
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
|
"else" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Else),
|
||||||
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
|
"while" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.While),
|
||||||
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
|
"return" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Return),
|
||||||
|
"module" => new TokenKeyword(line, startColumn, column - startColumn, Keyword.Module),
|
||||||
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
|
"true" => new TokenBoolLiteral(line, startColumn, column - startColumn, true),
|
||||||
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
|
"false" => new TokenBoolLiteral(line, startColumn, column - startColumn, false),
|
||||||
_ => new TokenIdent(line, startColumn, column - startColumn, value)
|
_ => new TokenIdent(line, startColumn, column - startColumn, value)
|
||||||
@@ -430,6 +437,7 @@ public enum Symbol
|
|||||||
Comma,
|
Comma,
|
||||||
Period,
|
Period,
|
||||||
Colon,
|
Colon,
|
||||||
|
ColonColon,
|
||||||
Caret,
|
Caret,
|
||||||
Bang,
|
Bang,
|
||||||
Equal,
|
Equal,
|
||||||
@@ -471,6 +479,7 @@ public enum Keyword
|
|||||||
Else,
|
Else,
|
||||||
While,
|
While,
|
||||||
Return,
|
Return,
|
||||||
|
Module,
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
public sealed class TokenKeyword(int line, int column, int length, Keyword keyword) : Token(line, column, length)
|
||||||
@@ -491,6 +500,7 @@ public static class TokenExtensions
|
|||||||
Symbol.Comma => ",",
|
Symbol.Comma => ",",
|
||||||
Symbol.Period => ",",
|
Symbol.Period => ",",
|
||||||
Symbol.Colon => ":",
|
Symbol.Colon => ":",
|
||||||
|
Symbol.ColonColon => "::",
|
||||||
Symbol.Caret => "^",
|
Symbol.Caret => "^",
|
||||||
Symbol.Bang => "!",
|
Symbol.Bang => "!",
|
||||||
Symbol.Equal => "=",
|
Symbol.Equal => "=",
|
||||||
@@ -531,6 +541,7 @@ public static class TokenExtensions
|
|||||||
Keyword.Else => "else",
|
Keyword.Else => "else",
|
||||||
Keyword.While => "while",
|
Keyword.While => "while",
|
||||||
Keyword.Return => "return",
|
Keyword.Return => "return",
|
||||||
|
Keyword.Module => "module",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,56 +1,63 @@
|
|||||||
namespace Compiler;
|
namespace Compiler;
|
||||||
|
|
||||||
public sealed class TypeChecker(string fileName, Ast ast)
|
public sealed class TypeChecker(string fileName, NodeDefinitionFunc function, ModuleGraph moduleGraph)
|
||||||
{
|
{
|
||||||
public static TypedAst Check(string fileName, Ast ast, out List<Diagnostic> diagnostics)
|
public static TypedNodeDefinitionFunc? CheckFunction(string fileName, NodeDefinitionFunc function, ModuleGraph moduleGraph, out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
return new TypeChecker(fileName, ast).Check(out diagnostics);
|
return new TypeChecker(fileName, function, moduleGraph).CheckFunction(out diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Scope scope = new(null);
|
private readonly Scope scope = new(null);
|
||||||
private Dictionary<string, NubTypeStruct> structTypes = new();
|
|
||||||
|
|
||||||
private TypedAst Check(out List<Diagnostic> diagnostics)
|
private TypedNodeDefinitionFunc? CheckFunction(out List<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
var functions = new List<TypedNodeDefinitionFunc>();
|
|
||||||
diagnostics = [];
|
diagnostics = [];
|
||||||
|
|
||||||
// todo(nub31): Types must be resolved better to prevent circular dependencies and independent ordering
|
var parameters = new List<TypedNodeDefinitionFunc.Param>();
|
||||||
foreach (var structDef in ast.Structs)
|
var invalidParameter = false;
|
||||||
{
|
TypedNodeStatement? body = null;
|
||||||
var fields = structDef.Fields.Select(x => new NubTypeStruct.Field(x.Name.Ident, CheckType(x.Type))).ToList();
|
NubType? returnType = null;
|
||||||
structTypes.Add(structDef.Name.Ident, new NubTypeStruct(fields));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var funcDef in ast.Functions)
|
foreach (var parameter in function.Parameters)
|
||||||
{
|
|
||||||
var type = new NubTypeFunc(funcDef.Parameters.Select(x => CheckType(x.Type)).ToList(), CheckType(funcDef.ReturnType));
|
|
||||||
scope.DeclareIdentifier(funcDef.Name.Ident, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var funcDef in ast.Functions)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
functions.Add(CheckDefinitionFunc(funcDef));
|
parameters.Add(CheckDefinitionFuncParameter(parameter));
|
||||||
}
|
}
|
||||||
catch (CompileException e)
|
catch (CompileException e)
|
||||||
{
|
{
|
||||||
diagnostics.Add(e.Diagnostic);
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
invalidParameter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TypedAst(structTypes.Values.ToList(), functions);
|
try
|
||||||
}
|
{
|
||||||
|
body = CheckStatement(function.Body);
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
private TypedNodeDefinitionFunc CheckDefinitionFunc(NodeDefinitionFunc definition)
|
try
|
||||||
{
|
{
|
||||||
return new TypedNodeDefinitionFunc(definition.Tokens, definition.Name, definition.Parameters.Select(CheckDefinitionFuncParameter).ToList(), CheckStatement(definition.Body), CheckType(definition.ReturnType));
|
returnType = ResolveType(function.ReturnType);
|
||||||
|
}
|
||||||
|
catch (CompileException e)
|
||||||
|
{
|
||||||
|
diagnostics.Add(e.Diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body == null || returnType == null || invalidParameter)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new TypedNodeDefinitionFunc(function.Tokens, function.Name, parameters, body, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
|
||||||
{
|
{
|
||||||
return new TypedNodeDefinitionFunc.Param(node.Tokens, node.Name, CheckType(node.Type));
|
return new TypedNodeDefinitionFunc.Param(node.Tokens, node.Name, ResolveType(node.Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeStatement CheckStatement(NodeStatement node)
|
private TypedNodeStatement CheckStatement(NodeStatement node)
|
||||||
@@ -95,7 +102,7 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
|
|
||||||
private TypedNodeStatementVariableDeclaration CheckStatementVariableDeclaration(NodeStatementVariableDeclaration statement)
|
private TypedNodeStatementVariableDeclaration CheckStatementVariableDeclaration(NodeStatementVariableDeclaration statement)
|
||||||
{
|
{
|
||||||
var type = CheckType(statement.Type);
|
var type = ResolveType(statement.Type);
|
||||||
var value = CheckExpression(statement.Value);
|
var value = CheckExpression(statement.Value);
|
||||||
|
|
||||||
if (type != value.Type)
|
if (type != value.Type)
|
||||||
@@ -118,7 +125,8 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
NodeExpressionBinary expression => CheckExpressionBinary(expression),
|
NodeExpressionBinary expression => CheckExpressionBinary(expression),
|
||||||
NodeExpressionUnary expression => CheckExpressionUnary(expression),
|
NodeExpressionUnary expression => CheckExpressionUnary(expression),
|
||||||
NodeExpressionBoolLiteral expression => CheckExpressionBoolLiteral(expression),
|
NodeExpressionBoolLiteral expression => CheckExpressionBoolLiteral(expression),
|
||||||
NodeExpressionIdent expression => CheckExpressionIdent(expression),
|
NodeExpressionLocalIdent expression => CheckExpressionIdent(expression),
|
||||||
|
NodeExpressionModuleIdent expression => CheckExpressionModuleIdent(expression),
|
||||||
NodeExpressionIntLiteral expression => CheckExpressionIntLiteral(expression),
|
NodeExpressionIntLiteral expression => CheckExpressionIntLiteral(expression),
|
||||||
NodeExpressionMemberAccess expression => CheckExpressionMemberAccess(expression),
|
NodeExpressionMemberAccess expression => CheckExpressionMemberAccess(expression),
|
||||||
NodeExpressionStringLiteral expression => CheckExpressionStringLiteral(expression),
|
NodeExpressionStringLiteral expression => CheckExpressionStringLiteral(expression),
|
||||||
@@ -175,7 +183,7 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
if (right.Type is not NubTypeSInt and not NubTypeUInt)
|
if (right.Type is not NubTypeSInt and not NubTypeUInt)
|
||||||
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of comparison: {right.Type}").At(fileName, right).Build());
|
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of comparison: {right.Type}").At(fileName, right).Build());
|
||||||
|
|
||||||
type = new NubTypeBool();
|
type = NubTypeBool.Instance;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NodeExpressionBinary.Op.LogicalAnd:
|
case NodeExpressionBinary.Op.LogicalAnd:
|
||||||
@@ -187,7 +195,7 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
if (right.Type is not NubTypeBool)
|
if (right.Type is not NubTypeBool)
|
||||||
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of logical operation: {right.Type}").At(fileName, right).Build());
|
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of logical operation: {right.Type}").At(fileName, right).Build());
|
||||||
|
|
||||||
type = new NubTypeBool();
|
type = NubTypeBool.Instance;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -240,7 +248,7 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
if (target.Type is not NubTypeBool)
|
if (target.Type is not NubTypeBool)
|
||||||
throw new CompileException(Diagnostic.Error($"Unsupported type for inversion: {target.Type}").At(fileName, target).Build());
|
throw new CompileException(Diagnostic.Error($"Unsupported type for inversion: {target.Type}").At(fileName, target).Build());
|
||||||
|
|
||||||
type = new NubTypeBool();
|
type = NubTypeBool.Instance;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -262,21 +270,32 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
|
|
||||||
private TypedNodeExpressionBoolLiteral CheckExpressionBoolLiteral(NodeExpressionBoolLiteral expression)
|
private TypedNodeExpressionBoolLiteral CheckExpressionBoolLiteral(NodeExpressionBoolLiteral expression)
|
||||||
{
|
{
|
||||||
return new TypedNodeExpressionBoolLiteral(expression.Tokens, new NubTypeBool(), expression.Value);
|
return new TypedNodeExpressionBoolLiteral(expression.Tokens, NubTypeBool.Instance, expression.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeExpressionIdent CheckExpressionIdent(NodeExpressionIdent expression)
|
private TypedNodeExpressionLocalIdent CheckExpressionIdent(NodeExpressionLocalIdent expression)
|
||||||
{
|
{
|
||||||
var type = scope.GetIdentifierType(expression.Value.Ident);
|
var type = scope.GetIdentifierType(expression.Value.Ident);
|
||||||
if (type == null)
|
if (type == null)
|
||||||
throw new CompileException(Diagnostic.Error($"Identifier '{expression.Value.Ident}' is not declared").At(fileName, expression.Value).Build());
|
throw new CompileException(Diagnostic.Error($"Identifier '{expression.Value.Ident}' is not declared").At(fileName, expression.Value).Build());
|
||||||
|
|
||||||
return new TypedNodeExpressionIdent(expression.Tokens, type, expression.Value);
|
return new TypedNodeExpressionLocalIdent(expression.Tokens, type, expression.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypedNodeExpressionModuleIdent CheckExpressionModuleIdent(NodeExpressionModuleIdent expression)
|
||||||
|
{
|
||||||
|
if (!moduleGraph.TryResolveModule(expression.Module.Ident, out var module))
|
||||||
|
throw new CompileException(Diagnostic.Error($"Module '{expression.Module.Ident}' not found").At(fileName, expression.Module).Build());
|
||||||
|
|
||||||
|
if (!module.TryResolveIdentifierType(expression.Value.Ident, out var identifierType))
|
||||||
|
throw new CompileException(Diagnostic.Error($"Identifier '{expression.Module.Ident}::{expression.Value.Ident}' not found").At(fileName, expression.Value).Build());
|
||||||
|
|
||||||
|
return new TypedNodeExpressionModuleIdent(expression.Tokens, identifierType, expression.Module, expression.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeExpressionIntLiteral CheckExpressionIntLiteral(NodeExpressionIntLiteral expression)
|
private TypedNodeExpressionIntLiteral CheckExpressionIntLiteral(NodeExpressionIntLiteral expression)
|
||||||
{
|
{
|
||||||
return new TypedNodeExpressionIntLiteral(expression.Tokens, new NubTypeSInt(32), expression.Value);
|
return new TypedNodeExpressionIntLiteral(expression.Tokens, NubTypeSInt.Get(32), expression.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeExpressionMemberAccess CheckExpressionMemberAccess(NodeExpressionMemberAccess expression)
|
private TypedNodeExpressionMemberAccess CheckExpressionMemberAccess(NodeExpressionMemberAccess expression)
|
||||||
@@ -287,26 +306,31 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
|
|
||||||
var field = structType.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
|
var field = structType.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
|
||||||
if (field == null)
|
if (field == null)
|
||||||
throw new CompileException(Diagnostic.Error($"Struct {target.Type} does not have a field matching the name '{expression.Name.Ident}'").At(fileName, target).Build());
|
throw new CompileException(Diagnostic.Error($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'").At(fileName, target).Build());
|
||||||
|
|
||||||
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name);
|
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeExpressionStringLiteral CheckExpressionStringLiteral(NodeExpressionStringLiteral expression)
|
private TypedNodeExpressionStringLiteral CheckExpressionStringLiteral(NodeExpressionStringLiteral expression)
|
||||||
{
|
{
|
||||||
return new TypedNodeExpressionStringLiteral(expression.Tokens, new NubTypeString(), expression.Value);
|
return new TypedNodeExpressionStringLiteral(expression.Tokens, NubTypeString.Instance, expression.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedNodeExpressionStructLiteral CheckExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
private TypedNodeExpressionStructLiteral CheckExpressionStructLiteral(NodeExpressionStructLiteral expression)
|
||||||
{
|
{
|
||||||
var type = structTypes.GetValueOrDefault(expression.Name.Ident);
|
if (!moduleGraph.TryResolveModule(expression.Module.Ident, out var module))
|
||||||
if (type == null)
|
throw new CompileException(Diagnostic.Error($"Module '{expression.Module.Ident}' not found").At(fileName, expression.Module).Build());
|
||||||
throw new CompileException(Diagnostic.Error($"Undeclared struct '{expression.Name.Ident}'").At(fileName, expression.Name).Build());
|
|
||||||
|
if (!module.TryResolveCustomType(expression.Name.Ident, out var customType))
|
||||||
|
throw new CompileException(Diagnostic.Error($"Struct '{expression.Module.Ident}::{expression.Name.Ident}' not found").At(fileName, expression.Name).Build());
|
||||||
|
|
||||||
|
if (customType is not NubTypeStruct structType)
|
||||||
|
throw new CompileException(Diagnostic.Error($"Cannot create struct literal of non-struct type '{expression.Module.Ident}::{expression.Name.Ident}'").At(fileName, expression.Name).Build());
|
||||||
|
|
||||||
var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>();
|
var initializers = new List<TypedNodeExpressionStructLiteral.Initializer>();
|
||||||
foreach (var initializer in expression.Initializers)
|
foreach (var initializer in expression.Initializers)
|
||||||
{
|
{
|
||||||
var field = type.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
|
var field = structType.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
|
||||||
if (field == null)
|
if (field == null)
|
||||||
throw new CompileException(Diagnostic.Error($"Field '{initializer.Name.Ident}' does not exist on struct '{expression.Name.Ident}'").At(fileName, initializer.Name).Build());
|
throw new CompileException(Diagnostic.Error($"Field '{initializer.Name.Ident}' does not exist on struct '{expression.Name.Ident}'").At(fileName, initializer.Name).Build());
|
||||||
|
|
||||||
@@ -317,37 +341,39 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
initializers.Add(new TypedNodeExpressionStructLiteral.Initializer(initializer.Tokens, initializer.Name, value));
|
initializers.Add(new TypedNodeExpressionStructLiteral.Initializer(initializer.Tokens, initializer.Name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TypedNodeExpressionStructLiteral(expression.Tokens, type, initializers);
|
return new TypedNodeExpressionStructLiteral(expression.Tokens, structType, initializers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NubType CheckType(NodeType node)
|
private NubType ResolveType(NodeType node)
|
||||||
{
|
{
|
||||||
return node switch
|
return node switch
|
||||||
{
|
{
|
||||||
NodeTypeBool type => new NubTypeBool(),
|
NodeTypeBool => NubTypeBool.Instance,
|
||||||
NodeTypeCustom type => CheckStructType(type),
|
NodeTypeCustom type => ResolveCustomType(type),
|
||||||
NodeTypeFunc type => new NubTypeFunc(type.Parameters.Select(CheckType).ToList(), CheckType(type.ReturnType)),
|
NodeTypeFunc type => NubTypeFunc.Get(type.Parameters.Select(ResolveType).ToList(), ResolveType(type.ReturnType)),
|
||||||
NodeTypePointer type => new NubTypePointer(CheckType(type.To)),
|
NodeTypePointer type => NubTypePointer.Get(ResolveType(type.To)),
|
||||||
NodeTypeSInt type => new NubTypeSInt(type.Width),
|
NodeTypeSInt type => NubTypeSInt.Get(type.Width),
|
||||||
NodeTypeUInt type => new NubTypeUInt(type.Width),
|
NodeTypeUInt type => NubTypeUInt.Get(type.Width),
|
||||||
NodeTypeString type => new NubTypeString(),
|
NodeTypeString => NubTypeString.Instance,
|
||||||
NodeTypeVoid type => new NubTypeVoid(),
|
NodeTypeVoid => NubTypeVoid.Instance,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private NubTypeStruct CheckStructType(NodeTypeCustom type)
|
private NubType ResolveCustomType(NodeTypeCustom type)
|
||||||
{
|
{
|
||||||
var structType = structTypes.GetValueOrDefault(type.Name.Ident);
|
if (!moduleGraph.TryResolveModule(type.Module.Ident, out var module))
|
||||||
if (structType == null)
|
throw new CompileException(Diagnostic.Error($"Module '{type.Module.Ident}' not found").At(fileName, type.Module).Build());
|
||||||
throw new CompileException(Diagnostic.Error($"Unknown custom type: {type}").At(fileName, type).Build());
|
|
||||||
|
|
||||||
return structType;
|
if (!module.TryResolveCustomType(type.Name.Ident, out var customType))
|
||||||
|
throw new CompileException(Diagnostic.Error($"Custom type '{type.Module.Ident}::{type.Name.Ident}' not found").At(fileName, type.Name).Build());
|
||||||
|
|
||||||
|
return customType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Scope(Scope? parent)
|
private class Scope(Scope? parent)
|
||||||
{
|
{
|
||||||
private Dictionary<string, NubType> identifiers = new();
|
private readonly Dictionary<string, NubType> identifiers = new();
|
||||||
|
|
||||||
public void DeclareIdentifier(string name, NubType type)
|
public void DeclareIdentifier(string name, NubType type)
|
||||||
{
|
{
|
||||||
@@ -363,12 +389,6 @@ public sealed class TypeChecker(string fileName, Ast ast)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TypedAst(List<NubTypeStruct> structTypes, List<TypedNodeDefinitionFunc> functions)
|
|
||||||
{
|
|
||||||
public readonly List<NubTypeStruct> StructTypes = structTypes;
|
|
||||||
public readonly List<TypedNodeDefinitionFunc> Functions = functions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class TypedNode(List<Token> tokens)
|
public abstract class TypedNode(List<Token> tokens)
|
||||||
{
|
{
|
||||||
public readonly List<Token> Tokens = tokens;
|
public readonly List<Token> Tokens = tokens;
|
||||||
@@ -390,18 +410,6 @@ public sealed class TypedNodeDefinitionFunc(List<Token> tokens, TokenIdent name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TypedNodeDefinitionStruct(List<Token> tokens, TokenIdent name, List<TypedNodeDefinitionStruct.Field> fields) : TypedNodeDefinition(tokens)
|
|
||||||
{
|
|
||||||
public readonly TokenIdent Name = name;
|
|
||||||
public readonly List<Field> Fields = fields;
|
|
||||||
|
|
||||||
public sealed class Field(List<Token> tokens, TokenIdent name, NubType type) : TypedNode(tokens)
|
|
||||||
{
|
|
||||||
public readonly TokenIdent Name = name;
|
|
||||||
public readonly NubType Type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class TypedNodeStatement(List<Token> tokens) : TypedNode(tokens);
|
public abstract class TypedNodeStatement(List<Token> tokens) : TypedNode(tokens);
|
||||||
|
|
||||||
public sealed class TypedNodeStatementBlock(List<Token> tokens, List<TypedNodeStatement> statements) : TypedNodeStatement(tokens)
|
public sealed class TypedNodeStatementBlock(List<Token> tokens, List<TypedNodeStatement> statements) : TypedNodeStatement(tokens)
|
||||||
@@ -483,11 +491,17 @@ public sealed class TypedNodeExpressionMemberAccess(List<Token> tokens, NubType
|
|||||||
public readonly TokenIdent Name = name;
|
public readonly TokenIdent Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TypedNodeExpressionIdent(List<Token> tokens, NubType type, TokenIdent value) : TypedNodeExpression(tokens, type)
|
public sealed class TypedNodeExpressionLocalIdent(List<Token> tokens, NubType type, TokenIdent value) : TypedNodeExpression(tokens, type)
|
||||||
{
|
{
|
||||||
public readonly TokenIdent Value = value;
|
public readonly TokenIdent Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class TypedNodeExpressionModuleIdent(List<Token> tokens, NubType type, TokenIdent module, TokenIdent value) : TypedNodeExpression(tokens, type)
|
||||||
|
{
|
||||||
|
public readonly TokenIdent Module = module;
|
||||||
|
public readonly TokenIdent Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class TypedNodeExpressionBinary(List<Token> tokens, NubType type, TypedNodeExpression left, TypedNodeExpressionBinary.Op operation, TypedNodeExpression right) : TypedNodeExpression(tokens, type)
|
public sealed class TypedNodeExpressionBinary(List<Token> tokens, NubType type, TypedNodeExpression left, TypedNodeExpressionBinary.Op operation, TypedNodeExpression right) : TypedNodeExpression(tokens, type)
|
||||||
{
|
{
|
||||||
public readonly TypedNodeExpression Left = left;
|
public readonly TypedNodeExpression Left = left;
|
||||||
@@ -531,144 +545,4 @@ public sealed class TypedNodeExpressionUnary(List<Token> tokens, NubType type, T
|
|||||||
Negate,
|
Negate,
|
||||||
Invert,
|
Invert,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class NubType : IEquatable<NubType>
|
|
||||||
{
|
|
||||||
public abstract override string ToString();
|
|
||||||
|
|
||||||
public abstract bool Equals(NubType? other);
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
if (obj is NubType otherNubType)
|
|
||||||
{
|
|
||||||
return Equals(otherNubType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract override int GetHashCode();
|
|
||||||
|
|
||||||
public static bool operator ==(NubType? left, NubType? right) => Equals(left, right);
|
|
||||||
public static bool operator !=(NubType? left, NubType? right) => !Equals(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeVoid : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "void";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeVoid;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeVoid));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeUInt(int width) : NubType
|
|
||||||
{
|
|
||||||
public readonly int Width = width;
|
|
||||||
|
|
||||||
public override string ToString() => $"u{Width}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeUInt otherUInt && Width == otherUInt.Width;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeUInt), Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeSInt(int width) : NubType
|
|
||||||
{
|
|
||||||
public readonly int Width = width;
|
|
||||||
|
|
||||||
public override string ToString() => $"i{Width}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeSInt otherUInt && Width == otherUInt.Width;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeSInt), Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeBool : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "bool";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeBool;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeBool));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeString : NubType
|
|
||||||
{
|
|
||||||
public override string ToString() => "string";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeString;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypeString));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeStruct(List<NubTypeStruct.Field> fields) : NubType
|
|
||||||
{
|
|
||||||
public readonly List<Field> Fields = fields;
|
|
||||||
public override string ToString() => $"struct {{ {string.Join(' ', Fields.Select(x => $"{x.Name}: {x.Type}"))} }}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other)
|
|
||||||
{
|
|
||||||
if (other is not NubTypeStruct structType)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (Fields.Count != structType.Fields.Count)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (var i = 0; i < Fields.Count; i++)
|
|
||||||
{
|
|
||||||
if (Fields[i].Name != structType.Fields[i].Name)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (Fields[i].Type != structType.Fields[i].Type)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
var hash = new HashCode();
|
|
||||||
hash.Add(typeof(NubTypeStruct));
|
|
||||||
foreach (var field in Fields)
|
|
||||||
{
|
|
||||||
hash.Add(field.Name);
|
|
||||||
hash.Add(field.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash.ToHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Field(string name, NubType type)
|
|
||||||
{
|
|
||||||
public readonly string Name = name;
|
|
||||||
public readonly NubType Type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypePointer(NubType to) : NubType
|
|
||||||
{
|
|
||||||
public readonly NubType To = to;
|
|
||||||
public override string ToString() => $"^{To}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypePointer pointer && To == pointer.To;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(NubTypePointer));
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NubTypeFunc(List<NubType> parameters, NubType returnType) : NubType
|
|
||||||
{
|
|
||||||
public readonly List<NubType> Parameters = parameters;
|
|
||||||
public readonly NubType ReturnType = returnType;
|
|
||||||
public override string ToString() => $"func({string.Join(' ', Parameters)}): {ReturnType}";
|
|
||||||
|
|
||||||
public override bool Equals(NubType? other) => other is NubTypeFunc func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
var hash = new HashCode();
|
|
||||||
hash.Add(typeof(NubTypeFunc));
|
|
||||||
hash.Add(ReturnType);
|
|
||||||
foreach (var param in Parameters)
|
|
||||||
hash.Add(param);
|
|
||||||
|
|
||||||
return hash.ToHashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
struct person {
|
module main
|
||||||
age: i32
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
func main(): i32 {
|
func main(): i32 {
|
||||||
let x: i32 = 23
|
let x: i32 = 23
|
||||||
@@ -23,11 +20,8 @@ func main(): i32 {
|
|||||||
x = i
|
x = i
|
||||||
}
|
}
|
||||||
|
|
||||||
let me: person = struct person { age = 21 name = "Oliver" }
|
let me: test::person = struct test::person { age = 21 name = "Oliver" }
|
||||||
|
|
||||||
do_something(me.name)
|
test::do_something(me.name)
|
||||||
return x
|
return x
|
||||||
}
|
|
||||||
|
|
||||||
func do_something(text: string): void {
|
|
||||||
}
|
}
|
||||||
9
compiler/test2.nub
Normal file
9
compiler/test2.nub
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module test
|
||||||
|
|
||||||
|
struct person {
|
||||||
|
age: i32
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
func do_something(name: string): void {
|
||||||
|
}
|
||||||
3
examples/.gitignore
vendored
3
examples/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
.build
|
|
||||||
out.a
|
|
||||||
out
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
obj=$(nubc main.nub)
|
|
||||||
clang $obj -o .build/out
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
module "main"
|
|
||||||
|
|
||||||
extern "puts" func puts(text: ^i8)
|
|
||||||
|
|
||||||
extern "main" func main(argc: i64, argv: [?]^i8): i64
|
|
||||||
{
|
|
||||||
puts("Hello, World!")
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
obj=$(nubc main.nub generated/raylib.nub)
|
|
||||||
clang $obj raylib-5.5_linux_amd64/lib/libraylib.a -lm -o .build/out
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
|||||||
import "raylib"
|
|
||||||
|
|
||||||
module "main"
|
|
||||||
|
|
||||||
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::InitWindow(1600, 900, "Hi from nub-lang")
|
|
||||||
defer raylib::CloseWindow()
|
|
||||||
|
|
||||||
raylib::SetTargetFPS(240)
|
|
||||||
|
|
||||||
let width: i32 = 150
|
|
||||||
let height: i32 = 150
|
|
||||||
|
|
||||||
let x: i32 = raylib::GetScreenWidth() / 2 - (width / 2)
|
|
||||||
let y: i32 = raylib::GetScreenHeight() / 2 - (height / 2)
|
|
||||||
|
|
||||||
let direction: raylib::Vector2 = { x = 1 y = 1 }
|
|
||||||
let speed: f32 = 250
|
|
||||||
|
|
||||||
let bgColor: raylib::Color = { r = 0 g = 0 b = 0 a = 255 }
|
|
||||||
let color: raylib::Color = { r = 255 g = 255 b = 255 a = 255 }
|
|
||||||
|
|
||||||
while !raylib::WindowShouldClose()
|
|
||||||
{
|
|
||||||
if x <= 0 {
|
|
||||||
direction.x = 1
|
|
||||||
} else if x + width >= raylib::GetScreenWidth() {
|
|
||||||
direction.x = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if y <= 0 {
|
|
||||||
direction.y = 1
|
|
||||||
} else if y + height >= raylib::GetScreenHeight() {
|
|
||||||
direction.y = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
x = x + @cast(direction.x * speed * raylib::GetFrameTime())
|
|
||||||
y = y + @cast(direction.y * speed * raylib::GetFrameTime())
|
|
||||||
|
|
||||||
raylib::BeginDrawing()
|
|
||||||
{
|
|
||||||
raylib::ClearBackground(bgColor)
|
|
||||||
raylib::DrawFPS(10, 10)
|
|
||||||
raylib::DrawRectangle(x, y, width, height, color)
|
|
||||||
}
|
|
||||||
raylib::EndDrawing()
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
|||||||
Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
|
|
||||||
|
|
||||||
This software is provided "as-is", without any express or implied warranty. In no event
|
|
||||||
will the authors be held liable for any damages arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose, including commercial
|
|
||||||
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not claim that you
|
|
||||||
wrote the original software. If you use this software in a product, an acknowledgment
|
|
||||||
in the product documentation would be appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
|
||||||
as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
<img align="left" style="width:260px" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_logo_animation.gif" width="288px">
|
|
||||||
|
|
||||||
**raylib is a simple and easy-to-use library to enjoy videogames programming.**
|
|
||||||
|
|
||||||
raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's especially well suited for prototyping, tooling, graphical applications, embedded systems and education.
|
|
||||||
|
|
||||||
*NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.*
|
|
||||||
|
|
||||||
Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
[](https://github.com/raysan5/raylib/releases)
|
|
||||||
[](https://github.com/raysan5/raylib/stargazers)
|
|
||||||
[](https://github.com/raysan5/raylib/commits/master)
|
|
||||||
[](https://github.com/sponsors/raysan5)
|
|
||||||
[](https://repology.org/project/raylib/versions)
|
|
||||||
[](LICENSE)
|
|
||||||
|
|
||||||
[](https://discord.gg/raylib)
|
|
||||||
[](https://www.reddit.com/r/raylib/)
|
|
||||||
[](https://www.youtube.com/c/raylib)
|
|
||||||
[](https://www.twitch.tv/raysan5)
|
|
||||||
|
|
||||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows)
|
|
||||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux)
|
|
||||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS)
|
|
||||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly)
|
|
||||||
|
|
||||||
[](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds)
|
|
||||||
[](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml)
|
|
||||||
[](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml)
|
|
||||||
|
|
||||||
features
|
|
||||||
--------
|
|
||||||
- **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external)
|
|
||||||
- Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!**
|
|
||||||
- Written in plain C code (C99) using PascalCase/camelCase notation
|
|
||||||
- Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**)
|
|
||||||
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
|
|
||||||
- Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts)
|
|
||||||
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
|
|
||||||
- **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
|
|
||||||
- Flexible Materials system, supporting classic maps and **PBR maps**
|
|
||||||
- **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF)
|
|
||||||
- Shaders support, including model shaders and **postprocessing** shaders
|
|
||||||
- **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h)
|
|
||||||
- Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD)
|
|
||||||
- **VR stereo rendering** support with configurable HMD device parameters
|
|
||||||
- Huge examples collection with [+140 code examples](https://github.com/raysan5/raylib/tree/master/examples)!
|
|
||||||
- Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
|
|
||||||
- **Free and open source**
|
|
||||||
|
|
||||||
basic example
|
|
||||||
--------------
|
|
||||||
This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window).
|
|
||||||
```c
|
|
||||||
#include "raylib.h"
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
InitWindow(800, 450, "raylib [core] example - basic window");
|
|
||||||
|
|
||||||
while (!WindowShouldClose())
|
|
||||||
{
|
|
||||||
BeginDrawing();
|
|
||||||
ClearBackground(RAYWHITE);
|
|
||||||
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
|
||||||
EndDrawing();
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseWindow();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
build and installation
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
raylib binary releases for Windows, Linux, macOS, Android and HTML5 are available at the [Github Releases page](https://github.com/raysan5/raylib/releases).
|
|
||||||
|
|
||||||
raylib is also available via multiple package managers on multiple OS distributions.
|
|
||||||
|
|
||||||
#### Installing and building raylib on multiple platforms
|
|
||||||
|
|
||||||
[raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms.
|
|
||||||
|
|
||||||
- [Working on Windows](https://github.com/raysan5/raylib/wiki/Working-on-Windows)
|
|
||||||
- [Working on macOS](https://github.com/raysan5/raylib/wiki/Working-on-macOS)
|
|
||||||
- [Working on GNU Linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux)
|
|
||||||
- [Working on Chrome OS](https://github.com/raysan5/raylib/wiki/Working-on-Chrome-OS)
|
|
||||||
- [Working on FreeBSD](https://github.com/raysan5/raylib/wiki/Working-on-FreeBSD)
|
|
||||||
- [Working on Raspberry Pi](https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi)
|
|
||||||
- [Working for Android](https://github.com/raysan5/raylib/wiki/Working-for-Android)
|
|
||||||
- [Working for Web (HTML5)](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5))
|
|
||||||
- [Working anywhere with CMake](https://github.com/raysan5/raylib/wiki/Working-with-CMake)
|
|
||||||
|
|
||||||
*Note that the Wiki is open for edit, if you find some issues while building raylib for your target platform, feel free to edit the Wiki or open an issue related to it.*
|
|
||||||
|
|
||||||
#### Setup raylib with multiple IDEs
|
|
||||||
|
|
||||||
raylib has been developed on Windows platform using [Notepad++](https://notepad-plus-plus.org/) and [MinGW GCC](https://www.mingw-w64.org/) compiler but it can be used with other IDEs on multiple platforms.
|
|
||||||
|
|
||||||
[Projects directory](https://github.com/raysan5/raylib/tree/master/projects) contains several ready-to-use **project templates** to build raylib and code examples with multiple IDEs.
|
|
||||||
|
|
||||||
*Note that there are lots of IDEs supported, some of the provided templates could require some review, so please, if you find some issue with a template or you think they could be improved, feel free to send a PR or open a related issue.*
|
|
||||||
|
|
||||||
learning and docs
|
|
||||||
------------------
|
|
||||||
|
|
||||||
raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
|
|
||||||
|
|
||||||
Some additional documentation about raylib design can be found in [raylib GitHub Wiki](https://github.com/raysan5/raylib/wiki). Here are the relevant links:
|
|
||||||
|
|
||||||
- [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html)
|
|
||||||
- [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture)
|
|
||||||
- [raylib library design](https://github.com/raysan5/raylib/wiki)
|
|
||||||
- [raylib examples collection](https://github.com/raysan5/raylib/tree/master/examples)
|
|
||||||
- [raylib games collection](https://github.com/raysan5/raylib-games)
|
|
||||||
|
|
||||||
|
|
||||||
contact and networks
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
raylib is present in several networks and raylib community is growing everyday. If you are using raylib and enjoying it, feel free to join us in any of these networks. The most active network is our [Discord server](https://discord.gg/raylib)! :)
|
|
||||||
|
|
||||||
- Webpage: [https://www.raylib.com](https://www.raylib.com)
|
|
||||||
- Discord: [https://discord.gg/raylib](https://discord.gg/raylib)
|
|
||||||
- Twitter: [https://www.twitter.com/raysan5](https://www.twitter.com/raysan5)
|
|
||||||
- Twitch: [https://www.twitch.tv/raysan5](https://www.twitch.tv/raysan5)
|
|
||||||
- Reddit: [https://www.reddit.com/r/raylib](https://www.reddit.com/r/raylib)
|
|
||||||
- Patreon: [https://www.patreon.com/raylib](https://www.patreon.com/raylib)
|
|
||||||
- YouTube: [https://www.youtube.com/channel/raylib](https://www.youtube.com/c/raylib)
|
|
||||||
|
|
||||||
contributors
|
|
||||||
------------
|
|
||||||
|
|
||||||
<a href="https://github.com/raysan5/raylib/graphs/contributors">
|
|
||||||
<img src="https://contrib.rocks/image?repo=raysan5/raylib&max=500&columns=20&anon=1" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
license
|
|
||||||
-------
|
|
||||||
|
|
||||||
raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
|
|
||||||
|
|
||||||
raylib uses internally some libraries for window/graphics/inputs management and also to support different file formats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on [raylib Wiki](https://github.com/raysan5/raylib/wiki) for details.
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
libraylib.so.550
|
|
||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
libraylib.so.5.5.0
|
|
||||||
4
vscode-lsp/.gitignore
vendored
4
vscode-lsp/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
node_modules
|
|
||||||
out
|
|
||||||
nub-*.vsix
|
|
||||||
server
|
|
||||||
17
vscode-lsp/.vscode/launch.json
vendored
17
vscode-lsp/.vscode/launch.json
vendored
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Run Extension",
|
|
||||||
"type": "extensionHost",
|
|
||||||
"request": "launch",
|
|
||||||
"args": [
|
|
||||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
|
||||||
],
|
|
||||||
"outFiles": [
|
|
||||||
"${workspaceFolder}/out/**/*.js"
|
|
||||||
],
|
|
||||||
"preLaunchTask": "${defaultBuildTask}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
8
vscode-lsp/.vscode/settings.json
vendored
8
vscode-lsp/.vscode/settings.json
vendored
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"files.exclude": {},
|
|
||||||
"search.exclude": {
|
|
||||||
"out": true,
|
|
||||||
"node_modules": true,
|
|
||||||
},
|
|
||||||
"typescript.tsc.autoDetect": "off"
|
|
||||||
}
|
|
||||||
18
vscode-lsp/.vscode/tasks.json
vendored
18
vscode-lsp/.vscode/tasks.json
vendored
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "watch",
|
|
||||||
"problemMatcher": "$tsc-watch",
|
|
||||||
"isBackground": true,
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "never"
|
|
||||||
},
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
{
|
|
||||||
"comments": {
|
|
||||||
"lineComment": {
|
|
||||||
"comment": "//"
|
|
||||||
},
|
|
||||||
"blockComment": [
|
|
||||||
"/*",
|
|
||||||
"*/"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"brackets": [
|
|
||||||
[
|
|
||||||
"{",
|
|
||||||
"}"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"[",
|
|
||||||
"]"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"(",
|
|
||||||
")"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"autoClosingPairs": [
|
|
||||||
{
|
|
||||||
"open": "{",
|
|
||||||
"close": "}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"open": "[",
|
|
||||||
"close": "]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"open": "(",
|
|
||||||
"close": ")"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"open": "\"",
|
|
||||||
"close": "\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"open": "'",
|
|
||||||
"close": "'"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"surroundingPairs": [
|
|
||||||
[
|
|
||||||
"{",
|
|
||||||
"}"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"[",
|
|
||||||
"]"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"(",
|
|
||||||
")"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"\"",
|
|
||||||
"\""
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"'",
|
|
||||||
"'"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
4811
vscode-lsp/package-lock.json
generated
4811
vscode-lsp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "nub",
|
|
||||||
"displayName": "Nub Language Support",
|
|
||||||
"description": "Language server client for nub lang",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"publisher": "nub31",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git.oliste.no/nub31/nub-lang"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"vscode": "^1.105.0"
|
|
||||||
},
|
|
||||||
"categories": [
|
|
||||||
"Programming Languages"
|
|
||||||
],
|
|
||||||
"main": "./out/extension.js",
|
|
||||||
"files": [
|
|
||||||
"out",
|
|
||||||
"server",
|
|
||||||
"syntaxes",
|
|
||||||
"language-configuration.json"
|
|
||||||
],
|
|
||||||
"contributes": {
|
|
||||||
"languages": [
|
|
||||||
{
|
|
||||||
"id": "nub",
|
|
||||||
"extensions": [
|
|
||||||
".nub"
|
|
||||||
],
|
|
||||||
"configuration": "./language-configuration.json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"grammars": [
|
|
||||||
{
|
|
||||||
"language": "nub",
|
|
||||||
"scopeName": "source.nub",
|
|
||||||
"path": "./syntaxes/nub.tmLanguage.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "esbuild src/extension.ts --bundle --platform=node --outfile=out/extension.js --external:vscode",
|
|
||||||
"update-lsp": "mkdir -p server && dotnet publish -c Release ../compiler/NubLang.LSP/NubLang.LSP.csproj && cp ../compiler/NubLang.LSP/bin/Release/net9.0/linux-x64/publish/nublsp server/",
|
|
||||||
"package": "npm run update-lsp && npm run build && vsce package --skip-license"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "22.x",
|
|
||||||
"@types/vscode": "^1.105.0",
|
|
||||||
"@vscode/vsce": "^3.6.2",
|
|
||||||
"esbuild": "^0.25.11",
|
|
||||||
"typescript": "^5.9.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"vscode-languageclient": "^9.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import path from 'path';
|
|
||||||
import vscode from 'vscode';
|
|
||||||
import { LanguageClient, TransportKind } from 'vscode-languageclient/node';
|
|
||||||
|
|
||||||
let client: LanguageClient;
|
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
|
||||||
const serverExecutable = path.join(context.asAbsolutePath('server'), "nublsp");
|
|
||||||
|
|
||||||
client = new LanguageClient(
|
|
||||||
'nub',
|
|
||||||
'nub lsp client',
|
|
||||||
{
|
|
||||||
run: {
|
|
||||||
command: serverExecutable,
|
|
||||||
transport: TransportKind.stdio,
|
|
||||||
},
|
|
||||||
debug: {
|
|
||||||
command: serverExecutable,
|
|
||||||
transport: TransportKind.stdio,
|
|
||||||
args: ['--debug'],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
documentSelector: [
|
|
||||||
{ scheme: 'file', language: 'nub' },
|
|
||||||
{ scheme: 'file', pattern: '**/*.nub' }
|
|
||||||
],
|
|
||||||
synchronize: {
|
|
||||||
fileEvents: vscode.workspace.createFileSystemWatcher('**/.clientrc')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
client.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deactivate(): Thenable<void> | undefined {
|
|
||||||
if (!client) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return client.stop();
|
|
||||||
}
|
|
||||||
@@ -1,304 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
|
||||||
"name": "nub",
|
|
||||||
"scopeName": "source.nub",
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"include": "#comments"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#keywords"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#modifiers"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#types"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#strings"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#numbers"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#operators"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#function-definition"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#struct-definition"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#function-call"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#identifiers"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"comments": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "comment.line.double-slash.nub",
|
|
||||||
"begin": "//",
|
|
||||||
"end": "$"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "comment.block.nub",
|
|
||||||
"begin": "/\\*",
|
|
||||||
"end": "\\*/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"keywords": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "keyword.control.nub",
|
|
||||||
"match": "\\b(if|else|while|for|in|break|continue|return|let|defer)\\b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.other.nub",
|
|
||||||
"match": "\\b(func|struct|module|import)\\b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"modifiers": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "storage.modifier.nub",
|
|
||||||
"match": "\\b(export|extern)\\b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"types": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"include": "#function-type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "storage.type.primitive.nub",
|
|
||||||
"match": "\\b(i8|i16|i32|i64|u8|u16|u32|u64|f32|f64|bool|string|cstring|void|any)\\b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "storage.type.array.nub",
|
|
||||||
"match": "\\[\\]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "storage.type.pointer.nub",
|
|
||||||
"match": "\\^"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"function-type": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"begin": "\\b(func)\\s*\\(",
|
|
||||||
"beginCaptures": {
|
|
||||||
"1": {
|
|
||||||
"name": "storage.type.function.nub"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"end": "(?<=\\))(?:\\s*:\\s*([^\\s,;{}()]+))?",
|
|
||||||
"endCaptures": {
|
|
||||||
"1": {
|
|
||||||
"name": "storage.type.nub"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"include": "#function-type-parameters"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"function-type-parameters": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"match": "\\.\\.\\.",
|
|
||||||
"name": "keyword.operator.variadic.nub"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#types"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match": ",",
|
|
||||||
"name": "punctuation.separator.nub"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"strings": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "string.quoted.double.nub",
|
|
||||||
"begin": "\"",
|
|
||||||
"end": "\"",
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "constant.character.escape.nub",
|
|
||||||
"match": "\\\\(n|t|r|\\\\|\"|')"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "constant.character.escape.nub",
|
|
||||||
"match": "\\\\[0-7]{1,3}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "constant.character.escape.nub",
|
|
||||||
"match": "\\\\x[0-9A-Fa-f]{1,2}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "string.quoted.single.nub",
|
|
||||||
"begin": "'",
|
|
||||||
"end": "'",
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "constant.character.escape.nub",
|
|
||||||
"match": "\\\\(n|t|r|\\\\|\"|')"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"numbers": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "constant.numeric.float.nub",
|
|
||||||
"match": "\\b\\d+\\.\\d*([eE][+-]?\\d+)?[fF]?\\b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "constant.numeric.integer.decimal.nub",
|
|
||||||
"match": "\\b\\d+\\b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "constant.numeric.integer.hexadecimal.nub",
|
|
||||||
"match": "\\b0[xX][0-9A-Fa-f]+\\b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "constant.numeric.integer.binary.nub",
|
|
||||||
"match": "\\b0[bB][01]+\\b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"operators": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.assignment.nub",
|
|
||||||
"match": "="
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.comparison.nub",
|
|
||||||
"match": "(==|!=|<=|>=|<|>)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.arithmetic.nub",
|
|
||||||
"match": "(\\+|\\-|\\*|/)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.logical.nub",
|
|
||||||
"match": "(&&|\\|\\||!)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.address.nub",
|
|
||||||
"match": "&"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.dereference.nub",
|
|
||||||
"match": "\\^"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.member-access.nub",
|
|
||||||
"match": "\\."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "keyword.operator.module-access.nub",
|
|
||||||
"match": "::"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"function-definition": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"begin": "\\b(export\\s+|extern\\s+)?(func)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(",
|
|
||||||
"beginCaptures": {
|
|
||||||
"1": {
|
|
||||||
"name": "storage.modifier.nub"
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"name": "keyword.other.nub"
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"name": "entity.name.function.nub"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"end": "\\)",
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"include": "#function-parameters"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"struct-definition": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"match": "\\b(struct)\\s+([a-zA-Z_][a-zA-Z0-9_]*)\\b",
|
|
||||||
"captures": {
|
|
||||||
"1": {
|
|
||||||
"name": "keyword.other.nub"
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"name": "entity.name.type.struct.nub"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"function-parameters": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"match": "\\.\\.\\.",
|
|
||||||
"name": "keyword.operator.variadic.nub"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match": "([a-zA-Z_][a-zA-Z0-9_]*)\\s*:\\s*",
|
|
||||||
"captures": {
|
|
||||||
"1": {
|
|
||||||
"name": "variable.parameter.nub"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#types"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"include": "#identifiers"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"function-call": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"match": "([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=\\()",
|
|
||||||
"captures": {
|
|
||||||
"1": {
|
|
||||||
"name": "entity.name.function.call.nub"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"identifiers": {
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"name": "variable.other.nub",
|
|
||||||
"match": "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "Node16",
|
|
||||||
"target": "ES2022",
|
|
||||||
"outDir": "out",
|
|
||||||
"lib": [
|
|
||||||
"ES2022"
|
|
||||||
],
|
|
||||||
"sourceMap": true,
|
|
||||||
"rootDir": "src",
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user