#include <vector>
#include <iterator>
#include <algorithm>
#include <memory>
#include <iostream>
#include <stdexcept>
class expression {
//addition and constants are unused, and merely shown for extendibility
enum exprtype{parameter_type, constant_type, multiplication_type, addition_type} type;
long long value; //for value types, and parameter number
std::unique_ptr<expression> left; //for unary and binary functions
std::unique_ptr<expression> right; //for binary functions
public:
//constructors
expression(long long val, bool is_variable=false)
:type(is_variable?parameter_type:constant_type), value(val)
{}
expression(const expression& rhs)
: type(rhs.type)
, value(rhs.value)
, left(rhs.left.get() ? std::unique_ptr<expression>(new expression(*rhs.left)) : std::unique_ptr<expression>(NULL))
, right(rhs.right.get() ? std::unique_ptr<expression>(new expression(*rhs.right)) : std::unique_ptr<expression>(NULL))
{}
expression(expression&& rhs)
:type(rhs.type), value(rhs.value), left(std::move(rhs.left)), right(std::move(rhs.right))
{}
//assignment operator
expression& operator=(expression rhs) {
type = rhs.type;
value = rhs.value;
left = std::move(rhs.left);
right = std::move(rhs.right);
return *this;
}
//operators
friend expression operator*(expression lhs, expression rhs) {
expression ret(0);
ret.type = multiplication_type;
ret.left = std::unique_ptr<expression>(new expression(std::move(lhs)));
ret.right = std::unique_ptr<expression>(new expression(std::move(rhs)));
return ret;
}
friend expression operator+(expression lhs, expression rhs) {
expression ret(0);
ret.type = addition_type;
ret.left = std::unique_ptr<expression>(new expression(std::move(lhs)));
ret.right = std::unique_ptr<expression>(new expression(std::move(rhs)));
return ret;
}
//skip the parameter list, don't care. Ignore it entirely
expression& operator<<(const expression&) {return *this;}
expression& operator,(const expression&) {return *this;}
template<class container>
void operator>>(container& rhs) {
for(auto it=rhs.begin(); it!=rhs.end(); ++it)
*it = execute(*it);
}
private:
//execution
template<class T>
T execute(const T& p0) {
switch(type) {
case parameter_type :
switch(value) {
case 0: return p0; //only one variable
default: throw std::runtime_error("Invalid parameter ID");
}
case constant_type:
return ((T)(value));
case multiplication_type:
return left->execute(p0) * right->execute(p0);
case addition_type:
return left->execute(p0) + right->execute(p0);
default:
throw std::runtime_error("Invalid expression type");
}
}
//This is also unused, and merely shown as extrapolation
template<class T>
T execute(const T& p0, const T& p1) {
switch(type) {
case parameter_type :
switch(value) {
case 0: return p0;
case 1: return p1; //this version has two variables
default: throw std::runtime_error("Invalid parameter ID");
}
case constant_type:
return value;
case multiplication_type:
return left->execute(p0, p1) * right->execute(p0, p1);
case addition_type:
return left->execute(p0, p1) + right->execute(p0, p1);
default:
throw std::runtime_error("Invalid expression type");
}
}
};
#define take
#define with <<
#define in >>
expression x(0, true); //x will be used as a parameter. Sorry: required :(
int main() {
std::vector<int> container;
container.push_back(-3);
container.push_back(0);
container.push_back(7);
take x*x with x in container;
for(unsigned i=0; i<container.size(); ++i)
std::cout << container[i] << ' ';
std::cout << '\n';
std::vector<float> container2;
container2.push_back(-2.3);
container2.push_back(0);
container2.push_back(7.1);
take 1+x with x in container2;
for(unsigned i=0; i<container2.size(); ++i)
std::cout << container2[i] << ' ';
return 0;
}