#pragma once
#include <xs/Sub.h>
#include <xs/Hash.h>
#include <xs/Glob.h>
#include <xs/next.h>
#include <xs/Array.h>
#include <xs/Scalar.h>
#include <xs/Simple.h>
#include <panda/string.h>
namespace xs {
using xs::my_perl;
struct Stash : Hash {
using string_view = panda::string_view;
struct op_proxy : Glob {
op_proxy (SV** ptr) : Glob(), ptr(ptr) { if (ptr) set(*ptr); }
op_proxy (const op_proxy&) = default;
op_proxy (op_proxy&&) = default;
op_proxy& operator= (SV*);
op_proxy& operator= (AV* v) { _throw(); slot(v); return *this; }
op_proxy& operator= (HV* v) { _throw(); slot(v); return *this; }
op_proxy& operator= (CV* v) { _throw(); slot(v); return *this; }
op_proxy& operator= (IO* v) { _throw(); slot(v); return *this; }
op_proxy& operator= (GV*);
op_proxy& operator= (std::nullptr_t) { return operator=((SV*)NULL); }
op_proxy& operator= (const Sv& v) { return operator=(v.get()); }
op_proxy& operator= (const Scalar& v) { _throw(); slot(v); return *this; }
op_proxy& operator= (const Array& v) { _throw(); slot(v); return *this; }
op_proxy& operator= (const Hash& v) { _throw(); slot(v); return *this; }
op_proxy& operator= (const Sub& v) { _throw(); slot(v); return *this; }
op_proxy& operator= (const Io& v) { _throw(); slot(v); return *this; }
op_proxy& operator= (const Glob& v) { return operator=(v.get<GV>()); }
op_proxy& operator= (const op_proxy& v) { return operator=(v.get<GV>()); }
inline void _throw () { if (!ptr) throw std::logic_error("store: empty object"); }
private:
SV** ptr;
};
static Stash root () { return PL_defstash; }
static Stash from_name (SV* fqn, I32 flags = 0) { return gv_stashsv(fqn, flags); }
Stash (std::nullptr_t = nullptr) {}
Stash (SV* sv, bool policy = INCREMENT) : Hash(sv, policy) { _validate(); }
Stash (HV* sv, bool policy = INCREMENT) : Hash(sv, policy) { _validate(); }
Stash (const string_view& package, I32 flags = 0) {
*this = gv_stashpvn(package.data(), package.length(), flags);
}
Stash (const Stash& oth) : Hash(oth) {}
Stash (const Hash& oth) : Hash(oth) { _validate(); }
Stash (const Sv& oth) : Stash(oth.get()) {}
Stash (Stash&& oth) : Hash(std::move(oth)) {}
Stash (Hash&& oth) : Hash(std::move(oth)) { _validate(); }
Stash (Sv&& oth) : Hash(std::move(oth)) { _validate(); }
Stash (const Simple&) = delete;
Stash (const Array&) = delete;
Stash (const Sub&) = delete;
Stash (const Glob&) = delete;
Stash (const Io&) = delete;
Stash& operator= (SV* val) { Hash::operator=(val); _validate(); return *this; }
Stash& operator= (HV* val) { Hash::operator=(val); _validate(); return *this; }
Stash& operator= (std::nullptr_t) { Hash::operator=(nullptr); return *this; }
Stash& operator= (const Stash& oth) { Hash::operator=(oth); return *this; }
Stash& operator= (Stash&& oth) { Hash::operator=(std::move(oth)); return *this; }
Stash& operator= (const Hash& oth) { Hash::operator=(oth); _validate(); return *this; }
Stash& operator= (Hash&& oth) { Hash::operator=(std::move(oth)); _validate(); return *this; }
Stash& operator= (const Sv& oth) { return operator=(oth.get()); }
Stash& operator= (Sv&& oth) { Hash::operator=(std::move(oth)); _validate(); return *this; }
Stash& operator= (const Simple&) = delete;
Stash& operator= (const Array&) = delete;
Stash& operator= (const Sub&) = delete;
Stash& operator= (const Glob&) = delete;
Stash& operator= (const Io&) = delete;
using Hash::set;
void set (HV* val) { Hash::operator=(val); }
Glob fetch (const string_view& key) const {
auto elem = Hash::fetch(key);
_promote(elem.get<GV>(), key);
return elem.get<GV>();
}
Glob at (const string_view& key) const {
Glob ret = fetch(key);
if (!ret) throw std::out_of_range("at: no key");
return ret;
}
Glob operator[] (const string_view& key) const { return fetch(key); }
void store (const string_view& key, SV* v) { operator[](key) = v; }
void store (const string_view& key, AV* v) { operator[](key) = v; }
void store (const string_view& key, HV* v) { operator[](key) = v; }
void store (const string_view& key, CV* v) { operator[](key) = v; }
void store (const string_view& key, GV* v) { operator[](key) = v; }
void store (const string_view& key, const Sv& v) { operator[](key) = v; }
void store (const string_view& key, const Scalar& v) { operator[](key) = v; }
void store (const string_view& key, const Array& v) { operator[](key) = v; }
void store (const string_view& key, const Hash& v) { operator[](key) = v; }
void store (const string_view& key, const Sub& v) { operator[](key) = v; }
void store (const string_view& key, const Io& v) { operator[](key) = v; }
void store (const string_view& key, const Glob& v) { operator[](key) = v; }
op_proxy operator[] (const string_view& key) {
if (!sv) return NULL;
SV** ref = hv_fetch((HV*)sv, key.data(), key.length(), 1);
_promote((GV*)*ref, key);
return ref;
}
string_view name () const { return string_view(HvNAME(sv), HvNAMELEN(sv)); }
HEK* name_hek () const { return HvNAME_HEK_NN((HV*)sv); }
const Simple& name_sv () const { if (!_name_sv) _name_sv = Simple::shared(name_hek()); return _name_sv; }
string_view effective_name () const { return string_view(HvENAME(sv), HvENAMELEN(sv)); }
panda::string path () const;
Scalar scalar (const string_view& name) const { return fetch(name).scalar(); }
Array array (const string_view& name) const { return fetch(name).array(); }
Hash hash (const string_view& name) const { return fetch(name).hash(); }
Sub sub (const string_view& name) const { return fetch(name).sub(); }
Io io (const string_view& name) const { return fetch(name).io(); }
void scalar (const string_view& name, const Scalar& v) { operator[](name) = v; }
void array (const string_view& name, const Array& v) { operator[](name) = v; }
void hash (const string_view& name, const Hash& v) { operator[](name) = v; }
void sub (const string_view& name, const Sub& v) { operator[](name) = v; }
void io (const string_view& name, const Io& v) { operator[](name) = v; }
Sub method (const Sv& name) const {
GV* gv = gv_fetchmeth_sv((HV*)sv, name, 0, 0);
return gv ? Sub(GvCV(gv)) : Sub();
}
Sub method (const string_view& name) const {
GV* gv = gv_fetchmeth_pvn((HV*)sv, name.data(), name.length(), 0, 0);
return gv ? Sub(GvCV(gv)) : Sub();
}
Sub method_strict (const Sv& name) const {
Sub ret = method(name);
if (!ret) _throw_nomethod(name);
return ret;
}
Sub method_strict (const string_view& name) const {
Sub ret = method(name);
if (!ret) _throw_nomethod(name);
return ret;
}
Sub next_method (const Sub& current) const { return xs::next::method((HV*)sv, current.get<CV>()); }
Sub next_method_strict (const Sub& current) const { return xs::next::method_strict((HV*)sv, current.get<CV>()); }
Sub super_method (const Sub& current) const { return xs::super::method((HV*)sv, current.get<CV>()); }
Sub super_method_strict (const Sub& current) const { return xs::super::method_strict((HV*)sv, current.get<CV>()); }
void mark_as_loaded (const Stash& source) const;
void mark_as_loaded (const string_view& source) const { mark_as_loaded(Stash(source, GV_ADD)); }
void inherit (const Stash& parent);
void inherit (const string_view& parent) { inherit(Stash(parent, GV_ADD)); }
bool isa (const string_view& parent, U32 hash = 0, int flags = 0) const;
bool isa (HEK* hek) const { return isa(string_view(HEK_KEY(hek), HEK_LEN(hek)), HEK_HASH(hek), HEK_UTF8(hek)); }
bool isa (const Stash& parent) const { return isa(HvNAME_HEK(parent.get<HV>())); }
template <class...R, class...A> Sub::call_t<R...> call (const Sv& name, A&&...args) const { return method_strict(name).call<R...>(name_sv(), std::forward<A>(args)...); }
template <class...R, class...A> Sub::call_t<R...> call (string_view name, A&&...args) const { return method_strict(name).call<R...>(name_sv(), std::forward<A>(args)...); }
template <class...R, class...A> Sub::call_t<R...> call_SUPER (const Sub& ctx, A&&...args) const { return ctx.SUPER_strict().call<R...>(name_sv(), std::forward<A>(args)...); }
template <class...R, class...A> Sub::call_t<R...> call_next (const Sub& ctx, A&&...args) const { return next_method_strict(ctx).call<R...>(name_sv(), std::forward<A>(args)...); }
template <class...R, class...A> Sub::call_t<R...> call_super (const Sub& ctx, A&&...args) const { return super_method_strict(ctx).call<R...>(name_sv(), std::forward<A>(args)...); }
template <class...R, class...A>
Sub::call_t<R...> call_next_maybe (const Sub& ctx, A&&...args) const {
auto sub = next_method(ctx);
if (!sub) return Sub::call_t<R...>();
return sub.call<R...>(name_sv(), std::forward<A>(args)...);
}
template <class...R, class...A>
Sub::call_t<R...> call_super_maybe (const Sub& ctx, A&&...args) const {
auto sub = super_method(ctx);
if (!sub) return Sub::call_t<R...>();
return sub.call<R...>(name_sv(), std::forward<A>(args)...);
}
Object bless () const;
Object bless (const Sv& what) const;
void add_const_sub (const panda::string_view& name, const Sv& val);
private:
mutable Simple _name_sv;
void _validate () {
if (!sv) return;
if (HvNAME(sv)) return;
if (is_undef()) return reset();
reset();
throw std::invalid_argument("SV is not a Stash or Stash reference");
}
void _promote (GV* gv, const panda::string_view& key) const;
void _throw_nomethod (const panda::string_view&) const;
void _throw_nomethod (const Sv& name) const {
panda::string_view _name = Simple(name);
_throw_nomethod(_name);
}
};
}