|
|
|
@@ -112,7 +112,7 @@ public class TypeChecker
|
|
|
|
private TypedNodeStatementFuncCall CheckStatementExpression(NodeStatementExpression statement)
|
|
|
|
private TypedNodeStatementFuncCall CheckStatementExpression(NodeStatementExpression statement)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (statement.Expression is not NodeExpressionFuncCall funcCall)
|
|
|
|
if (statement.Expression is not NodeExpressionFuncCall funcCall)
|
|
|
|
throw new CompileException(Diagnostic.Error("Expected statement or function call").At(fileName, statement).Build());
|
|
|
|
throw BasicError("Expected statement or function call", statement);
|
|
|
|
|
|
|
|
|
|
|
|
return new TypedNodeStatementFuncCall(statement.Tokens, CheckExpression(funcCall.Target), funcCall.Parameters.Select(CheckExpression).ToList());
|
|
|
|
return new TypedNodeStatementFuncCall(statement.Tokens, CheckExpression(funcCall.Target), funcCall.Parameters.Select(CheckExpression).ToList());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -133,7 +133,7 @@ public class TypeChecker
|
|
|
|
var value = CheckExpression(statement.Value);
|
|
|
|
var value = CheckExpression(statement.Value);
|
|
|
|
|
|
|
|
|
|
|
|
if (!value.Type.IsAssignableTo(type))
|
|
|
|
if (!value.Type.IsAssignableTo(type))
|
|
|
|
throw new CompileException(Diagnostic.Error("Type of variable does match type of assigned value").At(fileName, value).Build());
|
|
|
|
throw BasicError("Type of variable does match type of assigned value", value);
|
|
|
|
|
|
|
|
|
|
|
|
scope.DeclareIdentifier(statement.Name.Ident, type);
|
|
|
|
scope.DeclareIdentifier(statement.Name.Ident, type);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -198,10 +198,10 @@ public class TypeChecker
|
|
|
|
case NodeExpressionBinary.Op.Modulo:
|
|
|
|
case NodeExpressionBinary.Op.Modulo:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (left.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
if (left.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side arithmetic operation: {left.Type}").At(fileName, left).Build());
|
|
|
|
throw BasicError($"Unsupported type for left hand side arithmetic operation: {left.Type}", left);
|
|
|
|
|
|
|
|
|
|
|
|
if (right.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
if (right.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side arithmetic operation: {right.Type}").At(fileName, right).Build());
|
|
|
|
throw BasicError($"Unsupported type for right hand side arithmetic operation: {right.Type}", right);
|
|
|
|
|
|
|
|
|
|
|
|
type = left.Type;
|
|
|
|
type = left.Type;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -210,10 +210,10 @@ public class TypeChecker
|
|
|
|
case NodeExpressionBinary.Op.RightShift:
|
|
|
|
case NodeExpressionBinary.Op.RightShift:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (left.Type is not NubTypeUInt)
|
|
|
|
if (left.Type is not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of left/right shift operation: {left.Type}").At(fileName, left).Build());
|
|
|
|
throw BasicError($"Unsupported type for left hand side of left/right shift operation: {left.Type}", left);
|
|
|
|
|
|
|
|
|
|
|
|
if (right.Type is not NubTypeUInt)
|
|
|
|
if (right.Type is not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of left/right shift operation: {right.Type}").At(fileName, right).Build());
|
|
|
|
throw BasicError($"Unsupported type for right hand side of left/right shift operation: {right.Type}", right);
|
|
|
|
|
|
|
|
|
|
|
|
type = left.Type;
|
|
|
|
type = left.Type;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -226,10 +226,10 @@ public class TypeChecker
|
|
|
|
case NodeExpressionBinary.Op.GreaterThanOrEqual:
|
|
|
|
case NodeExpressionBinary.Op.GreaterThanOrEqual:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (left.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
if (left.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of comparison: {left.Type}").At(fileName, left).Build());
|
|
|
|
throw BasicError($"Unsupported type for left hand side of comparison: {left.Type}", left);
|
|
|
|
|
|
|
|
|
|
|
|
if (right.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
if (right.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of comparison: {right.Type}").At(fileName, right).Build());
|
|
|
|
throw BasicError($"Unsupported type for right hand side of comparison: {right.Type}", right);
|
|
|
|
|
|
|
|
|
|
|
|
type = NubTypeBool.Instance;
|
|
|
|
type = NubTypeBool.Instance;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -238,10 +238,10 @@ public class TypeChecker
|
|
|
|
case NodeExpressionBinary.Op.LogicalOr:
|
|
|
|
case NodeExpressionBinary.Op.LogicalOr:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (left.Type is not NubTypeBool)
|
|
|
|
if (left.Type is not NubTypeBool)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for left hand side of logical operation: {left.Type}").At(fileName, left).Build());
|
|
|
|
throw BasicError($"Unsupported type for left hand side of logical operation: {left.Type}", left);
|
|
|
|
|
|
|
|
|
|
|
|
if (right.Type is not NubTypeBool)
|
|
|
|
if (right.Type is not NubTypeBool)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for right hand side of logical operation: {right.Type}").At(fileName, right).Build());
|
|
|
|
throw BasicError($"Unsupported type for right hand side of logical operation: {right.Type}", right);
|
|
|
|
|
|
|
|
|
|
|
|
type = NubTypeBool.Instance;
|
|
|
|
type = NubTypeBool.Instance;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -286,7 +286,7 @@ public class TypeChecker
|
|
|
|
case NodeExpressionUnary.Op.Negate:
|
|
|
|
case NodeExpressionUnary.Op.Negate:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (target.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
if (target.Type is not NubTypeSInt and not NubTypeUInt)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for negation: {target.Type}").At(fileName, target).Build());
|
|
|
|
throw BasicError($"Unsupported type for negation: {target.Type}", target);
|
|
|
|
|
|
|
|
|
|
|
|
type = target.Type;
|
|
|
|
type = target.Type;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -294,7 +294,7 @@ public class TypeChecker
|
|
|
|
case NodeExpressionUnary.Op.Invert:
|
|
|
|
case NodeExpressionUnary.Op.Invert:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (target.Type is not NubTypeBool)
|
|
|
|
if (target.Type is not NubTypeBool)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Unsupported type for inversion: {target.Type}").At(fileName, target).Build());
|
|
|
|
throw BasicError($"Unsupported type for inversion: {target.Type}", target);
|
|
|
|
|
|
|
|
|
|
|
|
type = NubTypeBool.Instance;
|
|
|
|
type = NubTypeBool.Instance;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -325,7 +325,7 @@ public class TypeChecker
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var type = scope.GetIdentifierType(expression.Value.Ident);
|
|
|
|
var type = scope.GetIdentifierType(expression.Value.Ident);
|
|
|
|
if (type is null)
|
|
|
|
if (type is null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Identifier '{expression.Value.Ident}' is not declared").At(fileName, expression.Value).Build());
|
|
|
|
throw BasicError($"Identifier '{expression.Value.Ident}' is not declared", expression.Value);
|
|
|
|
|
|
|
|
|
|
|
|
return new TypedNodeExpressionLocalIdent(expression.Tokens, type, expression.Value);
|
|
|
|
return new TypedNodeExpressionLocalIdent(expression.Tokens, type, expression.Value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -351,43 +351,43 @@ public class TypeChecker
|
|
|
|
case NubTypeStruct structType:
|
|
|
|
case NubTypeStruct structType:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!moduleGraph.TryResolveModule(structType.Module, out var module))
|
|
|
|
if (!moduleGraph.TryResolveModule(structType.Module, out var module))
|
|
|
|
throw new CompileException(Diagnostic.Error($"Module '{structType.Module}' not found").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Module '{structType.Module}' not found", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
if (!module.TryResolveType(structType.Name, currentModule == structType.Module, out var typeDef))
|
|
|
|
if (!module.TryResolveType(structType.Name, currentModule == structType.Module, out var typeDef))
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type '{structType.Name}' not found in module '{structType.Module}'").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Type '{structType.Name}' not found in module '{structType.Module}'", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
if (typeDef is not Module.TypeInfoStruct structDef)
|
|
|
|
if (typeDef is not Module.TypeInfoStruct structDef)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type '{target.Type}' is not a struct").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Type '{target.Type}' is not a struct", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
var field = structDef.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
|
|
|
|
var field = structDef.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
|
|
|
|
if (field == null)
|
|
|
|
if (field == null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'").At(fileName, target).Build());
|
|
|
|
throw BasicError($"Struct '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target);
|
|
|
|
|
|
|
|
|
|
|
|
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name);
|
|
|
|
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case NubTypeEnumVariant enumVariantType:
|
|
|
|
case NubTypeEnumVariant enumVariantType:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!moduleGraph.TryResolveModule(enumVariantType.EnumType.Module, out var module))
|
|
|
|
if (!moduleGraph.TryResolveModule(enumVariantType.EnumType.Module, out var module))
|
|
|
|
throw new CompileException(Diagnostic.Error($"Module '{enumVariantType.EnumType.Module}' not found").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Module '{enumVariantType.EnumType.Module}' not found", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
if (!module.TryResolveType(enumVariantType.EnumType.Name, currentModule == enumVariantType.EnumType.Module, out var typeDef))
|
|
|
|
if (!module.TryResolveType(enumVariantType.EnumType.Name, currentModule == enumVariantType.EnumType.Module, out var typeDef))
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type '{enumVariantType.EnumType.Name}' not found in module '{enumVariantType.EnumType.Module}'").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Type '{enumVariantType.EnumType.Name}' not found in module '{enumVariantType.EnumType.Module}'", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
if (typeDef is not Module.TypeInfoEnum enumDef)
|
|
|
|
if (typeDef is not Module.TypeInfoEnum enumDef)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type '{enumVariantType.EnumType.Module}::{enumVariantType.EnumType.Name}' is not an enum").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Type '{enumVariantType.EnumType.Module}::{enumVariantType.EnumType.Name}' is not an enum", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
var variant = enumDef.Variants.FirstOrDefault(x => x.Name == enumVariantType.Variant);
|
|
|
|
var variant = enumDef.Variants.FirstOrDefault(x => x.Name == enumVariantType.Variant);
|
|
|
|
if (variant == null)
|
|
|
|
if (variant == null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type '{target.Type}' does not have a variant named '{enumVariantType.Variant}'").At(fileName, expression.Target).Build());
|
|
|
|
throw BasicError($"Type '{target.Type}' does not have a variant named '{enumVariantType.Variant}'", expression.Target);
|
|
|
|
|
|
|
|
|
|
|
|
var field = variant.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
|
|
|
|
var field = variant.Fields.FirstOrDefault(x => x.Name == expression.Name.Ident);
|
|
|
|
if (field == null)
|
|
|
|
if (field == null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Enum variant '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'").At(fileName, target).Build());
|
|
|
|
throw BasicError($"Enum variant '{target.Type}' does not have a field matching the name '{expression.Name.Ident}'", target);
|
|
|
|
|
|
|
|
|
|
|
|
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name);
|
|
|
|
return new TypedNodeExpressionMemberAccess(expression.Tokens, field.Type, target, expression.Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
throw new CompileException(Diagnostic.Error($"{target.Type} has no member '{expression.Name.Ident}'").At(fileName, target).Build());
|
|
|
|
throw BasicError($"{target.Type} has no member '{expression.Name.Ident}'", target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -395,7 +395,7 @@ public class TypeChecker
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var target = CheckExpression(expression.Target);
|
|
|
|
var target = CheckExpression(expression.Target);
|
|
|
|
if (target.Type is not NubTypeFunc funcType)
|
|
|
|
if (target.Type is not NubTypeFunc funcType)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Cannot invoke function call on type '{target.Type}'").At(fileName, target).Build());
|
|
|
|
throw BasicError($"Cannot invoke function call on type '{target.Type}'", target);
|
|
|
|
|
|
|
|
|
|
|
|
var parameters = expression.Parameters.Select(CheckExpression).ToList();
|
|
|
|
var parameters = expression.Parameters.Select(CheckExpression).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
@@ -416,11 +416,11 @@ public class TypeChecker
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var field = info.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
|
|
|
|
var field = info.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
|
|
|
|
if (field == null)
|
|
|
|
if (field == null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Field '{initializer.Name.Ident}' does not exist on struct '{expression.Module.Ident}::{expression.Name.Ident}'").At(fileName, initializer.Name).Build());
|
|
|
|
throw BasicError($"Field '{initializer.Name.Ident}' does not exist on struct '{expression.Module.Ident}::{expression.Name.Ident}'", initializer.Name);
|
|
|
|
|
|
|
|
|
|
|
|
var value = CheckExpression(initializer.Value);
|
|
|
|
var value = CheckExpression(initializer.Value);
|
|
|
|
if (!value.Type.IsAssignableTo(field.Type))
|
|
|
|
if (!value.Type.IsAssignableTo(field.Type))
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type of assignment ({value.Type}) does not match expected type of field '{field.Name}' ({field.Type})").At(fileName, initializer.Name).Build());
|
|
|
|
throw BasicError($"Type of assignment ({value.Type}) does not match expected type of field '{field.Name}' ({field.Type})", initializer.Name);
|
|
|
|
|
|
|
|
|
|
|
|
initializers.Add(new TypedNodeExpressionStructLiteral.Initializer(initializer.Tokens, initializer.Name, value));
|
|
|
|
initializers.Add(new TypedNodeExpressionStructLiteral.Initializer(initializer.Tokens, initializer.Name, value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -434,18 +434,18 @@ public class TypeChecker
|
|
|
|
|
|
|
|
|
|
|
|
var variant = info.Variants.FirstOrDefault(x => x.Name == expression.VariantName.Ident);
|
|
|
|
var variant = info.Variants.FirstOrDefault(x => x.Name == expression.VariantName.Ident);
|
|
|
|
if (variant == null)
|
|
|
|
if (variant == null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Enum '{expression.Module.Ident}::{expression.EnumName.Ident}' does not have a variant '{expression.VariantName.Ident}'").At(fileName, expression.VariantName).Build());
|
|
|
|
throw BasicError($"Enum '{expression.Module.Ident}::{expression.EnumName.Ident}' does not have a variant '{expression.VariantName.Ident}'", expression.VariantName);
|
|
|
|
|
|
|
|
|
|
|
|
var initializers = new List<TypedNodeExpressionEnumLiteral.Initializer>();
|
|
|
|
var initializers = new List<TypedNodeExpressionEnumLiteral.Initializer>();
|
|
|
|
foreach (var initializer in expression.Initializers)
|
|
|
|
foreach (var initializer in expression.Initializers)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var field = variant.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
|
|
|
|
var field = variant.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident);
|
|
|
|
if (field == null)
|
|
|
|
if (field == null)
|
|
|
|
throw new CompileException(Diagnostic.Error($"Field '{initializer.Name.Ident}' does not exist on enum variant '{expression.Module.Ident}::{expression.EnumName.Ident}.{expression.VariantName.Ident}'").At(fileName, initializer.Name).Build());
|
|
|
|
throw BasicError($"Field '{initializer.Name.Ident}' does not exist on enum variant '{expression.Module.Ident}::{expression.EnumName.Ident}.{expression.VariantName.Ident}'", initializer.Name);
|
|
|
|
|
|
|
|
|
|
|
|
var value = CheckExpression(initializer.Value);
|
|
|
|
var value = CheckExpression(initializer.Value);
|
|
|
|
if (!value.Type.IsAssignableTo(field.Type))
|
|
|
|
if (!value.Type.IsAssignableTo(field.Type))
|
|
|
|
throw new CompileException(Diagnostic.Error($"Type of assignment ({value.Type}) does not match expected type of field '{field.Name}' ({field.Type})").At(fileName, initializer.Name).Build());
|
|
|
|
throw BasicError($"Type of assignment ({value.Type}) does not match expected type of field '{field.Name}' ({field.Type})", initializer.Name);
|
|
|
|
|
|
|
|
|
|
|
|
initializers.Add(new TypedNodeExpressionEnumLiteral.Initializer(initializer.Tokens, initializer.Name, value));
|
|
|
|
initializers.Add(new TypedNodeExpressionEnumLiteral.Initializer(initializer.Tokens, initializer.Name, value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -590,6 +590,11 @@ public class TypeChecker
|
|
|
|
return new CompileException(Diagnostic.Error(message).At(fileName, node).Build());
|
|
|
|
return new CompileException(Diagnostic.Error(message).At(fileName, node).Build());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private CompileException BasicError(string message, TypedNode node)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return new CompileException(Diagnostic.Error(message).At(fileName, node).Build());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private sealed class Scope
|
|
|
|
private sealed class Scope
|
|
|
|
{
|
|
|
|
{
|
|
|
|
private readonly Stack<Dictionary<string, NubType>> scopes = new();
|
|
|
|
private readonly Stack<Dictionary<string, NubType>> scopes = new();
|
|
|
|
|