added basic functions for int
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Mon, 15 Nov 2010 21:56:53 +0000 (21:56 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Mon, 15 Nov 2010 21:56:53 +0000 (21:56 +0000)
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
src/elam.pro
src/elamengine.cpp
src/elamengine.h
src/elamfloatengine.cpp [new file with mode: 0644]
src/elamfloatengine.h [new file with mode: 0644]
src/elamintengine.cpp
src/elamintengine.h
src/elamvalue.cpp
src/elamvalue.h
tests/parser/parser.cpp

index e62e380..2c86388 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "elamengine.h"
 #include "elamintengine.h"
+#include "elamfloatengine.h"
 
 /** \mainpage ELAM - Elementary Logic and Arithmetic Machine
 
index c3b2961..e428bd5 100644 (file)
@@ -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 += .
index 00a4f87..7cb5de8 100644 (file)
@@ -28,6 +28,8 @@ class DPTR_CLASS_NAME(Engine):public DPtr
                        {return prio>l2.prio;}
                };
                QList<LiteralParser_s>parsers;
+               QMap<QString,UnaryOperator>unary;
+               QMap<QString,BinaryOperator>binary;
 };
 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;i<d->parsers.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;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;
        }
@@ -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
index 2c58557..572c206 100644 (file)
@@ -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<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
@@ -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 (file)
index 0000000..b2bc0ea
--- /dev/null
@@ -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 <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);
+}
diff --git a/src/elamfloatengine.h b/src/elamfloatengine.h
new file mode 100644 (file)
index 0000000..4c49cf1
--- /dev/null
@@ -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
index 849c8a4..35e1e42 100644 (file)
@@ -5,6 +5,8 @@
 
 #include "elamintengine.h"
 
+#include<QDebug>
+
 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<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;
@@ -30,11 +32,66 @@ QPair<QString,QVariant> IntLiteralParser(const QString&expr,Engine&engine,int st
        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);
 }
index 984d269..71ae4ec 100644 (file)
 
 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();
 };
 
 };
index 943b10d..1a9b63c 100644 (file)
@@ -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;
index dcb791c..3c3df79 100644 (file)
@@ -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<Exception>(*this);}
+               
+               ///the meta type ID (used with QVariant) of the exception type
                static int metaTypeId();
                
        private:
index 84c7771..9e54a90 100644 (file)
@@ -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<Token> tl=ie.tokenize(ex);
        /*qDebug()<<"expression:"<<ex<<"tokens:"<<tl.size();
        for(int i=0;i<tl.size();i++)
@@ -83,11 +84,13 @@ void ElamTest::tokenizer()
        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