visitFunctionDeclaration method

  1. @override
void visitFunctionDeclaration(
  1. FunctionDeclaration node
)
override

Implementation

@override
void visitFunctionDeclaration(FunctionDeclaration node) {
  final functionName = node.name.lexeme;
  Logger.debug(
      "[DeclarationVisitor.visitFunctionDeclaration] Processing function: $functionName");

  if (environment.isDefinedLocally(functionName)) {
    return;
  }

  // Handle type parameters for generic functions
  Environment? tempEnvironment;
  final typeParameters = node.functionExpression.typeParameters;

  if (typeParameters != null) {
    Logger.debug(
        "[DeclarationVisitor.visitFunctionDeclaration] Function '$functionName' has ${typeParameters.typeParameters.length} type parameters");

    // Create a temporary environment for type resolution
    tempEnvironment = Environment(enclosing: environment);

    // Create temporary type parameter placeholders
    for (final typeParam in typeParameters.typeParameters) {
      final paramName = typeParam.name.lexeme;

      // Create a simple TypeParameter placeholder in the temp environment
      final typeParamPlaceholder = TypeParameter(paramName);
      tempEnvironment.define(paramName, typeParamPlaceholder);

      Logger.debug(
          "[DeclarationVisitor.visitFunctionDeclaration]   Defined type parameter '$paramName' in temp environment");
    }
  }

  // Use the temp environment (if any) for type resolution, otherwise use the normal environment
  final resolveEnvironment = tempEnvironment ?? environment;

  // Now resolve the return type (which might reference type parameters)
  final returnTypeNode = node.returnType;
  RuntimeType declaredReturnType;

  if (returnTypeNode is NamedType) {
    final typeName = returnTypeNode.name2.lexeme;
    Logger.debug(
        "[DeclarationVisitor.visitFunctionDeclaration]   Return type node name: $typeName");

    try {
      final resolvedType = resolveEnvironment.get(typeName);
      Logger.debug(
          "[DeclarationVisitor.visitFunctionDeclaration]     environment.get('$typeName') resolved to: ${resolvedType?.runtimeType} with name: ${(resolvedType is RuntimeType ? resolvedType.name : 'N/A')}");

      if (resolvedType is RuntimeType) {
        declaredReturnType = resolvedType;
      } else {
        Logger.warn(
            "[DeclarationVisitor.visitFunctionDeclaration]     Type '$typeName' resolved to non-RuntimeType: $resolvedType. Using placeholder.");
        declaredReturnType = BridgedClass(nativeType: Object, name: typeName);
      }
    } on RuntimeError catch (e) {
      Logger.warn(
          "[DeclarationVisitor.visitFunctionDeclaration]     Type '$typeName' not found in environment (RuntimeError: ${e.message}). Using placeholder.");
      declaredReturnType = BridgedClass(nativeType: Object, name: typeName);
    }
  } else if (returnTypeNode == null) {
    declaredReturnType = BridgedClass(nativeType: Object, name: 'dynamic');
  } else {
    // For other TypeAnnotation types, use a generic placeholder
    declaredReturnType =
        BridgedClass(nativeType: Object, name: 'unknown_type_placeholder');
  }

  bool isNullable = returnTypeNode?.question != null; // Check for 'A?'

  final function = InterpretedFunction.declaration(
      node,
      environment, // The function captures the environment it's declared in
      declaredReturnType,
      isNullable);
  Logger.debug(
      "[DeclarationVisitor.visitFunctionDeclaration]   Defining function '$functionName' with declaredReturnType: ${declaredReturnType.name} (Hash: ${declaredReturnType.hashCode})");
  environment.define(functionName, function);
}