AbaKus – a complex calculator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

267 lines
7.0 KiB

/*
* rpnmuncher.cpp - part of abakus
* Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <math.h>
#include <kdebug.h>
#include <klocale.h>
#include <tqvaluestack.h>
#include <tqregexp.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include "rpnmuncher.h"
#include "valuemanager.h"
#include "function.h"
/**
* Holds either a textual identifier, or a numeric value.
*/
class Operand
{
public:
Operand() : m_isValue(true), m_value(0) { }
Operand(const TQString &ident) : m_isValue(false), m_text(ident) { }
Operand(Abakus::number_t value) : m_isValue(true), m_value(value) { }
Abakus::number_t value() const
{
if(m_isValue)
return m_value;
return ValueManager::instance()->value(m_text);
}
operator Abakus::number_t() const
{
return value();
}
TQString text() const { return m_text; }
private:
bool m_isValue;
TQString m_text;
Abakus::number_t m_value;
};
typedef enum { Number = 256, Func, Ident, Power, Set, Remove, Pop, Clear, Unknown } Token;
static int tokenize (const TQString &token);
TQString RPNParser::m_errorStr;
bool RPNParser::m_error(false);
OperandStack RPNParser::m_stack;
struct Counter
{
~Counter() {
Abakus::number_t count( static_cast<int>(RPNParser::stack().count()) );
ValueManager::instance()->setValue("stackCount", count);
}
};
Abakus::number_t RPNParser::rpnParseString(const TQString &text)
{
TQStringList tokens = TQStringList::split(TQRegExp("\\s"), text);
Counter counter; // Will update stack count when we leave proc.
(void) counter; // Avoid warnings about it being unused.
// Used in the case statements below
Operand l, r;
FunctionManager *manager = FunctionManager::instance();
Function *fn = 0;
m_error = false;
m_errorStr = TQString();
for(TQStringList::ConstIterator it = tokens.begin(); it != tokens.end(); ++it) {
switch(tokenize(*it))
{
case Number:
m_stack.push(Abakus::number_t((*it).latin1()));
break;
case Pop:
if(m_stack.isEmpty()) {
m_error = true;
m_errorStr = i18n("Can't pop from an empty stack.");
return Abakus::number_t::nan();
}
m_stack.pop();
break;
case Clear:
m_stack.clear();
break;
case Func:
if(m_stack.count() < 1) {
m_error = true;
m_errorStr = i18n("Insufficient operands for function %1").arg(*it);
return Abakus::number_t::nan();
}
fn = manager->function(*it);
l = m_stack.pop();
m_stack.push(evaluateFunction(fn, l));
break;
case Ident:
m_stack.push(*it);
break;
case Set:
case Remove:
m_error = true;
m_errorStr = i18n("The set and remove commands can only be used in normal mode.");
return Abakus::number_t::nan();
break;
case Power:
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for exponentiation operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value().pow(r));
break;
case Unknown:
m_error = true;
m_errorStr = i18n("Unknown token %1").arg(*it);
return Abakus::number_t::nan();
break;
case '=':
r = m_stack.pop();
l = m_stack.pop();
ValueManager::instance()->setValue(l.text(), r);
m_stack.push(l);
break;
case '+':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for addition operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() + r.value());
break;
case '-':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for subtraction operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() - r.value());
break;
case '*':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for multiplication operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() * r.value());
break;
case '/':
if(m_stack.count() < 2) {
m_error = true;
m_errorStr = i18n("Insufficient operands for division operator.");
return Abakus::number_t::nan();
}
r = m_stack.pop();
l = m_stack.pop();
m_stack.push(l.value() / r.value());
break;
default:
// Impossible case happened.
kdError() << "Impossible case happened in " << k_funcinfo << endl;
m_error = true;
m_errorStr = "Bug found in program, please report.";
return Abakus::number_t::nan();
}
}
// TODO: Should this be an error?
if(m_stack.isEmpty())
return Abakus::number_t::nan();
return m_stack.top();
}
static int tokenize (const TQString &token)
{
bool isOK;
token.toDouble(&isOK);
if(isOK)
return Number;
if(token == "**" || token == "^")
return Power;
if(FunctionManager::instance()->isFunction(token))
return Func;
if(token.lower() == "set")
return Set;
if(token.lower() == "pop")
return Pop;
if(token.lower() == "clear")
return Clear;
if(token.lower() == "remove")
return Remove;
if(TQRegExp("^\\w+$").search(token) != -1 &&
TQRegExp("\\d").search(token) == -1)
{
return Ident;
}
if(TQRegExp("^[-+*/=]$").search(token) != -1)
return token[0];
return Unknown;
}
// vim: set et sw=4 ts=8: