StrictModules/Objects/iterable_objects.cpp (1,180 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "StrictModules/Objects/iterable_objects.h" #include "StrictModules/Objects/callable_wrapper.h" #include "StrictModules/Objects/helper.h" #include "StrictModules/Objects/object_interface.h" #include "StrictModules/Objects/objects.h" #include "StrictModules/caller_context.h" #include "StrictModules/caller_context_impl.h" #include <fmt/format.h> #include <algorithm> namespace strictmod::objects { // -------------------------Iterable------------------------- // wrapped methods static inline bool strictIterableContainsHelper( const std::vector<std::shared_ptr<BaseStrictObject>>& data, const CallerContext& caller, std::shared_ptr<BaseStrictObject> item) { for (auto& elem : data) { if (iStrictObjectEq(item, elem, caller)) { return true; } } return false; } std::shared_ptr<StrictType> StrictIterableType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictIterableType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictIterableType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictObjectType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictIterableType)); return baseVec; } // -------------------------Sequence (random access)------------------------- StrictSequence::StrictSequence( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, std::vector<std::shared_ptr<BaseStrictObject>> data) : StrictIterable(std::move(type), std::move(creator)), data_(std::move(data)) {} StrictSequence::StrictSequence( std::shared_ptr<StrictType> type, std::shared_ptr<StrictModuleObject> creator, std::vector<std::shared_ptr<BaseStrictObject>> data) : StrictIterable(std::move(type), std::move(creator)), data_(std::move(data)) {} // wrapped methods std::shared_ptr<BaseStrictObject> StrictSequence::sequence__contains__( std::shared_ptr<StrictSequence> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> item) { return strictIterableContainsHelper(self->data_, caller, std::move(item)) ? StrictTrue() : StrictFalse(); } std::shared_ptr<BaseStrictObject> StrictSequence::sequence__len__( std::shared_ptr<StrictSequence> self, const CallerContext& caller) { return caller.makeInt(self->data_.size()); } std::shared_ptr<BaseStrictObject> StrictSequence::sequence__iter__( std::shared_ptr<StrictSequence> self, const CallerContext& caller) { return std::make_shared<StrictSequenceIterator>( SequenceIteratorType(), caller.caller, std::move(self)); } std::shared_ptr<BaseStrictObject> StrictSequence::sequence__reversed__( std::shared_ptr<StrictSequence> self, const CallerContext& caller) { return std::make_shared<StrictReverseSequenceIterator>( ReverseSequenceIteratorType(), caller.caller, std::move(self)); } static std::shared_ptr<BaseStrictObject> sequenceEqHelper( std::shared_ptr<StrictSequence> self, const CallerContext& caller, std::shared_ptr<StrictSequence> rhs) { auto& selfData = self->getData(); auto& rhsData = rhs->getData(); if (selfData.size() != rhsData.size()) { return StrictFalse(); } for (size_t i = 0; i < selfData.size(); ++i) { if (!iStrictObjectEq(selfData[i], rhsData[i], caller)) { return StrictFalse(); } } return StrictTrue(); } static std::shared_ptr<BaseStrictObject> sequenceAddHelper( std::shared_ptr<StrictSequence> self, std::shared_ptr<StrictType> type, const CallerContext& caller, std::shared_ptr<StrictSequence> rhs) { std::vector<std::shared_ptr<BaseStrictObject>> newData(self->getData()); auto& rhsData = rhs->getData(); newData.insert(newData.end(), rhsData.begin(), rhsData.end()); return self->makeSequence(std::move(type), caller.caller, std::move(newData)); } static std::shared_ptr<BaseStrictObject> sequenceMulHelper( std::shared_ptr<StrictSequence> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> other) { std::shared_ptr<StrictInt> multFactor = std::dynamic_pointer_cast<StrictInt>(other); if (multFactor == nullptr) { return NotImplemented(); } std::vector<std::shared_ptr<BaseStrictObject>> result; auto& data = self->getData(); int_type repeat = multFactor->getValueOr(0); result.reserve(data.size() * repeat); for (int i = 0; i < repeat; ++i) { result.insert(result.end(), data.begin(), data.end()); } return self->makeSequence(self->getType(), caller.caller, std::move(result)); } std::shared_ptr<BaseStrictObject> StrictSequence::sequence__mul__( std::shared_ptr<StrictSequence> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return sequenceMulHelper(std::move(self), caller, std::move(rhs)); } std::shared_ptr<BaseStrictObject> StrictSequence::sequence__rmul__( std::shared_ptr<StrictSequence> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> lhs) { return sequenceMulHelper(std::move(self), caller, std::move(lhs)); } static std::shared_ptr<BaseStrictObject> sequenceGetItemHelper( std::shared_ptr<StrictSequence> self, std::shared_ptr<StrictType> type, const CallerContext& caller, std::shared_ptr<BaseStrictObject> index) { auto& data = self->getData(); std::shared_ptr<StrictInt> intIndex = std::dynamic_pointer_cast<StrictInt>(index); if (intIndex != nullptr) { if (!intIndex->getValue()) { caller.raiseTypeError( "{} index out of range: {}", self->getTypeRef().getName(), intIndex->getDisplayName()); } int idx = normalizeIndex(*intIndex->getValue(), data.size()); if (idx >= 0 && (size_t)idx < data.size()) { return data[idx]; } else { caller.raiseTypeError( "{} index out of range: {}", self->getTypeRef().getName(), idx); } } std::shared_ptr<StrictSlice> sliceIndex = std::dynamic_pointer_cast<StrictSlice>(index); if (sliceIndex != nullptr) { int dataSize = int(data.size()); std::vector<std::shared_ptr<BaseStrictObject>> result; result.reserve(dataSize); int start, stop, step; std::tie(start, stop, step) = sliceIndex->normalizeToSequenceIndex(caller, dataSize); if (step > 0) { for (int i = std::max(0, start); i < std::min(stop, dataSize); i += step) { result.push_back(data[i]); } } else { for (int i = std::min(dataSize - 1, start); i > std::max(-1, stop); i += step) { result.push_back(data[i]); } } // sliced result is always the base type return self->makeSequence( std::move(type), caller.caller, std::move(result)); } caller.raiseTypeError( "{} indices must be integers or slices, not {}", self->getTypeRef().getName(), index->getTypeRef().getName()); } std::shared_ptr<StrictIteratorBase> StrictSequenceType::getElementsIter( std::shared_ptr<BaseStrictObject> obj, const CallerContext& caller) { auto seq = assertStaticCast<StrictSequence>(obj); return std::make_shared<StrictSequenceIterator>( SequenceIteratorType(), caller.caller, std::move(seq)); } std::vector<std::shared_ptr<BaseStrictObject>> StrictSequenceType::getElementsVec( std::shared_ptr<BaseStrictObject> obj, const CallerContext&) { auto seq = assertStaticCast<StrictSequence>(obj); return seq->getData(); } std::shared_ptr<StrictType> StrictSequenceType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictSequenceType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictSequenceType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictIterableType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictSequenceType)); return baseVec; } void StrictSequenceType::addMethods() { StrictIterableType::addMethods(); addMethod(kDunderContains, StrictSequence::sequence__contains__); addMethod(kDunderLen, StrictSequence::sequence__len__); addMethod("__mul__", StrictSequence::sequence__mul__); addMethod("__rmul__", StrictSequence::sequence__rmul__); addMethod(kDunderIter, StrictSequence::sequence__iter__); addMethod("__reversed__", StrictSequence::sequence__reversed__); } // -------------------------List------------------------- std::shared_ptr<StrictSequence> StrictList::makeSequence( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, std::vector<std::shared_ptr<BaseStrictObject>> data) { return std::make_shared<StrictList>( std::move(type), std::move(creator), std::move(data)); } std::string StrictList::getDisplayName() const { return fmt::format("[{}]", fmt::join(data_, ",")); } Ref<> StrictList::getPyObject() const { Ref<> pyObj = Ref<>::steal(PyList_New(data_.size())); if (pyObj == nullptr) { // allocation failed return nullptr; } for (size_t i = 0; i < data_.size(); ++i) { Ref<> elem = data_[i]->getPyObject(); if (elem == nullptr) { return nullptr; } // elem reference is stolen into the list // Thus, it's no longer managed by the Ref elem PyList_SET_ITEM(pyObj.get(), i, elem.get()); elem.release(); } return pyObj; } // wrapped methods std::shared_ptr<BaseStrictObject> StrictList::listAppend( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> elem) { checkExternalModification(self, caller); self->data_.push_back(std::move(elem)); return NoneObject(); } std::shared_ptr<BaseStrictObject> StrictList::listCopy( std::shared_ptr<StrictList> self, const CallerContext& caller) { return std::make_shared<StrictList>(ListType(), caller.caller, self->data_); } std::shared_ptr<BaseStrictObject> StrictList::list__init__( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> iterable) { if (iterable != nullptr) { self->data_ = iGetElementsVec(std::move(iterable), caller); } return NoneObject(); } std::shared_ptr<BaseStrictObject> StrictList::listExtend( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> iterable) { if (iterable != nullptr) { auto newVec = iGetElementsVec(std::move(iterable), caller); self->data_.insert( self->data_.end(), std::make_move_iterator(newVec.begin()), std::make_move_iterator(newVec.end())); } return NoneObject(); } std::shared_ptr<BaseStrictObject> StrictList::list__add__( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsList = std::dynamic_pointer_cast<StrictList>(rhs); if (!rhsList) { caller.raiseTypeError( "can only concatenate list (not {}) to list", rhs->getTypeRef().getDisplayName()); } // even list subclass add result in a list return sequenceAddHelper( std::move(self), ListType(), caller, std::move(rhsList)); } std::shared_ptr<BaseStrictObject> StrictList::list__eq__( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsList = std::dynamic_pointer_cast<StrictList>(rhs); if (!rhsList) { return StrictFalse(); } return sequenceEqHelper(std::move(self), caller, std::move(rhsList)); } std::shared_ptr<BaseStrictObject> StrictList::list__getitem__( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> index) { return sequenceGetItemHelper( std::move(self), ListType(), caller, std::move(index)); } std::shared_ptr<BaseStrictObject> StrictList::list__setitem__( std::shared_ptr<StrictList> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> index, std::shared_ptr<BaseStrictObject> value) { checkExternalModification(self, caller); auto& data = self->getData(); std::shared_ptr<StrictInt> intIndex = std::dynamic_pointer_cast<StrictInt>(index); if (intIndex != nullptr) { if (!intIndex->getValue()) { caller.raiseTypeError( "list assignment index out of range: {}", intIndex->getDisplayName()); } int idx = normalizeIndex(*intIndex->getValue(), data.size()); if (idx >= 0 && (size_t)idx < data.size()) { self->setData(idx, std::move(value)); } else { caller.raiseTypeError("list assignment index out of range: {}", idx); } return NoneObject(); } std::shared_ptr<StrictSlice> sliceIndex = std::dynamic_pointer_cast<StrictSlice>(index); if (sliceIndex != nullptr) { const auto& start = sliceIndex->getStart(); const auto& stop = sliceIndex->getStop(); const auto& step = sliceIndex->getStep(); if (start == NoneObject() && stop == NoneObject() && step == NoneObject()) { // special case, replace entire list self->data_ = iGetElementsVec(std::move(value), caller); } else { caller.error<UnsupportedException>( fmt::format("__setitem__([{},{},{}])", start, stop, step), self->getTypeRef().getName()); } return NoneObject(); } caller.raiseTypeError( "list indices must be integers or slices, not {}", index->getTypeRef().getName()); } std::unique_ptr<BaseStrictObject> StrictListType::constructInstance( std::weak_ptr<StrictModuleObject> caller) { return std::make_unique<StrictList>( std::static_pointer_cast<StrictType>(shared_from_this()), caller, kEmptyArgs); } Ref<> StrictListType::getPyObject() const { return Ref<>(reinterpret_cast<PyObject*>(&PyList_Type)); } std::shared_ptr<StrictType> StrictListType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictListType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictListType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictSequenceType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictListType)); return baseVec; } void StrictListType::addMethods() { StrictSequenceType::addMethods(); addMethod("append", StrictList::listAppend); addMethod("copy", StrictList::listCopy); addMethodDefault(kDunderInit, StrictList::list__init__, nullptr); addMethod("extend", StrictList::listExtend); addMethod("__add__", StrictList::list__add__); addMethod("__eq__", StrictList::list__eq__); addMethod(kDunderGetItem, StrictList::list__getitem__); addMethod(kDunderSetItem, StrictList::list__setitem__); addPyWrappedMethodObj<>( kDunderRepr, reinterpret_cast<PyObject*>(&PyList_Type), StrictString::strFromPyObj); } // -------------------------Tuple------------------------- StrictTuple::StrictTuple( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, std::vector<std::shared_ptr<BaseStrictObject>> data) : StrictSequence(std::move(type), std::move(creator), std::move(data)), pyObj_(nullptr), displayName_() {} StrictTuple::StrictTuple( std::shared_ptr<StrictType> type, std::shared_ptr<StrictModuleObject> creator, std::vector<std::shared_ptr<BaseStrictObject>> data) : StrictSequence(std::move(type), std::move(creator), std::move(data)), pyObj_(nullptr), displayName_() {} std::shared_ptr<StrictSequence> StrictTuple::makeSequence( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, std::vector<std::shared_ptr<BaseStrictObject>> data) { return std::make_shared<StrictTuple>( std::move(type), std::move(creator), std::move(data)); } bool StrictTuple::isHashable() const { for (auto& e : data_) { if (!e->isHashable()) { return false; } } return true; } size_t StrictTuple::hash() const { size_t h = data_.size(); // taken from boost.hash_combine for (auto& e : data_) { h ^= e->hash() + 0x9e3779b9 + (h << 6) + (h >> 2); } return h; } bool StrictTuple::eq(const BaseStrictObject& other) const { if (&other.getTypeRef() != type_.get()) { return false; } const StrictTuple& otherTuple = static_cast<const StrictTuple&>(other); if (data_.size() != otherTuple.data_.size()) { return false; } for (size_t i = 0; i < data_.size(); ++i) { const auto& dataI = data_[i]; const auto& otherI = otherTuple.data_[i]; if (!dataI->eq(*otherI) && !otherI->eq(*dataI)) { return false; } } return true; } std::string StrictTuple::getDisplayName() const { if (displayName_.empty()) { displayName_ = fmt::format("({})", fmt::join(data_, ",")); } return displayName_; } Ref<> StrictTuple::getPyObject() const { // We can cache the PyObject since tuple is immutable if (pyObj_ == nullptr) { pyObj_ = Ref<>::steal(PyTuple_New(data_.size())); if (pyObj_ == nullptr) { // allocation failed return nullptr; } for (size_t i = 0; i < data_.size(); ++i) { Ref<> elem = data_[i]->getPyObject(); if (elem == nullptr) { return nullptr; } // elem reference is stolen into the tuple PyTuple_SET_ITEM(pyObj_.get(), i, elem); elem.release(); } } return Ref<>(pyObj_.get()); } // wrapped methods std::shared_ptr<BaseStrictObject> StrictTuple::tupleIndex( std::shared_ptr<StrictTuple> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> item) { const auto& data = self->data_; for (size_t i = 0; i < data.size(); ++i) { if (iStrictObjectEq(item, data[i], caller)) { return caller.makeInt(i); } } caller.raiseExceptionStr(ValueErrorType(), "tuple.index(x): x not in tuple"); } std::shared_ptr<BaseStrictObject> StrictTuple::tuple__new__( std::shared_ptr<StrictTuple>, const CallerContext& caller, std::shared_ptr<BaseStrictObject> instType, std::shared_ptr<BaseStrictObject> elements) { std::shared_ptr<StrictTupleType> tType = std::dynamic_pointer_cast<StrictTupleType>(instType); if (tType == nullptr) { caller.raiseExceptionStr( TypeErrorType(), "X is not a tuple type object ({})", instType); } if (elements == nullptr) { // empty tuple return std::make_shared<StrictTuple>(tType, caller.caller, kEmptyArgs); } return std::make_shared<StrictTuple>( tType, caller.caller, iGetElementsVec(std::move(elements), caller)); } std::shared_ptr<BaseStrictObject> StrictTuple::tuple__add__( std::shared_ptr<StrictTuple> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsTuple = std::dynamic_pointer_cast<StrictTuple>(rhs); if (!rhsTuple) { caller.raiseTypeError( "can only concatenate tuple (not {}) to tuple", rhs->getTypeRef().getDisplayName()); } return sequenceAddHelper( std::move(self), TupleType(), caller, std::move(rhsTuple)); } std::shared_ptr<BaseStrictObject> StrictTuple::tuple__eq__( std::shared_ptr<StrictTuple> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsTuple = std::dynamic_pointer_cast<StrictTuple>(rhs); if (!rhsTuple) { return StrictFalse(); } return sequenceEqHelper(std::move(self), caller, std::move(rhsTuple)); } std::shared_ptr<BaseStrictObject> StrictTuple::tuple__getitem__( std::shared_ptr<StrictTuple> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> index) { return sequenceGetItemHelper( std::move(self), TupleType(), caller, std::move(index)); } std::unique_ptr<BaseStrictObject> StrictTupleType::constructInstance( std::weak_ptr<StrictModuleObject> caller) { return std::make_unique<StrictTuple>( std::static_pointer_cast<StrictType>(shared_from_this()), std::move(caller), kEmptyArgs); } Ref<> StrictTupleType::getPyObject() const { return Ref<>(reinterpret_cast<PyObject*>(&PyTuple_Type)); } std::shared_ptr<StrictType> StrictTupleType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictTupleType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictTupleType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictSequenceType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictTupleType)); return baseVec; } void StrictTupleType::addMethods() { StrictSequenceType::addMethods(); addMethod("index", StrictTuple::tupleIndex); addStaticMethodDefault("__new__", StrictTuple::tuple__new__, nullptr); addMethod("__add__", StrictTuple::tuple__add__); addMethod("__eq__", StrictTuple::tuple__eq__); addMethod(kDunderGetItem, StrictTuple::tuple__getitem__); addPyWrappedMethodObj<>( kDunderRepr, reinterpret_cast<PyObject*>(&PyTuple_Type), StrictString::strFromPyObj); } // -------------------------Set Like------------------------- StrictSetLike::StrictSetLike( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, SetDataT data) : StrictIterable(std::move(type), std::move(creator)), data_(std::move(data)) {} StrictSetLike::StrictSetLike( std::shared_ptr<StrictType> type, std::shared_ptr<StrictModuleObject> creator, SetDataT data) : StrictIterable(std::move(type), std::move(creator)), data_(std::move(data)) {} void StrictSetLike::addElement( const CallerContext&, std::shared_ptr<BaseStrictObject> element) { data_.insert(element); } std::shared_ptr<BaseStrictObject> StrictSetLike::copy( const CallerContext& caller) { SetDataT copied; copied.reserve(data_.size()); for (auto& e : data_) { copied.insert(e->copy(caller)); } return makeSetLike(type_, caller.caller, std::move(copied)); } static bool strictSetLikeContainsHelper( const SetDataT& data, const CallerContext&, std::shared_ptr<BaseStrictObject> obj) { auto got = data.find(std::move(obj)); return got != data.end(); } // wrapped methods std::shared_ptr<BaseStrictObject> StrictSetLike::set__contains__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> element) { if (strictSetLikeContainsHelper( self->getData(), caller, std::move(element))) { return StrictTrue(); } return StrictFalse(); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__len__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller) { return caller.makeInt(self->getData().size()); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__and__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsSetLike = std::dynamic_pointer_cast<StrictSetLike>(rhs); if (rhsSetLike != nullptr) { auto& rhsData = rhsSetLike->getData(); SetDataT newData; for (auto& elem : rhsData) { if (strictSetLikeContainsHelper(self->data_, caller, elem)) { newData.insert(elem); } } return self->makeSetLike(self->type_, caller.caller, std::move(newData)); } return NotImplemented(); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__or__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsSetLike = std::dynamic_pointer_cast<StrictSetLike>(rhs); if (rhsSetLike != nullptr) { auto& rhsData = rhsSetLike->getData(); SetDataT newData(self->data_); for (auto& elem : rhsData) { if (!strictSetLikeContainsHelper(newData, caller, elem)) { newData.insert(elem); } } return self->makeSetLike(self->type_, caller.caller, std::move(newData)); } return NotImplemented(); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__xor__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { auto rhsSetLike = std::dynamic_pointer_cast<StrictSetLike>(rhs); if (rhsSetLike != nullptr) { auto& rhsData = rhsSetLike->getData(); SetDataT newData; for (auto& elem : rhsData) { if (!strictSetLikeContainsHelper(self->data_, caller, elem)) { newData.insert(elem); } } for (auto& elem : self->data_) { if (!strictSetLikeContainsHelper(rhsData, caller, elem)) { newData.insert(elem); } } return self->makeSetLike(self->type_, caller.caller, std::move(newData)); } return NotImplemented(); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__iter__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller) { return std::make_shared<StrictSetIterator>( SetIteratorType(), caller.caller, std::move(self)); } // return true if lhs <= rhs, false otherwise bool subsetHelper( std::shared_ptr<StrictSetLike> lhs, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs, bool isStrict = false) { std::vector<std::shared_ptr<BaseStrictObject>> rhsElements = iGetElementsVec(std::move(rhs), caller); auto lhsData = lhs->getData(); if (lhsData.size() > rhsElements.size()) { return false; } if (isStrict && lhsData.size() == rhsElements.size()) { return false; } SetDataT rhsData( std::move_iterator(rhsElements.begin()), std::move_iterator(rhsElements.end())); for (auto& e : lhsData) { if (rhsData.find(e) == rhsData.end()) { return false; } } return true; } // return true if lhs <= rhs, false otherwise bool supersetHelper( std::shared_ptr<StrictSetLike> lhs, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs, bool isStrict = false) { std::vector<std::shared_ptr<BaseStrictObject>> rhsElements = iGetElementsVec(std::move(rhs), caller); auto lhsData = lhs->getData(); if (lhsData.size() < rhsElements.size()) { return false; } if (isStrict && lhsData.size() == rhsElements.size()) { return false; } for (auto& e : rhsElements) { if (lhsData.find(e) == lhsData.end()) { return false; } } return true; } std::shared_ptr<BaseStrictObject> StrictSetLike::setIssubset( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return caller.makeBool(subsetHelper(std::move(self), caller, std::move(rhs))); } std::shared_ptr<BaseStrictObject> StrictSetLike::setIssuperset( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return caller.makeBool( supersetHelper(std::move(self), caller, std::move(rhs))); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__le__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return caller.makeBool(subsetHelper(std::move(self), caller, std::move(rhs))); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__lt__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return caller.makeBool( subsetHelper(std::move(self), caller, std::move(rhs), true)); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__ge__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return caller.makeBool( supersetHelper(std::move(self), caller, std::move(rhs))); } std::shared_ptr<BaseStrictObject> StrictSetLike::set__gt__( std::shared_ptr<StrictSetLike> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> rhs) { return caller.makeBool( supersetHelper(std::move(self), caller, std::move(rhs), true)); } void StrictSetLikeType::addMethods() { addMethod(kDunderContains, StrictSetLike::set__contains__); addMethod("__len__", StrictSetLike::set__len__); addMethod("__and__", StrictSetLike::set__and__); addMethod("__or__", StrictSetLike::set__or__); addMethod("__xor__", StrictSetLike::set__xor__); addMethod("__le__", StrictSetLike::set__le__); addMethod("__lt__", StrictSetLike::set__lt__); addMethod("__ge__", StrictSetLike::set__ge__); addMethod("__gt__", StrictSetLike::set__gt__); addMethod("issubset", StrictSetLike::setIssubset); addMethod("issuperset", StrictSetLike::setIssuperset); addMethod(kDunderIter, StrictSetLike::set__iter__); } std::shared_ptr<StrictType> StrictSetLikeType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictSetLikeType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictSetLikeType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictObjectType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictSetLikeType)); return baseVec; } std::shared_ptr<StrictIteratorBase> StrictSetLikeType::getElementsIter( std::shared_ptr<BaseStrictObject> obj, const CallerContext& caller) { auto set = assertStaticCast<StrictSetLike>(obj); return std::make_shared<StrictSetIterator>( SetIteratorType(), caller.caller, std::move(set)); } std::vector<std::shared_ptr<BaseStrictObject>> StrictSetLikeType::getElementsVec( std::shared_ptr<BaseStrictObject> obj, const CallerContext&) { auto seq = assertStaticCast<StrictSetLike>(obj); auto& data = seq->getData(); return std::vector<std::shared_ptr<BaseStrictObject>>( data.begin(), data.end()); } // -------------------------Set------------------------- std::string StrictSet::getDisplayName() const { if (data_.empty()) { return "set()"; } return fmt::format("{{{}}}", fmt::join(data_, ",")); } Ref<> StrictSet::getPyObject() const { // this give empty set Ref<> pyObj = Ref<>::steal(PySet_New(nullptr)); if (pyObj == nullptr) { // allocation failed return nullptr; } for (auto& v : data_) { Ref<> elem = v->getPyObject(); if (elem == nullptr) { return nullptr; } // set keeps its own reference to elem if (PySet_Add(pyObj, elem) < 0) { PyErr_Clear(); return nullptr; } } return pyObj; } std::shared_ptr<StrictSetLike> StrictSet::makeSetLike( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, SetDataT data) { return std::make_shared<StrictSet>( std::move(type), std::move(creator), std::move(data)); } // wrapped methods std::shared_ptr<BaseStrictObject> StrictSet::setAdd( std::shared_ptr<StrictSet> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> item) { checkExternalModification(self, caller); if (!strictSetLikeContainsHelper(self->data_, caller, item)) { self->data_.insert(std::move(item)); } return NoneObject(); } std::shared_ptr<BaseStrictObject> StrictSet::set__init__( std::shared_ptr<StrictSet> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> arg) { checkExternalModification(self, caller); self->data_.clear(); if (arg) { auto elementsVec = iGetElementsVec(std::move(arg), caller); self->data_.reserve(elementsVec.size()); self->data_.insert( std::move_iterator(elementsVec.begin()), std::move_iterator(elementsVec.end())); } return NoneObject(); } std::shared_ptr<BaseStrictObject> StrictSet::setUpdate( std::shared_ptr<StrictSet> self, const CallerContext& caller, std::shared_ptr<BaseStrictObject> arg) { checkExternalModification(self, caller); auto elementsVec = iGetElementsVec(std::move(arg), caller); self->data_.reserve(elementsVec.size() + self->data_.size()); self->data_.insert( std::move_iterator(elementsVec.begin()), std::move_iterator(elementsVec.end())); return NoneObject(); } std::unique_ptr<BaseStrictObject> StrictSetType::constructInstance( std::weak_ptr<StrictModuleObject> caller) { return std::make_unique<StrictSet>( std::static_pointer_cast<StrictType>(shared_from_this()), caller); } std::shared_ptr<StrictType> StrictSetType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictSetType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictSetType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictSetLikeType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictSetType)); return baseVec; } void StrictSetType::addMethods() { StrictSetLikeType::addMethods(); addMethod("add", StrictSet::setAdd); addMethodDefault("__init__", StrictSet::set__init__, nullptr); addMethod("update", StrictSet::setUpdate); addPyWrappedMethodObj<>( kDunderRepr, reinterpret_cast<PyObject*>(&PySet_Type), StrictString::strFromPyObj); } // -------------------------FrozenSet------------------------- StrictFrozenSet::StrictFrozenSet( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, SetDataT data) : StrictSetLike(std::move(type), std::move(creator), std::move(data)), pyObj_(nullptr), displayName_() {} StrictFrozenSet::StrictFrozenSet( std::shared_ptr<StrictType> type, std::shared_ptr<StrictModuleObject> creator, SetDataT data) : StrictSetLike(std::move(type), std::move(creator), std::move(data)), pyObj_(nullptr), displayName_() {} std::string StrictFrozenSet::getDisplayName() const { if (displayName_.empty()) { if (data_.empty()) { displayName_ = "frozenset()"; } displayName_ = fmt::format("frozenset({{{}}})", fmt::join(data_, ",")); } return displayName_; } Ref<> StrictFrozenSet::getPyObject() const { if (pyObj_ == nullptr) { pyObj_ = Ref<>::steal(PyFrozenSet_New(nullptr)); if (pyObj_ == nullptr) { return nullptr; } for (auto& v : data_) { Ref<> elem = v->getPyObject(); if (elem == nullptr) { return nullptr; } // set keeps its own reference to elem if (PySet_Add(pyObj_, elem) < 0) { PyErr_Clear(); return nullptr; } } } return Ref<>(pyObj_.get()); } std::shared_ptr<StrictSetLike> StrictFrozenSet::makeSetLike( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, SetDataT data) { return std::make_shared<StrictFrozenSet>( std::move(type), std::move(creator), std::move(data)); } // wrapped methods std::shared_ptr<BaseStrictObject> StrictFrozenSet::frozensetNew( std::shared_ptr<StrictFrozenSet>, const CallerContext& caller, std::shared_ptr<BaseStrictObject> instTypeArg, std::shared_ptr<BaseStrictObject> arg) { auto instType = std::dynamic_pointer_cast<StrictFrozenSetType>(instTypeArg); if (!instType) { caller.raiseTypeError("{} is not a subtype of frozenset", instTypeArg); } SetDataT data; if (arg) { auto elementsVec = iGetElementsVec(std::move(arg), caller); data.reserve(elementsVec.size()); data.insert( std::move_iterator(elementsVec.begin()), std::move_iterator(elementsVec.end())); } return std::make_shared<StrictFrozenSet>( std::move(instType), caller.caller, std::move(data)); } std::unique_ptr<BaseStrictObject> StrictFrozenSetType::constructInstance( std::weak_ptr<StrictModuleObject> caller) { return std::make_unique<StrictFrozenSet>( std::static_pointer_cast<StrictType>(shared_from_this()), caller); } std::shared_ptr<StrictType> StrictFrozenSetType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictFrozenSetType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } std::vector<std::type_index> StrictFrozenSetType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictSetLikeType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictFrozenSetType)); return baseVec; } void StrictFrozenSetType::addMethods() { StrictSetLikeType::addMethods(); addStaticMethod("__new__", StrictFrozenSet::frozensetNew); addPyWrappedMethodObj<>( kDunderRepr, reinterpret_cast<PyObject*>(&PyFrozenSet_Type), StrictString::strFromPyObj); } // Slice StrictSlice::StrictSlice( std::shared_ptr<StrictType> type, std::weak_ptr<StrictModuleObject> creator, std::shared_ptr<BaseStrictObject> start, std::shared_ptr<BaseStrictObject> stop, std::shared_ptr<BaseStrictObject> step) : StrictInstance(std::move(type), std::move(creator)), start_(std::move(start)), stop_(std::move(stop)), step_(std::move(step)) {} std::string StrictSlice::getDisplayName() const { return fmt::format("slice({}, {}, {})", start_, stop_, step_); } static std::shared_ptr<StrictInt> getSliceIndex( const std::shared_ptr<BaseStrictObject>& idx, const CallerContext& caller) { auto idxInt = std::dynamic_pointer_cast<StrictInt>(idx); if (!idxInt) { caller.raiseTypeError( "slice indices must be int or None, not {}", idx->getTypeRef().getName()); } return idxInt; } std::tuple<int, int, int> StrictSlice::normalizeToSequenceIndex( const CallerContext& caller, int sequenceSize) { int start, stop, step; // step if (step_ == NoneObject()) { step = 1; } else { auto stepInt = getSliceIndex(step_, caller); step = stepInt->getValueOr(0); } if (step == 0) { caller.raiseTypeError("slice step cannot be 0"); } // start if (start_ == NoneObject()) { // start is sign dependent on step start = step > 0 ? 0 : sequenceSize - 1; } else { auto startInt = getSliceIndex(start_, caller); start = startInt->getValueOr(0); } if (start < 0) { start += sequenceSize; } // stop if (stop_ == NoneObject()) { // stop is sign dependent on step stop = step > 0 ? sequenceSize : -sequenceSize - 1; } else { auto stopInt = getSliceIndex(stop_, caller); stop = stopInt->getValueOr(-1); } if (stop < 0) { stop += sequenceSize; } return std::make_tuple(start, stop, step); } // Range object StrictRange::StrictRange( std::weak_ptr<StrictModuleObject> creator, std::shared_ptr<BaseStrictObject> start, std::shared_ptr<BaseStrictObject> stop, std::shared_ptr<BaseStrictObject> step) : StrictInstance(RangeType(), std::move(creator)), start_(std::move(start)), stop_(std::move(stop)), step_(std::move(step)) {} std::string StrictRange::getDisplayName() const { return fmt::format("range({}, {}, {})", start_, stop_, step_); } // wrapped methods std::shared_ptr<BaseStrictObject> StrictRange::range__new__( std::shared_ptr<BaseStrictObject>, const std::vector<std::shared_ptr<BaseStrictObject>>& args, const std::vector<std::string>& namedArgs, const CallerContext& caller) { if (!namedArgs.empty()) { caller.raiseTypeError("range() does not take keyword args"); } if (args.size() > 4) { caller.raiseTypeError( "range() expects at most 3 argument, got {}", args.size()); } for (std::size_t i = 1; i < args.size(); ++i) { auto& arg = args[i]; auto intObj = std::dynamic_pointer_cast<StrictInt>(arg); if (!intObj) { caller.raiseTypeError( "range() arg {} should be int, got {} object", i - 1, arg->getTypeRef().getName()); } } // first arg is cls, which is always range and we skip over it // if only one other argument, it is stop if (args.size() == 2) { return std::make_shared<StrictRange>( caller.caller, caller.makeInt(0), args[1], caller.makeInt(1)); } else if (args.size() < 2) { caller.raiseTypeError("range() expects at least 1 argument, got 0"); } // otherwise, it is in order of start, stop, step std::shared_ptr<BaseStrictObject> step; std::shared_ptr<BaseStrictObject> start = args[1]; std::shared_ptr<BaseStrictObject> stop = args[2]; if (args.size() > 3) { step = args[3]; } else { step = caller.makeInt(1); } return std::make_shared<StrictRange>( caller.caller, std::move(start), std::move(stop), std::move(step)); } std::shared_ptr<BaseStrictObject> StrictRange::range__iter__( std::shared_ptr<StrictRange> self, const CallerContext& caller) { return std::make_shared<StrictRangeIterator>( RangeIteratorType(), caller.caller, std::move(self)); } std::unique_ptr<BaseStrictObject> StrictRangeType::constructInstance( std::weak_ptr<StrictModuleObject> caller) { return std::make_unique<StrictRange>( caller, std::make_shared<StrictInt>(IntType(), caller, 0), std::make_shared<StrictInt>(IntType(), caller, 0), std::make_shared<StrictInt>(IntType(), caller, 1)); } std::shared_ptr<StrictType> StrictRangeType::recreate( std::string name, std::weak_ptr<StrictModuleObject> caller, std::vector<std::shared_ptr<BaseStrictObject>> bases, std::shared_ptr<DictType> members, std::shared_ptr<StrictType> metatype, bool isImmutable) { return createType<StrictRangeType>( std::move(name), std::move(caller), std::move(bases), std::move(members), std::move(metatype), isImmutable); } void StrictRangeType::addMethods() { addBuiltinFunctionOrMethod("__new__", StrictRange::range__new__); addMethod(kDunderIter, StrictRange::range__iter__); } std::vector<std::type_index> StrictRangeType::getBaseTypeinfos() const { std::vector<std::type_index> baseVec = StrictObjectType::getBaseTypeinfos(); baseVec.emplace_back(typeid(StrictRangeType)); return baseVec; } bool StrictRangeType::isBaseType() const { return false; } } // namespace strictmod::objects