visitPostfixExpression method

  1. @override
Object? visitPostfixExpression(
  1. PostfixExpression node
)
override

Implementation

@override
Object? visitPostfixExpression(PostfixExpression node) {
  final operatorType = node.operator.type;

  // Support for the non-null assertion operator (!)
  if (operatorType == TokenType.BANG) {
    final operandValue = node.operand.accept<Object?>(this);
    if (operandValue == null) {
      throw RuntimeD4rtException(
          "Null check operator used on a null value at ${node.toString()}");
    }
    return operandValue;
  }

  // Check if operand is assignable (SimpleIdentifier or PropertyAccess)
  if (node.operand is SimpleIdentifier) {
    final variableName = (node.operand as SimpleIdentifier).name;
    Object? operandValue;
    InterpretedInstance? thisInstance;
    bool isInstanceField = false;

    // Try lexical scope first, then implicit 'this'
    try {
      operandValue = environment.get(variableName); // Try lexical scope
    } on RuntimeD4rtException {
      // Not found lexically, try implicit 'this'
      try {
        final potentialThis = environment.get('this');
        if (potentialThis is InterpretedInstance) {
          thisInstance = potentialThis;
          operandValue = thisInstance.get(variableName); // Get from instance
          isInstanceField = true;
        } else {
          throw RuntimeD4rtException("Undefined variable: $variableName");
        }
      } on RuntimeD4rtException {
        throw RuntimeD4rtException("Undefined variable: $variableName");
      }
    }
    final bridgedInstance = toBridgedInstance(operandValue);
    final currentValue =
        bridgedInstance.$2 ? bridgedInstance.$1!.nativeObject : operandValue;

    if (currentValue is num) {
      final newValue = operatorType == TokenType.PLUS_PLUS
          ? currentValue + 1
          : currentValue - 1;

      // Assign back to correct target (lexical or instance)
      if (isInstanceField && thisInstance != null) {
        final setter = thisInstance.klass.findInstanceSetter(variableName);
        if (setter != null) {
          setter.bind(thisInstance).call(this, [newValue], {});
        } else {
          thisInstance.set(
              variableName, newValue, this); // Assign to instance field
        }
      } else {
        environment.assign(
            variableName, newValue); // Assign to lexical variable
      }

      return operandValue; // Return the original value
    } else if (operandValue is InterpretedInstance) {
      // Use custom + operator with literal 1
      final operatorMethod = operandValue.findOperator('+');
      if (operatorMethod != null) {
        try {
          // For x++, we create a literal 1 and call x + 1
          final operand = _createIncrementOperand(
              currentValue, operatorType == TokenType.PLUS_PLUS);
          Object? newValue =
              operatorMethod.bind(operandValue).call(this, [operand], {});

          // Assign back to correct target (lexical or instance)
          if (isInstanceField && thisInstance != null) {
            final setter =
                thisInstance.klass.findInstanceSetter(variableName);
            if (setter != null) {
              setter.bind(thisInstance).call(this, [newValue], {});
            } else {
              thisInstance.set(
                  variableName, newValue, this); // Assign to instance field
            }
          } else {
            environment.assign(
                variableName, newValue); // Assign to lexical variable
          }

          return operandValue; // Return the original value for postfix
        } on ReturnException catch (e) {
          final newValue = e.value;

          // Assign back to correct target (lexical or instance)
          if (isInstanceField && thisInstance != null) {
            final setter =
                thisInstance.klass.findInstanceSetter(variableName);
            if (setter != null) {
              setter.bind(thisInstance).call(this, [newValue], {});
            } else {
              thisInstance.set(
                  variableName, newValue, this); // Assign to instance field
            }
          } else {
            environment.assign(
                variableName, newValue); // Assign to lexical variable
          }

          return operandValue; // Return the original value for postfix
        } catch (e) {
          throw RuntimeD4rtException(
              "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
        }
      } else {
        throw RuntimeD4rtException(
            "Cannot increment/decrement object of type '${operandValue.klass.name}': No operator '+' found.");
      }
    } else {
      throw RuntimeD4rtException(
          "Operand for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' must be a number, but was ${operandValue?.runtimeType}.");
    }
  } else if (node.operand is PropertyAccess) {
    // Handle property access like obj.field++
    final propertyAccess = node.operand as PropertyAccess;
    final targetValue = propertyAccess.target?.accept<Object?>(this);
    final propertyName = propertyAccess.propertyName.name;

    if (targetValue is InterpretedInstance) {
      // Get current value via getter or field
      final currentValue = targetValue.get(propertyName);
      final originalValue = currentValue; // Save for return

      // Calculate new value
      Object? newValue;
      if (currentValue is num) {
        newValue = operatorType == TokenType.PLUS_PLUS
            ? currentValue + 1
            : currentValue - 1;
      } else if (currentValue is InterpretedInstance) {
        // Use custom + operator with literal 1
        final operatorMethod = currentValue.findOperator('+');
        if (operatorMethod != null) {
          try {
            // For x++, we create a literal 1 and call x + 1
            final operand = _createIncrementOperand(
                currentValue, operatorType == TokenType.PLUS_PLUS);
            newValue =
                operatorMethod.bind(currentValue).call(this, [operand], {});
          } on ReturnException catch (e) {
            newValue = e.value;
          } catch (e) {
            throw RuntimeD4rtException(
                "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
          }
        } else {
          throw RuntimeD4rtException(
              "Cannot increment/decrement object of type '${currentValue.klass.name}': No operator '+' found.");
        }
      } else {
        throw RuntimeD4rtException(
            "Cannot increment/decrement property '$propertyName' of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
      }

      // Set new value via setter or field
      final setter = targetValue.klass.findInstanceSetter(propertyName);
      if (setter != null) {
        setter.bind(targetValue).call(this, [newValue], {});
      } else {
        targetValue.set(propertyName, newValue, this);
      }

      // Return the *original* value for postfix operators
      return originalValue;
    } else {
      throw RuntimeD4rtException(
          "Cannot increment/decrement property on non-instance object of type '${targetValue?.runtimeType}'.");
    }
  } else if (node.operand is PrefixedIdentifier) {
    // Handle prefixed identifier like obj.field++ (parsed as PrefixedIdentifier)
    final prefixedIdentifier = node.operand as PrefixedIdentifier;
    final targetValue = prefixedIdentifier.prefix.accept<Object?>(this);
    final propertyName = prefixedIdentifier.identifier.name;

    if (targetValue is InterpretedInstance) {
      // Get current value via getter or field
      final currentValue = targetValue.get(propertyName);
      final originalValue = currentValue; // Save for return

      // Calculate new value
      Object? newValue;
      if (currentValue is num) {
        newValue = operatorType == TokenType.PLUS_PLUS
            ? currentValue + 1
            : currentValue - 1;
      } else if (currentValue is InterpretedInstance) {
        // Use custom + operator with literal 1
        final operatorMethod = currentValue.findOperator('+');
        if (operatorMethod != null) {
          try {
            // For x++, we create a literal 1 and call x + 1
            final operand = _createIncrementOperand(
                currentValue, operatorType == TokenType.PLUS_PLUS);
            newValue =
                operatorMethod.bind(currentValue).call(this, [operand], {});
          } on ReturnException catch (e) {
            newValue = e.value;
          } catch (e) {
            throw RuntimeD4rtException(
                "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
          }
        } else {
          throw RuntimeD4rtException(
              "Cannot increment/decrement object of type '${currentValue.klass.name}': No operator '+' found.");
        }
      } else {
        throw RuntimeD4rtException(
            "Cannot increment/decrement property '$propertyName' of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
      }

      // Set new value via setter or field
      final setter = targetValue.klass.findInstanceSetter(propertyName);
      if (setter != null) {
        setter.bind(targetValue).call(this, [newValue], {});
      } else {
        targetValue.set(propertyName, newValue, this);
      }

      // Return the *original* value for postfix operators
      return originalValue;
    } else if (targetValue is InterpretedExtension) {
      // Handle static field/getter increment/decrement on extension
      final extension = targetValue;

      // Get current value via static getter or field
      Object? currentValue;
      final staticGetter = extension.findStaticGetter(propertyName);
      if (staticGetter != null) {
        currentValue = staticGetter.call(this, [], {});
      } else if (extension.staticFields.containsKey(propertyName)) {
        currentValue = extension.getStaticField(propertyName);
      } else {
        throw RuntimeD4rtException(
            "Extension '${extension.name}' has no static field or getter named '$propertyName'.");
      }

      final originalValue = currentValue; // Save for return

      // Calculate new value
      Object? newValue;
      if (currentValue is num) {
        newValue = operatorType == TokenType.PLUS_PLUS
            ? currentValue + 1
            : currentValue - 1;
      } else {
        throw RuntimeD4rtException(
            "Cannot increment/decrement static property '$propertyName' of type '${currentValue?.runtimeType}': Expected number.");
      }

      // Set new value via static setter or field
      final staticSetter = extension.findStaticSetter(propertyName);
      if (staticSetter != null) {
        staticSetter.call(this, [newValue], {});
      } else if (extension.staticFields.containsKey(propertyName)) {
        extension.setStaticField(propertyName, newValue);
      } else {
        throw RuntimeD4rtException(
            "Extension '${extension.name}' has no static setter or field named '$propertyName'.");
      }

      // Return the *original* value for postfix operators
      return originalValue;
    } else {
      throw RuntimeD4rtException(
          "Cannot increment/decrement property on non-instance object of type '${targetValue?.runtimeType}'.");
    }
  } else if (node.operand is IndexExpression) {
    // Handle index access like array[i]++
    final indexExpression = node.operand as IndexExpression;
    final targetValue = indexExpression.target?.accept<Object?>(this);
    final indexValue = indexExpression.index.accept<Object?>(this);

    // Get current value via [] operator or direct access
    Object? currentValue;
    if (targetValue is List) {
      final index = indexValue as int;
      currentValue = targetValue[index];
    } else if (targetValue is Map) {
      currentValue = targetValue[indexValue];
    } else if (targetValue is InterpretedInstance) {
      // Use class operator [] if available
      final operatorMethod = targetValue.findOperator('[]');
      if (operatorMethod != null) {
        try {
          currentValue =
              operatorMethod.bind(targetValue).call(this, [indexValue], {});
        } on ReturnException catch (e) {
          currentValue = e.value;
        } catch (e) {
          throw RuntimeD4rtException(
              "Error executing class operator '[]' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
        }
      } else {
        throw RuntimeD4rtException(
            "Cannot read index for postfix increment/decrement on ${targetValue.klass.name}: No operator '[]' found.");
      }
    } else {
      throw RuntimeD4rtException(
          "Cannot apply postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' to index of type '${targetValue?.runtimeType}'.");
    }

    final originalValue = currentValue; // Save for return

    // Calculate new value
    Object? newValue;
    if (currentValue is num) {
      newValue = operatorType == TokenType.PLUS_PLUS
          ? currentValue + 1
          : currentValue - 1;
    } else if (currentValue is InterpretedInstance) {
      // Use custom + operator with literal 1
      final operatorMethod = currentValue.findOperator('+');
      if (operatorMethod != null) {
        try {
          final operand = _createIncrementOperand(
              currentValue, operatorType == TokenType.PLUS_PLUS);
          newValue =
              operatorMethod.bind(currentValue).call(this, [operand], {});
        } on ReturnException catch (e) {
          newValue = e.value;
        } catch (e) {
          throw RuntimeD4rtException(
              "Error executing custom operator '+' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
        }
      } else {
        throw RuntimeD4rtException(
            "Cannot increment/decrement object at index of type '${currentValue.klass.name}': No operator '+' found.");
      }
    } else {
      throw RuntimeD4rtException(
          "Cannot increment/decrement value at index of type '${currentValue?.runtimeType}': Expected number or object with '+' operator.");
    }

    // Set new value via []= operator or direct access
    if (targetValue is List) {
      final index = indexValue as int;
      targetValue[index] = newValue;
    } else if (targetValue is Map) {
      targetValue[indexValue] = newValue;
    } else if (targetValue is InterpretedInstance) {
      // Use class operator []= if available
      final operatorMethod = targetValue.findOperator('[]=');
      if (operatorMethod != null) {
        try {
          operatorMethod
              .bind(targetValue)
              .call(this, [indexValue, newValue], {});
        } on ReturnException catch (_) {
          // []= should not return a value, but assignment expression returns assigned value
        } catch (e) {
          throw RuntimeD4rtException(
              "Error executing class operator '[]=' for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}': $e");
        }
      } else {
        throw RuntimeD4rtException(
            "Cannot write index for postfix increment/decrement on ${targetValue.klass.name}: No operator '[]=' found.");
      }
    }

    // Return the *original* value for postfix operators
    return originalValue;
  } else {
    throw RuntimeD4rtException(
        "Operand for postfix '${operatorType == TokenType.PLUS_PLUS ? '++' : '--'}' must be an assignable variable or property.");
  }
}