Function pointers are working
This commit is contained in:
@@ -337,4 +337,4 @@ namespace sys
|
|||||||
// let rseq: i64 = 334
|
// let rseq: i64 = 334
|
||||||
// let pkey_mprotect: i64 = 335
|
// 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
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
namespace c
|
namespace c
|
||||||
|
|
||||||
extern func printf(fmt: ^u8, ...args: any): void
|
// extern func printf(fmt: ^u8, ...args: any): void
|
||||||
extern func getchar(): i32
|
// extern func getchar(): i32
|
||||||
extern func puts(fmt: ^u8)
|
extern func puts(fmt: []u8)
|
||||||
|
|
||||||
extern func malloc(size: i64): ^void
|
// extern func malloc(size: i64): ^void
|
||||||
extern func calloc(num: i64, size: i64): ^void
|
// extern func calloc(num: i64, size: i64): ^void
|
||||||
extern func realloc(ptr: ^void, size: i64): ^void
|
// extern func realloc(ptr: ^void, size: i64): ^void
|
||||||
extern func free(ptr: ^void)
|
// extern func free(ptr: ^void)
|
||||||
|
|
||||||
extern func sin(x: f64): f64
|
// extern func sin(x: f64): f64
|
||||||
extern func cos(x: f64): f64
|
// extern func cos(x: f64): f64
|
||||||
extern func tan(x: f64): f64
|
// extern func tan(x: f64): f64
|
||||||
extern func sqrt(x: f64): f64
|
// extern func sqrt(x: f64): f64
|
||||||
extern func pow(x: f64, y: f64): f64
|
// extern func pow(x: f64, y: f64): f64
|
||||||
extern func abs(x: i32): i32
|
// extern func abs(x: i32): i32
|
||||||
|
|
||||||
extern func time(t: ^i64): i64
|
// extern func time(t: ^i64): i64
|
||||||
extern func clock(): i64
|
// extern func clock(): i64
|
||||||
extern func sleep(seconds: u32): i32
|
// extern func sleep(seconds: u32): i32
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
namespace main
|
namespace main
|
||||||
|
|
||||||
struct Human {
|
struct Human {
|
||||||
age: ^u64
|
age: u64
|
||||||
|
print_age: func() = () => {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export func main(args: []^string): i64 {
|
export func main(args: []^string): i64 {
|
||||||
let age: u64 = 23
|
|
||||||
|
|
||||||
let me = alloc Human {
|
let me = alloc Human {
|
||||||
age = &age
|
age = 23
|
||||||
}
|
}
|
||||||
|
|
||||||
me.age^ = 24.5
|
me.print_age()
|
||||||
|
|
||||||
test(me.age^)
|
print_age()
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func test(me: u64) {
|
func print_age() {
|
||||||
c::printf("%d\n", me)
|
c::puts("TEST")
|
||||||
}
|
}
|
||||||
@@ -107,6 +107,7 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType => "l",
|
NubStructType => "l",
|
||||||
NubFixedArrayType => "l",
|
NubFixedArrayType => "l",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
|
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
@@ -134,6 +135,7 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType => "l",
|
NubStructType => "l",
|
||||||
NubFixedArrayType => "l",
|
NubFixedArrayType => "l",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
|
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
@@ -161,6 +163,7 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType => "l",
|
NubStructType => "l",
|
||||||
NubFixedArrayType => "l",
|
NubFixedArrayType => "l",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
|
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
|
||||||
}}";
|
}}";
|
||||||
}
|
}
|
||||||
@@ -195,17 +198,16 @@ public class QBEGenerator
|
|||||||
case NubStructType nubStructType:
|
case NubStructType nubStructType:
|
||||||
{
|
{
|
||||||
var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name);
|
var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name);
|
||||||
// Struct alignment is the maximum alignment of its fields
|
|
||||||
return definition.Fields.Max(f => AlignmentOf(f.Type));
|
return definition.Fields.Max(f => AlignmentOf(f.Type));
|
||||||
}
|
}
|
||||||
case NubPointerType:
|
case NubPointerType:
|
||||||
case NubArrayType:
|
case NubArrayType:
|
||||||
|
case NubFuncType:
|
||||||
{
|
{
|
||||||
return 8; // Pointer alignment (assuming 64-bit)
|
return 8;
|
||||||
}
|
}
|
||||||
case NubFixedArrayType nubFixedArrayType:
|
case NubFixedArrayType nubFixedArrayType:
|
||||||
{
|
{
|
||||||
// Array alignment is same as element alignment
|
|
||||||
return AlignmentOf(nubFixedArrayType.ElementType);
|
return AlignmentOf(nubFixedArrayType.ElementType);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -261,27 +263,25 @@ public class QBEGenerator
|
|||||||
int fieldAlignment = AlignmentOf(field.Type);
|
int fieldAlignment = AlignmentOf(field.Type);
|
||||||
maxAlignment = Math.Max(maxAlignment, fieldAlignment);
|
maxAlignment = Math.Max(maxAlignment, fieldAlignment);
|
||||||
|
|
||||||
// Align current position for this field
|
|
||||||
size = AlignTo(size, fieldAlignment);
|
size = AlignTo(size, fieldAlignment);
|
||||||
|
|
||||||
// Add field size
|
|
||||||
size += SizeOf(field.Type);
|
size += SizeOf(field.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Align final size to struct's alignment for array compatibility
|
|
||||||
size = AlignTo(size, maxAlignment);
|
size = AlignTo(size, maxAlignment);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
case NubPointerType:
|
case NubPointerType:
|
||||||
case NubArrayType:
|
case NubArrayType:
|
||||||
|
case NubFuncType:
|
||||||
{
|
{
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
case NubFixedArrayType nubFixedArrayType:
|
case NubFixedArrayType nubFixedArrayType:
|
||||||
{
|
{
|
||||||
int elementSize = SizeOf(nubFixedArrayType.ElementType);
|
int elementSize = SizeOf(nubFixedArrayType.ElementType);
|
||||||
return elementSize * nubFixedArrayType.Capacity + 8; // +8 for length/capacity info
|
return elementSize * nubFixedArrayType.Capacity + 8;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@@ -299,6 +299,7 @@ public class QBEGenerator
|
|||||||
NubPrimitiveType => false,
|
NubPrimitiveType => false,
|
||||||
NubStructType => true,
|
NubStructType => true,
|
||||||
NubFixedArrayType => true,
|
NubFixedArrayType => true,
|
||||||
|
NubFuncType => false,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type))
|
_ => throw new ArgumentOutOfRangeException(nameof(type))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -336,6 +337,7 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
||||||
NubFixedArrayType => "l",
|
NubFixedArrayType => "l",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{node.ReturnType}' type cannot be used as a function return type")
|
_ => throw new NotSupportedException($"'{node.ReturnType}' type cannot be used as a function return type")
|
||||||
});
|
});
|
||||||
_builder.Append(' ');
|
_builder.Append(' ');
|
||||||
@@ -343,7 +345,7 @@ public class QBEGenerator
|
|||||||
|
|
||||||
_builder.Append(_funcNames[node]);
|
_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",
|
NubArrayType => "l",
|
||||||
NubPointerType => "l",
|
NubPointerType => "l",
|
||||||
@@ -364,6 +366,7 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
||||||
NubFixedArrayType => "l",
|
NubFixedArrayType => "l",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type")
|
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type")
|
||||||
}} %{parameter.Name}");
|
}} %{parameter.Name}");
|
||||||
|
|
||||||
@@ -439,6 +442,7 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
||||||
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
|
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
|
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
|
||||||
};
|
};
|
||||||
_builder.Append(fieldQbeType + ", ");
|
_builder.Append(fieldQbeType + ", ");
|
||||||
@@ -619,7 +623,7 @@ public class QBEGenerator
|
|||||||
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
|
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
|
||||||
{
|
{
|
||||||
var value = GenerateExpression(variableAssignment.Value);
|
var value = GenerateExpression(variableAssignment.Value);
|
||||||
var pointer = _variables[variableAssignment.Identifier.Identifier];
|
var pointer = _variables[variableAssignment.Identifier.Name];
|
||||||
GenerateCopy(variableAssignment.Value.Type, value, pointer);
|
GenerateCopy(variableAssignment.Value.Type, value, pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,7 +799,7 @@ public class QBEGenerator
|
|||||||
case DereferenceNode dereference:
|
case DereferenceNode dereference:
|
||||||
return GenerateExpression(dereference.Expression);
|
return GenerateExpression(dereference.Expression);
|
||||||
case IdentifierNode identifier:
|
case IdentifierNode identifier:
|
||||||
return _variables[identifier.Identifier];
|
return _variables[identifier.Name];
|
||||||
case MemberAccessNode memberAccess:
|
case MemberAccessNode memberAccess:
|
||||||
return GenerateMemberAccessPointer(memberAccess);
|
return GenerateMemberAccessPointer(memberAccess);
|
||||||
default:
|
default:
|
||||||
@@ -1031,7 +1035,14 @@ public class QBEGenerator
|
|||||||
|
|
||||||
private string GenerateIdentifier(IdentifierNode identifier)
|
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)
|
private string GenerateLiteral(LiteralNode literal)
|
||||||
@@ -1223,21 +1234,12 @@ public class QBEGenerator
|
|||||||
|
|
||||||
private string GenerateFuncCall(FuncCallNode funcCall)
|
private string GenerateFuncCall(FuncCallNode funcCall)
|
||||||
{
|
{
|
||||||
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name);
|
var funcType = (NubFuncType)funcCall.Expression.Type;
|
||||||
if (funcDefinition == null)
|
|
||||||
{
|
|
||||||
throw new Exception($"Unknown function {funcCall}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameterStrings = new List<string>();
|
var parameterStrings = new List<string>();
|
||||||
|
|
||||||
for (var i = 0; i < funcCall.Parameters.Count; i++)
|
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 parameter = funcCall.Parameters[i];
|
||||||
var result = GenerateExpression(parameter);
|
var result = GenerateExpression(parameter);
|
||||||
|
|
||||||
@@ -1262,22 +1264,23 @@ public class QBEGenerator
|
|||||||
},
|
},
|
||||||
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
|
||||||
NubFixedArrayType => "l",
|
NubFixedArrayType => "l",
|
||||||
|
NubFuncType => "l",
|
||||||
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
|
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
|
||||||
};
|
};
|
||||||
parameterStrings.Add($"{qbeParameterType} {result}");
|
parameterStrings.Add($"{qbeParameterType} {result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var funcName = _funcNames[funcDefinition];
|
var funcPointer = GenerateExpression(funcCall.Expression);
|
||||||
|
|
||||||
if (funcDefinition.ReturnType is not NubVoidType)
|
if (funcType.ReturnType is not NubVoidType)
|
||||||
{
|
{
|
||||||
var outputName = GenVarName();
|
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;
|
return outputName;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_builder.AppendLine($" call {funcName}({string.Join(", ", parameterStrings)})");
|
_builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})");
|
||||||
return "fuck";
|
return "fuck";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ using Nub.Lang.Frontend.Typing;
|
|||||||
|
|
||||||
namespace Nub.Lang.Frontend.Parsing.Definitions;
|
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 string Name { get; } = name;
|
||||||
public NubType Type { get; } = type;
|
public NubType Type { get; } = type;
|
||||||
public bool Variadic { get; } = variadic;
|
|
||||||
|
|
||||||
public override string ToString() => $"{Name}: {Type}";
|
public override string ToString() => $"{Name}: {Type}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace Nub.Lang.Frontend.Parsing.Expressions;
|
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 ExpressionNode Expression = expression;
|
||||||
public string Name { get; } = name;
|
|
||||||
public List<ExpressionNode> Parameters { get; } = parameters;
|
public List<ExpressionNode> Parameters { get; } = parameters;
|
||||||
|
|
||||||
public override string ToString() => $"{Name}::{Name}()";
|
public override string ToString() => $"{Expression}({string.Join(", ", Parameters)})";
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
namespace Nub.Lang.Frontend.Parsing.Expressions;
|
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;
|
||||||
}
|
}
|
||||||
@@ -167,19 +167,11 @@ public class Parser
|
|||||||
|
|
||||||
private FuncParameter ParseFuncParameter()
|
private FuncParameter ParseFuncParameter()
|
||||||
{
|
{
|
||||||
var variadic = false;
|
|
||||||
if (TryExpectSymbol(Symbol.Period))
|
|
||||||
{
|
|
||||||
ExpectSymbol(Symbol.Period);
|
|
||||||
ExpectSymbol(Symbol.Period);
|
|
||||||
variadic = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = ExpectIdentifier();
|
var name = ExpectIdentifier();
|
||||||
ExpectSymbol(Symbol.Colon);
|
ExpectSymbol(Symbol.Colon);
|
||||||
var type = ParseType();
|
var type = ParseType();
|
||||||
|
|
||||||
return new FuncParameter(name.Value, type, variadic);
|
return new FuncParameter(name.Value, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatementNode ParseStatement()
|
private StatementNode ParseStatement()
|
||||||
@@ -430,58 +422,15 @@ public class Parser
|
|||||||
}
|
}
|
||||||
case IdentifierToken identifier:
|
case IdentifierToken identifier:
|
||||||
{
|
{
|
||||||
var next = Peek();
|
var @namespace = _namespace;
|
||||||
switch (next.Value)
|
var name = identifier.Value;
|
||||||
|
if (TryExpectSymbol(Symbol.DoubleColon))
|
||||||
{
|
{
|
||||||
case SymbolToken { Symbol: Symbol.DoubleColon }:
|
@namespace = identifier.Value;
|
||||||
{
|
name = ExpectIdentifier().Value;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr = new IdentifierNode(GetTokensForNode(startIndex), @namespace, name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SymbolToken symbolToken:
|
case SymbolToken symbolToken:
|
||||||
@@ -628,6 +577,26 @@ public class Parser
|
|||||||
continue;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,6 +658,29 @@ public class Parser
|
|||||||
return new NubPointerType(baseType);
|
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))
|
if (TryExpectSymbol(Symbol.OpenBracket))
|
||||||
{
|
{
|
||||||
if (Peek().TryGetValue(out var token) && token is LiteralToken { Kind: LiteralKind.Integer, Value: var sizeValue })
|
if (Peek().TryGetValue(out var token) && token is LiteralToken { Kind: LiteralKind.Integer, Value: var sizeValue })
|
||||||
|
|||||||
@@ -48,6 +48,27 @@ public abstract class NubType
|
|||||||
public abstract override string ToString();
|
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 class NubStructType(string @namespace, string name) : NubType
|
||||||
{
|
{
|
||||||
public string Namespace { get; } = @namespace;
|
public string Namespace { get; } = @namespace;
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public class TypeChecker
|
|||||||
|
|
||||||
private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment)
|
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);
|
ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment);
|
||||||
return;
|
return;
|
||||||
@@ -243,48 +243,34 @@ public class TypeChecker
|
|||||||
return new NubFixedArrayType(fixedArrayInitializer.ElementType, fixedArrayInitializer.Capacity);
|
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);
|
var identType = TypeCheckExpression(funcCall.Expression);
|
||||||
if (funcDefinition == null)
|
if (identType is not NubFuncType funcType)
|
||||||
{
|
{
|
||||||
ReportError($"Function '{funcCall}' is not defined", node);
|
ReportError("Cannot call function on non-function type", funcCall);
|
||||||
return null;
|
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);
|
ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < funcCall.Parameters.Count; i++)
|
for (var i = 0; i < funcCall.Parameters.Count; i++)
|
||||||
{
|
{
|
||||||
NubType paramType;
|
var parameter = funcCall.Parameters[i];
|
||||||
if (i < funcDefinition.Parameters.Count)
|
var parameterType = TypeCheckExpression(parameter);
|
||||||
{
|
if (parameterType == null) return null;
|
||||||
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 (!NubType.IsCompatibleWith(parameterType, funcType.Parameters[i]))
|
||||||
if (argType == null) return null;
|
|
||||||
|
|
||||||
if (!NubType.IsCompatibleWith(argType, paramType))
|
|
||||||
{
|
{
|
||||||
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)
|
private void TypeCheckIf(IfNode ifNode)
|
||||||
@@ -364,7 +350,7 @@ public class TypeChecker
|
|||||||
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
|
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
|
||||||
DereferenceNode dereference => TypeCheckDereference(dereference),
|
DereferenceNode dereference => TypeCheckDereference(dereference),
|
||||||
FixedArrayInitializerNode fixedArray => TypeCheckFixedInitializerArray(fixedArray),
|
FixedArrayInitializerNode fixedArray => TypeCheckFixedInitializerArray(fixedArray),
|
||||||
FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr, funcCallExpr),
|
FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr),
|
||||||
StructInitializerNode structInit => TypeCheckStructInitializer(structInit),
|
StructInitializerNode structInit => TypeCheckStructInitializer(structInit),
|
||||||
UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression),
|
UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression),
|
||||||
MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess),
|
MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess),
|
||||||
@@ -447,13 +433,24 @@ public class TypeChecker
|
|||||||
|
|
||||||
private NubType? TypeCheckIdentifier(IdentifierNode identifier)
|
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 null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return varType;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NubType? TypeCheckAddressOf(AddressOfNode addressOf)
|
private NubType? TypeCheckAddressOf(AddressOfNode addressOf)
|
||||||
|
|||||||
Reference in New Issue
Block a user