#include "elamengine.h"
#include "elamintengine.h"
+#include "elamfloatengine.h"
/** \mainpage ELAM - Elementary Logic and Arithmetic Machine
elamengine.h \
elamexpression.h \
elamvalue.h \
- elamintengine.h
+ elamintengine.h \
+ elamfloatengine.h
SOURCES += \
elamvalue.cpp \
elamengine.cpp \
elamintengine.cpp \
- elamexpression.cpp
+ elamexpression.cpp \
+ elamfloatengine.cpp
INCLUDEPATH += .
DEPENDPATH += .
{return prio>l2.prio;}
};
QList<LiteralParser_s>parsers;
+ QMap<QString,UnaryOperator>unary;
+ QMap<QString,BinaryOperator>binary;
};
DEFINE_DPTR(Engine);
bool Engine::setLiteralParser ( ELAM::LiteralParser parser, QString startchars, int prio )
{
+ //base check
if(parser==0 || startchars=="" || prio<0 || prio>100)
return false;
+ //search for existing entry
for(int i=0;i<d->parsers.size();i++){
if(d->parsers[i].parser==parser){
d->parsers[i].start=startchars;
return true;
}
}
+ //none found: create new entry
Private::LiteralParser_s s;
s.parser=parser;
s.prio=prio;
qSort(cand);
//execute
for(int i=0;i<cand.size();i++){
- QPair< QString, QVariant >r=cand[0].parser(ex,*this,start);
+ QPair< QString, QVariant >r=cand[i].parser(ex,*this,start);
if(r.first.size()>0)
return r;
}
return QPair< QString, QVariant >();
}
+BinaryOperator Engine::binaryOperator ( QString name )
+{
+ if(!d->binary.contains(name))
+ d->binary.insert(name,BinaryOperator());
+ return d->binary[name];
+}
+
+UnaryOperator Engine::unaryOperator ( QString name )
+{
+ if(!d->unary.contains(name))
+ d->unary.insert(name,UnaryOperator());
+ return d->unary[name];
+}
+
/////////////////////////////////////////////////////////////////
// character classes
{
}
-BinaryOperator::BinaryOperator()
+BinaryOperator& BinaryOperator::operator= ( const ELAM::BinaryOperator& op )
{
+ d=op.d;
+ return *this;
+}
+
+BinaryOperator::BinaryOperator()
+{
}
BinaryOperator::~BinaryOperator()
{
+}
+
+QVariant BinaryOperator::execute ( const QVariant&op1,const QVariant&op2 ) const
+{
+ //TODO: implement
+ return QVariant();
+}
+
+BinaryOperatorCall BinaryOperator::getCallback ( QString type1, QString type2 ) const
+{
+ //TODO: implement
+ return 0;
+}
+BinaryOperatorCall BinaryOperator::getCallback ( int type1, int type2 ) const
+{
+ //TODO: implement
+ return 0;
}
+QList< QPair< int, int > > BinaryOperator::getTypeIds() const
+{
+ //TODO: implement
+ return QList< QPair< int, int > > ();
+}
+QList< QPair< QString, QString > > BinaryOperator::getTypeNames() const
+{
+ //TODO: implement
+ return QList< QPair< QString, QString > >();
+}
+void BinaryOperator::removeCallback ( BinaryOperatorCall )
+{
+ //TODO: implement
+
+}
+void BinaryOperator::removeCallback ( QString , QString )
+{
+ //TODO: implement
+
+}
+void BinaryOperator::removeCallback ( int , int )
+{
+ //TODO: implement
+
+}
+void BinaryOperator::setCallback ( BinaryOperatorCall callback, QString type1, QString type2 )
+{
+ //TODO: implement
+
+}
+void BinaryOperator::setCallback ( BinaryOperatorCall callback, int type1, int type2 )
+{
+ //TODO: implement
+
+}
+
+
//end of namespace
};
\ No newline at end of file
BinaryOperator(const BinaryOperator&);
BinaryOperator();
~BinaryOperator();
+ /**the operator becomes a shared copy of op and abandones its old link*/
+ BinaryOperator& operator=(const BinaryOperator&op);
+
+ /**sets a callback function for the operator and a specific typ
+ \param callback the function to call, if it is null the type is deleted from this operators type list
+ \param type the type of variable to work on, this must be a type registered with QVariant, if this type is already known to the operator its callback is replaced
+ */
+ void setCallback(BinaryOperatorCall callback,QString type1,QString type2);
+ /**sets a callback function for the operator and a specific typ
+ \param callback the function to call, if it is null the type is deleted from this operators type list
+ \param type the type of variable to work on, this must be a type registered with QVariant, if this type is already known to the operator its callback is replaced
+ */
+ void setCallback(BinaryOperatorCall callback,int type1,int type2);
+ /**returns the callback function attached to the type or NULL if there is none*/
+ BinaryOperatorCall getCallback(QString type1,QString type2)const;
+ /**returns the callback function attached to the type or NULL if there is none*/
+ BinaryOperatorCall getCallback(int type1,int type2)const;
+
+ /**removes all types attached to this callback from the operator*/
+ void removeCallback(BinaryOperatorCall);
+ ///removes the type from this operators list
+ void removeCallback(QString,QString);
+ ///removes the type from this operators list
+ void removeCallback(int,int);
+
+ ///returns all combinations of type names that have a valid callback in this operator
+ QList<QPair<QString,QString> > getTypeNames()const;
+ ///returns all combinations of type IDs that have a valid callback in this operator
+ QList<QPair<int,int> > getTypeIds()const;
+
+ ///calls the callback function associated with the type of the argument, throws an exception if there is no callback
+ QVariant execute(const QVariant&,const QVariant&)const;
};
/**pointer to a function wrapping a mathematical function
/**sets the parser routine for a literal value
\param parser pointer to the parser routine
- \param startchars characters that the literal can start with, all of those characters must be part of the literalStart class
+ \param startchars characters that the literal can start with, at least some of those characters must be part of the literalStart class, the ones which are not part of it will be ignored when recognizing a literal - if none are part of the class the literal cannot be used until the class changes
\param prio a value between 0 and 100, parsers with higher values are preferred over those with lower values if they share a start character
\returns true if the parser is (re-)registered successfully, or false if:
- the parser is null
bool setLiteralParser(LiteralParser parser,QString startchars,int prio=50);
///removes a parser function
void removeLiteralParser(LiteralParser parser);
+
+ /** \brief returns an existing or new unary operator object
+ \param name the name token of the operator, if the name is not a valid operator it cannot be called from this engine until character classes change*/
+ UnaryOperator unaryOperator(QString name);
+
+ /** \brief returns an existing or new binary operator object
+ \param name the name token of the operator, if the name is not a valid operator it cannot be called from this engine until character classes change*/
+ BinaryOperator binaryOperator(QString name);
public slots:
///simply parses an expression string into an object
Expression expression(QString);
--- /dev/null
+// ELAM int engine definition implementation
+//
+// (c) Konrad Rosenbaum, 2010
+// protected under the GNU LGPL v3 or at your option any newer
+
+#include "elamfloatengine.h"
+#include "elamintengine.h"
+
+#include <QDebug>
+
+using namespace ELAM;
+
+FloatEngine::FloatEngine()
+{
+ configureFloatEngine(*this);
+}
+
+IntFloatEngine::IntFloatEngine()
+{
+ IntEngine::configureIntEngine(*this);
+ FloatEngine::configureFloatEngine(*this);
+}
+
+
+/* There are two literal parser routines because of possible interactions with int:
+
+ floatLiteralParser1 has higher priority than the default int parser and tries to assess whether this is a real float (ie. it contains a dot or exponent)
+
+ floatLiteralParser2 has lower priority than the default int parser and acts as a fallback in case no int parser is installed, it consumes the remaining floats that would normally be ints
+*/
+
+//primary float parser: tries to find a dot or ...e...
+static QPair<QString,QVariant> floatLiteralParser1(const QString&expr,Engine&engine,int start)
+{
+ QString ls;
+ //parse to end of expression
+ QString nc=engine.characterClasses().nameClass().second + "0123456789.eE-+";
+ for(int i=start;i<expr.size();i++){
+ if(nc.contains(expr[i]))ls+=expr[i];
+ else break;
+ }
+ ls=ls.toLower();
+ if(!ls.contains('.') && !ls.contains('e'))
+ return QPair<QString,QVariant>();
+ //is it float?
+ bool ok;
+ qreal r=ls.toDouble(&ok);
+ if(ok)return QPair<QString,QVariant>(ls,r);
+ else return QPair<QString,QVariant>();
+}
+
+//secondary float parser: fallback for int
+static QPair<QString,QVariant> floatLiteralParser2(const QString&expr,Engine&engine,int start)
+{
+ QString ls;
+ //parse to end of expression
+ QString nc=engine.characterClasses().nameClass().second + "0123456789.eE-+";
+ for(int i=start;i<expr.size();i++){
+ if(nc.contains(expr[i]))ls+=expr[i];
+ else break;
+ }
+ //is it float?
+ bool ok;
+ qreal r=ls.toDouble(&ok);
+ if(ok)return QPair<QString,QVariant>(ls,r);
+ else return QPair<QString,QVariant>();
+}
+
+static QVariant floatAdd(const QVariant &o1,const QVariant &o2)
+{
+ return o1.toDouble()+o2.toDouble();
+}
+static QVariant floatPlus(const QVariant &o)
+{
+ return o;
+}
+static QVariant floatMinus(const QVariant &o)
+{
+ return -o.toDouble();
+}
+static QVariant floatMinus(const QVariant &o1,const QVariant &o2)
+{
+ return o1.toDouble()-o2.toDouble();
+}
+static QVariant floatMult(const QVariant &o1,const QVariant &o2)
+{
+ return o1.toDouble()*o2.toDouble();
+}
+static QVariant floatDiv(const QVariant &o1,const QVariant &o2)
+{
+ double d2=o2.toDouble();
+ if(d2==0.0)
+ return QVariant::fromValue<Exception>(Exception(Exception::OperationError,"division by zero"));
+ return o1.toDouble()/d2;
+}
+
+static QVariant floatFunc(const QList<QVariant>&lf)
+{
+ if(lf.size()!=1)
+ return Exception(Exception::ArgumentListError, "expecting exactly one argument");
+ if(!lf[0].canConvert<qreal>())
+ return Exception(Exception::TypeMismatchError,"cannot convert to float");
+ return lf[0].toDouble();
+}
+
+int FloatEngine::floatHighParserPrio()
+{
+ return 30;
+}
+int FloatEngine::floatLowParserPrio()
+{
+ return 10;
+}
+
+
+void FloatEngine::configureFloatEngine(ELAM::Engine& eng)
+{
+ eng.setLiteralParser(floatLiteralParser1,"0123456789.",floatHighParserPrio());
+ eng.setLiteralParser(floatLiteralParser2,"0123456789.",floatLowParserPrio());
+ int fid=QVariant::Double;
+ int iid=QVariant::LongLong;
+ //unary
+ eng.unaryOperator("-").setCallback(floatMinus,fid);
+ eng.unaryOperator("+").setCallback(floatPlus,fid);
+ //binary
+ eng.binaryOperator("-").setCallback(floatMinus,fid,fid);
+ eng.binaryOperator("-").setCallback(floatMinus,iid,fid);
+ eng.binaryOperator("-").setCallback(floatMinus,fid,iid);
+ eng.binaryOperator("+").setCallback(floatAdd,fid,fid);
+ eng.binaryOperator("+").setCallback(floatAdd,iid,fid);
+ eng.binaryOperator("+").setCallback(floatAdd,fid,iid);
+ eng.binaryOperator("*").setCallback(floatMult,fid,fid);
+ eng.binaryOperator("*").setCallback(floatMult,iid,fid);
+ eng.binaryOperator("*").setCallback(floatMult,fid,iid);
+ eng.binaryOperator("/").setCallback(floatDiv,fid,fid);
+ eng.binaryOperator("/").setCallback(floatDiv,iid,fid);
+ eng.binaryOperator("/").setCallback(floatDiv,fid,iid);
+ //cast
+ eng.setFunction("float",floatFunc);
+}
--- /dev/null
+//ELAM integer engine header
+//
+// (c) Konrad Rosenbaum, 2010
+// protected under the GNU LGPL v3 or at your option any newer
+
+#ifndef ELAM_FLOATENGINE_H
+#define ELAM_FLOATENGINE_H
+
+#include "elamengine.h"
+
+namespace ELAM {
+
+/**an engine that has floating point (double) numbers implemented
+
+This engine type plays nice with the IntEngine functions by supplemening its operators to be able to mix float and int in operations.
+
+Per default only the float versions are pre-installed. Use IntEngine::configureIntEngine or IntFloatEngine to have both.
+*/
+class FloatEngine:public Engine
+{
+ public:
+ ///instantiates a pre-initialized engine
+ FloatEngine();
+
+ ///configures any engine to support float
+ static void configureFloatEngine(Engine&);
+
+ ///returns the high priority for float literal parsers, this applies to floats that are definitely not int
+ static int floatHighParserPrio();
+ ///returns the low priority for float literal parsers, this applies to floats that would be interpreted as int if an int engine is present
+ static int floatLowParserPrio();
+};
+
+///an engine with integer and float math pre-installed, see IntEngine and FloatEngine
+class IntFloatEngine:public Engine
+{
+ public:
+ ///instantiates a pre-installed engine
+ IntFloatEngine();
+};
+
+//end of namespace
+};
+
+#endif
#include "elamintengine.h"
+#include<QDebug>
+
using namespace ELAM;
IntEngine::IntEngine()
// octal: 0[0-7]*
// hex: 0x[0-9a-fA-F]+
// end of expression: anything not a nameClass.second
-QPair<QString,QVariant> IntLiteralParser(const QString&expr,Engine&engine,int start)
+static QPair<QString,QVariant> IntLiteralParser(const QString&expr,Engine&engine,int start)
{
QString ls;
//parse to end of expression
- QString nc=engine.characterClasses().nameClass().second;
+ QString nc=engine.characterClasses().nameClass().second + "0123456789abcdefABCDEFx";
for(int i=start;i<expr.size();i++){
if(nc.contains(expr[i]))ls+=expr[i];
else break;
bool ok;
qlonglong r=ls.toLongLong(&ok,0);
if(ok)return QPair<QString,QVariant>(ls,r);
- else QPair<QString,QVariant>();
+ else return QPair<QString,QVariant>();
+}
+
+static QVariant intFunc(const QList<QVariant>&lf)
+{
+ if(lf.size()!=1)
+ return Exception(Exception::ArgumentListError, "expecting exactly one argument");
+ if(!lf[0].canConvert<qlonglong>())
+ return Exception(Exception::TypeMismatchError,"cannot convert to int");
+ return lf[0].toLongLong();
+}
+
+static QVariant intPlus(const QVariant&o)
+{
+ return o;
+}
+
+static QVariant intMinus(const QVariant&o)
+{
+ return -o.toLongLong();
}
+static QVariant intAdd(const QVariant&o1,const QVariant&o2)
+{
+ return o1.toLongLong()+o2.toLongLong();
+}
+static QVariant intMinus(const QVariant&o1,const QVariant&o2)
+{
+ return o1.toLongLong()-o2.toLongLong();
+}
+static QVariant intMult(const QVariant&o1,const QVariant&o2)
+{
+ return o1.toLongLong()*o2.toLongLong();
+}
+static QVariant intDiv(const QVariant&o1,const QVariant&o2)
+{
+ qlonglong l2=o2.toLongLong();
+ if(l2==0)
+ return Exception(Exception::OperationError,"division by zero");
+ return o1.toLongLong()/l2;
+}
+
+int IntEngine::intParserPrio()
+{
+ return 20;
+}
+
+
void IntEngine::configureIntEngine(ELAM::Engine& eng)
{
- //TODO: implement
- eng.setLiteralParser(IntLiteralParser,"0123456789",40);
+ int iid=QVariant::LongLong;
+ eng.setLiteralParser(IntLiteralParser,"0123456789",intParserPrio());
+ //cast
+ eng.setFunction("int",intFunc);
+ //unaries
+ eng.unaryOperator("-").setCallback(intMinus,iid);
+ eng.unaryOperator("+").setCallback(intPlus,iid);
+ //binaries
+ eng.binaryOperator("-").setCallback(intMinus,iid,iid);
+ eng.binaryOperator("+").setCallback(intAdd,iid,iid);
+ eng.binaryOperator("*").setCallback(intMult,iid,iid);
+ eng.binaryOperator("/").setCallback(intDiv,iid,iid);
}
namespace ELAM {
+///integer math enabled engine
class IntEngine:public Engine
{
public:
+ ///instantiates a pre-configured engine
IntEngine();
+ ///configures any engine to support basic int math
static void configureIntEngine(Engine&);
+
+ ///returns the default priority of the int literal parser
+ static int intParserPrio();
};
};
namespace ELAM {
-Exception::Exception()
-{
-}
Exception::Exception(const Exception& e)
{
operator=(e);
}
Exception& Exception::operator=(const Exception& e)
{
+ mtype=e.mtype;
merr=e.merr;
mpos=e.mpos;
return *this;
class Exception
{
public:
+ ///Type of Exception
enum ErrorType{
+ ///not an exception
NoError=0,
+ ///error while parsing the expression
ParserError,
+ ///the operator is not known
UnknownOperatorError,
+ ///the function is not known
UnknownFunctionError,
+ ///the operator or function is known, but the type of one of the arguments is not supported
TypeMismatchError,
+ ///the amount of arguments to a function is wrong or the syntax of arguments
+ ArgumentListError,
+ ///the operation itself failed, e.g. division by zero
+ OperationError,
};
- Exception();
+ ///instantiates a copy of an exception
Exception(const Exception&);
- Exception(ErrorType type,QString errorText=QString(),Position p=Position());
+ /**instantiates an exception
+ \param type the type of exception
+ \param errorText some human readable text describing the problem
+ \param pos the position in the original expression string where the problem occurred*/
+ Exception(ErrorType type=NoError,QString errorText=QString(),Position pos=Position());
+ /**instantiates an exception
+ \param type the type of exception
+ \param errorText some human readable text describing the problem
+ \param pos the position in the original expression string where the problem occurred*/
+ Exception(ErrorType type,Position pos,QString errorText=QString());
+ ///copies an exception
Exception& operator=(const Exception&);
+ ///returns the describing text of the exception (if existing)
QString errorText()const{return merr;}
+ ///type of error represented by the exception
+ ErrorType errorType()const{return mtype;}
+ ///the position in the original expression where the error originated
+ Position errorPos()const{return mpos;}
+ ///the line in the original expression where the error originated
int errorLine()const{return mpos.line();}
+ ///the column in the original expression where the error originated
int errorColumn()const{return mpos.column();}
- Position errorPos()const{return mpos;}
+ ///converts the exception to a variant
+ operator QVariant()const{return QVariant::fromValue<Exception>(*this);}
+
+ ///the meta type ID (used with QVariant) of the exception type
static int metaTypeId();
private:
void ElamTest::tokenizer()
{
IntEngine ie;
- QString ex="a= bcd +345*efg*(65/(5))";
+ FloatEngine::configureFloatEngine(ie);
+ QString ex="a= bcd +345*efg*(65.3/(5))";
QList<Token> tl=ie.tokenize(ex);
/*qDebug()<<"expression:"<<ex<<"tokens:"<<tl.size();
for(int i=0;i<tl.size();i++)
QCOMPARE(tl[4].type(),Token::Literal);//345
QCOMPARE(tl[4].literalValue().toInt(),345);//345
QCOMPARE(tl[4].literalValue().typeName(),"qlonglong");//345
+ QCOMPARE(tl[4].literalValue().type(),QVariant::LongLong);//345
QCOMPARE(tl[5].type(),Token::Operator);//*
QCOMPARE(tl[6].type(),Token::Name);//efg
QCOMPARE(tl[7].type(),Token::Operator);//*
QCOMPARE(tl[8].type(),Token::ParOpen);//(
- QCOMPARE(tl[9].type(),Token::Literal);//65
+ QCOMPARE(tl[9].type(),Token::Literal);//65.3
+ QCOMPARE(tl[9].literalValue().type(),QVariant::Double);
QCOMPARE(tl[10].type(),Token::Operator);// /
QCOMPARE(tl[11].type(),Token::ParOpen);//(
QCOMPARE(tl[12].type(),Token::Literal);//5