Function pointers are working

This commit is contained in:
nub31
2025-06-08 15:54:36 +02:00
parent 535bb1bf38
commit de071808e6
10 changed files with 165 additions and 152 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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")
}

View File

@@ -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<string>();
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";
}
}

View File

@@ -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}";
}

View File

@@ -2,11 +2,10 @@
namespace Nub.Lang.Frontend.Parsing.Expressions;
public class FuncCallNode(IReadOnlyList<Token> tokens, string @namespace, string name, List<ExpressionNode> parameters) : ExpressionNode(tokens)
public class FuncCallNode(IReadOnlyList<Token> tokens, ExpressionNode expression, List<ExpressionNode> parameters) : ExpressionNode(tokens)
{
public string Namespace { get; } = @namespace;
public string Name { get; } = name;
public ExpressionNode Expression = expression;
public List<ExpressionNode> Parameters { get; } = parameters;
public override string ToString() => $"{Name}::{Name}()";
public override string ToString() => $"{Expression}({string.Join(", ", Parameters)})";
}

View File

@@ -2,9 +2,10 @@
namespace Nub.Lang.Frontend.Parsing.Expressions;
public class IdentifierNode(IReadOnlyList<Token> tokens, string identifier) : LValueNode(tokens)
public class IdentifierNode(IReadOnlyList<Token> 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;
}

View File

@@ -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<ExpressionNode>();
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<ExpressionNode>();
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<ExpressionNode>();
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<NubType> 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))
{

View File

@@ -48,6 +48,27 @@ public abstract class NubType
public abstract override string ToString();
}
public class NubFuncType(NubType returnType, List<NubType> parameters) : NubType
{
public NubType ReturnType { get; } = returnType;
public List<NubType> 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;

View File

@@ -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)