From f63cee1011f84b061fa1ad5569708da7bb26abcf Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 26 May 2025 20:10:22 +0200 Subject: [PATCH] ... --- example/program.nub | 4 +- .../Nub.Lang/Frontend/Parsing/Parser.cs | 14 ++- .../Nub.Lang/Frontend/Typing/TypeChecker.cs | 114 ++++++++++++++++-- src/compiler/Nub.Lang/NubType.cs | 15 +-- src/compiler/Nub.Lang/Program.cs | 6 +- 5 files changed, 127 insertions(+), 26 deletions(-) diff --git a/example/program.nub b/example/program.nub index f0478e4..2a46b39 100644 --- a/example/program.nub +++ b/example/program.nub @@ -5,10 +5,10 @@ namespace main global func main(args: []string) { i = 0 - printf("%d\n", args.count) + c::printf("%d\n", args.count) while i < args.count { - printf("%s\n", args[i]) + c::printf("%s\n", args[i]) i = i + 1 } diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index 9f53ce4..dc92c18 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -596,7 +596,19 @@ public class Parser { if (TryExpectIdentifier(out var name)) { - return NubType.Parse(name); + if (NubPrimitiveType.TryParse(name, out var primitiveTypeKind)) + { + return new NubPrimitiveType(primitiveTypeKind.Value); + } + else + { + var @namespace = _namespace; + if (TryExpectSymbol(Symbol.DoubleColon)) + { + @namespace = ExpectIdentifier().Value; + } + return new NubStructType(@namespace, name); + } } if (TryExpectSymbol(Symbol.Caret)) diff --git a/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs b/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs index f06a776..96dcf27 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs @@ -10,27 +10,26 @@ public class TypeChecker private List _diagnostics = []; private NubType? _currentFunctionReturnType; private bool _hasReturnStatement; - private List _definitions = []; public DiagnosticsResult> TypeCheck(List sourceFiles) { _variables = new Dictionary(); _diagnostics = []; - _definitions = sourceFiles.SelectMany(x => x.Definitions).ToList(); _currentFunctionReturnType = null; _hasReturnStatement = false; + _sourceFiles = sourceFiles; - foreach (var structDef in _definitions.OfType()) + foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { TypeCheckStructDef(structDef); } - foreach (var funcDef in _definitions.OfType()) + foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { TypeCheckFuncDef(funcDef); } - return new DiagnosticsResult>(_diagnostics, _definitions); + return new DiagnosticsResult>(_diagnostics, _sourceFiles.SelectMany(f => f.Definitions).ToList()); } private void TypeCheckStructDef(StructDefinitionNode structDef) @@ -169,8 +168,16 @@ public class TypeChecker private NubType? TypeCheckFuncCall(FuncCall funcCall, Node node) { - var localFuncDef = _definitions.OfType().FirstOrDefault(f => f.Name == funcCall.Name); - var externFuncDef = _definitions.OfType().FirstOrDefault(f => f.Name == funcCall.Name); + List parameterTypes = []; + foreach (var funcCallParameter in funcCall.Parameters) + { + var parameterType = TypeCheckExpression(funcCallParameter); + if (parameterType == null) return null; + parameterTypes.Add(parameterType); + } + + var localFuncDef = LookupLocalFuncDefinition(funcCall.Namespace, funcCall.Name, parameterTypes); + var externFuncDef = LookupExternFuncDefinition(funcCall.Namespace, funcCall.Name, parameterTypes); List parameters; Optional returnType; @@ -198,8 +205,7 @@ public class TypeChecker for (var i = 0; i < funcCall.Parameters.Count; i++) { - var argType = TypeCheckExpression(funcCall.Parameters[i]); - if (argType == null) continue; + var argType = funcCall.Parameters[i].Type; NubType paramType; if (i < parameters.Count) @@ -420,7 +426,7 @@ public class TypeChecker return null; } - var definition = _definitions.OfType().FirstOrDefault(s => s.Name == structInit.StructType.Name); + var definition = LookupStructDefinition(customType.Namespace, customType.Name); if (definition == null) { ReportError($"Struct type '{customType.Name}' is not defined", structInit); @@ -519,7 +525,7 @@ public class TypeChecker } case NubStructType structType: { - var definition = _definitions.OfType().FirstOrDefault(s => s.Name == structType.Name); + var definition = LookupStructDefinition(structType.Namespace, structType.Name); if (definition == null) { ReportError($"Struct type '{structType.Name}' is not defined", memberAccess); @@ -599,4 +605,90 @@ public class TypeChecker return false; } } + + private TDefinition? LookupDefinition(string @namespace, Func predicate) where TDefinition : DefinitionNode + { + return _sourceFiles + .Where(f => f.Namespace == @namespace) + .SelectMany(f => f.Definitions) + .OfType() + .FirstOrDefault(predicate); + } + + private ExternFuncDefinitionNode? LookupExternFuncDefinition(string @namespace, string name, List parameters) + { + return LookupDefinition(@namespace, SignatureMatches); + + bool SignatureMatches(ExternFuncDefinitionNode node) + { + if (node.Name != name) return false; + if (node.Parameters.Count == 0 && parameters.Count == 0) return true; + if (node.Parameters.Count > parameters.Count) return false; + + for (var i = 0; i < parameters.Count; i++) + { + if (i > node.Parameters.Count) + { + if (node.Parameters[^1].Variadic) + { + if (!AreTypesCompatible(parameters[i], node.Parameters[^1].Type)) + { + return false; + } + } + else + { + return false; + } + } + else if (!AreTypesCompatible(parameters[i], node.Parameters[i].Type)) + { + return false; + } + } + + return true; + } + } + + private LocalFuncDefinitionNode? LookupLocalFuncDefinition(string @namespace, string name, List parameters) + { + return LookupDefinition(@namespace, SignatureMatches); + + bool SignatureMatches(LocalFuncDefinitionNode node) + { + if (node.Name != name) return false; + if (node.Parameters.Count == 0 && parameters.Count == 0) return true; + if (node.Parameters.Count > parameters.Count) return false; + + for (var i = 0; i < parameters.Count; i++) + { + if (i > node.Parameters.Count) + { + if (node.Parameters[^1].Variadic) + { + if (!AreTypesCompatible(parameters[i], node.Parameters[^1].Type)) + { + return false; + } + } + else + { + return false; + } + } + else if (!AreTypesCompatible(parameters[i], node.Parameters[i].Type)) + { + return false; + } + } + + return true; + } + } + + private StructDefinitionNode? LookupStructDefinition(string @namespace, string name) + { + return LookupDefinition(@namespace, d => d.Name == name); + } } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs index 56e983e..c2b9777 100644 --- a/src/compiler/Nub.Lang/NubType.cs +++ b/src/compiler/Nub.Lang/NubType.cs @@ -11,22 +11,15 @@ public abstract class NubType public string Name { get; } - public static NubType Parse(string s) - { - if (NubPrimitiveType.TryParse(s, out var kind)) - { - return new NubPrimitiveType(kind.Value); - } - - return new NubStructType(s); - } - public override bool Equals(object? obj) => obj is NubType item && Name.Equals(item.Name); public override int GetHashCode() => HashCode.Combine(Name); public override string ToString() => Name; } -public class NubStructType(string name) : NubType(name); +public class NubStructType(string name, string @namespace) : NubType(name) +{ + public string Namespace { get; } = @namespace; +} public class NubPointerType(NubType baseType) : NubType("^" + baseType) { diff --git a/src/compiler/Nub.Lang/Program.cs b/src/compiler/Nub.Lang/Program.cs index e81dc79..25631fd 100644 --- a/src/compiler/Nub.Lang/Program.cs +++ b/src/compiler/Nub.Lang/Program.cs @@ -30,6 +30,7 @@ internal static class Program private static int Compile(string srcDir) { + var error = false; var lexer = new Lexer(); var parser = new Parser(); var typeChecker = new TypeChecker(); @@ -41,9 +42,11 @@ internal static class Program var tokenizeResult = lexer.Tokenize(new SourceText(file, content)); tokenizeResult.PrintAllDiagnostics(); + error = error || tokenizeResult.HasErrors; var parseResult = parser.ParseModule(tokenizeResult.Value); parseResult.PrintAllDiagnostics(); + error = error || parseResult.HasErrors; if (parseResult.Value != null) { @@ -53,12 +56,13 @@ internal static class Program var typeCheckResult = typeChecker.TypeCheck(files); typeCheckResult.PrintAllDiagnostics(); + error = error || typeCheckResult.HasErrors; var generator = new Generator(typeCheckResult.Value); var result = generator.Generate(); Console.Out.Write(result); - return 0; + return error ? 1 : 0; } } \ No newline at end of file