diff --git a/example/core/sys.nub b/example/core/sys.nub index e0ed545..9f42837 100644 --- a/example/core/sys.nub +++ b/example/core/sys.nub @@ -337,4 +337,4 @@ namespace sys // let rseq: i64 = 334 // let pkey_mprotect: i64 = 335 -extern func call(num: i64, ...args: any) calls core_syscall +// extern func call(num: i64, ...args: any) calls core_syscall diff --git a/example/interop/c.nub b/example/interop/c.nub index 868164c..14fb4ff 100644 --- a/example/interop/c.nub +++ b/example/interop/c.nub @@ -1,21 +1,21 @@ namespace c -extern func printf(fmt: ^u8, ...args: any): void -extern func getchar(): i32 -extern func puts(fmt: ^u8) +// extern func printf(fmt: ^u8, ...args: any): void +// extern func getchar(): i32 +extern func puts(fmt: []u8) -extern func malloc(size: i64): ^void -extern func calloc(num: i64, size: i64): ^void -extern func realloc(ptr: ^void, size: i64): ^void -extern func free(ptr: ^void) +// extern func malloc(size: i64): ^void +// extern func calloc(num: i64, size: i64): ^void +// extern func realloc(ptr: ^void, size: i64): ^void +// extern func free(ptr: ^void) -extern func sin(x: f64): f64 -extern func cos(x: f64): f64 -extern func tan(x: f64): f64 -extern func sqrt(x: f64): f64 -extern func pow(x: f64, y: f64): f64 -extern func abs(x: i32): i32 +// extern func sin(x: f64): f64 +// extern func cos(x: f64): f64 +// extern func tan(x: f64): f64 +// extern func sqrt(x: f64): f64 +// extern func pow(x: f64, y: f64): f64 +// extern func abs(x: i32): i32 -extern func time(t: ^i64): i64 -extern func clock(): i64 -extern func sleep(seconds: u32): i32 +// extern func time(t: ^i64): i64 +// extern func clock(): i64 +// extern func sleep(seconds: u32): i32 diff --git a/example/main.nub b/example/main.nub index 08ed021..49008ae 100644 --- a/example/main.nub +++ b/example/main.nub @@ -1,23 +1,24 @@ namespace main struct Human { - age: ^u64 + age: u64 + print_age: func() = () => { + + } } export func main(args: []^string): i64 { - let age: u64 = 23 - let me = alloc Human { - age = &age + age = 23 } - me.age^ = 24.5 + me.print_age() - test(me.age^) + print_age() return 0 } -func test(me: u64) { - c::printf("%d\n", me) +func print_age() { + c::puts("TEST") } \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs b/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs index b6531b7..745f443 100644 --- a/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs +++ b/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs @@ -107,6 +107,7 @@ public class QBEGenerator }, NubStructType => "l", NubFixedArrayType => "l", + NubFuncType => "l", _ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions") }}"; } @@ -134,6 +135,7 @@ public class QBEGenerator }, NubStructType => "l", NubFixedArrayType => "l", + NubFuncType => "l", _ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions") }}"; } @@ -161,6 +163,7 @@ public class QBEGenerator }, NubStructType => "l", NubFixedArrayType => "l", + NubFuncType => "l", _ => throw new NotSupportedException($"'{type}' type cannot be used in variables") }}"; } @@ -195,17 +198,16 @@ public class QBEGenerator case NubStructType nubStructType: { var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name); - // Struct alignment is the maximum alignment of its fields return definition.Fields.Max(f => AlignmentOf(f.Type)); } case NubPointerType: case NubArrayType: + case NubFuncType: { - return 8; // Pointer alignment (assuming 64-bit) + return 8; } case NubFixedArrayType nubFixedArrayType: { - // Array alignment is same as element alignment return AlignmentOf(nubFixedArrayType.ElementType); } default: @@ -261,27 +263,25 @@ public class QBEGenerator int fieldAlignment = AlignmentOf(field.Type); maxAlignment = Math.Max(maxAlignment, fieldAlignment); - // Align current position for this field size = AlignTo(size, fieldAlignment); - // Add field size size += SizeOf(field.Type); } - // Align final size to struct's alignment for array compatibility size = AlignTo(size, maxAlignment); return size; } case NubPointerType: case NubArrayType: + case NubFuncType: { return 8; } case NubFixedArrayType nubFixedArrayType: { int elementSize = SizeOf(nubFixedArrayType.ElementType); - return elementSize * nubFixedArrayType.Capacity + 8; // +8 for length/capacity info + return elementSize * nubFixedArrayType.Capacity + 8; } default: { @@ -299,6 +299,7 @@ public class QBEGenerator NubPrimitiveType => false, NubStructType => true, NubFixedArrayType => true, + NubFuncType => false, _ => throw new ArgumentOutOfRangeException(nameof(type)) }; } @@ -336,6 +337,7 @@ public class QBEGenerator }, NubStructType structType => $":{structType.Namespace}_{structType.Name}", NubFixedArrayType => "l", + NubFuncType => "l", _ => throw new NotSupportedException($"'{node.ReturnType}' type cannot be used as a function return type") }); _builder.Append(' '); @@ -343,7 +345,7 @@ public class QBEGenerator _builder.Append(_funcNames[node]); - var parameterStrings = node.Parameters.Select(parameter => parameter.Variadic ? "..." : $"{parameter.Type switch + var parameterStrings = node.Parameters.Select(parameter => $"{parameter.Type switch { NubArrayType => "l", NubPointerType => "l", @@ -364,6 +366,7 @@ public class QBEGenerator }, NubStructType structType => $":{structType.Namespace}_{structType.Name}", NubFixedArrayType => "l", + NubFuncType => "l", _ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type") }} %{parameter.Name}"); @@ -439,6 +442,7 @@ public class QBEGenerator }, NubStructType structType => $":{structType.Namespace}_{structType.Name}", NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}", + NubFuncType => "l", _ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs") }; _builder.Append(fieldQbeType + ", "); @@ -619,7 +623,7 @@ public class QBEGenerator private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) { var value = GenerateExpression(variableAssignment.Value); - var pointer = _variables[variableAssignment.Identifier.Identifier]; + var pointer = _variables[variableAssignment.Identifier.Name]; GenerateCopy(variableAssignment.Value.Type, value, pointer); } @@ -795,7 +799,7 @@ public class QBEGenerator case DereferenceNode dereference: return GenerateExpression(dereference.Expression); case IdentifierNode identifier: - return _variables[identifier.Identifier]; + return _variables[identifier.Name]; case MemberAccessNode memberAccess: return GenerateMemberAccessPointer(memberAccess); default: @@ -1031,7 +1035,14 @@ public class QBEGenerator private string GenerateIdentifier(IdentifierNode identifier) { - return _variables[identifier.Identifier]; + if (_variables.TryGetValue(identifier.Name, out var value)) + { + return value; + } + else + { + return _funcNames[LookupFuncSignature(identifier.Namespace, identifier.Name)]; + } } private string GenerateLiteral(LiteralNode literal) @@ -1223,21 +1234,12 @@ public class QBEGenerator private string GenerateFuncCall(FuncCallNode funcCall) { - var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name); - if (funcDefinition == null) - { - throw new Exception($"Unknown function {funcCall}"); - } - + var funcType = (NubFuncType)funcCall.Expression.Type; + var parameterStrings = new List(); for (var i = 0; i < funcCall.Parameters.Count; i++) { - if (i < funcDefinition.Parameters.Count && funcDefinition.Parameters[i].Variadic) - { - parameterStrings.Add("..."); - } - var parameter = funcCall.Parameters[i]; var result = GenerateExpression(parameter); @@ -1262,22 +1264,23 @@ public class QBEGenerator }, NubStructType structType => $":{structType.Namespace}_{structType.Name}", NubFixedArrayType => "l", + NubFuncType => "l", _ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls") }; parameterStrings.Add($"{qbeParameterType} {result}"); } - var funcName = _funcNames[funcDefinition]; - - if (funcDefinition.ReturnType is not NubVoidType) + var funcPointer = GenerateExpression(funcCall.Expression); + + if (funcType.ReturnType is not NubVoidType) { var outputName = GenVarName(); - _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcName}({string.Join(", ", parameterStrings)})"); + _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); return outputName; } else { - _builder.AppendLine($" call {funcName}({string.Join(", ", parameterStrings)})"); + _builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})"); return "fuck"; } } diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs index 19331e9..fdb50db 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Definitions/FuncDefinitionNode.cs @@ -4,11 +4,10 @@ using Nub.Lang.Frontend.Typing; namespace Nub.Lang.Frontend.Parsing.Definitions; -public class FuncParameter(string name, NubType type, bool variadic) +public class FuncParameter(string name, NubType type) { public string Name { get; } = name; public NubType Type { get; } = type; - public bool Variadic { get; } = variadic; public override string ToString() => $"{Name}: {Type}"; } diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/FuncCallNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/FuncCallNode.cs index 30aca4c..7a44a78 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/FuncCallNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/FuncCallNode.cs @@ -2,11 +2,10 @@ namespace Nub.Lang.Frontend.Parsing.Expressions; -public class FuncCallNode(IReadOnlyList tokens, string @namespace, string name, List parameters) : ExpressionNode(tokens) +public class FuncCallNode(IReadOnlyList tokens, ExpressionNode expression, List parameters) : ExpressionNode(tokens) { - public string Namespace { get; } = @namespace; - public string Name { get; } = name; + public ExpressionNode Expression = expression; public List Parameters { get; } = parameters; - public override string ToString() => $"{Name}::{Name}()"; + public override string ToString() => $"{Expression}({string.Join(", ", Parameters)})"; } \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs index 877354e..5ccc392 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs @@ -2,9 +2,10 @@ namespace Nub.Lang.Frontend.Parsing.Expressions; -public class IdentifierNode(IReadOnlyList tokens, string identifier) : LValueNode(tokens) +public class IdentifierNode(IReadOnlyList tokens, string @namespace, string name) : LValueNode(tokens) { - public string Identifier { get; } = identifier; + public string Namespace { get; } = @namespace; + public string Name { get; } = name; - public override string ToString() => Identifier; + public override string ToString() => Name; } \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs index 20c3a09..eb0d730 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -167,19 +167,11 @@ public class Parser private FuncParameter ParseFuncParameter() { - var variadic = false; - if (TryExpectSymbol(Symbol.Period)) - { - ExpectSymbol(Symbol.Period); - ExpectSymbol(Symbol.Period); - variadic = true; - } - var name = ExpectIdentifier(); ExpectSymbol(Symbol.Colon); var type = ParseType(); - return new FuncParameter(name.Value, type, variadic); + return new FuncParameter(name.Value, type); } private StatementNode ParseStatement() @@ -430,58 +422,15 @@ public class Parser } case IdentifierToken identifier: { - var next = Peek(); - switch (next.Value) + var @namespace = _namespace; + var name = identifier.Value; + if (TryExpectSymbol(Symbol.DoubleColon)) { - case SymbolToken { Symbol: Symbol.DoubleColon }: - { - Next(); - var name = ExpectIdentifier(); - ExpectSymbol(Symbol.OpenParen); - var parameters = new List(); - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) - { - _diagnostics.Add(Diagnostic - .Warning("Missing comma between function arguments") - .WithHelp("Add a ',' to separate arguments") - .At(nextToken) - .Build()); - } - } - - expr = new FuncCallNode(GetTokensForNode(startIndex), identifier.Value, name.Value, parameters); - break; - } - case SymbolToken { Symbol: Symbol.OpenParen }: - { - Next(); - var parameters = new List(); - while (!TryExpectSymbol(Symbol.CloseParen)) - { - parameters.Add(ParseExpression()); - if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) - { - _diagnostics.Add(Diagnostic - .Warning("Missing comma between function arguments") - .WithHelp("Add a ',' to separate arguments") - .At(nextToken) - .Build()); - } - } - - expr = new FuncCallNode(GetTokensForNode(startIndex), _namespace, identifier.Value, parameters); - break; - } - default: - { - expr = new IdentifierNode(GetTokensForNode(startIndex), identifier.Value); - break; - } + @namespace = identifier.Value; + name = ExpectIdentifier().Value; } + expr = new IdentifierNode(GetTokensForNode(startIndex), @namespace, name); break; } case SymbolToken symbolToken: @@ -628,6 +577,26 @@ public class Parser continue; } + if (TryExpectSymbol(Symbol.OpenParen)) + { + var parameters = new List(); + while (!TryExpectSymbol(Symbol.CloseParen)) + { + parameters.Add(ParseExpression()); + if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) + { + _diagnostics.Add(Diagnostic + .Warning("Missing comma between function arguments") + .WithHelp("Add a ',' to separate arguments") + .At(nextToken) + .Build()); + } + } + + expr = new FuncCallNode(GetTokensForNode(startIndex), expr, parameters); + continue; + } + break; } @@ -688,6 +657,29 @@ public class Parser var baseType = ParseType(); return new NubPointerType(baseType); } + + if (TryExpectSymbol(Symbol.Func)) + { + ExpectSymbol(Symbol.OpenParen); + List parameters = []; + while (!TryExpectSymbol(Symbol.CloseParen)) + { + var parameter = ParseType(); + parameters.Add(parameter); + if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen }) + { + _diagnostics.Add(Diagnostic + .Warning("Missing comma between func type arguments") + .WithHelp("Add a ',' to separate arguments") + .At(nextToken) + .Build()); + } + } + + var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType(); + + return new NubFuncType(returnType, parameters); + } if (TryExpectSymbol(Symbol.OpenBracket)) { diff --git a/src/lang/Nub.Lang/Frontend/Typing/NubType.cs b/src/lang/Nub.Lang/Frontend/Typing/NubType.cs index f67e4e3..aee1a68 100644 --- a/src/lang/Nub.Lang/Frontend/Typing/NubType.cs +++ b/src/lang/Nub.Lang/Frontend/Typing/NubType.cs @@ -48,6 +48,27 @@ public abstract class NubType public abstract override string ToString(); } +public class NubFuncType(NubType returnType, List parameters) : NubType +{ + public NubType ReturnType { get; } = returnType; + public List Parameters { get; } = parameters; + + public override bool Equals(object? obj) + { + return obj is NubFuncType other && other.ReturnType.Equals(ReturnType) && other.Parameters.SequenceEqual(Parameters); + } + + public override int GetHashCode() + { + return HashCode.Combine(ReturnType, Parameters); + } + + public override string ToString() + { + return $"func({string.Join(", ", Parameters)}): {ReturnType}"; + } +} + public class NubStructType(string @namespace, string name) : NubType { public string Namespace { get; } = @namespace; diff --git a/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs b/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs index faf10f8..e7057d0 100644 --- a/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs +++ b/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs @@ -169,7 +169,7 @@ public class TypeChecker private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment) { - if (!_variables.TryGetValue(variableAssignment.Identifier.Identifier, out var variable)) + if (!_variables.TryGetValue(variableAssignment.Identifier.Name, out var variable)) { ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment); return; @@ -243,48 +243,34 @@ public class TypeChecker return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity); } - private NubType? TypeCheckFuncCall(FuncCallNode funcCall, Node node) + private NubType? TypeCheckFuncCall(FuncCallNode funcCall) { - var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name); - if (funcDefinition == null) + var identType = TypeCheckExpression(funcCall.Expression); + if (identType is not NubFuncType funcType) { - ReportError($"Function '{funcCall}' is not defined", node); + ReportError("Cannot call function on non-function type", funcCall); return null; } - if (funcDefinition.Parameters.Take(funcDefinition.Parameters.Count - 1).Any(x => x.Variadic)) + if (funcCall.Parameters.Count != funcType.Parameters.Count) { - ReportError($"Function '{funcCall}' has multiple variadic parameters", node); - return null; + ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall); } - + for (var i = 0; i < funcCall.Parameters.Count; i++) { - NubType paramType; - if (i < funcDefinition.Parameters.Count) - { - paramType = funcDefinition.Parameters[i].Type; - } - else if (funcDefinition.Parameters.LastOrDefault()?.Variadic ?? false) - { - paramType = funcDefinition.Parameters[^1].Type; - } - else - { - ReportError($"Function '{funcCall}' does not take {funcCall.Parameters.Count} parameters", node); - continue; - } - - var argType = TypeCheckExpression(funcCall.Parameters[i], paramType); - if (argType == null) return null; + var parameter = funcCall.Parameters[i]; + var parameterType = TypeCheckExpression(parameter); + if (parameterType == null) return null; - if (!NubType.IsCompatibleWith(argType, paramType)) + if (!NubType.IsCompatibleWith(parameterType, funcType.Parameters[i])) { - ReportError($"Parameter {i + 1} of function '{funcCall}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]); + ReportError($"'{parameterType}' does not match expected type {funcType.Parameters[i]}", funcCall); + return null; } } - return funcDefinition.ReturnType; + return funcType.ReturnType; } private void TypeCheckIf(IfNode ifNode) @@ -364,7 +350,7 @@ public class TypeChecker BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr), DereferenceNode dereference => TypeCheckDereference(dereference), FixedArrayInitializerNode fixedArray => TypeCheckFixedInitializerArray(fixedArray), - FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr, funcCallExpr), + FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr), StructInitializerNode structInit => TypeCheckStructInitializer(structInit), UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression), MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess), @@ -447,13 +433,24 @@ public class TypeChecker private NubType? TypeCheckIdentifier(IdentifierNode identifier) { - if (!_variables.TryGetValue(identifier.Identifier, out var varType)) + var result = _variables.GetValueOrDefault(identifier.Name); + + if (result == null) { - ReportError($"Variable '{identifier.Identifier}' is not defined", identifier); + var func = LookupFuncSignature(identifier.Namespace, identifier.Name); + if (func != null) + { + result = new NubFuncType(func.ReturnType, func.Parameters.Select(p => p.Type).ToList()); + } + } + + if (result == null) + { + ReportError($"Identifier '{identifier.Name}' is not defined", identifier); return null; } - return varType; + return result; } private NubType? TypeCheckAddressOf(AddressOfNode addressOf)