This commit is contained in:
nub31
2025-09-29 16:23:39 +02:00
parent 857e29d77b
commit c0948e856a
13 changed files with 568 additions and 474 deletions

View File

@@ -12,4 +12,6 @@ public record StructFieldNode(string Name, NubType Type, ExpressionNode? Value)
public record StructFuncNode(string Name, string? Hook, FuncSignatureNode Signature, BlockNode Body) : Node;
public record StructNode(string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions) : DefinitionNode(Module, Name);
public record StructNode(string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions) : DefinitionNode(Module, Name);
public record GlobalVariableNode(string Module, string Name, ExpressionNode Value) : DefinitionNode(Module, Name);

View File

@@ -46,27 +46,33 @@ public sealed class TypeChecker
Definitions.Clear();
ReferencedStructTypes.Clear();
foreach (var definition in _syntaxTree.Definitions)
using (BeginRootScope(_syntaxTree.Metadata.ModuleName))
{
try
foreach (var definition in _syntaxTree.Definitions)
{
switch (definition)
try
{
case FuncSyntax funcSyntax:
Definitions.Add(CheckFuncDefinition(funcSyntax));
break;
case StructSyntax structSyntax:
Definitions.Add(CheckStructDefinition(structSyntax));
break;
case StructTemplateSyntax:
break;
default:
throw new ArgumentOutOfRangeException();
switch (definition)
{
case FuncSyntax funcSyntax:
Definitions.Add(CheckFuncDefinition(funcSyntax));
break;
case StructSyntax structSyntax:
Definitions.Add(CheckStructDefinition(structSyntax));
break;
case GlobalVariableSyntax globalVariableSyntax:
Definitions.Add(CheckGlobalVariable(globalVariableSyntax));
break;
case StructTemplateSyntax:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
catch (TypeCheckerException e)
{
Diagnostics.Add(e.Diagnostic);
}
}
catch (TypeCheckerException e)
{
Diagnostics.Add(e.Diagnostic);
}
}
}
@@ -85,9 +91,41 @@ public sealed class TypeChecker
return new ScopeDisposer(this);
}
private ScopeDisposer BeginRootScope(string module)
private ScopeDisposer BeginRootScope(string moduleName)
{
_scopes.Push(new Scope(module));
if (!_visibleModules.TryGetValue(moduleName, out var moduleScope))
{
throw new TypeCheckerException(Diagnostic.Error($"Module with name {moduleName} not found").Build());
}
var scope = new Scope(moduleName);
_scopes.Push(scope);
foreach (var globalVariable in moduleScope.GlobalVariables(true))
{
NubType? type;
if (globalVariable.ExplicitType != null)
{
type = ResolveType(globalVariable.ExplicitType);
var valueExpression = CheckExpression(globalVariable.Value, type);
if (valueExpression.Type != ResolveType(globalVariable.ExplicitType))
{
throw new TypeCheckerException(Diagnostic
.Error("Value does not match explicit type of global variable")
.At(globalVariable.Value)
.Build());
}
}
else
{
type = CheckExpression(globalVariable.Value).Type;
}
scope.DeclareVariable(new Variable(globalVariable.Name, type, VariableKind.LValue));
}
return new ScopeDisposer(this);
}
@@ -105,30 +143,27 @@ public sealed class TypeChecker
private StructNode CheckStructDefinition(StructSyntax node)
{
using (BeginRootScope(_syntaxTree.Metadata.ModuleName))
{
var fieldTypes = node.Fields
.Select(x => new NubStructFieldType(x.Name, ResolveType(x.Type), x.Value != null))
.ToList();
var fieldTypes = node.Fields
.Select(x => new NubStructFieldType(x.Name, ResolveType(x.Type), x.Value != null))
.ToList();
var fieldFunctions = node.Functions
.Select(x =>
{
var parameters = x.Signature.Parameters.Select(y => ResolveType(y.Type)).ToList();
var returnType = ResolveType(x.Signature.ReturnType);
return new NubStructFuncType(x.Name, x.Hook, parameters, returnType);
})
.ToList();
var fieldFunctions = node.Functions
.Select(x =>
{
var parameters = x.Signature.Parameters.Select(y => ResolveType(y.Type)).ToList();
var returnType = ResolveType(x.Signature.ReturnType);
return new NubStructFuncType(x.Name, x.Hook, parameters, returnType);
})
.ToList();
var structType = new NubStructType(CurrentScope.Module, node.Name, fieldTypes, fieldFunctions);
var structType = new NubStructType(CurrentScope.Module, node.Name, fieldTypes, fieldFunctions);
CurrentScope.DeclareVariable(new Variable("this", structType, VariableKind.RValue));
CurrentScope.DeclareVariable(new Variable("this", structType, VariableKind.RValue));
var fields = node.Fields.Select(CheckStructField).ToList();
var functions = node.Functions.Select(CheckStructFunc).ToList();
var fields = node.Fields.Select(CheckStructField).ToList();
var functions = node.Functions.Select(CheckStructFunc).ToList();
return new StructNode(CurrentScope.Module, node.Name, fields, functions);
}
return new StructNode(CurrentScope.Module, node.Name, fields, functions);
}
private StructFuncNode CheckStructFunc(StructFuncSyntax function)
@@ -157,42 +192,44 @@ public sealed class TypeChecker
private FuncNode CheckFuncDefinition(FuncSyntax node)
{
using (BeginRootScope(_syntaxTree.Metadata.ModuleName))
foreach (var parameter in node.Signature.Parameters)
{
foreach (var parameter in node.Signature.Parameters)
{
CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue));
}
var signature = CheckFuncSignature(node.Signature);
BlockNode? body = null;
if (node.Body != null)
{
_funcReturnTypes.Push(signature.ReturnType);
body = CheckBlock(node.Body);
if (!AlwaysReturns(body))
{
if (signature.ReturnType is NubVoidType)
{
body.Statements.Add(new ReturnNode(null));
}
else
{
Diagnostics.Add(Diagnostic
.Error("Not all code paths return a value")
.At(node.Body)
.Build());
}
}
_funcReturnTypes.Pop();
}
return new FuncNode(CurrentScope.Module, node.Name, node.ExternSymbol, signature, body);
CurrentScope.DeclareVariable(new Variable(parameter.Name, ResolveType(parameter.Type), VariableKind.RValue));
}
var signature = CheckFuncSignature(node.Signature);
BlockNode? body = null;
if (node.Body != null)
{
_funcReturnTypes.Push(signature.ReturnType);
body = CheckBlock(node.Body);
if (!AlwaysReturns(body))
{
if (signature.ReturnType is NubVoidType)
{
body.Statements.Add(new ReturnNode(null));
}
else
{
Diagnostics.Add(Diagnostic
.Error("Not all code paths return a value")
.At(node.Body)
.Build());
}
}
_funcReturnTypes.Pop();
}
return new FuncNode(CurrentScope.Module, node.Name, node.ExternSymbol, signature, body);
}
private GlobalVariableNode CheckGlobalVariable(GlobalVariableSyntax node)
{
return new GlobalVariableNode(CurrentScope.Module, node.Name, CheckExpression(node.Value));
}
private AssignmentNode CheckAssignment(AssignmentSyntax statement)
@@ -701,6 +738,19 @@ public sealed class TypeChecker
var includePrivate = expression.Module == CurrentScope.Module;
var globalVariable = module.GlobalVariables(includePrivate).FirstOrDefault(x => x.Name == expression.Name);
if (globalVariable != null)
{
// todo(nub31): This should be done in the global scope
NubType? type = null;
if (globalVariable.ExplicitType != null)
{
type = ResolveType(globalVariable.ExplicitType);
}
return CheckExpression(globalVariable.Value, type);
}
// First, look for the exported function in the specified module
var function = module.Functions(includePrivate).FirstOrDefault(x => x.Name == expression.Name);
if (function != null)
@@ -733,8 +783,8 @@ public sealed class TypeChecker
{
return floatType.Width switch
{
32 => new Float32LiteralNode(floatType, float.Parse(expression.Value)),
64 => new Float64LiteralNode(floatType, double.Parse(expression.Value)),
32 => new Float32LiteralNode(floatType, float.Parse(expression.Value, CultureInfo.InvariantCulture)),
64 => new Float64LiteralNode(floatType, double.Parse(expression.Value, CultureInfo.InvariantCulture)),
_ => throw new ArgumentOutOfRangeException()
};
}
@@ -749,6 +799,30 @@ public sealed class TypeChecker
? new Float32LiteralNode(type, float.Parse(expression.Value, CultureInfo.InvariantCulture))
: new Float64LiteralNode(type, double.Parse(expression.Value, CultureInfo.InvariantCulture));
}
case LiteralKind.Hex:
{
if (expectedType is NubIntType intType)
{
return intType.Signed
? new IntLiteralNode(intType, Convert.ToInt64(expression.Value, 16))
: new UIntLiteralNode(intType, Convert.ToUInt64(expression.Value, 16));
}
var type = new NubIntType(true, 64);
return new IntLiteralNode(type, Convert.ToInt64(expression.Value, 16));
}
case LiteralKind.Binary:
{
if (expectedType is NubIntType intType)
{
return intType.Signed
? new IntLiteralNode(intType, Convert.ToInt64(expression.Value[2..], 2))
: new UIntLiteralNode(intType, Convert.ToUInt64(expression.Value[2..], 2));
}
var type = new NubIntType(true, 64);
return new IntLiteralNode(type, Convert.ToInt64(expression.Value.Substring(2), 2));
}
case LiteralKind.String:
{
return expectedType switch