This commit is contained in:
nub31
2025-05-25 02:13:26 +02:00
parent 6e96c149a7
commit 15a4bd7ea2
5 changed files with 202 additions and 103 deletions

View File

@@ -4,7 +4,7 @@ import c
// Test2 // Test2
// Test3 // Test3
// Test4 // Test4
global func main(args: f []string) { global func main(args: []string) {
i = 0 i = 0
printf("%d\n", args.count) printf("%d\n", args.count)
while i < args.count { while i < args.count {
@@ -12,4 +12,6 @@ global func main(args: f []string) {
printf("%s\n", args[i]) printf("%s\n", args[i])
i = i + 1 i = i + 1
} }
i: string = "test"
} }

View File

@@ -0,0 +1,21 @@
using Nub.Lang.Frontend.Diagnostics;
namespace Nub.Lang.Frontend;
public class DiagnosticsResult(List<Diagnostic> diagnostics)
{
public bool HasErrors => diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error);
public void PrintAllDiagnostics()
{
foreach (var diagnostic in diagnostics)
{
Console.WriteLine(diagnostic.Format());
}
}
}
public class DiagnosticsResult<TResult>(List<Diagnostic> diagnostics, TResult value) : DiagnosticsResult(diagnostics)
{
public TResult Value { get; } = value;
}

View File

@@ -10,9 +10,7 @@ public class Parser
private int _index; private int _index;
private List<Diagnostic> _diagnostics = []; private List<Diagnostic> _diagnostics = [];
public IReadOnlyList<Diagnostic> Diagnostics => _diagnostics; public DiagnosticsResult<ModuleNode> ParseModule(List<Token> tokens, string rootFilePath)
public ModuleNode ParseModule(List<Token> tokens, string rootFilePath)
{ {
_index = 0; _index = 0;
_tokens = tokens; _tokens = tokens;
@@ -42,7 +40,7 @@ public class Parser
} }
} }
return new ModuleNode(GetTokensForNode(0), rootFilePath, imports, definitions); return new DiagnosticsResult<ModuleNode>(_diagnostics, new ModuleNode(GetTokensForNode(0), rootFilePath, imports, definitions));
} }
private DefinitionNode ParseDefinition() private DefinitionNode ParseDefinition()

View File

@@ -1,18 +1,13 @@
using Nub.Lang.Frontend.Parsing; using Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Diagnostics;
namespace Nub.Lang.Frontend.Typing; namespace Nub.Lang.Frontend.Typing;
public class TypeCheckingException : Exception
{
public TypeCheckingException(string message) : base(message)
{
}
}
public class TypeChecker public class TypeChecker
{ {
private readonly Dictionary<string, NubType> _variables = new(); private readonly Dictionary<string, NubType> _variables = new();
private readonly List<DefinitionNode> _definitions; private readonly List<DefinitionNode> _definitions;
private readonly List<Diagnostic> _diagnostics = [];
private NubType? _currentFunctionReturnType; private NubType? _currentFunctionReturnType;
private bool _hasReturnStatement; private bool _hasReturnStatement;
@@ -21,8 +16,10 @@ public class TypeChecker
_definitions = definitions; _definitions = definitions;
} }
public void TypeCheck() public DiagnosticsResult TypeCheck()
{ {
_diagnostics.Clear();
foreach (var structDef in _definitions.OfType<StructDefinitionNode>()) foreach (var structDef in _definitions.OfType<StructDefinitionNode>())
{ {
TypeCheckStructDef(structDef); TypeCheckStructDef(structDef);
@@ -32,6 +29,8 @@ public class TypeChecker
{ {
TypeCheckFuncDef(funcDef); TypeCheckFuncDef(funcDef);
} }
return new DiagnosticsResult(_diagnostics.ToList());
} }
private void TypeCheckStructDef(StructDefinitionNode structDef) private void TypeCheckStructDef(StructDefinitionNode structDef)
@@ -41,14 +40,16 @@ public class TypeChecker
{ {
if (fields.ContainsKey(field.Name)) if (fields.ContainsKey(field.Name))
{ {
throw new TypeCheckingException($"Duplicate field '{field.Name}' in struct '{structDef.Name}'"); ReportError($"Duplicate field '{field.Name}' in struct '{structDef.Name}'", structDef);
continue;
} }
if (field.Value.HasValue) if (field.Value.HasValue)
{ {
if (!TypeCheckExpression(field.Value.Value).Equals(field.Type)) var fieldType = TypeCheckExpression(field.Value.Value);
if (fieldType != null && !fieldType.Equals(field.Type))
{ {
throw new TypeCheckingException("Default field initializer does not match the defined type"); ReportError("Default field initializer does not match the defined type", field.Value.Value);
} }
} }
@@ -71,7 +72,7 @@ public class TypeChecker
if (_currentFunctionReturnType != null && !_hasReturnStatement) if (_currentFunctionReturnType != null && !_hasReturnStatement)
{ {
throw new TypeCheckingException($"Function '{funcDef.Name}' must return a value of type '{_currentFunctionReturnType}'"); ReportError($"Function '{funcDef.Name}' must return a value of type '{_currentFunctionReturnType}'", funcDef);
} }
} }
@@ -91,7 +92,7 @@ public class TypeChecker
TypeCheckVariableAssignment(varAssign); TypeCheckVariableAssignment(varAssign);
break; break;
case FuncCallStatementNode funcCall: case FuncCallStatementNode funcCall:
TypeCheckFuncCall(funcCall.FuncCall); TypeCheckFuncCall(funcCall.FuncCall, funcCall);
break; break;
case IfNode ifNode: case IfNode ifNode:
TypeCheckIf(ifNode); TypeCheckIf(ifNode);
@@ -106,42 +107,67 @@ public class TypeChecker
case ContinueNode: case ContinueNode:
break; break;
default: default:
throw new TypeCheckingException($"Unsupported statement type: {statement.GetType().Name}"); ReportError($"Unsupported statement type: {statement.GetType().Name}", statement);
break;
} }
} }
private void TypeCheckVariableAssignment(VariableAssignmentNode varAssign) private void TypeCheckVariableAssignment(VariableAssignmentNode varAssign)
{ {
var valueType = TypeCheckExpression(varAssign.Value); var valueType = TypeCheckExpression(varAssign.Value);
if (valueType == null) return;
if (varAssign.ExplicitType.HasValue) if (_variables.TryGetValue(varAssign.Name, out var existingVariable))
{ {
var explicitType = varAssign.ExplicitType.Value; if (varAssign.ExplicitType.HasValue)
if (!AreTypesCompatible(valueType, explicitType))
{ {
throw new TypeCheckingException($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'"); if (!AreTypesCompatible(existingVariable, varAssign.ExplicitType.Value))
{
ReportError($"Explicit type '{varAssign.ExplicitType.Value}' on variable '{varAssign.Name}' is not compatible with declared type '{existingVariable}'", varAssign);
return;
}
}
if (!AreTypesCompatible(valueType, existingVariable))
{
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{existingVariable}'", varAssign);
} }
_variables[varAssign.Name] = explicitType;
} }
else else
{ {
_variables[varAssign.Name] = valueType; if (varAssign.ExplicitType.HasValue)
{
var explicitType = varAssign.ExplicitType.Value;
if (!AreTypesCompatible(valueType, explicitType))
{
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'", varAssign);
return;
}
_variables[varAssign.Name] = explicitType;
}
else
{
_variables[varAssign.Name] = valueType;
}
} }
} }
private NubType TypeCheckDereference(DereferenceNode dereference) private NubType? TypeCheckDereference(DereferenceNode dereference)
{ {
TypeCheckExpression(dereference.Expression); var exprType = TypeCheckExpression(dereference.Expression);
if (dereference.Expression.Type is not NubPointerType nubPointerType) if (exprType == null) return null;
if (exprType is not NubPointerType nubPointerType)
{ {
throw new TypeCheckingException($"Cannot dereference a non-pointer type {dereference.Expression.Type}"); ReportError($"Cannot dereference a non-pointer type {exprType}", dereference);
return null;
} }
return nubPointerType.BaseType; return nubPointerType.BaseType;
} }
private NubType TypeCheckFuncCall(FuncCall funcCall) private NubType? TypeCheckFuncCall(FuncCall funcCall, Node node)
{ {
var localFuncDef = _definitions.OfType<LocalFuncDefinitionNode>().FirstOrDefault(f => f.Name == funcCall.Name); var localFuncDef = _definitions.OfType<LocalFuncDefinitionNode>().FirstOrDefault(f => f.Name == funcCall.Name);
var externFuncDef = _definitions.OfType<ExternFuncDefinitionNode>().FirstOrDefault(f => f.Name == funcCall.Name); var externFuncDef = _definitions.OfType<ExternFuncDefinitionNode>().FirstOrDefault(f => f.Name == funcCall.Name);
@@ -158,20 +184,22 @@ public class TypeChecker
parameters = externFuncDef.Parameters; parameters = externFuncDef.Parameters;
returnType = externFuncDef.ReturnType; returnType = externFuncDef.ReturnType;
} }
else else
{ {
throw new TypeCheckingException($"Function '{funcCall.Name}' is not defined"); ReportError($"Function '{funcCall.Name}' is not defined", node);
return null;
} }
if (parameters.Take(parameters.Count - 1).Any(x => x.Variadic)) if (parameters.Take(parameters.Count - 1).Any(x => x.Variadic))
{ {
throw new TypeCheckingException($"Function '{funcCall.Name}' has multiple variadic parameters"); ReportError($"Function '{funcCall.Name}' has multiple variadic parameters", node);
return null;
} }
for (var i = 0; i < funcCall.Parameters.Count; i++) for (var i = 0; i < funcCall.Parameters.Count; i++)
{ {
var argType = TypeCheckExpression(funcCall.Parameters[i]); var argType = TypeCheckExpression(funcCall.Parameters[i]);
if (argType == null) continue;
NubType paramType; NubType paramType;
if (i < parameters.Count) if (i < parameters.Count)
@@ -180,16 +208,17 @@ public class TypeChecker
} }
else if (parameters.LastOrDefault()?.Variadic ?? false) else if (parameters.LastOrDefault()?.Variadic ?? false)
{ {
return parameters[^1].Type; paramType = parameters[^1].Type;
} }
else else
{ {
throw new TypeCheckingException($"Function '{funcCall.Name}' does not take {funcCall.Parameters.Count} parameters"); ReportError($"Function '{funcCall.Name}' does not take {funcCall.Parameters.Count} parameters", node);
continue;
} }
if (!AreTypesCompatible(argType, paramType)) if (!AreTypesCompatible(argType, paramType))
{ {
throw new TypeCheckingException($"Parameter {i} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'"); ReportError($"Parameter {i + 1} of function '{funcCall.Name}' expects type '{paramType}', but got '{argType}'", funcCall.Parameters[i]);
} }
} }
@@ -199,9 +228,9 @@ public class TypeChecker
private void TypeCheckIf(IfNode ifNode) private void TypeCheckIf(IfNode ifNode)
{ {
var conditionType = TypeCheckExpression(ifNode.Condition); var conditionType = TypeCheckExpression(ifNode.Condition);
if (!conditionType.Equals(NubPrimitiveType.Bool)) if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool))
{ {
throw new TypeCheckingException($"If condition must be a boolean expression, got '{conditionType}'"); ReportError($"If condition must be a boolean expression, got '{conditionType}'", ifNode.Condition);
} }
TypeCheckBlock(ifNode.Body); TypeCheckBlock(ifNode.Body);
@@ -216,9 +245,9 @@ public class TypeChecker
private void TypeCheckWhile(WhileNode whileNode) private void TypeCheckWhile(WhileNode whileNode)
{ {
var conditionType = TypeCheckExpression(whileNode.Condition); var conditionType = TypeCheckExpression(whileNode.Condition);
if (!conditionType.Equals(NubPrimitiveType.Bool)) if (conditionType != null && !conditionType.Equals(NubPrimitiveType.Bool))
{ {
throw new TypeCheckingException($"While condition must be a boolean expression, got '{conditionType}'"); ReportError($"While condition must be a boolean expression, got '{conditionType}'", whileNode.Condition);
} }
TypeCheckBlock(whileNode.Body); TypeCheckBlock(whileNode.Body);
@@ -231,24 +260,26 @@ public class TypeChecker
if (returnNode.Value.HasValue) if (returnNode.Value.HasValue)
{ {
var returnType = TypeCheckExpression(returnNode.Value.Value); var returnType = TypeCheckExpression(returnNode.Value.Value);
if (returnType == null) return;
if (_currentFunctionReturnType == null) if (_currentFunctionReturnType == null)
{ {
throw new TypeCheckingException("Cannot return a value from a function with no return type"); ReportError("Cannot return a value from a function with no return type", returnNode.Value.Value);
return;
} }
if (!AreTypesCompatible(returnType, _currentFunctionReturnType)) if (!AreTypesCompatible(returnType, _currentFunctionReturnType))
{ {
throw new TypeCheckingException($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'"); ReportError($"Return value of type '{returnType}' is not compatible with function return type '{_currentFunctionReturnType}'", returnNode.Value.Value);
} }
} }
else if (_currentFunctionReturnType != null) else if (_currentFunctionReturnType != null)
{ {
throw new TypeCheckingException($"Function must return a value of type '{_currentFunctionReturnType}'"); ReportError($"Function must return a value of type '{_currentFunctionReturnType}'", returnNode);
} }
} }
private NubType TypeCheckExpression(ExpressionNode expression) private NubType? TypeCheckExpression(ExpressionNode expression)
{ {
var resultType = expression switch var resultType = expression switch
{ {
@@ -259,63 +290,83 @@ public class TypeChecker
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr), BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
CastNode cast => TypeCheckCast(cast), CastNode cast => TypeCheckCast(cast),
DereferenceNode dereference => TypeCheckDereference(dereference), DereferenceNode dereference => TypeCheckDereference(dereference),
FuncCallExpressionNode funcCallExpr => TypeCheckFuncCall(funcCallExpr.FuncCall), FuncCallExpressionNode funcCallExpr => TypeCheckFuncCall(funcCallExpr.FuncCall, 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),
_ => throw new TypeCheckingException($"Unsupported expression type: {expression.GetType().Name}") _ => ReportUnsupportedExpression(expression)
}; };
expression.Type = resultType; if (resultType != null)
{
expression.Type = resultType;
}
return resultType; return resultType;
} }
private NubType TypeCheckArrayIndex(ArrayIndexNode arrayIndex) private NubType? ReportUnsupportedExpression(ExpressionNode expression)
{
ReportError($"Unsupported expression type: {expression.GetType().Name}", expression);
return null;
}
private NubType? TypeCheckArrayIndex(ArrayIndexNode arrayIndex)
{ {
var expressionType = TypeCheckExpression(arrayIndex.Expression); var expressionType = TypeCheckExpression(arrayIndex.Expression);
if (expressionType == null) return null;
if (expressionType is not NubArrayType arrayType) if (expressionType is not NubArrayType arrayType)
{ {
throw new TypeCheckingException($"Annot access index of non-array type {expressionType}"); ReportError($"Cannot access index of non-array type {expressionType}", arrayIndex.Expression);
return null;
} }
var indexType = TypeCheckExpression(arrayIndex.Index); var indexType = TypeCheckExpression(arrayIndex.Index);
if (!IsInteger(indexType)) if (indexType != null && !IsInteger(indexType))
{ {
throw new TypeCheckingException("Array index type must be an integer"); ReportError("Array index type must be an integer", arrayIndex.Index);
} }
return arrayType.BaseType; return arrayType.BaseType;
} }
private NubType TypeCheckIdentifier(IdentifierNode identifier) private NubType? TypeCheckIdentifier(IdentifierNode identifier)
{ {
if (!_variables.TryGetValue(identifier.Identifier, out var varType)) if (!_variables.TryGetValue(identifier.Identifier, out var varType))
{ {
throw new TypeCheckingException($"Variable '{identifier.Identifier}' is not defined"); ReportError($"Variable '{identifier.Identifier}' is not defined", identifier);
return null;
} }
return varType; return varType;
} }
private NubType TypeCheckAddressOf(AddressOfNode addressOf) private NubType? TypeCheckAddressOf(AddressOfNode addressOf)
{ {
TypeCheckExpression(addressOf.Expression); var exprType = TypeCheckExpression(addressOf.Expression);
if (exprType == null) return null;
if (addressOf.Expression is not (IdentifierNode or MemberAccessNode)) if (addressOf.Expression is not (IdentifierNode or MemberAccessNode))
{ {
throw new TypeCheckingException($"Cannot take the address of {addressOf.Expression.Type}"); ReportError($"Cannot take the address of {exprType}", addressOf.Expression);
return null;
} }
return new NubPointerType(addressOf.Expression.Type); return new NubPointerType(exprType);
} }
private NubType TypeCheckBinaryExpression(BinaryExpressionNode binaryExpr) private NubType? TypeCheckBinaryExpression(BinaryExpressionNode binaryExpr)
{ {
var leftType = TypeCheckExpression(binaryExpr.Left); var leftType = TypeCheckExpression(binaryExpr.Left);
var rightType = TypeCheckExpression(binaryExpr.Right); var rightType = TypeCheckExpression(binaryExpr.Right);
if (leftType == null || rightType == null) return null;
if (!leftType.Equals(rightType)) if (!leftType.Equals(rightType))
{ {
throw new TypeCheckingException($"Left '{leftType}' and right '{rightType}' side of the binary expression is not equal"); ReportError($"Left '{leftType}' and right '{rightType}' side of the binary expression must be the same type", binaryExpr);
return null;
} }
switch (binaryExpr.Operator) switch (binaryExpr.Operator)
@@ -329,7 +380,8 @@ public class TypeChecker
case BinaryExpressionOperator.LessThanOrEqual: case BinaryExpressionOperator.LessThanOrEqual:
if (!IsNumeric(leftType)) if (!IsNumeric(leftType))
{ {
throw new TypeCheckingException($"Comparison operators require numeric operands, got '{leftType}' and '{rightType}'"); ReportError($"Comparison operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr);
return null;
} }
return NubPrimitiveType.Bool; return NubPrimitiveType.Bool;
@@ -339,36 +391,40 @@ public class TypeChecker
case BinaryExpressionOperator.Divide: case BinaryExpressionOperator.Divide:
if (!IsNumeric(leftType)) if (!IsNumeric(leftType))
{ {
throw new TypeCheckingException($"Arithmetic operators require numeric operands, got '{leftType}' and '{rightType}'"); ReportError($"Arithmetic operators require numeric operands, got '{leftType}' and '{rightType}'", binaryExpr);
return null;
} }
return leftType; return leftType;
default: default:
throw new TypeCheckingException($"Unsupported binary operator: {binaryExpr.Operator}"); ReportError($"Unsupported binary operator: {binaryExpr.Operator}", binaryExpr);
return null;
} }
} }
private NubType TypeCheckCast(CastNode cast) private NubType? TypeCheckCast(CastNode cast)
{ {
TypeCheckExpression(cast.Expression); TypeCheckExpression(cast.Expression);
// TODO: Check if castable // TODO: Check if castable
return cast.TargetType; return cast.TargetType;
} }
private NubType TypeCheckStructInitializer(StructInitializerNode structInit) private NubType? TypeCheckStructInitializer(StructInitializerNode structInit)
{ {
var initialized = new HashSet<string>(); var initialized = new HashSet<string>();
var structType = structInit.StructType; var structType = structInit.StructType;
if (structType is not NubStructType customType) if (structType is not NubStructType customType)
{ {
throw new TypeCheckingException($"Type '{structType}' is not a struct type"); ReportError($"Type '{structType}' is not a struct type", structInit);
return null;
} }
var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == structInit.StructType.Name); var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == structInit.StructType.Name);
if (definition == null) if (definition == null)
{ {
throw new TypeCheckingException($"Struct type '{customType.Name}' is not defined"); ReportError($"Struct type '{customType.Name}' is not defined", structInit);
return null;
} }
foreach (var initializer in structInit.Initializers) foreach (var initializer in structInit.Initializers)
@@ -376,13 +432,14 @@ public class TypeChecker
var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key); var definitionField = definition.Fields.FirstOrDefault(f => f.Name == initializer.Key);
if (definitionField == null) if (definitionField == null)
{ {
throw new TypeCheckingException($"Field '{initializer.Key}' does not exist in struct '{customType.Name}'"); ReportError($"Field '{initializer.Key}' does not exist in struct '{customType.Name}'", initializer.Value);
continue;
} }
var initializerType = TypeCheckExpression(initializer.Value); var initializerType = TypeCheckExpression(initializer.Value);
if (!AreTypesCompatible(initializerType, definitionField.Type)) if (initializerType != null && !AreTypesCompatible(initializerType, definitionField.Type))
{ {
throw new TypeCheckingException($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'"); ReportError($"Cannot initialize field '{initializer.Key}' of type '{definitionField.Type}' with expression of type '{initializerType}'", initializer.Value);
} }
initialized.Add(initializer.Key); initialized.Add(initializer.Key);
@@ -397,16 +454,17 @@ public class TypeChecker
{ {
if (!initialized.Contains(field.Name)) if (!initialized.Contains(field.Name))
{ {
throw new TypeCheckingException($"Struct field '{field.Name}' is not initialized on type '{customType.Name}'"); ReportError($"Struct field '{field.Name}' is not initialized on type '{customType.Name}'", structInit);
} }
} }
return structType; return structType;
} }
private NubType TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression) private NubType? TypeCheckUnaryExpression(UnaryExpressionNode unaryExpression)
{ {
var operandType = TypeCheckExpression(unaryExpression.Operand); var operandType = TypeCheckExpression(unaryExpression.Operand);
if (operandType == null) return null;
switch (unaryExpression.Operator) switch (unaryExpression.Operator)
{ {
@@ -422,27 +480,32 @@ public class TypeChecker
return operandType; return operandType;
} }
throw new TypeCheckingException($"Cannot negate non-numeric type {operandType}"); ReportError($"Cannot negate non-numeric type {operandType}", unaryExpression.Operand);
return null;
} }
case UnaryExpressionOperator.Invert: case UnaryExpressionOperator.Invert:
{ {
if (!operandType.Equals(NubPrimitiveType.Bool)) if (!operandType.Equals(NubPrimitiveType.Bool))
{ {
throw new TypeCheckingException($"Cannot invert non-boolean type {operandType}"); ReportError($"Cannot invert non-boolean type {operandType}", unaryExpression.Operand);
return null;
} }
return operandType; return operandType;
} }
default: default:
{ {
throw new ArgumentOutOfRangeException(); ReportError($"Unsupported unary operator: {unaryExpression.Operator}", unaryExpression);
return null;
} }
} }
} }
private NubType TypeCheckMemberAccess(MemberAccessNode memberAccess) private NubType? TypeCheckMemberAccess(MemberAccessNode memberAccess)
{ {
var expressionType = TypeCheckExpression(memberAccess.Expression); var expressionType = TypeCheckExpression(memberAccess.Expression);
if (expressionType == null) return null;
switch (expressionType) switch (expressionType)
{ {
case NubArrayType: case NubArrayType:
@@ -459,20 +522,29 @@ public class TypeChecker
var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == structType.Name); var definition = _definitions.OfType<StructDefinitionNode>().FirstOrDefault(s => s.Name == structType.Name);
if (definition == null) if (definition == null)
{ {
throw new TypeCheckingException($"Struct type '{structType.Name}' is not defined"); ReportError($"Struct type '{structType.Name}' is not defined", memberAccess);
return null;
} }
var field = definition.Fields.FirstOrDefault(f => f.Name == memberAccess.Member); var field = definition.Fields.FirstOrDefault(f => f.Name == memberAccess.Member);
if (field == null) if (field == null)
{ {
throw new TypeCheckingException($"Field '{memberAccess.Member}' does not exist in struct '{structType.Name}'"); ReportError($"Field '{memberAccess.Member}' does not exist in struct '{structType.Name}'", memberAccess);
return null;
} }
return field.Type; return field.Type;
} }
} }
throw new TypeCheckingException($"Cannot access member '{memberAccess.Member}' on type '{expressionType}'"); ReportError($"Cannot access member '{memberAccess.Member}' on type '{expressionType}'", memberAccess);
return null;
}
private void ReportError(string message, Node node)
{
var diagnostic = Diagnostic.Error(message).At(node).Build();
_diagnostics.Add(diagnostic);
} }
private static bool AreTypesCompatible(NubType sourceType, NubType targetType) private static bool AreTypesCompatible(NubType sourceType, NubType targetType)

View File

@@ -10,7 +10,7 @@ internal static class Program
{ {
private static readonly Lexer Lexer = new(); private static readonly Lexer Lexer = new();
private static readonly Parser Parser = new(); private static readonly Parser Parser = new();
public static int Main(string[] args) public static int Main(string[] args)
{ {
if (args.Length != 2) if (args.Length != 2)
@@ -19,16 +19,16 @@ internal static class Program
Console.WriteLine("Example: nub src out.asm"); Console.WriteLine("Example: nub src out.asm");
return 1; return 1;
} }
var input = Path.GetFullPath(args[0]); var input = Path.GetFullPath(args[0]);
var output = Path.GetFullPath(args[1]); var output = Path.GetFullPath(args[1]);
if (!Directory.Exists(input)) if (!Directory.Exists(input))
{ {
Console.WriteLine($"Error: Input directory '{input}' does not exist."); Console.WriteLine($"Error: Input directory '{input}' does not exist.");
return 1; return 1;
} }
var outputDir = Path.GetDirectoryName(output); var outputDir = Path.GetDirectoryName(output);
if (outputDir == null || !Directory.Exists(outputDir)) if (outputDir == null || !Directory.Exists(outputDir))
{ {
@@ -42,24 +42,25 @@ internal static class Program
return 1; return 1;
} }
List<Diagnostic> diagnostics = []; List<ModuleNode> modules;
try
var modules = RunFrontend(input, diagnostics);
foreach (var diagnostic in diagnostics)
{ {
Console.WriteLine(diagnostic.Format()); modules = RunFrontend(input);
} }
catch (Exception)
if (diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
{ {
return 1; return 1;
} }
var definitions = modules.SelectMany(f => f.Definitions).ToList(); var definitions = modules.SelectMany(f => f.Definitions).ToList();
var typeChecker = new TypeChecker(definitions); var typeChecker = new TypeChecker(definitions);
typeChecker.TypeCheck(); var typeCheckResult = typeChecker.TypeCheck();
typeCheckResult.PrintAllDiagnostics();
if (typeCheckResult.HasErrors)
{
return 1;
}
var generator = new Generator(definitions); var generator = new Generator(definitions);
var result = generator.Generate(); var result = generator.Generate();
@@ -68,14 +69,14 @@ internal static class Program
return 0; return 0;
} }
private static List<ModuleNode> RunFrontend(string rootFilePath, List<Diagnostic> diagnostics) private static List<ModuleNode> RunFrontend(string rootFilePath)
{ {
List<ModuleNode> modules = []; List<ModuleNode> modules = [];
RunFrontend(rootFilePath, modules, diagnostics); RunFrontend(rootFilePath, modules);
return modules; return modules;
} }
private static void RunFrontend(string rootFilePath, List<ModuleNode> modules, List<Diagnostic> diagnostics) private static void RunFrontend(string rootFilePath, List<ModuleNode> modules)
{ {
var filePaths = Directory.EnumerateFiles(rootFilePath, "*.nub", SearchOption.TopDirectoryOnly); var filePaths = Directory.EnumerateFiles(rootFilePath, "*.nub", SearchOption.TopDirectoryOnly);
@@ -86,16 +87,21 @@ internal static class Program
tokens.AddRange(Lexer.Lex(src, new SourceFile(filePath, src))); tokens.AddRange(Lexer.Lex(src, new SourceFile(filePath, src)));
} }
var module = Parser.ParseModule(tokens, rootFilePath); var parseResult = Parser.ParseModule(tokens, rootFilePath);
diagnostics.AddRange(Parser.Diagnostics); parseResult.PrintAllDiagnostics();
modules.Add(module); if (parseResult.HasErrors)
foreach (var import in module.Imports)
{ {
var importPath = Path.GetFullPath(import, module.Path); throw new Exception();
}
modules.Add(parseResult.Value);
foreach (var import in parseResult.Value.Imports)
{
var importPath = Path.GetFullPath(import, parseResult.Value.Path);
if (modules.All(m => m.Path != importPath)) if (modules.All(m => m.Path != importPath))
{ {
RunFrontend(importPath, modules, diagnostics); RunFrontend(importPath, modules);
} }
} }
} }