summaryrefslogtreecommitdiffstats
path: root/src/rpnmuncher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpnmuncher.cpp')
-rw-r--r--src/rpnmuncher.cpp267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/rpnmuncher.cpp b/src/rpnmuncher.cpp
new file mode 100644
index 0000000..ad75495
--- /dev/null
+++ b/src/rpnmuncher.cpp
@@ -0,0 +1,267 @@
+/*
+ * rpnmuncher.cpp - part of abakus
+ * Copyright (C) 2004, 2005 Michael Pyne <[email protected]>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <math.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qvaluestack.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.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 QString &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();
+ }
+
+ QString text() const { return m_text; }
+
+ private:
+ bool m_isValue;
+ QString m_text;
+ Abakus::number_t m_value;
+};
+
+typedef enum { Number = 256, Func, Ident, Power, Set, Remove, Pop, Clear, Unknown } Token;
+
+static int tokenize (const QString &token);
+
+QString 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 QString &text)
+{
+ QStringList tokens = QStringList::split(QRegExp("\\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 = QString::null;
+
+ for(QStringList::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 QString &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(QRegExp("^\\w+$").search(token) != -1 &&
+ QRegExp("\\d").search(token) == -1)
+ {
+ return Ident;
+ }
+
+ if(QRegExp("^[-+*/=]$").search(token) != -1)
+ return token[0];
+
+ return Unknown;
+}
+
+// vim: set et sw=4 ts=8: