Fix type checking for anonymous functions
This commit is contained in:
8
build.sh
8
build.sh
@@ -1,16 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
./clean.sh
|
||||
|
||||
mkdir -p out
|
||||
|
||||
dotnet build src/lang/Nub.Lang.CLI
|
||||
|
||||
nub example > out/out.qbe
|
||||
nub example > out/out.ssa
|
||||
|
||||
nasm -g -felf64 src/runtime/runtime.asm -o out/runtime.o
|
||||
nasm -g -felf64 src/runtime/core/syscall.asm -o out/syscall.o
|
||||
|
||||
qbe out/out.qbe > out/out.s
|
||||
qbe out/out.ssa > out/out.s
|
||||
|
||||
gcc -c -g out/out.s -o out/out.o
|
||||
gcc -nostartfiles -o out/program out/runtime.o out/syscall.o out/out.o
|
||||
gcc -nostartfiles -o out/program out/runtime.o out/syscall.o out/out.o -no-pie
|
||||
@@ -2,23 +2,42 @@ namespace main
|
||||
|
||||
struct Human {
|
||||
age: u64
|
||||
print_age: func() = () => {
|
||||
|
||||
print_age: func() = func() {
|
||||
c::puts("pwp")
|
||||
}
|
||||
}
|
||||
|
||||
export func main(args: []^string): i64 {
|
||||
let x = 2
|
||||
|
||||
let uwu = func() {
|
||||
c::puts("uwu")
|
||||
}
|
||||
|
||||
uwu()
|
||||
|
||||
func() {
|
||||
c::puts("owo")
|
||||
}()
|
||||
|
||||
let me = alloc Human {
|
||||
age = 23
|
||||
}
|
||||
|
||||
me.print_age()
|
||||
|
||||
print_age()
|
||||
if true {
|
||||
// do something
|
||||
}
|
||||
|
||||
c::puts("test")
|
||||
|
||||
let i = 1
|
||||
while i <= 10 {
|
||||
c::puts("test")
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func print_age() {
|
||||
c::puts("TEST")
|
||||
}
|
||||
1
run.sh
1
run.sh
@@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
./clean.sh
|
||||
./build.sh
|
||||
bash -c './out/program; echo "Process exited with status code $?"'
|
||||
|
||||
@@ -24,6 +24,8 @@ public class QBEGenerator
|
||||
private int _labelIndex;
|
||||
private bool _codeIsReachable = true;
|
||||
private Dictionary<IFuncSignature, string> _funcNames = [];
|
||||
private Dictionary<AnonymousFuncNode, string> _anonymousFunctions = [];
|
||||
private int _anonymousFuncIndex;
|
||||
|
||||
public string Generate(List<SourceFile> sourceFiles)
|
||||
{
|
||||
@@ -34,8 +36,10 @@ public class QBEGenerator
|
||||
_funcNames = [];
|
||||
_breakLabels = [];
|
||||
_continueLabels = [];
|
||||
_anonymousFunctions = [];
|
||||
_variableIndex = 0;
|
||||
_labelIndex = 0;
|
||||
_anonymousFuncIndex = 0;
|
||||
_codeIsReachable = true;
|
||||
|
||||
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
|
||||
@@ -69,7 +73,13 @@ public class QBEGenerator
|
||||
|
||||
foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<LocalFuncDefinitionNode>())
|
||||
{
|
||||
GenerateFuncDefinition(funcDef);
|
||||
GenerateFuncDefinition(_funcNames[funcDef], funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported);
|
||||
_builder.AppendLine();
|
||||
}
|
||||
|
||||
foreach (var (func, name) in _anonymousFunctions)
|
||||
{
|
||||
GenerateFuncDefinition(name, func.Parameters, func.ReturnType, func.Body, false);
|
||||
_builder.AppendLine();
|
||||
}
|
||||
|
||||
@@ -304,19 +314,19 @@ public class QBEGenerator
|
||||
};
|
||||
}
|
||||
|
||||
private void GenerateFuncDefinition(LocalFuncDefinitionNode node)
|
||||
private void GenerateFuncDefinition(string name, List<FuncParameter> parameters, NubType returnType, BlockNode body, bool exported)
|
||||
{
|
||||
_variables.Clear();
|
||||
|
||||
if (node.Exported)
|
||||
if (exported)
|
||||
{
|
||||
_builder.Append("export ");
|
||||
}
|
||||
|
||||
_builder.Append("function ");
|
||||
if (node.ReturnType is not NubVoidType)
|
||||
if (returnType is not NubVoidType)
|
||||
{
|
||||
_builder.Append(node.ReturnType switch
|
||||
_builder.Append(returnType switch
|
||||
{
|
||||
NubArrayType => "l",
|
||||
NubPointerType => "l",
|
||||
@@ -338,14 +348,14 @@ 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")
|
||||
_ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type")
|
||||
});
|
||||
_builder.Append(' ');
|
||||
}
|
||||
|
||||
_builder.Append(_funcNames[node]);
|
||||
_builder.Append(name);
|
||||
|
||||
var parameterStrings = node.Parameters.Select(parameter => $"{parameter.Type switch
|
||||
var parameterStrings = parameters.Select(parameter => $"{parameter.Type switch
|
||||
{
|
||||
NubArrayType => "l",
|
||||
NubPointerType => "l",
|
||||
@@ -373,7 +383,7 @@ public class QBEGenerator
|
||||
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
|
||||
_builder.AppendLine("@start");
|
||||
|
||||
foreach (var parameter in node.Parameters)
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
var parameterName = "%" + parameter.Name;
|
||||
|
||||
@@ -403,11 +413,11 @@ public class QBEGenerator
|
||||
_variables[parameter.Name] = parameterName;
|
||||
}
|
||||
|
||||
GenerateBlock(node.Body);
|
||||
GenerateBlock(body);
|
||||
|
||||
if (node.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||
if (body.Statements.LastOrDefault() is not ReturnNode)
|
||||
{
|
||||
if (node.ReturnType is NubVoidType)
|
||||
if (returnType is NubVoidType)
|
||||
{
|
||||
_builder.AppendLine(" ret");
|
||||
}
|
||||
@@ -673,6 +683,7 @@ public class QBEGenerator
|
||||
return expression switch
|
||||
{
|
||||
AddressOfNode addressOf => GenerateAddressOf(addressOf),
|
||||
AnonymousFuncNode anonymousFunc => GenerateAnonymousFunc(anonymousFunc),
|
||||
ArrayIndexAccessNode arrayIndex => GenerateArrayAccessIndex(arrayIndex),
|
||||
ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer),
|
||||
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
|
||||
@@ -688,6 +699,16 @@ public class QBEGenerator
|
||||
};
|
||||
}
|
||||
|
||||
private string GenerateAnonymousFunc(AnonymousFuncNode anonymousFunc)
|
||||
{
|
||||
var name = $"$anon_func{++_anonymousFuncIndex}";
|
||||
_anonymousFunctions[anonymousFunc] = name;
|
||||
var pointer = GenVarName();
|
||||
_builder.AppendLine($" {pointer} =l alloc8 8");
|
||||
_builder.AppendLine($" storel {name}, {pointer}");
|
||||
return pointer;
|
||||
}
|
||||
|
||||
private string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess)
|
||||
{
|
||||
var array = GenerateExpression(arrayIndexAccess.Array);
|
||||
@@ -1037,11 +1058,24 @@ public class QBEGenerator
|
||||
{
|
||||
if (_variables.TryGetValue(identifier.Name, out var value))
|
||||
{
|
||||
return value;
|
||||
if (IsLargeType(identifier.Type))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = GenVarName();
|
||||
_builder.AppendLine($" {result} {QBEAssign(identifier.Type)} {QBELoad(identifier.Type)} {value}");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _funcNames[LookupFuncSignature(identifier.Namespace, identifier.Name)];
|
||||
var funcName = _funcNames[LookupFuncSignature(identifier.Namespace, identifier.Name)];
|
||||
var pointer = GenVarName();
|
||||
_builder.AppendLine($" {pointer} =l alloc8 8");
|
||||
_builder.AppendLine($" storel {funcName}, {pointer}");
|
||||
return pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1270,7 +1304,10 @@ public class QBEGenerator
|
||||
parameterStrings.Add($"{qbeParameterType} {result}");
|
||||
}
|
||||
|
||||
var funcPointer = GenerateExpression(funcCall.Expression);
|
||||
// var funcPointer = GenerateExpression(funcCall.Expression);
|
||||
var funcPointerPointer = GenerateExpression(funcCall.Expression);
|
||||
var funcPointer = GenVarName();
|
||||
_builder.AppendLine($" {funcPointer} =l loadl {funcPointerPointer}");
|
||||
|
||||
if (funcType.ReturnType is not NubVoidType)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Nub.Lang.Frontend.Lexing;
|
||||
using Nub.Lang.Frontend.Parsing.Definitions;
|
||||
using Nub.Lang.Frontend.Parsing.Statements;
|
||||
using Nub.Lang.Frontend.Typing;
|
||||
|
||||
namespace Nub.Lang.Frontend.Parsing.Expressions;
|
||||
|
||||
public class AnonymousFuncNode(IReadOnlyList<Token> tokens, List<FuncParameter> parameters, BlockNode body, NubType returnType) : ExpressionNode(tokens)
|
||||
{
|
||||
public List<FuncParameter> Parameters { get; } = parameters;
|
||||
public BlockNode Body { get; } = body;
|
||||
public NubType ReturnType { get; } = returnType;
|
||||
}
|
||||
@@ -437,6 +437,31 @@ public class Parser
|
||||
{
|
||||
switch (symbolToken.Symbol)
|
||||
{
|
||||
case Symbol.Func:
|
||||
{
|
||||
List<FuncParameter> parameters = [];
|
||||
ExpectSymbol(Symbol.OpenParen);
|
||||
while (!TryExpectSymbol(Symbol.CloseParen))
|
||||
{
|
||||
var parameter = ParseFuncParameter();
|
||||
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 function arguments")
|
||||
.WithHelp("Add a ',' to separate arguments")
|
||||
.At(nextToken)
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
|
||||
var returnType = TryExpectSymbol(Symbol.Colon) ? ParseType() : new NubVoidType();
|
||||
|
||||
var body = ParseBlock();
|
||||
|
||||
expr = new AnonymousFuncNode(GetTokensForNode(startIndex), parameters, body, returnType);
|
||||
break;
|
||||
}
|
||||
case Symbol.OpenParen:
|
||||
{
|
||||
var expression = ParseExpression();
|
||||
|
||||
@@ -14,6 +14,7 @@ public class TypeChecker
|
||||
private List<SourceFile> _sourceFiles = [];
|
||||
private List<Diagnostic> _diagnostics = [];
|
||||
private NubType? _currentFunctionReturnType;
|
||||
private List<AnonymousFuncNode> _anonymousFunctions = [];
|
||||
|
||||
public DiagnosticsResult TypeCheck(List<SourceFile> sourceFiles)
|
||||
{
|
||||
@@ -21,6 +22,7 @@ public class TypeChecker
|
||||
_diagnostics = [];
|
||||
_currentFunctionReturnType = null;
|
||||
_sourceFiles = sourceFiles;
|
||||
_anonymousFunctions = [];
|
||||
|
||||
var externFuncDefinitions = _sourceFiles
|
||||
.SelectMany(f => f.Definitions)
|
||||
@@ -50,7 +52,12 @@ public class TypeChecker
|
||||
|
||||
foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<LocalFuncDefinitionNode>())
|
||||
{
|
||||
TypeCheckFuncDef(funcDef);
|
||||
TypeCheckFuncDef(funcDef.Parameters, funcDef.Body, funcDef.ReturnType);
|
||||
}
|
||||
|
||||
foreach (var anonymousFuncNode in _anonymousFunctions)
|
||||
{
|
||||
TypeCheckFuncDef(anonymousFuncNode.Parameters, anonymousFuncNode.Body, anonymousFuncNode.ReturnType);
|
||||
}
|
||||
|
||||
return new DiagnosticsResult(_diagnostics);
|
||||
@@ -80,17 +87,17 @@ public class TypeChecker
|
||||
}
|
||||
}
|
||||
|
||||
private void TypeCheckFuncDef(LocalFuncDefinitionNode funcDef)
|
||||
private void TypeCheckFuncDef(List<FuncParameter> parameters, BlockNode body, NubType returnType)
|
||||
{
|
||||
_variables.Clear();
|
||||
_currentFunctionReturnType = funcDef.ReturnType;
|
||||
_currentFunctionReturnType = returnType;
|
||||
|
||||
foreach (var param in funcDef.Parameters)
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
_variables[param.Name] = param.Type;
|
||||
}
|
||||
|
||||
TypeCheckBlock(funcDef.Body);
|
||||
TypeCheckBlock(body);
|
||||
}
|
||||
|
||||
private void TypeCheckBlock(BlockNode block)
|
||||
@@ -343,6 +350,7 @@ public class TypeChecker
|
||||
var resultType = expression switch
|
||||
{
|
||||
AddressOfNode addressOf => TypeCheckAddressOf(addressOf),
|
||||
AnonymousFuncNode anonymousFunc => TypeCheckAnonymousFunc(anonymousFunc),
|
||||
ArrayIndexAccessNode arrayIndex => TypeCheckArrayIndex(arrayIndex),
|
||||
ArrayInitializerNode arrayInitializer => TypeCheckArrayInitializer(arrayInitializer),
|
||||
LiteralNode literal => TypeCheckLiteral(literal, expectedType),
|
||||
@@ -365,6 +373,12 @@ public class TypeChecker
|
||||
return resultType;
|
||||
}
|
||||
|
||||
private NubType TypeCheckAnonymousFunc(AnonymousFuncNode anonymousFunc)
|
||||
{
|
||||
_anonymousFunctions.Add(anonymousFunc);
|
||||
return new NubFuncType(anonymousFunc.ReturnType, anonymousFunc.Parameters.Select(p => p.Type).ToList());
|
||||
}
|
||||
|
||||
private NubType? TypeCheckLiteral(LiteralNode literal, NubType? expectedType = null)
|
||||
{
|
||||
if (expectedType != null)
|
||||
|
||||
Reference in New Issue
Block a user