#include <iostream>
#include <string>
#include <tuple>
#include <boost/variant.hpp>
using namespace std;
class Value {
int x;
public:
explicit Value(int x) : x(x) {}
Value(Value&&) = default;
Value(const Value&) = delete;
friend ostream& operator << (ostream& ost, const Value& v) {
return ost << v.x;
}
int copy() const { return x; }
int take()&& { int y = x; x = 100500; return y; }
};
Value operator ~(Value v) {
return Value(-move(v).take());
}
Value operator *(Value a, Value b) {
return Value(move(a).take() * move(b).take());
}
using Trace = tuple<Value, string>;
ostream& operator << (ostream& ost, const Trace& t) {
return ost << get<0>(t) << " = " << get<1>(t);
}
Trace trace(Value v) {
string s = to_string(v.copy());
return Trace{move(v), move(s)};
}
Trace tkonst(int x) {
return trace(Value(x));
}
Trace operator ~(Trace t) {
return Trace{
~move(get<0>(t)),
"~(" + get<1>(t) + ")"
};
}
Trace operator *(Trace a, Trace b) {
return Trace{
move(get<0>(a)) * move(get<0>(b)),
"(" + get<1>(a) + ")*(" + get<1>(b) + ")"
};
}
using Either = boost::variant<Trace, string>;
Either&& emove(Either& e) { return static_cast<Either&&>(e); }
bool ok(const Either& e) {
return boost::get<Trace>(&e) != nullptr;
}
Trace move_then(Either& e) {
return boost::get<Trace>(emove(e));
}
const Trace& peek_then(const Either& e) {
return boost::get<Trace>(e);
}
string move_else(Either& e) {
return boost::get<string>(emove(e));
}
const string& peek_else(const Either& e) {
return boost::get<string>(e);
}
ostream& operator << (ostream& ost, const Either& e) {
return ok(e) ? (ost << "THEN " << peek_then(e))
: (ost << "ELSE " << peek_else(e));
}
Either good(Trace t) {
return t;
}
Either error(string s) {
return s;
}
Either ekonst(int x) {
return good(trace(Value(x)));
}
Either operator ~(Either e) {
if (!ok(e)) return e;
return good(~move_then(e));
}
Either operator *(Either a, Either b) {
if (!ok(a)) return a;
if (!ok(b)) return b;
return good(move_then(a) * move_then(b));
}
Either operator |(Either a, Either b) {
if (ok(a)) return a;
if (ok(b)) return b;
return error(move_else(a) + " | " + move_else(b));
}
template<class F> struct Lazy { F f; };
template<class F> auto lazy(F f) { return Lazy<F>{f}; }
template<class F> auto run(Lazy<F> f)->Either { return f.f(); }
template<class F> auto operator ~(Lazy<F> f) {
return lazy([f]()->Either { return ~run(f); });
}
template<class F, class G> auto operator *(Lazy<F> f, Lazy<G> g) {
return lazy([f, g]()->Either {
Either a = run(f);
if (!ok(a)) return a;
return emove(a) * run(g);
});
}
template<class F, class G> auto operator |(Lazy<F> f, Lazy<G> g) {
return lazy([f, g]()->Either {
Either a = run(f);
if (ok(a)) return a;
return emove(a) | run(g);
});
}
auto will_eval(int x) {
return lazy([x]() {
cout << "evaluating " << x << "..." << endl;
return ekonst(x);
});
}
auto will_fail(string s) {
return lazy([s]() {
cout << "failing " << s << "..." << endl;
return error(s);
});
}
int main() {
cout << run(will_eval(123) * ~will_eval(456) | ~will_eval(789) * will_eval(100500)) << endl;
cout << run(will_eval(123) * will_fail("ahaha")) << endl;
cout << run(will_fail("ohoho") | will_fail("ehehe")) << endl;
}