diff --git a/example/interop/bindings.nub b/example/interop/bindings.nub index e164518..08144f4 100644 --- a/example/interop/bindings.nub +++ b/example/interop/bindings.nub @@ -1,5 +1,6 @@ namespace c +extern func printf(fmt: string, ...args: any) extern func printf(fmt: string, ...args: any) extern func getchar(): i32 extern func puts(str: string) diff --git a/src/compiler/Nub.Lang/Backend/Generator.cs b/src/compiler/Nub.Lang/Backend/Generator.cs index 05b6430..5c053ea 100644 --- a/src/compiler/Nub.Lang/Backend/Generator.cs +++ b/src/compiler/Nub.Lang/Backend/Generator.cs @@ -6,31 +6,35 @@ namespace Nub.Lang.Backend; public class Generator { - private readonly List _definitions; - private readonly StringBuilder _builder = new(); - private readonly Dictionary _variables = new(); - private readonly List _strings = []; - private readonly Stack _breakLabels = new(); - private readonly Stack _continueLabels = new(); + private List _sourceFiles = []; + private StringBuilder _builder = new(); + private Dictionary _variables = []; + private List _strings = []; + private Stack _breakLabels = []; + private Stack _continueLabels = []; private int _variableIndex; private bool _codeIsReachable = true; - public Generator(List definitions) + public string Generate(List sourceFiles) { - _definitions = definitions; - } - - public string Generate() - { - foreach (var structDefinition in _definitions.OfType()) + _sourceFiles = sourceFiles; + _builder = new StringBuilder(); + _variables = new Dictionary(); + _strings = []; + _breakLabels = []; + _continueLabels = []; + _variableIndex = 0; + _codeIsReachable = true; + + foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { - GenerateStructDefinition(structDefinition); + GenerateStructDefinition(structDef); _builder.AppendLine(); } - foreach (var funcDefinition in _definitions.OfType()) + foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { - GenerateFuncDefinition(funcDefinition); + GenerateFuncDefinition(funcDef); _builder.AppendLine(); } @@ -213,12 +217,12 @@ public class Generator throw new ArgumentOutOfRangeException(); } } - case NubStructType nubCustomType: + case NubStructType nubStructType: { - var definition = _definitions.OfType().FirstOrDefault(s => s.Name == nubCustomType.Name); + var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name); if (definition == null) { - throw new Exception($"Cannot determine size of non-existent type {nubCustomType}"); + throw new Exception($"Cannot determine size of non-existent type {nubStructType}"); } return definition.Fields.Sum(f => QbeTypeSize(f.Type)); @@ -362,12 +366,8 @@ public class Generator private string GenerateFuncCall(FuncCall funcCall) { - var parameterDefinitions = _definitions - .OfType() - .FirstOrDefault(d => d.Name == funcCall.Name) - ?.Parameters; - - if (parameterDefinitions == null) + var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList()); + if (funcDefinition == null) { throw new Exception($"Unknown function {funcCall}"); } @@ -376,19 +376,19 @@ public class Generator for (var i = 0; i < funcCall.Parameters.Count; i++) { - if (i < parameterDefinitions.Count && parameterDefinitions[i].Variadic) + if (i < funcDefinition.Parameters.Count && funcDefinition.Parameters[i].Variadic) { parameterStrings.Add("..."); } NubType expectedType; - if (i < parameterDefinitions.Count) + if (i < funcDefinition.Parameters.Count) { - expectedType = parameterDefinitions[i].Type; + expectedType = funcDefinition.Parameters[i].Type; } - else if (parameterDefinitions[^1].Variadic) + else if (funcDefinition.Parameters[^1].Variadic) { - expectedType = parameterDefinitions[^1].Type; + expectedType = funcDefinition.Parameters[^1].Type; } else { @@ -1295,7 +1295,7 @@ public class Generator private string GenerateStructInitializer(StructInitializerNode structInitializer) { - var structDefinition = _definitions.OfType().FirstOrDefault(s => s.Name == structInitializer.StructType.Name); + var structDefinition = LookupStructDefinition(structInitializer.StructType.Namespace, structInitializer.StructType.Name); if (structDefinition == null) { throw new Exception($"Struct {structInitializer.StructType.Name} is not defined"); @@ -1399,10 +1399,7 @@ public class Generator } case NubStructType structType: { - var structDefinition = _definitions - .OfType() - .FirstOrDefault(s => s.Name == structType.Name); - + var structDefinition = LookupStructDefinition(structType.Namespace, structType.Namespace); if (structDefinition == null) { throw new Exception($"Struct {structType.Name} is not defined"); @@ -1449,6 +1446,24 @@ public class Generator return $"v{++_variableIndex}"; } + private IFuncSignature? LookupFuncSignature(string @namespace, string name, List parameters) + { + return _sourceFiles + .Where(f => f.Namespace == @namespace) + .SelectMany(f => f.Definitions) + .OfType() + .FirstOrDefault(f => f.SignatureMatches(name, parameters)); + } + + private StructDefinitionNode? LookupStructDefinition(string @namespace, string name) + { + return _sourceFiles + .Where(f => f.Namespace == @namespace) + .SelectMany(f => f.Definitions) + .OfType() + .FirstOrDefault(d => d.Name == name); + } + private class Variable { public required string Pointer { get; init; } diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/FuncDefinitionNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/FuncDefinitionNode.cs index bcfbc56..3cf9977 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/FuncDefinitionNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/FuncDefinitionNode.cs @@ -8,10 +8,41 @@ public interface IFuncSignature public List Parameters { get; } public Optional ReturnType { get; } + public bool SignatureMatches(string name, List parameters) + { + if (Name != name) return false; + if (Parameters.Count == 0 && parameters.Count == 0) return true; + if (Parameters.Count > parameters.Count) return false; + + for (var i = 0; i < parameters.Count; i++) + { + if (i > Parameters.Count) + { + if (Parameters[^1].Variadic) + { + if (!NubType.IsCompatibleWith(parameters[i], Parameters[^1].Type)) + { + return false; + } + } + else + { + return false; + } + } + else if (!NubType.IsCompatibleWith(parameters[i], Parameters[i].Type)) + { + return false; + } + } + + return true; + } + public string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}"; } -public class LocalFuncDefinitionNode(IReadOnlyList tokens, Optional documentation, string name, List parameters, BlockNode body, Optional returnType, bool global) : DefinitionNode(tokens, documentation) +public class LocalFuncDefinitionNode(IReadOnlyList tokens, Optional documentation, string name, List parameters, BlockNode body, Optional returnType, bool global) : DefinitionNode(tokens, documentation), IFuncSignature { public string Name { get; } = name; public List Parameters { get; } = parameters; diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs index dc92c18..112fef6 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/Parser.cs @@ -484,17 +484,36 @@ public class Parser case Symbol.New: { var type = ParseType(); - Dictionary initializers = []; - ExpectSymbol(Symbol.OpenBrace); - while (!TryExpectSymbol(Symbol.CloseBrace)) + switch (type) { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - initializers.Add(name, value); - } + case NubStructType structType: + { + Dictionary initializers = []; + ExpectSymbol(Symbol.OpenBrace); + while (!TryExpectSymbol(Symbol.CloseBrace)) + { + var name = ExpectIdentifier().Value; + ExpectSymbol(Symbol.Assign); + var value = ParseExpression(); + initializers.Add(name, value); + } - expr = new StructInitializerNode(GetTokensForNode(startIndex), type, initializers); + expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers); + break; + } + case NubArrayType arrayType: + { + throw new NotImplementedException(); + } + default: + { + throw new ParseException(Diagnostic + .Error($"Cannot use new keyword on type {type}") + .At(symbolToken) + .Build()); + } + } + break; } case Symbol.Ampersand: diff --git a/src/compiler/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs b/src/compiler/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs index 6c8a0a3..894b641 100644 --- a/src/compiler/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs +++ b/src/compiler/Nub.Lang/Frontend/Parsing/StructInitializerNode.cs @@ -2,8 +2,8 @@ namespace Nub.Lang.Frontend.Parsing; -public class StructInitializerNode(IReadOnlyList tokens, NubType structType, Dictionary initializers) : ExpressionNode(tokens) +public class StructInitializerNode(IReadOnlyList tokens, NubStructType structType, Dictionary initializers) : ExpressionNode(tokens) { - public NubType StructType { get; } = structType; + public NubStructType StructType { get; } = structType; public Dictionary Initializers { get; } = initializers; } \ No newline at end of file diff --git a/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs b/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs index 4fb75af..e7978ff 100644 --- a/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs +++ b/src/compiler/Nub.Lang/Frontend/Typing/TypeChecker.cs @@ -11,7 +11,7 @@ public class TypeChecker private NubType? _currentFunctionReturnType; private bool _hasReturnStatement; - public DiagnosticsResult> TypeCheck(List sourceFiles) + public DiagnosticsResult TypeCheck(List sourceFiles) { _variables = new Dictionary(); _diagnostics = []; @@ -19,6 +19,16 @@ public class TypeChecker _hasReturnStatement = false; _sourceFiles = sourceFiles; + var externFunctionNames = _sourceFiles + .SelectMany(f => f.Definitions) + .OfType() + .ToArray(); + + foreach (var funcName in externFunctionNames.Where(x => externFunctionNames.Count(y => x.Name == y.Name) > 1)) + { + ReportError($"Extern function '{funcName}' has been declared more than once", funcName); + } + foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { TypeCheckStructDef(structDef); @@ -28,8 +38,8 @@ public class TypeChecker { TypeCheckFuncDef(funcDef); } - - return new DiagnosticsResult>(_diagnostics, _sourceFiles.SelectMany(f => f.Definitions).ToList()); + + return new DiagnosticsResult(_diagnostics); } private void TypeCheckStructDef(StructDefinitionNode structDef) @@ -120,14 +130,14 @@ public class TypeChecker { if (varAssign.ExplicitType.HasValue) { - if (!AreTypesCompatible(existingVariable, varAssign.ExplicitType.Value)) + if (!NubType.IsCompatibleWith(existingVariable, varAssign.ExplicitType.Value)) { ReportError($"Explicit type '{varAssign.ExplicitType.Value}' on variable '{varAssign.Name}' is not compatible with declared type '{existingVariable}'", varAssign); return; } } - if (!AreTypesCompatible(valueType, existingVariable)) + if (!NubType.IsCompatibleWith(valueType, existingVariable)) { ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{existingVariable}'", varAssign); } @@ -137,7 +147,7 @@ public class TypeChecker if (varAssign.ExplicitType.HasValue) { var explicitType = varAssign.ExplicitType.Value; - if (!AreTypesCompatible(valueType, explicitType)) + if (!NubType.IsCompatibleWith(valueType, explicitType)) { ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'", varAssign); return; @@ -208,7 +218,7 @@ public class TypeChecker continue; } - if (!AreTypesCompatible(argType, paramType)) + if (!NubType.IsCompatibleWith(argType, paramType)) { ReportError($"Parameter {i + 1} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]); } @@ -260,7 +270,7 @@ public class TypeChecker return; } - if (!AreTypesCompatible(returnType, _currentFunctionReturnType)) + if (!NubType.IsCompatibleWith(returnType, _currentFunctionReturnType)) { ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value); } @@ -405,17 +415,10 @@ public class TypeChecker { var initialized = new HashSet(); - var structType = structInit.StructType; - if (structType is not NubStructType customType) - { - ReportError($"Type '{structType}' is not a struct type", structInit); - return null; - } - - var definition = LookupStructDefinition(customType.Namespace, customType.Name); + var definition = LookupStructDefinition(structInit.StructType.Namespace, structInit.StructType.Name); if (definition == null) { - ReportError($"Struct type '{customType.Name}' is not defined", structInit); + ReportError($"Struct type '{structInit.StructType.Name}' is not defined", structInit); return null; } @@ -424,12 +427,12 @@ public class TypeChecker var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key); if (definitionField == null) { - ReportError($"Field '{initializer.Key}' does not exist in struct '{customType.Name}'", initializer.Value); + ReportError($"Field '{initializer.Key}' does not exist in struct '{structInit.StructType.Name}'", initializer.Value); continue; } var initializerType = TypeCheckExpression(initializer.Value); - if (initializerType != null && !AreTypesCompatible(initializerType, definitionField.Type)) + if (initializerType != null && !NubType.IsCompatibleWith(initializerType, definitionField.Type)) { ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value); } @@ -446,11 +449,11 @@ public class TypeChecker { if (!initialized.Contains(field.Name)) { - ReportError($"Struct field '{field.Name}' is not initialized on type '{customType.Name}'", structInit); + ReportError($"Struct field '{field.Name}' is not initialized on type '{structInit.StructType.Name}'", structInit); } } - return structType; + return structInit.StructType; } private NubType? TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression) @@ -539,11 +542,6 @@ public class TypeChecker _diagnostics.Add(diagnostic); } - private static bool AreTypesCompatible(NubType sourceType, NubType targetType) - { - return targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType); - } - private static bool IsNumeric(NubType type) { if (type is not NubPrimitiveType primitiveType) @@ -598,38 +596,7 @@ public class TypeChecker .Where(f => f.Namespace == @namespace) .SelectMany(f => f.Definitions) .OfType() - .FirstOrDefault(SignatureMatches); - - bool SignatureMatches(IFuncSignature 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; - } + .FirstOrDefault(f => f.SignatureMatches(name, parameters)); } private StructDefinitionNode? LookupStructDefinition(string @namespace, string name) diff --git a/src/compiler/Nub.Lang/NubType.cs b/src/compiler/Nub.Lang/NubType.cs index c2b9777..0d58dd4 100644 --- a/src/compiler/Nub.Lang/NubType.cs +++ b/src/compiler/Nub.Lang/NubType.cs @@ -11,6 +11,11 @@ public abstract class NubType public string Name { get; } + public static bool IsCompatibleWith(NubType sourceType, NubType targetType) + { + return targetType.Equals(NubPrimitiveType.Any) || sourceType.Equals(targetType); + } + 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; diff --git a/src/compiler/Nub.Lang/Program.cs b/src/compiler/Nub.Lang/Program.cs index 25631fd..10e9043 100644 --- a/src/compiler/Nub.Lang/Program.cs +++ b/src/compiler/Nub.Lang/Program.cs @@ -58,8 +58,8 @@ internal static class Program typeCheckResult.PrintAllDiagnostics(); error = error || typeCheckResult.HasErrors; - var generator = new Generator(typeCheckResult.Value); - var result = generator.Generate(); + var generator = new Generator(); + var result = generator.Generate(files); Console.Out.Write(result);