visitPostfixExpression method
Object?
visitPostfixExpression(
- 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.");
}
}