Skip to content

Commit

Permalink
Treewalk: Move handling of __ne__ onto Object as a method so that it …
Browse files Browse the repository at this point in the history
…works even as member access before invocation
  • Loading branch information
Jones Beach committed May 2, 2024
1 parent 8058bc0 commit f06e04b
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 31 deletions.
58 changes: 27 additions & 31 deletions src/treewalk/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl Interpreter {
.ok_or(InterpreterError::ExpectedBoolean(self.state.call_stack()))?
}

fn evaluate_binary_operation(
fn evaluate_binary_operation_outer(
&self,
left: &Expr,
op: &BinOp,
Expand All @@ -171,6 +171,15 @@ impl Interpreter {
let left = self.evaluate_expr(left)?;
let right = self.evaluate_expr(right)?;

self.evaluate_binary_operation(left, op, right)
}

fn evaluate_binary_operation(
&self,
left: ExprResult,
op: &BinOp,
right: ExprResult,
) -> Result<ExprResult, InterpreterError> {
if matches!(op, BinOp::In) {
if let Some(mut iterable) = right.try_into_iter() {
return Ok(ExprResult::Boolean(iterable.contains(left)));
Expand Down Expand Up @@ -227,35 +236,12 @@ impl Interpreter {
&& right.as_object().is_some()
&& matches!(op, BinOp::Equals | BinOp::NotEquals)
{
match op {
BinOp::Equals => self.evaluate_method(
left,
Dunder::Eq.value(),
&ResolvedArguments::default().add_arg(right),
),
BinOp::NotEquals => {
if left
.as_object()
.unwrap()
.get(self, Dunder::Ne.value())
.is_some()
{
self.evaluate_method(
left,
Dunder::Ne.value(),
&ResolvedArguments::default().add_arg(right),
)
} else {
let result = self.evaluate_method(
left,
Dunder::Eq.value(),
&ResolvedArguments::default().add_arg(right),
)?;
Ok(result.inverted())
}
}
let dunder = match op {
BinOp::Equals => Dunder::Eq.value(),
BinOp::NotEquals => Dunder::Ne.value(),
_ => unreachable!(),
}
};
self.evaluate_method(left, dunder, &ResolvedArguments::default().add_arg(right))
} else {
evaluators::evaluate_object_comparison(left, op, right)
}
Expand Down Expand Up @@ -740,7 +726,7 @@ impl Interpreter {
value: &Expr,
) -> Result<(), InterpreterError> {
let op = operator.to_bin_op();
let result = self.evaluate_binary_operation(target, &op, value)?;
let result = self.evaluate_binary_operation_outer(target, &op, value)?;
self.evaluate_assignment_inner(target, result)
}

Expand Down Expand Up @@ -1422,7 +1408,7 @@ impl Interpreter {
} => self.evaluate_dict_comprehension(key, value, range, key_body, value_body),
Expr::UnaryOperation { op, right } => self.evaluate_unary_operation(op, right),
Expr::BinaryOperation { left, op, right } => {
self.evaluate_binary_operation(left, op, right)
self.evaluate_binary_operation_outer(left, op, right)
}
Expr::Await { right } => self.evaluate_await(right),
Expr::FunctionCall { name, args, callee } => {
Expand Down Expand Up @@ -8512,6 +8498,8 @@ f = Foo(4)
g = Foo(4)
a = f == g
b = f != g
c = f.__ne__
d = c(g)
"#;

let (mut parser, mut interpreter) = init(input);
Expand All @@ -8524,6 +8512,14 @@ b = f != g
interpreter.state.read("b"),
Some(ExprResult::Boolean(false))
);
let Some(ExprResult::Method(method)) = interpreter.state.read("c") else {
panic!("Expected a method!");
};
assert!(matches!(method.receiver(), Some(ExprResult::Object(_))));
assert_eq!(
interpreter.state.read("d"),
Some(ExprResult::Boolean(false))
);
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/treewalk/types/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl Object {
Box::new(InitBuiltin),
Box::new(NewBuiltin),
Box::new(EqBuiltin),
Box::new(NeBuiltin),
]
}

Expand Down Expand Up @@ -230,3 +231,34 @@ impl Callable for EqBuiltin {
BindingType::Instance
}
}

/// The default behavior in Python for the `!=` sign is to call the `Dunder::Eq` and invert the
/// result. This is only used when `Dunder::Ne` is not overridden by a user-defined class.
struct NeBuiltin;

impl Callable for NeBuiltin {
fn call(
&self,
interpreter: &Interpreter,
args: ResolvedArguments,
) -> Result<ExprResult, InterpreterError> {
let receiver = args.get_self().ok_or(InterpreterError::ExpectedObject(
interpreter.state.call_stack(),
))?;
let result = interpreter.evaluate_method(
receiver,
Dunder::Eq.value(),
&ResolvedArguments::default().add_arg(args.get_arg(0)),
)?;

Ok(result.inverted())
}

fn name(&self) -> String {
Dunder::Ne.into()
}

fn binding_type(&self) -> BindingType {
BindingType::Instance
}
}

0 comments on commit f06e04b

Please sign in to comment.