/*
* Copyright (C) 2012-2015 Robin Haberkorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include "sciteco.h"
#include "error.h"
#include "expressions.h"
namespace SciTECO {
Expressions expressions;
tecoInt
Expressions::push(tecoInt 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);
}
tecoInt
Expressions::pop_num(guint index)
{
tecoInt n = 0;
Operator op = pop_op();
g_assert(op == OP_NUMBER);
if (numbers.items()) {
n = numbers.pop(index);
numbers.undo_push(n, index);
}
return n;
}
tecoInt
Expressions::pop_num_calc(guint index, tecoInt imply)
{
eval();
if (num_sign < 0)
set_num_sign(1);
return args() > 0 ? pop_num(index) : imply;
}
tecoInt
Expressions::add_digit(gchar digit)
{
tecoInt 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)
{
gint first = first_op();
/* calculate if op has lower precedence than op on stack */
if (first >= 0 && operators.peek(first) <= op)
calc();
return push(op);
}
Expressions::Operator
Expressions::pop_op(guint index)
{
Operator op = OP_NIL;
if (operators.items()) {
op = operators.pop(index);
operators.undo_push(op, index);
}
return op;
}
void
Expressions::calc(void)
{
tecoInt result;
tecoInt vright;
Operator op;
tecoInt vleft;
if (operators.peek() != OP_NUMBER)
throw Error("Missing right operand");
vright = pop_num();
op = pop_op();
if (operators.peek() != OP_NUMBER)
throw Error("Missing left operand");
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:
if (!vright)
throw Error("Division by zero");
result = vleft / vright;
break;
case OP_MOD:
if (!vright)
throw Error("Remainder of division by zero");
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_XOR:
result = vleft ^ vright;
break;
case OP_OR:
result = vleft | vright;
break;
default:
/* shouldn't happen */
g_assert_not_reached();
}
push(result);
}
void
Expressions::eval(bool pop_brace)
{
for (;;) {
gint n = first_op();
Operator op;
if (n < 0)
break;
op = operators.peek(n);
if (op == OP_LOOP)
break;
if (op == OP_BRACE) {
if (pop_brace)
pop_op(n);
break;
}
if (n < 1)
break;
calc();
}
}
guint
Expressions::args(void)
{
guint n = 0;
guint items = operators.items();
while (n < items && operators.peek(n) == OP_NUMBER)
n++;
return n;
}
gint
Expressions::find_op(Operator op)
{
guint items = operators.items();
for (guint i = 0; i < items; i++)
if (operators.peek(i) == op)
return i;
return -1; /* not found */
}
gint
Expressions::first_op(void)
{
guint items = operators.items();
for (guint i = 0; i < items; i++) {
switch (operators.peek(i)) {
case OP_NUMBER:
case OP_NEW:
break;
default:
return i;
}
}
return -1; /* no operator */
}
void
Expressions::discard_args(void)
{
eval();
for (guint i = args(); i; i--)
pop_num_calc();
}
const gchar *
Expressions::format(tecoInt number)
{
/* maximum length if radix = 2 */
static gchar buf[1+sizeof(number)*8+1];
gchar *p = buf + sizeof(buf);
tecoInt v = ABS(number);
*--p = '\0';
do {
*--p = '0' + (v % radix);
if (*p > '9')
*p += 'A' - '9' - 1;
} while ((v /= radix));
if (number < 0)
*--p = '-';
return p;
}
} /* namespace SciTECO */