/*
* Copyright (C) 2012-2016 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 .
*/
#ifndef __EXPRESSIONS_H
#define __EXPRESSIONS_H
#include
#include "undo.h"
#include "error.h"
namespace SciTECO {
template
class ValueStack {
/*
* NOTE: Since value stacks are usually singleton,
* we pass them as a template parameter, saving space
* in the undo token.
*/
template &stack>
class UndoTokenPush : public UndoTokenWithSize> {
Type value;
guint index;
public:
UndoTokenPush(Type _value, guint _index = 0)
: value(_value), index(_index) {}
void
run(void)
{
stack.push(value, index);
}
};
template &stack>
class UndoTokenPop : public UndoTokenWithSize> {
guint index;
public:
UndoTokenPop(guint _index = 0)
: index(_index) {}
void
run(void)
{
stack.pop(index);
}
};
/** Beginning of stack area */
Type *stack;
/** End of stack area */
Type *stack_top;
/** Pointer to top element on stack */
Type *sp;
public:
ValueStack(gsize size = 1024)
{
stack = new Type[size];
/* stack grows to smaller addresses */
sp = stack_top = stack+size;
}
~ValueStack()
{
delete[] stack;
}
inline guint
items(void)
{
return stack_top - sp;
}
inline Type &
push(Type value, guint index = 0)
{
if (G_UNLIKELY(sp == stack))
throw Error("Stack overflow");
/* reserve space for new element */
sp--;
g_assert(items() > index);
/* move away elements after index (index > 0) */
for (guint i = 0; i < index; i++)
sp[i] = sp[i+1];
return sp[index] = value;
}
template &stack>
static inline void
undo_push(Type value, guint index = 0)
{
undo.push>(value, index);
}
inline Type
pop(guint index = 0)
{
/* peek() already asserts */
Type v = peek(index);
/* elements after index are moved to index (index > 0) */
while (index--)
sp[index+1] = sp[index];
/* free space of element to pop */
sp++;
return v;
}
template &stack>
static inline void
undo_pop(guint index = 0)
{
undo.push>(index);
}
inline Type &
peek(guint index = 0)
{
g_assert(items() > index);
return sp[index];
}
/** Clear all but `keep_items` items. */
inline void
clear(guint keep_items = 0)
{
g_assert(keep_items <= items());
sp = stack_top - keep_items;
}
};
/**
* Arithmetic expression stacks
*/
extern class Expressions {
public:
/**
* Operator type.
* The enumeration value divided by 16 represents
* its precedence (small values mean low precedence).
* In other words, the value's lower nibble is
* reserved for enumerating operators of the
* same precedence.
*/
enum Operator {
/*
* Pseudo operators
*/
OP_NIL = 0x00,
OP_NEW,
OP_BRACE,
OP_NUMBER,
/*
* Real operators
*/
OP_POW = 0x60, // ^*
OP_MOD = 0x50, // ^/
OP_DIV, // /
OP_MUL, // *
OP_SUB = 0x40, // -
OP_ADD, // +
OP_AND = 0x30, // &
OP_XOR = 0x20, // ^#
OP_OR = 0x10 // #
};
private:
/** Get operator precedence */
inline gint
precedence(Operator op)
{
return op >> 4;
}
/*
* Number and operator stacks are static, so
* they can be passed to the undo token constructors.
* This is OK since Expression is singleton.
*/
typedef ValueStack NumberStack;
static NumberStack numbers;
typedef ValueStack OperatorStack;
static OperatorStack operators;
public:
Expressions() : num_sign(1), radix(10), brace_level(0) {}
gint num_sign;
inline void
set_num_sign(gint sign)
{
undo.push_var(num_sign) = sign;
}
gint radix;
inline void
set_radix(gint r)
{
undo.push_var(radix) = r;
}
tecoInt push(tecoInt number);
/**
* Push characters of a C-string.
* Could be overloaded on push(tecoInt)
* but this confuses GCC.
*/
inline void
push_str(const gchar *str)
{
while (*str)
push(*str++);
}
inline tecoInt
peek_num(guint index = 0)
{
return numbers.peek(index);
}
tecoInt pop_num(guint index = 0);
tecoInt pop_num_calc(guint index, tecoInt imply);
inline tecoInt
pop_num_calc(guint index = 0)
{
return pop_num_calc(index, num_sign);
}
tecoInt add_digit(gchar digit);
Operator push(Operator op);
Operator push_calc(Operator op);
inline Operator
peek_op(guint index = 0)
{
return operators.peek(index);
}
Operator pop_op(guint index = 0);
void eval(bool pop_brace = false);
guint args(void);
void discard_args(void);
/** The nesting level of braces */
guint brace_level;
inline void
brace_open(void)
{
push(OP_BRACE);
undo.push_var(brace_level)++;
}
void brace_return(guint keep_braces, guint args = 0);
inline void
brace_close(void)
{
if (!brace_level)
throw Error("Missing opening brace");
undo.push_var(brace_level)--;
eval(true);
}
inline void
clear(void)
{
numbers.clear();
operators.clear();
brace_level = 0;
}
const gchar *format(tecoInt number);
private:
void calc(void);
gint first_op(void);
} expressions;
} /* namespace SciTECO */
#endif