www.pudn.com > calculator.rar > engine.cpp


/****************************************************************************
**
** Copyright (C) 2000-2006 TROLLTECH ASA. All rights reserved.
**
** This file is part of the Phone Edition of the Qtopia Toolkit.
**
** Licensees holding a valid license agreement from Trolltech or any of its
** authorized distributors may use this file in accordance with
** the License Agreement provided with the Licensed Software.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
** information about Trolltech's Commercial License Agreements.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**
**
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include <qtopia/qtopiaapplication.h>


#include "engine.h"
#include "doubleinstruction.h"
#ifdef ENABLE_FRACTION
#include "fractioninstruction.h"
#endif
#ifdef ENABLE_INTEGER
#include "integerinstruction.h"
#endif

// Braces
class iBraceOpen:public Instruction {
public:
iBraceOpen():Instruction() {
name = "Open brace"; // No tr
precedence = 0;
displayString = "("; // No tr
argCount = 0;
};
~iBraceOpen(){};
void eval(){ systemEngine->incBraceCount(); };
};
class iBraceOpenImpl:public Instruction {
public:
iBraceOpenImpl():Instruction() {
name = "Open brace impl"; // No tr
precedence = 1;
displayString = "("; // No tr
argCount = 0;
};
~iBraceOpenImpl(){};
void eval(){};
};
void Engine::openBrace () {
if (state == sError)
return;
#ifdef QTOPIA_PHONE
if (!Qtopia::mousePreferred())
// prevents crash when x-y*(...) is entered => calc is not entirely correct
if (state == sAppend)
return;
#endif
pushInstruction("Open brace"); // No tr
if (state != sError)
changeState(sStart);
}
void Engine::closeBrace () {
if (braceCount) {

if (state == sStart) // this might be wrong here...
executeInstructionOnStack("Factory"); // No tr
doEvalStack(0,true);
//braceCount--;
}
}

// Engine class
Engine::Engine():QObject() {
// Variable initialisation
state = sAppend;
changeResetState(drNone);
mem = recoveredDStack = 0;
kDesc = 0;
lcd = 0;
memMark = kMark = 0;
braceCount = previousInstructionsPrecedence = 0;
currentType = wantedType = "NONE"; // No tr

// Register the common instructions

// System instructions - null, open/close braces
Instruction *da = new Instruction();
registerInstruction(da);
da = new iBraceOpen();
registerInstruction(da);
da = new iBraceOpenImpl();
registerInstruction(da);

if (Qtopia::mousePreferred()) {
// Factory
da = new iDoubleFactory();
registerInstruction(da);
// Normal instructions with full precedence
da = new iAddDoubleDouble();
registerInstruction(da);
da = new iMultiplyDoubleDouble();
registerInstruction(da);
da = new iSubtractDoubleDouble();
registerInstruction(da);
da = new iDivideDoubleDouble();
registerInstruction(da);
da = new iDoubleCopy();
registerInstruction(da);
da = new iDoubleNegate();
registerInstruction(da);
#ifdef ENABLE_FRACTION
da = new iNegateFractionFraction();
registerInstruction(da);
#endif
#if !defined(QTOPIA_PHONE) &amt;&amt; defined(ENABLE_SCIENCE)
da = new iDoublePow();
registerInstruction(da);
da = new iDoubleExp();
registerInstruction(da);
da = new iDoubleSinDeg();//Sin
registerInstruction(da);
da = new iDoubleSinRad();
registerInstruction(da);
da = new iDoubleSinGra();
registerInstruction(da);
da = new iDoubleCosDeg();//Cos
registerInstruction(da);
da = new iDoubleCosRad();
registerInstruction(da);
da = new iDoubleCosGra();
registerInstruction(da);
da = new iDoubleTanDeg();//Tan
registerInstruction(da);
da = new iDoubleTanRad();
registerInstruction(da);
da = new iDoubleTanGra();
registerInstruction(da);
da = new iDoubleASinDeg();//ASin
registerInstruction(da);
da = new iDoubleASinRad();
registerInstruction(da);
da = new iDoubleASinGra();
registerInstruction(da);
da = new iDoubleACosRad();//ACos
registerInstruction(da);
da = new iDoubleACosDeg();
registerInstruction(da);
da = new iDoubleACosGra();
registerInstruction(da);
da = new iDoubleATanDeg();//ATan
registerInstruction(da);
da = new iDoubleATanRad();
registerInstruction(da);
da = new iDoubleATanGra();
registerInstruction(da);
da = new iDoubleLog();
registerInstruction(da);
da = new iDoubleLn();
registerInstruction(da);
da = new iDoubleOneOverX();
registerInstruction(da);
da = new iDoubleFactorial();
registerInstruction(da);
da = new iDoubleSquareRoot();
registerInstruction(da);
da = new iDoubleCubeRoot();
registerInstruction(da);
da = new iDoubleXRootY();
registerInstruction(da);
da = new iDoubleSquare();
registerInstruction(da);
#ifdef ENABLE_FRACTION
da = new iFractionCopy();
registerInstruction(da);
da = new iFractionFactory();
registerInstruction(da);
da = new iConvertDoubleFraction();
registerInstruction(da);
da = new iAddFractionFraction();
registerInstruction(da);
da = new iSubtractFractionFraction();
registerInstruction(da);
da = new iMultiplyFractionFraction();
registerInstruction(da);
da = new iDivideFractionFraction();
registerInstruction(da);
da = new iConvertFractionDouble();
registerInstruction(da);
#endif
#ifdef ENABLE_INTEGER
da = new iConvertIntDouble();
registerInstruction(da);
#endif
#endif
}
}
Engine::~Engine() {
while (!dStack.isEmpty())
delete dStack.pop();
dStack.clear();
while (!iStack.isEmpty())
delete iStack.pop();
iStack.clear();
delete mem;
while (!list.isEmpty())
delete list.takeLast();
};

void Engine::registerInstruction(Instruction *i) {
Instruction *it;
for (int c = 0; c < list.count(); c++) {
it = list.at(c);
if (it->name == i->name
&amt;&amt; it->type == i->type
&amt;&amt; it->retType == i->retType)
return;
}
list.append(i);
}
Instruction * Engine::resolve(QString name) {
// Create a shortlist of instructions with the same name
QList<Instruction*> shortList;
Instruction *it = 0;
int c = 0;
for (; c < list.count(); c++) {
it = list.at(c);
if (it->name == name)
shortList.append(list.at(c));
}

// No instructions by that name have been found
if (!shortList.count()) {
return resolve("NULL"); // No tr
}

// Should reuse "Factory" with a parameter?
if (name == "Convert") { // No tr
// Exact match
for (c = 0; c < shortList.count(); c++) {
it = shortList.at(c);
if (it->type == currentType &amt;&amt; it->retType == wantedType)
return it;
}
} else {
if (wantedType != currentType)
currentType = wantedType;

// Exact match
for (c = 0; c < shortList.count(); c++) {
it = shortList.at(c);
if (it->type == currentType &amt;&amt; it->retType == currentType)
return it;
}
// Dont match return type to currentType
for (c = 0; c < shortList.count(); c++) {
it = shortList.at(c);
if (it->type == currentType )
return it;
}
// Dont match type to currentType
for (c = 0; c < shortList.count(); c++) {
it = shortList.at(c);
return it;
}
}

// Fail
return new Instruction();
}

void Engine::evaluate() {
if (!Qtopia::mousePreferred())
// this could go in doEvalStack but its more efficient here
if (!iStack.isEmpty()
&amt;&amt; ((*iStack.top() == "EvaluateLine") || braceCount > 0)) // No tr
return;

if (state == sStart)
executeInstructionOnStack("Factory"); // No tr
doEvalStack();
if (Qtopia::mousePreferred())
braceCount = 0;
}

void Engine::doEvalStack(int p,bool inbrace) {
if (state == sError || iStack.isEmpty())
return;
evalStack(p,inbrace);
if (!Qtopia::mousePreferred()) {
if (braceCount == 0)
iStack.push(new QString("EvaluateLine")); // No tr
if (braceCount > 0 &amt;&amt; inbrace)
--braceCount;
}
if (state != sError)
changeState(sAppend);
emit(stackChanged());
}

void Engine::evalStack(int p,bool inbrace) {
if (state != sError) {
#ifdef QTOPIA_PHONE
QStack<QString*> tmpIStack;
#endif
// could be more efficient and only resolve i once
Instruction *i;
while (!iStack.isEmpty ()
&amt;&amt; state != sError
#ifdef QTOPIA_PHONE
&amt;&amt; *iStack.top() != "EvaluateLine" // No tr
#endif
&amt;&amt; (p <= resolve(*iStack.top())->precedence)) {
// Pop the next instruction
QString *iString = iStack.pop();
i = resolve(*iString);
if (Qtopia::mousePreferred())
delete iString;
#ifdef QTOPIA_PHONE
else
tmpIStack.push(iString);
#endif

// Stop at the open brace
if (i->name == "Open brace impl" &amt;&amt; inbrace) { // No tr
i->eval();
if (Qtopia::mousePreferred())
return;
else {
#ifdef QTOPIA_PHONE
//--braceCount;
delete tmpIStack.pop(); // delete open brace instraction
#endif
return;
}
}

// Compare precedence with the next instruction on the stack
if (!iStack.isEmpty ()) {
// Recurse into next instruction if necessary
int topPrec = resolve(*iStack.top())->precedence;
if ((p &amt;&amt; p <= topPrec) || (!p &amt;&amt; i->precedence <= topPrec)) {
QStack<Data*> holdingStack;
for (int c = 1;c < i->argCount;c++)
holdingStack.push(dStack.pop());
evalStack(p,inbrace);
for (int c = 1;c < i->argCount;c++)
dStack.push(holdingStack.pop());
}
}

// Evaluate
i->eval();

#ifdef QTOPIA_PHONE
if (!Qtopia::mousePreferred()) {
if (!braceCount &amt;&amt; !dStack.isEmpty()){
Data *top = dStack.pop();
for (int c = 0;c < i->argCount;c++)
dStack.push(tmpDStack.pop());
dStack.push(top);
} else {
for (int c = 0;c < i->argCount;c++)
delete tmpDStack.pop();
delete tmpIStack.pop();
}
}
#endif
}
#ifdef QTOPIA_PHONE
if (!Qtopia::mousePreferred())
while (!tmpIStack.isEmpty())
iStack.push(tmpIStack.pop());
#endif
}
}

// Reset
void Engine::dualReset() {
if (drs == drHard)
hardReset();
else
softReset();
}
void Engine::softReset() {
if (state == sStart &amt;&amt; previousInstructionsPrecedence) {
if (!iStack.isEmpty())
delete iStack.pop();
}

if (!dStack.isEmpty())
dStack.top()->clear();
state = sAppend;

if (dStack.count() == 1 &amt;&amt; !iStack.count())
changeResetState(drNone);
else
changeResetState(drHard);
emit(stackChanged());
}
void Engine::hardReset() {
while (!dStack.isEmpty())
delete dStack.pop();
dStack.clear();
while (!iStack.isEmpty())
delete iStack.pop();
iStack.clear();
braceCount = 0;
if (recoveredDStack) {
delete recoveredDStack;
recoveredDStack = 0;
}
softReset();
changeResetState(drNone);
}

// Input and output
void Engine::pushInstruction(QString name) {
if (!checkState())
return;

Instruction *i = resolve(name);
previousInstructionsPrecedence = i->precedence;

// Immediate instructions
if (!i->precedence) {
executeInstructionOnStack(i);
emit(stackChanged());
return;
}

// Overwrite last instruction
if (!iStack.isEmpty()
&amt;&amt; state == sStart
&amt;&amt; i->precedence
&amt;&amt; previousInstructionsPrecedence) {
if ("Open brace impl" == *(iStack.top())) { // No tr
executeInstructionOnStack("Factory"); // No tr
} else {
delete iStack.pop();
}
}

// Evaluate previous high-precedence instructions
if (!iStack.isEmpty()) {
Instruction *top = resolve(*(iStack.top()));
if (i->precedence <= top->precedence)
doEvalStack(top->precedence);
}

// Push instruction onto stack
if (state != sError) {
iStack.push(new QString(name));
changeState(sStart);
}
emit(stackChanged());
}

void Engine::push(char c) {
if (!checkState())
return;
if (state == sStart) {
executeInstructionOnStack("Copy"); // No tr
if (!checkState()) {
setError("Failed to copy some data"); // No tr
return;
}
dStack.top()->clear();
if (!dStack.top()->push(c, false)) {
delete dStack.pop();
return;
}
dStack.top()->clear();
changeState(sAppend);
} else {

#ifdef QTOPIA_PHONE
if (!Qtopia::mousePreferred() &amt;&amt; !iStack.isEmpty() &amt;&amt; *iStack.top() == "EvaluateLine") // No tr
hardReset();
else
#endif
changeResetState(drSoft);
if (!dStack.top()->push(c, false)) {
return;
}
}

dStack.top()->push(c);
emit(stackChanged());
}
void Engine::push(QString s) {
if (state == sAppend &amt;&amt; dStack.count() >= 1)
dStack.top()->clear();
for (int i=0;i < s.length();i++)
push(s[(int)i].toLatin1());
}
void Engine::delChar() {
if (!checkState())
return;
if (state == sStart) {
if (iStack.isEmpty())
return;
if (*iStack.top() != "Open brace impl") // No tr
changeState(sAppend);
delete iStack.pop();
}
else if (state == sAppend) {
if(dStack.top()->del()) {
if (dStack.count() == 1) {
hardReset();
} else if (dStack.count() > 1) {
delete dStack.pop();
if (iStack.count() >= 1) {
if (*iStack.top() == "EvaluateLine" ||
*iStack.top() != "Open brace impl") {
changeState(sAppend);
} else {
changeState(sStart);
}
delete iStack.pop();
} else {
changeState(sStart);
}
}
}
}
emit(stackChanged());
}
bool Engine::checkState() {
if (state == sError)
return false;
if (dStack.isEmpty())
executeInstructionOnStack("Factory"); // No tr
if (dStack.isEmpty()) {
setError(eNoDataFactory);
return false;
}
if (currentType == "NULL")
return false;
if (state == sError)
return false;
return true;
}
Data *Engine::getData() {
#ifdef QTOPIA_PHONE
if (!Qtopia::mousePreferred()) {
executeInstructionOnStack("Copy"); // No tr
tmpDStack.push(dStack.pop());
}
#endif
return dStack.pop();
}
void Engine::putData(Data *d) {
dStack.push(d);
}

// Memory
void Engine::memorySave() {
if (!checkState())
return;

//save previous data on stack
executeInstructionOnStack("Copy"); // No tr
if (mem) {
dStack.push(mem);
executeInstructionOnStack("Add"); // No tr
}
if (state != sError)
mem = dStack.pop();

executeInstructionOnStack("Factory"); // No tr
if (mem->getFormattedOutput() != dStack.top()->getFormattedOutput()) {
memMark->show();
} else {
delete mem;
mem = 0;
memMark->hide();
}
delete dStack.pop();

qApp->processEvents();
}
void Engine::memoryRecall() {
if (!checkState())
return;

if (state == sAppend)
delete dStack.pop();

if ( mem ) {
dStack.push(mem);
//memory may have different data type
if (currentType != mem->getType()) {
wantedType = currentType;
currentType = mem->getType();
executeInstructionOnStack("Convert");
currentType = wantedType;
}

executeInstructionOnStack("Copy"); // No tr
if (!checkState())
return;
mem = dStack.pop();
}
else
executeInstructionOnStack("Factory"); // No tr

changeState(sAppend);
emit(stackChanged());
}
void Engine::memoryReset() {
if (mem) {
delete mem;
mem = 0;
}
memMark->hide();
}

void Engine::setError(Error e, bool resetStack){
QString s;
switch (e) {
case eNotANumber:
s = qApp->translate("Engine",
"Not a number");
break;
case eNegInf:
s = qApp->translate("Engine",
"Negative infinity");
break;
case eInf:
s = qApp->translate("Engine",
"Infinity");
break;
case eNonPositive:
s = qApp->translate("Engine",
"Input is not a positive number");
break;
case eNonInteger:
s = qApp->translate("Engine",
"Not an integer");
break;
case eOutOfRange:
s = qApp->translate("Engine",
"Out of range");
break;
case eDivZero:
s = qApp->translate("Engine",
"Divide by zero error");
break;
case eNotSolvable:
s = qApp->translate("Engine",
"Not solvable");
break;
case eNoDataFactory:
s = qApp->translate("Engine",
"No data factory found");
case eSurpassLimits:
s = qApp->translate("Engine",
"Calc surpasses limit");
case eError:
default:
// let setError(QString) pick it up
break;
}
setError(s, resetStack);
}
void Engine::setError(QString s, bool resetStack) {
if (s.isEmpty())
s = qApp->translate("Engine","Error");
if (!resetStack) {
recoveredDStack = getData();
putData(recoveredDStack);
}

changeState(sError);
errorString = s;
emit(stackChanged());
}
void Engine::setDisplay(MyLcdDisplay *l) {
lcd = l;

memMark = new QLabel( "m", lcd );
memMark->setBackgroundRole( QPalette::Base );
memMark->setFont( QFont( "helvetica", 12, QFont::Bold, true ) );
memMark->resize( 12, 12 );
memMark->move( 4, 2 );
memMark->hide();

kMark = new QLabel( "k", lcd );
kMark->setBackgroundRole( QPalette::Base );
kMark->setFont( QFont( "helvetica", 12, QFont::Bold, true ) );
kMark->resize( 12, 12 );
kMark->move( 4, 14 );
kMark->hide();
}

void Engine::executeInstructionOnStack(QString name) {
executeInstructionOnStack(resolve(name));
}
void Engine::executeInstructionOnStack(Instruction *i) {
if (i->name == "Factory") { // No tr
i->eval();
return;
}

while (dStack.count() < i->argCount)
executeInstructionOnStack("Factory"); // No tr

if (state != sError)
i->eval();
}
void Engine::setAccType(QString type) {
if (currentType != type || recoveredDStack) {
if (dStack.isEmpty()) {
currentType = type;
executeInstructionOnStack("Factory"); // No tr
state = sAppend;
} else {
if (recoveredDStack) {
//don't call dualReset() or recoveredDStack will be deleted
dStack.clear();
iStack.clear();
braceCount = 0;
softReset();
changeResetState(drNone);
putData(recoveredDStack);
}
wantedType = type;

if (currentType == "NONE") // workaround for bug #3356
currentType = dStack.top()->getType();

if (currentType != wantedType) {
if (!recoveredDStack)
executeInstructionOnStack("Convert"); // No tr
else // don't convert recoveredStack
recoveredDStack = 0;
}
}
// Doublecheck the type in case a factory or convert doesnt go as planned.
currentType = dStack.top()->getType();
emit(stackChanged());
}
changeResetState(drs);
}
QString Engine::getDisplay() {
return dStack.top()->getFormattedOutput();
}

void Engine::changeState(State s) {
switch (s) {
case sStart:
case sAppend:
changeResetState(drSoft);
break;
case sError:
changeResetState(drHard);
break;
}
state = s;
}
void Engine::changeResetState(ResetState s) {
emit(dualResetStateChanged(s));
drs = s;
}
void Engine::incBraceCount() {
if (systemEngine->state == sAppend)
systemEngine->iStack.push(new QString("Multiply")); // No tr
systemEngine->iStack.push(new QString("Open brace impl")); // No tr
braceCount++;
}

#ifdef QTOPIA_PHONE
bool Engine::error(){
if ( state == sError )
return true;
return false;
}

int Engine::numOps(){
return dStack.count();
}

#endif