diff --git a/compiler/Parser.cs b/compiler/Parser.cs index ed30ba6..a665eea 100644 --- a/compiler/Parser.cs +++ b/compiler/Parser.cs @@ -81,7 +81,7 @@ public class Parser foreach (var modifier in modifiers) // todo(nub31): Add to diagnostics instead of throwing - throw new CompileException(Diagnostic.Error("Invalid modifier for function").At(fileName, modifier.Value).Build()); + throw BasicError("Invalid modifier for function", modifier.Value); var name = ExpectIdent(); var parameters = new List(); @@ -111,7 +111,7 @@ public class Parser foreach (var modifier in modifiers) // todo(nub31): Add to diagnostics instead of throwing - throw new CompileException(Diagnostic.Error("Invalid modifier for struct").At(fileName, modifier.Value).Build()); + throw BasicError("Invalid modifier for struct", modifier.Value); var name = ExpectIdent(); var fields = new List(); @@ -135,7 +135,7 @@ public class Parser foreach (var modifier in modifiers) // todo(nub31): Add to diagnostics instead of throwing - throw new CompileException(Diagnostic.Error("Invalid modifier for struct").At(fileName, modifier.Value).Build()); + throw BasicError("Invalid modifier for struct", modifier.Value); var name = ExpectIdent(); var variants = new List(); @@ -174,7 +174,7 @@ public class Parser foreach (var modifier in modifiers) // todo(nub31): Add to diagnostics instead of throwing - throw new CompileException(Diagnostic.Error("Invalid modifier for global variable").At(fileName, modifier.Value).Build()); + throw BasicError("Invalid modifier for global variable", modifier.Value); var name = ExpectIdent(); ExpectSymbol(Symbol.Colon); @@ -183,7 +183,7 @@ public class Parser return new NodeDefinitionGlobalVariable(TokensFrom(startIndex), exported, name, type); } - throw new CompileException(Diagnostic.Error("Not a valid definition").At(fileName, Peek()).Build()); + throw BasicError("Not a valid definition", Peek()); } private NodeStatement ParseStatement() @@ -403,7 +403,7 @@ public class Parser } else { - throw new CompileException(Diagnostic.Error("Expected start of expression").At(fileName, Peek()).Build()); + throw BasicError("Expected start of expression", Peek()); } while (true) @@ -495,7 +495,7 @@ public class Parser } } - throw new CompileException(Diagnostic.Error("Expected type").At(fileName, Peek()).Build()); + throw BasicError("Expected type", Peek()); } private List TokensFrom(int startIndex) @@ -511,7 +511,7 @@ public class Parser return; } - throw new CompileException(Diagnostic.Error($"Expected '{keyword.AsString()}'").At(fileName, Peek()).Build()); + throw BasicError($"Expected '{keyword.AsString()}'", Peek()); } private bool TryExpectKeyword(Keyword keyword) @@ -533,7 +533,7 @@ public class Parser return; } - throw new CompileException(Diagnostic.Error($"Expected '{symbol.AsString()}'").At(fileName, Peek()).Build()); + throw BasicError($"Expected '{symbol.AsString()}'", Peek()); } private bool TryExpectSymbol(Symbol symbol) @@ -555,7 +555,7 @@ public class Parser return token; } - throw new CompileException(Diagnostic.Error("Expected identifier").At(fileName, Peek()).Build()); + throw BasicError("Expected identifier", Peek()); } private bool TryExpectIdent([NotNullWhen(true)] out TokenIdent? ident) @@ -613,7 +613,7 @@ public class Parser private void Next() { if (index >= tokens.Count) - throw new CompileException(Diagnostic.Error("Unexpected end of tokens").At(fileName, Peek()).Build()); + throw BasicError("Unexpected end of tokens", Peek()); index += 1; } @@ -686,6 +686,16 @@ public class Parser return false; } } + + private CompileException BasicError(string message, Token? ident) + { + return new CompileException(Diagnostic.Error(message).At(fileName, ident).Build()); + } + + private CompileException BasicError(string message, Node node) + { + return new CompileException(Diagnostic.Error(message).At(fileName, node).Build()); + } } public class Ast(string fileName, TokenIdent moduleName, List definitions) diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index 50d5257..ddfbfb5 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -112,7 +112,7 @@ public class TypeChecker private TypedNodeStatementFuncCall CheckStatementExpression(NodeStatementExpression statement) { 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()); } @@ -133,7 +133,7 @@ public class TypeChecker var value = CheckExpression(statement.Value); 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); @@ -198,10 +198,10 @@ public class TypeChecker case NodeExpressionBinary.Op.Modulo: { 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) - 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; break; @@ -210,10 +210,10 @@ public class TypeChecker case NodeExpressionBinary.Op.RightShift: { 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) - 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; break; @@ -226,10 +226,10 @@ public class TypeChecker case NodeExpressionBinary.Op.GreaterThanOrEqual: { 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) - 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; break; @@ -238,10 +238,10 @@ public class TypeChecker case NodeExpressionBinary.Op.LogicalOr: { 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) - 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; break; @@ -286,7 +286,7 @@ public class TypeChecker case NodeExpressionUnary.Op.Negate: { 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; break; @@ -294,7 +294,7 @@ public class TypeChecker case NodeExpressionUnary.Op.Invert: { 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; break; @@ -325,7 +325,7 @@ public class TypeChecker { var type = scope.GetIdentifierType(expression.Value.Ident); 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); } @@ -351,43 +351,43 @@ public class TypeChecker case NubTypeStruct structType: { 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)) - 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) - 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); 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); } case NubTypeEnumVariant enumVariantType: { 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)) - 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) - 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); 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); 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); } 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); 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(); @@ -416,11 +416,11 @@ public class TypeChecker { var field = info.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident); 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); 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)); } @@ -434,18 +434,18 @@ public class TypeChecker var variant = info.Variants.FirstOrDefault(x => x.Name == expression.VariantName.Ident); 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(); foreach (var initializer in expression.Initializers) { var field = variant.Fields.FirstOrDefault(x => x.Name == initializer.Name.Ident); 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); 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)); } @@ -590,6 +590,11 @@ public class TypeChecker 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 readonly Stack> scopes = new();