From 9adf1721e3c17528fe4ab886e7c3ca132a1a6cff Mon Sep 17 00:00:00 2001 From: konrad Date: Mon, 15 Nov 2010 21:56:53 +0000 Subject: [PATCH] added basic functions for int made int parser more robust added float parser git-svn-id: https://silmor.de/svn/softmagic/elam/trunk@631 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- src/elam.h | 1 + src/elam.pro | 6 +- src/elamengine.cpp | 83 +++++++++++++++++++++++++++- src/elamengine.h | 42 ++++++++++++++- src/elamfloatengine.cpp | 140 +++++++++++++++++++++++++++++++++++++++++++++++ src/elamfloatengine.h | 45 +++++++++++++++ src/elamintengine.cpp | 67 +++++++++++++++++++++-- src/elamintengine.h | 6 ++ src/elamvalue.cpp | 4 +- src/elamvalue.h | 36 +++++++++++- tests/parser/parser.cpp | 7 ++- 11 files changed, 419 insertions(+), 18 deletions(-) create mode 100644 src/elamfloatengine.cpp create mode 100644 src/elamfloatengine.h diff --git a/src/elam.h b/src/elam.h index e62e380..2c86388 100644 --- a/src/elam.h +++ b/src/elam.h @@ -8,6 +8,7 @@ #include "elamengine.h" #include "elamintengine.h" +#include "elamfloatengine.h" /** \mainpage ELAM - Elementary Logic and Arithmetic Machine diff --git a/src/elam.pro b/src/elam.pro index c3b2961..e428bd5 100644 --- a/src/elam.pro +++ b/src/elam.pro @@ -14,13 +14,15 @@ HEADERS += \ 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 += . diff --git a/src/elamengine.cpp b/src/elamengine.cpp index 00a4f87..7cb5de8 100644 --- a/src/elamengine.cpp +++ b/src/elamengine.cpp @@ -28,6 +28,8 @@ class DPTR_CLASS_NAME(Engine):public DPtr {return prio>l2.prio;} }; QListparsers; + QMapunary; + QMapbinary; }; DEFINE_DPTR(Engine); @@ -232,8 +234,10 @@ void Engine::removeFunction(QString n) 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;iparsers.size();i++){ if(d->parsers[i].parser==parser){ d->parsers[i].start=startchars; @@ -241,6 +245,7 @@ bool Engine::setLiteralParser ( ELAM::LiteralParser parser, QString startchars, return true; } } + //none found: create new entry Private::LiteralParser_s s; s.parser=parser; s.prio=prio; @@ -272,7 +277,7 @@ QPair< QString, QVariant > Engine::parseLiteral ( QString ex, int start) qSort(cand); //execute for(int i=0;ir=cand[0].parser(ex,*this,start); + QPair< QString, QVariant >r=cand[i].parser(ex,*this,start); if(r.first.size()>0) return r; } @@ -280,6 +285,20 @@ QPair< QString, QVariant > Engine::parseLiteral ( QString ex, int start) 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 @@ -623,16 +642,76 @@ BinaryOperator::BinaryOperator(const BinaryOperator& op) { } -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 diff --git a/src/elamengine.h b/src/elamengine.h index 2c58557..572c206 100644 --- a/src/elamengine.h +++ b/src/elamengine.h @@ -88,6 +88,38 @@ class BinaryOperator 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 > getTypeNames()const; + ///returns all combinations of type IDs that have a valid callback in this operator + QList > 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 @@ -311,7 +343,7 @@ class Engine:public QObject /**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 @@ -323,6 +355,14 @@ class Engine:public QObject 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); diff --git a/src/elamfloatengine.cpp b/src/elamfloatengine.cpp new file mode 100644 index 0000000..b2bc0ea --- /dev/null +++ b/src/elamfloatengine.cpp @@ -0,0 +1,140 @@ +// 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 + +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 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(); + //is it float? + bool ok; + qreal r=ls.toDouble(&ok); + if(ok)return QPair(ls,r); + else return QPair(); +} + +//secondary float parser: fallback for int +static QPair 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(ls,r); + else return QPair(); +} + +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::OperationError,"division by zero")); + return o1.toDouble()/d2; +} + +static QVariant floatFunc(const QList&lf) +{ + if(lf.size()!=1) + return Exception(Exception::ArgumentListError, "expecting exactly one argument"); + if(!lf[0].canConvert()) + 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); +} diff --git a/src/elamfloatengine.h b/src/elamfloatengine.h new file mode 100644 index 0000000..4c49cf1 --- /dev/null +++ b/src/elamfloatengine.h @@ -0,0 +1,45 @@ +//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 diff --git a/src/elamintengine.cpp b/src/elamintengine.cpp index 849c8a4..35e1e42 100644 --- a/src/elamintengine.cpp +++ b/src/elamintengine.cpp @@ -5,6 +5,8 @@ #include "elamintengine.h" +#include + using namespace ELAM; IntEngine::IntEngine() @@ -17,11 +19,11 @@ IntEngine::IntEngine() // octal: 0[0-7]* // hex: 0x[0-9a-fA-F]+ // end of expression: anything not a nameClass.second -QPair IntLiteralParser(const QString&expr,Engine&engine,int start) +static QPair 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 IntLiteralParser(const QString&expr,Engine&engine,int st bool ok; qlonglong r=ls.toLongLong(&ok,0); if(ok)return QPair(ls,r); - else QPair(); + else return QPair(); +} + +static QVariant intFunc(const QList&lf) +{ + if(lf.size()!=1) + return Exception(Exception::ArgumentListError, "expecting exactly one argument"); + if(!lf[0].canConvert()) + 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); } diff --git a/src/elamintengine.h b/src/elamintengine.h index 984d269..71ae4ec 100644 --- a/src/elamintengine.h +++ b/src/elamintengine.h @@ -10,12 +10,18 @@ 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(); }; }; diff --git a/src/elamvalue.cpp b/src/elamvalue.cpp index 943b10d..1a9b63c 100644 --- a/src/elamvalue.cpp +++ b/src/elamvalue.cpp @@ -9,9 +9,6 @@ namespace ELAM { -Exception::Exception() -{ -} Exception::Exception(const Exception& e) { operator=(e); @@ -30,6 +27,7 @@ int Exception::metaTypeId() } Exception& Exception::operator=(const Exception& e) { + mtype=e.mtype; merr=e.merr; mpos=e.mpos; return *this; diff --git a/src/elamvalue.h b/src/elamvalue.h index dcb791c..3c3df79 100644 --- a/src/elamvalue.h +++ b/src/elamvalue.h @@ -58,25 +58,55 @@ QDebug&operator<<(QDebug&,const Position&); 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(*this);} + + ///the meta type ID (used with QVariant) of the exception type static int metaTypeId(); private: diff --git a/tests/parser/parser.cpp b/tests/parser/parser.cpp index 84c7771..9e54a90 100644 --- a/tests/parser/parser.cpp +++ b/tests/parser/parser.cpp @@ -70,7 +70,8 @@ void ElamTest::charClass() 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 tl=ie.tokenize(ex); /*qDebug()<<"expression:"<