Skip to content

Commit fcd097b

Browse files
committed
Runtime: Implemented divmod
1 parent dea6c45 commit fcd097b

File tree

6 files changed

+178
-196
lines changed

6 files changed

+178
-196
lines changed

src/runtime/PyNumber.cpp

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
#include "PyNumber.hpp"
2+
#include "NotImplemented.hpp"
23
#include "PyFloat.hpp"
34
#include "PyInteger.hpp"
5+
#include "PyNone.hpp"
6+
#include "PyObject.hpp"
47
#include "PyString.hpp"
58
#include "PyType.hpp"
69
#include "TypeError.hpp"
7-
#include "PyNone.hpp"
8-
#include "PyObject.hpp"
910
#include "Value.hpp"
10-
#include "NotImplemented.hpp"
11+
#include "runtime/PyTuple.hpp"
12+
#include "runtime/ValueError.hpp"
1113
#include "types/builtin.hpp"
1214

1315
#include "interpreter/Interpreter.hpp"
16+
#include <cmath>
1417
#include <variant>
1518

1619
namespace py {
@@ -96,34 +99,79 @@ PyResult<PyObject *> PyNumber::__mod__(const PyObject *obj) const
9699
}
97100
}
98101

99-
PyResult<PyObject *> PyNumber::__mul__(const PyObject *obj) const
102+
PyResult<PyObject *> PyNumber::__divmod__(PyObject *obj)
100103
{
101104
if (auto rhs = as_number(obj)) {
102-
return PyNumber::create(m_value * rhs->value());
105+
if (rhs->value() == Number{ 0 }) {
106+
// TODO: implement ZeroDivisionError
107+
return Err(value_error("ZeroDivisionError"));
108+
}
109+
return std::visit(
110+
overloaded{
111+
[](const mpz_class &lhs_value, const mpz_class &rhs_value) -> PyResult<PyTuple *> {
112+
// For integers, the result is the same as (a // b, a % b)
113+
return PyTuple::create(
114+
Number{ lhs_value / rhs_value }, Number{ lhs_value % rhs_value });
115+
},
116+
[](const mpz_class &lhs_value, const double &rhs_value) -> PyResult<PyTuple *> {
117+
// For floating-point numbers the result is (q, a % b), where q is usually
118+
// math.floor(a / b) but may be 1 less than that
119+
const auto r = std::remainder(lhs_value.get_d(), rhs_value);
120+
const auto q = Number{ (-r + lhs_value.get_d()) / rhs_value };
121+
return PyTuple::create(q, Number{ r });
122+
},
123+
[](const double &lhs_value, const mpz_class &rhs_value) -> PyResult<PyTuple *> {
124+
// For floating-point numbers the result is (q, a % b), where q is usually
125+
// math.floor(a / b) but may be 1 less than that
126+
const auto r = std::remainder(lhs_value, rhs_value.get_d());
127+
const auto q = Number{ (-r + lhs_value) / rhs_value.get_d() };
128+
return PyTuple::create(q, Number{ r });
129+
},
130+
[](const double &lhs_value, const double &rhs_value) -> PyResult<PyTuple *> {
131+
// For floating-point numbers the result is (q, a % b), where q is usually
132+
// math.floor(a / b) but may be 1 less than that
133+
const auto r = std::remainder(lhs_value, rhs_value);
134+
const auto q = Number{ (-r + lhs_value) / rhs_value };
135+
return PyTuple::create(q, Number{ r });
136+
},
137+
},
138+
m_value.value,
139+
rhs->m_value.value);
103140
} else {
104-
return Err(type_error("unsupported operand type(s) for *: \'{}\' and \'{}\'",
141+
return Err(type_error("unsupported operand type(s) for %: \'{}\' and \'{}\'",
105142
type()->name(),
106143
obj->type()->name()));
107144
}
108145
}
109146

147+
PyResult<PyObject *> PyNumber::__mul__(const PyObject *obj) const
148+
{
149+
if (auto rhs = as_number(obj)) {
150+
return PyNumber::create(m_value * rhs->value());
151+
} else {
152+
return Ok(not_implemented());
153+
}
154+
}
155+
110156
PyResult<PyObject *> PyNumber::__pow__(const PyObject *obj, const PyObject *modulo_obj) const
111157
{
112158
if (auto rhs_ = as_number(obj)) {
113-
if (modulo_obj == py_none()) {
114-
return PyObject::from(m_value.exp(rhs_->value()));
115-
}
159+
if (modulo_obj == py_none()) { return PyObject::from(m_value.exp(rhs_->value())); }
116160
auto modulo_ = as_number(modulo_obj);
117-
if (!modulo_) {
118-
return Ok(not_implemented());
119-
}
161+
if (!modulo_) { return Ok(not_implemented()); }
120162
mpz_class result{};
121-
mpz_class lhs = std::holds_alternative<BigIntType>( m_value.value) ? std::get<BigIntType>(m_value.value) : BigIntType{std::get<double>(m_value.value)};
122-
mpz_class rhs = std::holds_alternative<BigIntType>( rhs_->value().value) ? std::get<BigIntType>(rhs_->value().value) : BigIntType{std::get<double>(rhs_->value().value)};
123-
mpz_class modulo = std::holds_alternative<BigIntType>( modulo_->value().value) ? std::get<BigIntType>(modulo_->value().value) : BigIntType{std::get<double>(modulo_->value().value)};
163+
mpz_class lhs = std::holds_alternative<BigIntType>(m_value.value)
164+
? std::get<BigIntType>(m_value.value)
165+
: BigIntType{ std::get<double>(m_value.value) };
166+
mpz_class rhs = std::holds_alternative<BigIntType>(rhs_->value().value)
167+
? std::get<BigIntType>(rhs_->value().value)
168+
: BigIntType{ std::get<double>(rhs_->value().value) };
169+
mpz_class modulo = std::holds_alternative<BigIntType>(modulo_->value().value)
170+
? std::get<BigIntType>(modulo_->value().value)
171+
: BigIntType{ std::get<double>(modulo_->value().value) };
124172
mpz_powm(result.get_mpz_t(), lhs.get_mpz_t(), rhs.get_mpz_t(), modulo.get_mpz_t());
125173

126-
return PyNumber::create(Number{result});
174+
return PyNumber::create(Number{ result });
127175
} else {
128176
return Err(type_error("unsupported operand type(s) for **: \'{}\' and \'{}\'",
129177
type()->name(),

src/runtime/PyNumber.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class PyNumber : public PyBaseObject
2323
PyResult<PyObject *> __add__(const PyObject *obj) const;
2424
PyResult<PyObject *> __sub__(const PyObject *obj) const;
2525
PyResult<PyObject *> __mod__(const PyObject *obj) const;
26+
PyResult<PyObject *> __divmod__(PyObject *obj);
2627
PyResult<PyObject *> __mul__(const PyObject *obj) const;
2728
PyResult<PyObject *> __pow__(const PyObject *obj, const PyObject *modulo) const;
2829
PyResult<PyObject *> __truediv__(const PyObject *obj) const;

src/runtime/PyObject.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "PyType.hpp"
2020
#include "StopIteration.hpp"
2121
#include "TypeError.hpp"
22+
#include "runtime/Value.hpp"
2223
#include "types/api.hpp"
2324
#include "types/builtin.hpp"
2425

@@ -830,6 +831,16 @@ PyResult<PyObject *> PyObject::modulo(const PyObject *other) const
830831
other->type_prototype().__name__));
831832
}
832833

834+
PyResult<PyObject *> PyObject::divmod(PyObject *other)
835+
{
836+
if (type_prototype().__divmod__.has_value()) {
837+
return call_slot(*type_prototype().__divmod__, this, other);
838+
}
839+
return Err(type_error("unsupported operand type(s) for divmod(): \'{}\' and \'{}\'",
840+
type_prototype().__name__,
841+
other->type_prototype().__name__));
842+
}
843+
833844
PyResult<PyObject *> PyObject::and_(PyObject *other)
834845
{
835846
if (type_prototype().__and__.has_value()) {

src/runtime/PyObject.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ using RightShiftSlotFunctionType =
139139
std::function<PyResult<PyObject *>(const PyObject *, const PyObject *)>;
140140
using ModuloSlotFunctionType =
141141
std::function<PyResult<PyObject *>(const PyObject *, const PyObject *)>;
142+
using DivmodSlotFunctionType = std::function<PyResult<PyObject *>(PyObject *, PyObject *)>;
142143
using AndSlotFunctionType = std::function<PyResult<PyObject *>(PyObject *, PyObject *)>;
143144
using OrSlotFunctionType = std::function<PyResult<PyObject *>(PyObject *, PyObject *)>;
144145
using XorSlotFunctionType = std::function<PyResult<PyObject *>(PyObject *, PyObject *)>;
@@ -252,6 +253,7 @@ struct TypePrototype
252253
std::optional<std::variant<LeftShiftSlotFunctionType, PyObject *>> __lshift__;
253254
std::optional<std::variant<RightShiftSlotFunctionType, PyObject *>> __rshift__;
254255
std::optional<std::variant<ModuloSlotFunctionType, PyObject *>> __mod__;
256+
std::optional<std::variant<DivmodSlotFunctionType, PyObject *>> __divmod__;
255257
std::optional<std::variant<AndSlotFunctionType, PyObject *>> __and__;
256258
std::optional<std::variant<OrSlotFunctionType, PyObject *>> __or__;
257259
std::optional<std::variant<XorSlotFunctionType, PyObject *>> __xor__;
@@ -391,6 +393,7 @@ class PyObject : public Cell
391393
PyResult<PyObject *> lshift(const PyObject *other) const;
392394
PyResult<PyObject *> rshift(const PyObject *other) const;
393395
PyResult<PyObject *> modulo(const PyObject *other) const;
396+
PyResult<PyObject *> divmod(PyObject *other);
394397
PyResult<PyObject *> and_(PyObject *other);
395398
PyResult<PyObject *> or_(PyObject *other);
396399
PyResult<PyObject *> xor_(PyObject *other);
@@ -769,6 +772,11 @@ std::unique_ptr<TypePrototype> TypePrototype::create(std::string_view name, Args
769772
return static_cast<const Type *>(self)->__mod__(other);
770773
};
771774
}
775+
if constexpr (HasDivmod<Type>) {
776+
type_prototype->__divmod__ = +[](PyObject *self, PyObject *other) -> PyResult<PyObject *> {
777+
return static_cast<Type *>(self)->__divmod__(other);
778+
};
779+
}
772780
if constexpr (HasAnd<Type>) {
773781
type_prototype->__and__ = +[](PyObject *self, PyObject *other) -> PyResult<PyObject *> {
774782
return static_cast<Type *>(self)->__and__(other);

0 commit comments

Comments
 (0)