WIP: dev #1

Draft
nub31 wants to merge 103 commits from dev into master
7 changed files with 215 additions and 64 deletions
Showing only changes of commit 6ae10d5f90 - Show all commits

View File

@@ -14,6 +14,9 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
private string Emit()
{
foreach (var (i, structType) in moduleGraph.GetModules().SelectMany(x => x.GetCustomTypes().OfType<NubTypeStruct>().Index()))
structTypeNames[structType] = $"s{i}";
writer.WriteLine("""
#include <float.h>
#include <stdarg.h>
@@ -28,9 +31,6 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
""");
foreach (var (i, structType) in moduleGraph.GetModules().SelectMany(x => x.GetCustomTypes().OfType<NubTypeStruct>().Index()))
structTypeNames[structType] = $"s{i}";
foreach (var typeName in structTypeNames)
writer.WriteLine($"struct {typeName.Value};");
@@ -45,30 +45,55 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
foreach (var field in typeName.Key.Fields)
writer.WriteLine($"{CType(field.Type, field.Name)};");
}
writer.WriteLine("};");
}
writer.WriteLine();
foreach (var node in functions)
foreach (var module in moduleGraph.GetModules())
{
var parameters = node.Parameters.Select(x => CType(x.Type, x.Name.Ident));
writer.WriteLine($"{CType(node.ReturnType, node.Name.Ident)}({string.Join(", ", parameters)});");
foreach (var (name, type) in module.GetIdentifierTypes())
{
if (type is NubTypeFunc fn)
{
if (!functions.Any(x => x.GetMangledName() == SymbolNameGen.Exported(module.Name, name, type)))
writer.Write("extern ");
writer.WriteLine($"{CType(fn.ReturnType)} {SymbolNameGen.Exported(module.Name, name, type)}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});");
}
else
{
writer.WriteLine($"{CType(type)} {SymbolNameGen.Exported(module.Name, name, type)};");
}
}
}
writer.WriteLine();
foreach (var node in functions)
var main = functions.FirstOrDefault(x => x.Module == "main" && x.Name.Ident == "main");
if (main != null)
{
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("int main(int argc, char *argv[])");
writer.WriteLine("{");
using (writer.Indent())
{
EmitStatement(node.Body);
writer.WriteLine($"return {main.GetMangledName()}();");
}
writer.WriteLine("}");
}
writer.WriteLine();
foreach (var function in functions)
{
var parameters = function.Parameters.Select(x => CType(x.Type, x.Name.Ident));
writer.WriteLine($"{CType(function.ReturnType, function.GetMangledName())}({string.Join(", ", parameters)})");
writer.WriteLine("{");
using (writer.Indent())
{
EmitStatement(function.Body);
}
writer.WriteLine("}");
writer.WriteLine();
}
@@ -114,7 +139,6 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
foreach (var statement in node.Statements)
EmitStatement(statement);
}
writer.WriteLine("}");
}
@@ -153,7 +177,6 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
{
EmitStatement(statement.ThenBlock);
}
writer.WriteLine("}");
if (statement.ElseBlock != null)
@@ -169,7 +192,6 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
{
EmitStatement(statement.ElseBlock);
}
writer.WriteLine("}");
}
}
@@ -183,7 +205,6 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
{
EmitStatement(statement.Block);
}
writer.WriteLine("}");
}
@@ -199,7 +220,7 @@ public sealed class Generator(List<TypedNodeDefinitionFunc> functions, ModuleGra
TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression),
TypedNodeExpressionMemberAccess expression => EmitExpressionMemberAccess(expression),
TypedNodeExpressionLocalIdent expression => expression.Value.Ident,
TypedNodeExpressionModuleIdent expression => expression.Value.Ident,
TypedNodeExpressionModuleIdent expression => SymbolNameGen.Exported(expression.Module.Ident, expression.Value.Ident, expression.Type),
TypedNodeExpressionFuncCall expression => EmitExpressionFuncCall(expression),
_ => throw new ArgumentOutOfRangeException(nameof(node), node, null)
};

View File

@@ -24,11 +24,16 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
private readonly Dictionary<string, NubType> customTypes = new();
private readonly Dictionary<string, NubType> identifierTypes = new();
public List<NubType> GetCustomTypes()
public IReadOnlyList<NubType> GetCustomTypes()
{
return customTypes.Values.ToList();
}
public IReadOnlyDictionary<string, NubType> GetIdentifierTypes()
{
return identifierTypes;
}
public bool TryResolveCustomType(string name, [NotNullWhen(true)] out NubType? customType)
{
customType = customTypes.GetValueOrDefault(name);
@@ -61,7 +66,7 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
asts.Add(ast);
}
public ModuleGraph Build(out List<Diagnostic> diagnostics)
public ModuleGraph? Build(out List<Diagnostic> diagnostics)
{
diagnostics = [];
@@ -71,26 +76,13 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
// 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)
if (!modules.ContainsKey(ast.ModuleName.Ident))
{
var currentModule = moduleDefinitions[0].Name.Ident;
if (!modules.ContainsKey(currentModule))
{
var module = new Module(currentModule);
modules.Add(currentModule, module);
var module = new Module(ast.ModuleName.Ident);
modules.Add(ast.ModuleName.Ident, module);
astModuleCache[ast] = module;
}
}
}
// Second pass: Register struct types without fields
foreach (var ast in asts)
@@ -133,6 +125,9 @@ public class ModuleGraph(Dictionary<string, ModuleGraph.Module> modules)
}
}
if (diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
return null;
return new ModuleGraph(modules);
NubType ResolveType(NodeType node)

View File

@@ -1,3 +1,5 @@
using System.Text;
namespace Compiler;
public abstract class NubType
@@ -153,6 +155,8 @@ public sealed class NubTypeFunc : NubType
public IReadOnlyList<NubType> Parameters { get; }
public NubType ReturnType { get; }
public string MangledName(string name, string module) => SymbolNameGen.Exported(name, module, this);
private NubTypeFunc(List<NubType> parameters, NubType returnType)
{
Parameters = parameters;
@@ -163,3 +167,109 @@ public sealed class NubTypeFunc : NubType
private readonly record struct Signature(IReadOnlyList<NubType> Parameters, NubType ReturnType);
}
static class TypeMangler
{
public static string Encode(NubType type)
{
var sb = new StringBuilder();
Encode(type, sb);
return sb.ToString();
}
private static void Encode(NubType type, StringBuilder sb)
{
switch (type)
{
case NubTypeVoid:
sb.Append('V');
break;
case NubTypeBool:
sb.Append('B');
break;
case NubTypeUInt u:
sb.Append("U(");
sb.Append(u.Width);
sb.Append(')');
break;
case NubTypeSInt s:
sb.Append("U(");
sb.Append(s.Width);
sb.Append(')');
break;
case NubTypeString:
sb.Append('S');
break;
case NubTypePointer p:
sb.Append("P(");
Encode(p.To, sb);
sb.Append(')');
break;
case NubTypeStruct st:
sb.Append($"T({st.Module}::{st.Name})").Append(st.Module).Append("::").Append(st.Name).Append(')');
break;
case NubTypeFunc fn:
sb.Append("F(");
for (int i = 0; i < fn.Parameters.Count; i++)
{
Encode(fn.Parameters[i], sb);
sb.Append(',');
}
Encode(fn.ReturnType, sb);
sb.Append(')');
break;
default:
throw new NotSupportedException(type.GetType().Name);
}
}
}
static class Hashing
{
public static ulong Fnv1a64(string text)
{
const ulong offset = 14695981039346656037UL;
const ulong prime = 1099511628211UL;
ulong hash = offset;
foreach (var c in Encoding.UTF8.GetBytes(text))
{
hash ^= c;
hash *= prime;
}
return hash;
}
}
static class SymbolNameGen
{
public static string Exported(string module, string function, NubType type)
{
var canonical = TypeMangler.Encode(type);
var hash = Hashing.Fnv1a64(canonical);
return $"nub_{Sanitize(module)}_{Sanitize(function)}_{hash:x16}";
}
private static string Sanitize(string s)
{
var sb = new StringBuilder(s.Length);
foreach (var c in s)
{
if (char.IsLetterOrDigit(c))
sb.Append(c);
else
sb.Append('_');
}
return sb.ToString();
}
}

View File

@@ -4,20 +4,25 @@ namespace Compiler;
public sealed class Parser(string fileName, List<Token> tokens)
{
public static Ast Parse(string fileName, List<Token> tokens, out List<Diagnostic> diagnostics)
public static Ast? Parse(string fileName, List<Token> tokens, out List<Diagnostic> diagnostics)
{
return new Parser(fileName, tokens).Parse(out diagnostics);
}
private int index;
private Ast Parse(out List<Diagnostic> diagnostics)
private Ast? Parse(out List<Diagnostic> diagnostics)
{
var definitions = new List<NodeDefinition>();
diagnostics = [];
TokenIdent? moduleName = null;
try
{
ExpectKeyword(Keyword.Module);
moduleName = ExpectIdent();
while (Peek() != null)
{
definitions.Add(ParseDefinition());
@@ -28,7 +33,10 @@ public sealed class Parser(string fileName, List<Token> tokens)
diagnostics.Add(e.Diagnostic);
}
return new Ast(definitions, fileName);
if (moduleName == null || diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
return null;
return new Ast(fileName, moduleName, definitions);
}
private NodeDefinition ParseDefinition()
@@ -76,12 +84,6 @@ public sealed class Parser(string fileName, List<Token> tokens)
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());
}
@@ -355,6 +357,17 @@ public sealed class Parser(string fileName, List<Token> tokens)
return tokens.GetRange(startIndex, index - startIndex);
}
private void ExpectKeyword(Keyword keyword)
{
if (Peek() is TokenKeyword token && token.Keyword == keyword)
{
Next();
return;
}
throw new CompileException(Diagnostic.Error($"Expected '{keyword.AsString()}'").At(fileName, Peek()).Build());
}
private bool TryExpectKeyword(Keyword keyword)
{
if (Peek() is TokenKeyword token && token.Keyword == keyword)
@@ -529,9 +542,10 @@ public sealed class Parser(string fileName, List<Token> tokens)
}
}
public sealed class Ast(List<NodeDefinition> definitions, string fileName)
public sealed class Ast(string fileName, TokenIdent moduleName, List<NodeDefinition> definitions)
{
public string FileName { get; } = fileName;
public TokenIdent ModuleName { get; } = moduleName;
public List<NodeDefinition> Definitions { get; } = definitions;
}
@@ -542,11 +556,6 @@ public abstract class Node(List<Token> tokens)
public abstract class NodeDefinition(List<Token> tokens) : Node(tokens);
public sealed class NodeDefinitionModule(List<Token> tokens, TokenIdent name) : NodeDefinition(tokens)
{
public TokenIdent Name { get; } = name;
}
public sealed class NodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<NodeDefinitionFunc.Param> parameters, NodeStatement body, NodeType returnType) : NodeDefinition(tokens)
{
public TokenIdent Name { get; } = name;

View File

@@ -13,7 +13,7 @@ foreach (var fileName in args)
foreach (var diagnostic in tokenizerDiagnostics)
DiagnosticFormatter.Print(diagnostic, Console.Error);
if (tokenizerDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
if (tokens == null)
return 1;
var ast = Parser.Parse(fileName, tokens, out var parserDiagnostics);
@@ -21,7 +21,7 @@ foreach (var fileName in args)
foreach (var diagnostic in parserDiagnostics)
DiagnosticFormatter.Print(diagnostic, Console.Error);
if (parserDiagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
if (ast == null)
return 1;
moduleGraphBuilder.AddAst(ast);
@@ -33,7 +33,7 @@ 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))
if (moduleGraph == null)
return 1;
var functions = new List<TypedNodeDefinitionFunc>();
@@ -42,7 +42,7 @@ foreach (var ast in asts)
{
foreach (var func in ast.Definitions.OfType<NodeDefinitionFunc>())
{
var typedFunction = TypeChecker.CheckFunction(ast.FileName, func, moduleGraph, out var typeCheckerDiagnostics);
var typedFunction = TypeChecker.CheckFunction(ast.FileName, ast.ModuleName.Ident, func, moduleGraph, out var typeCheckerDiagnostics);
foreach (var diagnostic in typeCheckerDiagnostics)
DiagnosticFormatter.Print(diagnostic, Console.Error);
@@ -61,6 +61,6 @@ Directory.CreateDirectory(".build");
File.WriteAllText(".build/out.c", output);
Process.Start("gcc", ["-Og", "-g", "-o", ".build/out", ".build/out.c"]);
Process.Start("gcc", ["-Og", "-g", "-o", ".build/out", ".build/out.c", "-fvisibility=hidden","-fno-builtin"]);
return 0;

View File

@@ -5,7 +5,7 @@ namespace Compiler;
public sealed class Tokenizer(string fileName, string contents)
{
public static List<Token> Tokenize(string fileName, string contents, out List<Diagnostic> diagnostics)
public static List<Token>? Tokenize(string fileName, string contents, out List<Diagnostic> diagnostics)
{
return new Tokenizer(fileName, contents).Tokenize(out diagnostics);
}
@@ -14,7 +14,7 @@ public sealed class Tokenizer(string fileName, string contents)
private int line = 1;
private int column = 1;
private List<Token> Tokenize(out List<Diagnostic> diagnostics)
private List<Token>? Tokenize(out List<Diagnostic> diagnostics)
{
var tokens = new List<Token>();
diagnostics = [];
@@ -53,6 +53,9 @@ public sealed class Tokenizer(string fileName, string contents)
}
}
if (diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))
return null;
return tokens;
}

View File

@@ -1,10 +1,10 @@
namespace Compiler;
public sealed class TypeChecker(string fileName, NodeDefinitionFunc function, ModuleGraph moduleGraph)
public sealed class TypeChecker(string fileName, string moduleName, NodeDefinitionFunc function, ModuleGraph moduleGraph)
{
public static TypedNodeDefinitionFunc? CheckFunction(string fileName, NodeDefinitionFunc function, ModuleGraph moduleGraph, out List<Diagnostic> diagnostics)
public static TypedNodeDefinitionFunc? CheckFunction(string fileName, string moduleName, NodeDefinitionFunc function, ModuleGraph moduleGraph, out List<Diagnostic> diagnostics)
{
return new TypeChecker(fileName, function, moduleGraph).CheckFunction(out diagnostics);
return new TypeChecker(fileName, moduleName, function, moduleGraph).CheckFunction(out diagnostics);
}
private readonly Scope scope = new(null);
@@ -52,7 +52,7 @@ public sealed class TypeChecker(string fileName, NodeDefinitionFunc function, Mo
if (body == null || returnType == null || invalidParameter)
return null;
return new TypedNodeDefinitionFunc(function.Tokens, function.Name, parameters, body, returnType);
return new TypedNodeDefinitionFunc(function.Tokens, moduleName, function.Name, parameters, body, returnType);
}
private TypedNodeDefinitionFunc.Param CheckDefinitionFuncParameter(NodeDefinitionFunc.Param node)
@@ -409,15 +409,28 @@ public abstract class TypedNode(List<Token> tokens)
public List<Token> Tokens { get; } = tokens;
}
public abstract class TypedNodeDefinition(List<Token> tokens) : TypedNode(tokens);
public abstract class TypedNodeDefinition(List<Token> tokens, string module) : TypedNode(tokens)
{
public string Module { get; } = module;
}
public sealed class TypedNodeDefinitionFunc(List<Token> tokens, TokenIdent name, List<TypedNodeDefinitionFunc.Param> parameters, TypedNodeStatement body, NubType returnType) : TypedNodeDefinition(tokens)
public sealed class TypedNodeDefinitionFunc(List<Token> tokens, string module, TokenIdent name, List<TypedNodeDefinitionFunc.Param> parameters, TypedNodeStatement body, NubType returnType) : TypedNodeDefinition(tokens, module)
{
public TokenIdent Name { get; } = name;
public List<Param> Parameters { get; } = parameters;
public TypedNodeStatement Body { get; } = body;
public NubType ReturnType { get; } = returnType;
public NubTypeFunc GetNubType()
{
return NubTypeFunc.Get(Parameters.Select(x => x.Type).ToList(), ReturnType);
}
public string GetMangledName()
{
return SymbolNameGen.Exported(Module, Name.Ident, GetNubType());
}
public sealed class Param(List<Token> tokens, TokenIdent name, NubType type) : TypedNode(tokens)
{
public TokenIdent Name { get; } = name;