diff options
Diffstat (limited to 'src/expressions.cpp')
-rw-r--r-- | src/expressions.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/expressions.cpp b/src/expressions.cpp new file mode 100644 index 0000000..ac06b43 --- /dev/null +++ b/src/expressions.cpp @@ -0,0 +1,201 @@ +#include <glib.h> + +#include "sciteco.h" +#include "undo.h" +#include "expressions.h" + +Expressions expressions; + +void +Expressions::set_num_sign(gint sign) +{ + undo.push_var<gint>(num_sign); + num_sign = sign; +} + +void +Expressions::set_radix(gint r) +{ + undo.push_var<gint>(radix); + radix = r; +} + +gint64 +Expressions::push(gint64 number) +{ + while (operators.items() && operators.peek() == OP_NEW) + pop_op(); + + push(OP_NUMBER); + + if (num_sign < 0) { + set_num_sign(1); + number *= -1; + } + + numbers.undo_pop(); + return numbers.push(number); +} + +gint64 +Expressions::pop_num(int index) +{ + gint64 n = 0; + + pop_op(); + if (numbers.items() > 0) { + n = numbers.pop(index); + numbers.undo_push(n, index); + } + + return n; +} + +gint64 +Expressions::pop_num_calc(int index, gint64 imply) +{ + eval(); + if (num_sign < 0) + set_num_sign(1); + + return args() > 0 ? pop_num(index) : imply; +} + +gint64 +Expressions::add_digit(gchar digit) +{ + gint64 n = args() > 0 ? pop_num() : 0; + + return push(n*radix + (n < 0 ? -1 : 1)*(digit - '0')); +} + +Expressions::Operator +Expressions::push(Expressions::Operator op) +{ + operators.undo_pop(); + return operators.push(op); +} + +Expressions::Operator +Expressions::push_calc(Expressions::Operator op) +{ + int first = first_op(); + + /* calculate if op has lower precedence than op on stack */ + if (first && operators.peek(first) <= op) + calc(); + + return push(op); +} + +Expressions::Operator +Expressions::pop_op(int index) +{ + Operator op = OP_NIL; + + if (operators.items() > 0) { + op = operators.pop(index); + operators.undo_push(op, index); + } + + return op; +} + +void +Expressions::calc(void) +{ + gint64 result; + + gint64 vright = pop_num(); + Operator op = pop_op(); + gint64 vleft = pop_num(); + + switch (op) { + case OP_POW: for (result = 1; vright--; result *= vleft); break; + case OP_MUL: result = vleft * vright; break; + case OP_DIV: result = vleft / vright; break; + case OP_MOD: result = vleft % vright; break; + case OP_ADD: result = vleft + vright; break; + case OP_SUB: result = vleft - vright; break; + case OP_AND: result = vleft & vright; break; + case OP_OR: result = vleft | vright; break; + default: + /* shouldn't happen */ + g_assert(false); + } + + push(result); +} + +void +Expressions::eval(bool pop_brace) +{ + for (;;) { + gint n = first_op(); + Operator op; + + if (n < 2) + break; + + op = operators.peek(n); + if (op == OP_LOOP) + break; + if (op == OP_BRACE) { + if (pop_brace) + pop_op(n); + break; + } + + calc(); + } +} + +int +Expressions::args(void) +{ + int n = 0; + int items = operators.items(); + + while (n < items && operators.peek(n+1) == OP_NUMBER) + n++; + + return n; +} + +int +Expressions::find_op(Operator op) +{ + int items = operators.items(); + + for (int i = 1; i <= items; i++) + if (operators.peek(i) == op) + return i; + + return 0; +} + +int +Expressions::first_op(void) +{ + int items = operators.items(); + + for (int i = 1; i <= items; i++) { + switch (operators.peek(i)) { + case OP_NUMBER: + case OP_NEW: + break; + default: + return i; + } + } + + return 0; +} + +void +Expressions::discard_args(void) +{ + eval(); + for (int i = args(); i; i--) + pop_num_calc(); +} |