summaryrefslogtreecommitdiff
path: root/expr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'expr.cc')
-rw-r--r--expr.cc602
1 files changed, 602 insertions, 0 deletions
diff --git a/expr.cc b/expr.cc
new file mode 100644
index 00000000..7318e903
--- /dev/null
+++ b/expr.cc
@@ -0,0 +1,602 @@
+#include "expr.h"
+
+namespace ledger {
+
+balance_t node_t::compute(const std::time_t begin,
+ const std::time_t end,
+ const item_t * item) const
+{
+ balance_t temp;
+
+ switch (type) {
+ case CONSTANT_A:
+ temp = constant_a;
+ break;
+
+ case CONSTANT_T:
+ temp = amount_t((unsigned int) constant_t);
+ break;
+
+ case AMOUNT:
+ temp = item->value.quantity;
+ break;
+ case COST:
+ temp = item->value.cost;
+ break;
+
+ case BALANCE:
+ temp = item->total.quantity - item->value.quantity;
+ break;
+ case COST_BALANCE:
+ temp = item->total.cost - item->value.cost;
+ break;
+
+ case TOTAL:
+ temp = item->total.quantity;
+ break;
+ case COST_TOTAL:
+ temp = item->total.cost;
+ break;
+
+ case DATE:
+ temp = amount_t((unsigned int) item->date);
+ break;
+
+ case INDEX:
+ temp = amount_t(item->index + 1);
+ break;
+
+ case BEGIN_DATE:
+ temp = amount_t((unsigned int) begin);
+ break;
+
+ case END_DATE:
+ temp = amount_t((unsigned int) end);
+ break;
+
+ case F_ARITH_MEAN:
+ assert(left);
+ temp = left->compute(begin, end, item);
+ temp /= amount_t(item->index + 1);
+ break;
+
+ case F_NEG:
+ assert(left);
+ temp = left->compute(begin, end, item).negated();
+ break;
+
+ case F_ABS:
+ assert(left);
+ temp = abs(left->compute(begin, end, item));
+ break;
+
+ case F_REGEXP:
+ assert(mask);
+ temp = (item->account &&
+ mask->match(item->account->fullname())) ? 1 : 0;
+ break;
+
+ case F_VALUE: {
+ assert(left);
+ temp = left->compute(begin, end, item);
+
+ std::time_t moment = -1;
+ if (right) {
+ switch (right->type) {
+ case DATE: moment = item->date; break;
+ case BEGIN_DATE: moment = begin; break;
+ case END_DATE: moment = end; break;
+ default: assert(0); break;
+ }
+ }
+ temp = temp.value(moment);
+ break;
+ }
+
+ case O_NOT:
+ temp = left->compute(begin, end, item) ? 0 : 1;
+ break;
+
+ case O_QUES:
+ temp = left->compute(begin, end, item);
+ if (temp)
+ temp = right->left->compute(begin, end, item);
+ else
+ temp = right->right->compute(begin, end, item);
+ break;
+
+ case O_AND:
+ case O_OR:
+ case O_EQ:
+ case O_LT:
+ case O_LTE:
+ case O_GT:
+ case O_GTE:
+ case O_ADD:
+ case O_SUB:
+ case O_MUL:
+ case O_DIV: {
+ assert(left);
+ assert(right);
+ balance_t left_bal = left->compute(begin, end, item);
+ balance_t right_bal = right->compute(begin, end, item);
+ switch (type) {
+ case O_AND: temp = (left_bal && right_bal) ? 1 : 0; break;
+ case O_OR: temp = (left_bal || right_bal) ? 1 : 0; break;
+ case O_EQ: temp = (left_bal == right_bal) ? 1 : 0; break;
+ case O_LT: temp = (left_bal < right_bal) ? 1 : 0; break;
+ case O_LTE: temp = (left_bal <= right_bal) ? 1 : 0; break;
+ case O_GT: temp = (left_bal > right_bal) ? 1 : 0; break;
+ case O_GTE: temp = (left_bal >= right_bal) ? 1 : 0; break;
+ case O_ADD: temp = left_bal + right_bal; break;
+ case O_SUB: temp = left_bal - right_bal; break;
+ case O_MUL: temp = left_bal * right_bal; break;
+ case O_DIV: temp = left_bal / right_bal; break;
+ default: assert(0); break;
+ }
+ break;
+ }
+
+ case LAST:
+ default:
+ assert(0);
+ break;
+ }
+
+ return temp;
+}
+
+void dump_tree(std::ostream& out, node_t * node)
+{
+ switch (node->type) {
+ case CONSTANT_A: out << "CONST[" << node->constant_a << "]"; break;
+ case CONSTANT_T: out << "DATE/TIME[" << node->constant_t << "]"; break;
+ case AMOUNT: out << "AMOUNT"; break;
+ case COST: out << "COST"; break;
+ case DATE: out << "DATE"; break;
+ case INDEX: out << "INDEX"; break;
+ case BALANCE: out << "BALANCE"; break;
+ case COST_BALANCE: out << "COST_BALANCE"; break;
+ case TOTAL: out << "TOTAL"; break;
+ case COST_TOTAL: out << "COST_TOTAL"; break;
+ case BEGIN_DATE: out << "BEGIN"; break;
+ case END_DATE: out << "END"; break;
+
+ case F_ARITH_MEAN:
+ out << "MEAN(";
+ dump_tree(out, node->left);
+ out << ")";
+ break;
+
+ case F_NEG:
+ out << "ABS(";
+ dump_tree(out, node->left);
+ out << ")";
+ break;
+
+ case F_ABS:
+ out << "ABS(";
+ dump_tree(out, node->left);
+ out << ")";
+ break;
+
+ case F_REGEXP:
+ assert(node->mask);
+ out << "RE(" << node->mask->pattern << ")";
+ break;
+
+ case F_VALUE:
+ out << "VALUE(";
+ dump_tree(out, node->left);
+ if (node->right) {
+ out << ", ";
+ dump_tree(out, node->right);
+ }
+ out << ")";
+ break;
+
+ case O_NOT:
+ out << "!";
+ dump_tree(out, node->left);
+ break;
+
+ case O_QUES:
+ dump_tree(out, node->left);
+ out << "?";
+ dump_tree(out, node->right->left);
+ out << ":";
+ dump_tree(out, node->right->right);
+ break;
+
+ case O_AND:
+ case O_OR:
+ case O_EQ:
+ case O_LT:
+ case O_LTE:
+ case O_GT:
+ case O_GTE:
+ case O_ADD:
+ case O_SUB:
+ case O_MUL:
+ case O_DIV:
+ out << "(";
+ dump_tree(out, node->left);
+ switch (node->type) {
+ case O_AND: out << " & "; break;
+ case O_OR: out << " | "; break;
+ case O_EQ: out << "="; break;
+ case O_LT: out << "<"; break;
+ case O_LTE: out << "<="; break;
+ case O_GT: out << ">"; break;
+ case O_GTE: out << ">="; break;
+ case O_ADD: out << "+"; break;
+ case O_SUB: out << "-"; break;
+ case O_MUL: out << "*"; break;
+ case O_DIV: out << "/"; break;
+ default: assert(0); break;
+ }
+ dump_tree(out, node->right);
+ out << ")";
+ break;
+
+ case LAST:
+ default:
+ assert(0);
+ break;
+ }
+}
+
+node_t * parse_term(std::istream& in, ledger_t * ledger);
+
+inline node_t * parse_term(const char * p, ledger_t * ledger) {
+ std::istringstream stream(p);
+ return parse_term(stream, ledger);
+}
+
+node_t * parse_term(std::istream& in, ledger_t * ledger)
+{
+ node_t * node = NULL;
+
+ char c = in.peek();
+ if (std::isdigit(c) || c == '.' || c == '{') {
+ std::string ident;
+
+ if (c == '{') {
+ in.get(c);
+ c = in.peek();
+ while (! in.eof() && c != '}') {
+ in.get(c);
+ ident += c;
+ c = in.peek();
+ }
+ if (c == '}')
+ in.get(c);
+ else
+ ident = "0";
+ } else {
+ while (! in.eof() && std::isdigit(c) || c == '.') {
+ in.get(c);
+ ident += c;
+ c = in.peek();
+ }
+ }
+
+ if (! ident.empty()) {
+ node = new node_t(CONSTANT_A);
+ node->constant_a.parse(ident, ledger);
+ }
+ return node;
+ }
+
+ in.get(c);
+ switch (c) {
+ // Basic terms
+ case 'a': node = new node_t(AMOUNT); break;
+ case 'c': node = new node_t(COST); break;
+ case 'd': node = new node_t(DATE); break;
+ case 'b': node = new node_t(BEGIN_DATE); break;
+ case 'e': node = new node_t(END_DATE); break;
+ case 'i': node = new node_t(INDEX); break;
+ case 'B': node = new node_t(BALANCE); break;
+ case 'T': node = new node_t(TOTAL); break;
+ case 'C': node = new node_t(COST_TOTAL); break;
+
+ // Compound terms
+ case 'v': node = parse_expr("P(a,d)", ledger); break;
+ case 'V': node = parse_term("P(T,d)", ledger); break;
+ case 'g': node = parse_expr("v-c", ledger); break;
+ case 'G': node = parse_expr("V-C", ledger); break;
+ case 'o': node = parse_expr("d-b", ledger); break;
+ case 'w': node = parse_expr("e-d", ledger); break;
+
+ // Functions
+ case '-':
+ node = new node_t(F_NEG);
+ node->left = parse_term(in, ledger);
+ break;
+
+ case 'A': // absolute value ("positive")
+ node = new node_t(F_ABS);
+ node->left = parse_term(in, ledger);
+ break;
+
+ case 'M':
+ node = new node_t(F_ARITH_MEAN);
+ node->left = parse_term(in, ledger);
+ break;
+
+ case 'D': {
+ node = new node_t(O_SUB);
+ node->left = parse_term("a", ledger);
+ node->right = parse_term(in, ledger);
+ break;
+ }
+
+ case 'P':
+ node = new node_t(F_VALUE);
+ if (in.peek() == '(') {
+ in.get(c);
+ node->left = parse_expr(in, ledger);
+ if (in.peek() == ',') {
+ in.get(c);
+ node->right = parse_expr(in, ledger);
+ }
+ if (in.peek() == ')')
+ in.get(c);
+ } else {
+ node->left = parse_term(in, ledger);
+ }
+ break;
+
+ // Other
+ case '/': {
+ std::string ident;
+
+ c = in.peek();
+ while (! in.eof() && c != '/') {
+ in.get(c);
+ if (c == '\\')
+ in.get(c);
+ ident += c;
+ c = in.peek();
+ }
+ if (c == '/') {
+ in.get(c);
+ node = new node_t(F_REGEXP);
+ node->mask = new mask_t(ident);
+ } else {
+ assert(0);
+ }
+ break;
+ }
+
+ case '(':
+ node = parse_expr(in, ledger);
+ if (in.peek() == ')')
+ in.get(c);
+ else
+ assert(0);
+ break;
+
+ case '[': {
+ std::string ident;
+
+ c = in.peek();
+ while (! in.eof() && c != ']') {
+ in.get(c);
+ ident += c;
+ c = in.peek();
+ }
+ if (c == ']') {
+ in.get(c);
+ node = new node_t(CONSTANT_T);
+ if (! parse_date(ident.c_str(), &node->constant_t))
+ assert(0);
+ } else {
+ assert(0);
+ }
+ break;
+ }
+
+ default:
+ in.unget();
+ break;
+ }
+
+ return node;
+}
+
+node_t * parse_mul_expr(std::istream& in, ledger_t * ledger)
+{
+ node_t * node = NULL;
+
+ node = parse_term(in, ledger);
+
+ if (node && ! in.eof()) {
+ char c = in.peek();
+ while (c == '*' || c == '/') {
+ in.get(c);
+ switch (c) {
+ case '*': {
+ node_t * prev = node;
+ node = new node_t(O_MUL);
+ node->left = prev;
+ node->right = parse_term(in, ledger);
+ break;
+ }
+
+ case '/': {
+ node_t * prev = node;
+ node = new node_t(O_DIV);
+ node->left = prev;
+ node->right = parse_term(in, ledger);
+ break;
+ }
+ }
+ c = in.peek();
+ }
+ }
+
+ return node;
+}
+
+node_t * parse_add_expr(std::istream& in, ledger_t * ledger)
+{
+ node_t * node = NULL;
+
+ node = parse_mul_expr(in, ledger);
+
+ if (node && ! in.eof()) {
+ char c = in.peek();
+ while (c == '+' || c == '-') {
+ in.get(c);
+ switch (c) {
+ case '+': {
+ node_t * prev = node;
+ node = new node_t(O_ADD);
+ node->left = prev;
+ node->right = parse_mul_expr(in, ledger);
+ break;
+ }
+
+ case '-': {
+ node_t * prev = node;
+ node = new node_t(O_SUB);
+ node->left = prev;
+ node->right = parse_mul_expr(in, ledger);
+ break;
+ }
+ }
+ c = in.peek();
+ }
+ }
+
+ return node;
+}
+
+node_t * parse_logic_expr(std::istream& in, ledger_t * ledger)
+{
+ node_t * node = NULL;
+
+ if (in.peek() == '!') {
+ char c;
+ in.get(c);
+ node = new node_t(O_NOT);
+ node->left = parse_logic_expr(in, ledger);
+ return node;
+ }
+
+ node = parse_add_expr(in, ledger);
+
+ if (node && ! in.eof()) {
+ char c = in.peek();
+ if (c == '=' || c == '<' || c == '>') {
+ in.get(c);
+ switch (c) {
+ case '=': {
+ node_t * prev = node;
+ node = new node_t(O_EQ);
+ node->left = prev;
+ node->right = parse_add_expr(in, ledger);
+ break;
+ }
+
+ case '<': {
+ node_t * prev = node;
+ node = new node_t(O_LT);
+ if (in.peek() == '=') {
+ in.get(c);
+ node->type = O_LTE;
+ }
+ node->left = prev;
+ node->right = parse_add_expr(in, ledger);
+ break;
+ }
+
+ case '>': {
+ node_t * prev = node;
+ node = new node_t(O_GT);
+ if (in.peek() == '=') {
+ in.get(c);
+ node->type = O_GTE;
+ }
+ node->left = prev;
+ node->right = parse_add_expr(in, ledger);
+ break;
+ }
+
+ default:
+ if (! in.eof())
+ assert(0);
+ break;
+ }
+ }
+ }
+
+ return node;
+}
+
+node_t * parse_expr(std::istream& in, ledger_t * ledger)
+{
+ node_t * node = NULL;
+
+ node = parse_logic_expr(in, ledger);
+
+ if (node && ! in.eof()) {
+ char c = in.peek();
+ while (c == '&' || c == '|' || c == '?') {
+ in.get(c);
+ switch (c) {
+ case '&': {
+ node_t * prev = node;
+ node = new node_t(O_AND);
+ node->left = prev;
+ node->right = parse_logic_expr(in, ledger);
+ break;
+ }
+
+ case '|': {
+ node_t * prev = node;
+ node = new node_t(O_OR);
+ node->left = prev;
+ node->right = parse_logic_expr(in, ledger);
+ break;
+ }
+
+ case '?': {
+ node_t * prev = node;
+ node = new node_t(O_QUES);
+ node->left = prev;
+ node_t * choices = new node_t(O_COL);
+ node->right = choices;
+ choices->left = parse_logic_expr(in, ledger);
+ c = in.peek();
+ assert(c == ':');
+ in.get(c);
+ choices->right = parse_logic_expr(in, ledger);
+ break;
+ }
+
+ default:
+ if (! in.eof())
+ assert(0);
+ break;
+ }
+ c = in.peek();
+ }
+ }
+
+ return node;
+}
+
+} // namespace ledger
+
+#ifdef TEST
+
+int main(int argc, char *argv[])
+{
+ ledger::dump_tree(std::cout, ledger::parse_expr(argv[1], NULL));
+ std::cout << std::endl;
+}
+
+#endif