enable evaluation
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Sun, 21 Nov 2010 20:27:31 +0000 (20:27 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Sun, 21 Nov 2010 20:27:31 +0000 (20:27 +0000)
git-svn-id: https://silmor.de/svn/softmagic/elam/trunk@638 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

14 files changed:
src/elambinary.cpp
src/elambinary.h
src/elamboolengine.cpp
src/elamengine.cpp
src/elamengine.h
src/elamexpression.cpp
src/elamexpression.h
src/elamunary.cpp
src/elamunary.h
src/elamvalue.cpp
src/elamvalue.h
tests/eval/eval.cpp
tests/eval/eval.h
tests/parser/parser.cpp

index 85a16f4..8fc47b3 100644 (file)
@@ -42,57 +42,86 @@ BinaryOperator::~BinaryOperator()
 
 QVariant BinaryOperator::execute ( const QVariant&op1,const QVariant&op2 ) const
 {
-       //TODO: implement
-       return QVariant();
+       //search for match
+       QPair<int,int>k(op1.userType(),op2.userType());
+       QPair<int,int>kl(k.first,AnyType::metaTypeId());
+       QPair<int,int>kr(AnyType::metaTypeId(),k.second);
+       QPair<int,int>ka(AnyType::metaTypeId(),AnyType::metaTypeId());
+       BinaryOperatorCall bc=0;
+       //search for perfect, then left, then right, then any match
+       if(d->callmap.contains(k))bc=d->callmap[k];else
+       if(d->callmap.contains(kl))bc=d->callmap[kl];else
+       if(d->callmap.contains(kr))bc=d->callmap[kr];else
+       if(d->callmap.contains(ka))bc=d->callmap[ka];
+       //bail out if none found
+       if(bc==0)
+               return Exception(Exception::TypeMismatchError, "operator cannot work on this type");
+       //execute
+       return bc(op1,op2);
 }
 
 BinaryOperatorCall BinaryOperator::getCallback ( QString type1, QString type2 ) const
 {
-       //TODO: implement
-       return 0;
+       QPair<int,int>k( QVariant::nameToType(type1.toAscii().data()), QVariant::nameToType(type2.toAscii().data()));
+       if(d->callmap.contains(k))
+               return d->callmap[k];
+       else
+               return 0;
 }
 
 BinaryOperatorCall BinaryOperator::getCallback ( int type1, int type2 ) const
 {
-       //TODO: implement
-       return 0;
+       QPair<int,int>k(type1,type2);
+       if(d->callmap.contains(k))
+               return d->callmap[k];
+       else
+               return 0;
 }
 
 QList< QPair< int, int > > BinaryOperator::getTypeIds() const
 {
-       //TODO: implement
-       return QList< QPair< int, int > > ();
+       return d->callmap.keys();
 }
 QList< QPair< QString, QString > > BinaryOperator::getTypeNames() const
 {
-       //TODO: implement
-       return QList< QPair< QString, QString > >();
+       QList<QPair<int,int> > k=d->callmap.keys();
+       QList<QPair<QString,QString> >ret;
+       for(int i=0;i<k.size();i++)
+               ret<<QPair<QString,QString>( QVariant::typeToName((QVariant::Type)k[i].first), QVariant::typeToName((QVariant::Type)k[i].second));
+       return ret;
 }
-void BinaryOperator::removeCallback ( BinaryOperatorCall )
+void BinaryOperator::removeCallback ( BinaryOperatorCall c)
 {
-       //TODO: implement
-
+       QList<QPair<int,int> > k=d->callmap.keys();
+       for(int i=0;i<k.size();i++)
+               if(d->callmap[k[i]]==c)
+                       d->callmap.remove(k[i]);
 }
-void BinaryOperator::removeCallback ( QString , QString )
+void BinaryOperator::removeCallback ( QString t1, QString t2)
 {
-       //TODO: implement
-
+       removeCallback( QVariant::nameToType(t1.toAscii().data()), QVariant::nameToType(t2.toAscii().data()));
 }
-void BinaryOperator::removeCallback ( int , int )
+void BinaryOperator::removeCallback ( int t1, int t2)
 {
-       //TODO: implement
-
+       d->callmap.remove(QPair<int,int>(t1,t2));
 }
 void BinaryOperator::setCallback ( BinaryOperatorCall callback, QString type1, QString type2 )
 {
-       //TODO: implement
-
+       setCallback(callback, QVariant::nameToType(type1.toAscii().data()), QVariant::nameToType(type2.toAscii().data()));
 }
 void BinaryOperator::setCallback ( BinaryOperatorCall callback, int type1, int type2 )
 {
-       //TODO: implement
+       if(callback==0)
+               d->callmap.remove(QPair<int,int>(type1,type2));
+       else
+               d->callmap.insert(QPair<int,int>(type1,type2),callback);
+}
 
+bool BinaryOperator::isNull() const
+{
+       return d->callmap.isEmpty();
 }
 
+
 //end of namespace
 };
\ No newline at end of file
index 055b749..ccbe70a 100644 (file)
@@ -63,6 +63,9 @@ class BinaryOperator
                
                ///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;
+               
+               ///true if this operator has no callbacks
+               bool isNull()const;
 };
 
 //end of namespace
index f577b20..98f9ba5 100644 (file)
@@ -109,21 +109,21 @@ static QVariant isExceptionFunc(const QList<QVariant>&lf)
 {
        if(lf.size()!=1)
                return Exception(Exception::ArgumentListError, "expecting exactly one argument");
-       return lf[0].type()==Exception::metaTypeId();
+       return lf[0].userType()==Exception::metaTypeId();
 }
 
 static QVariant isExceptionOrNullFunc(const QList<QVariant>&lf)
 {
        if(lf.size()!=1)
                return Exception(Exception::ArgumentListError, "expecting exactly one argument");
-       return lf[0].isNull() || lf[0].type()==Exception::metaTypeId();
+       return lf[0].isNull() || lf[0].userType()==Exception::metaTypeId();
 }
 
 static QVariant catchFunc(const QList<QVariant>&lf)
 {
        if(lf.size()<1||lf.size()>3)
                return Exception(Exception::ArgumentListError, "expecting 1-3 arguments");
-       if(lf[0].type()==Exception::metaTypeId()){
+       if(lf[0].userType()==Exception::metaTypeId()){
                if(lf.size()>1)return lf[1];
                else return true;
        }else{
index bba3723..dabbd6b 100644 (file)
@@ -301,6 +301,9 @@ BinaryOperator Engine::binaryOperator ( QString name,int prio,PriorityMatch matc
        //check whether prio matches
        if(d->binary[name].prio==prio)
                return d->binary[name].op;
+       //override if the op is empty
+       if(d->binary[name].op.isNull())
+               match=OverridePrio;
        //check matching mode
        switch(match){
                case OverridePrio:
@@ -348,6 +351,18 @@ bool Engine::isAssignment(QString name) const
        return d->cclass.isAssignment(name);
 }
 
+bool Engine::hasBinaryOperator(QString name) const
+{
+       if(!d->binary.contains(name))return false;
+       return ! d->binary[name].op.isNull();
+}
+
+bool Engine::hasUnaryOperator(QString name) const
+{
+       if(!d->unary.contains(name))return false;
+       return ! d->unary[name].isNull();
+}
+
 
 //end of namespace
 };
\ No newline at end of file
index c828c20..d186d70 100644 (file)
@@ -68,6 +68,11 @@ class Engine:public QObject
                Q_INVOKABLE bool hasFunction(QString)const;
                ///returns the pointer to the function
                Q_INVOKABLE Function getFunction(QString)const;
+               
+               ///returns true if there is an unaryOperator with this name
+               Q_INVOKABLE bool hasUnaryOperator(QString name)const;
+               ///returns true if there is an binaryOperator with this name
+               Q_INVOKABLE bool hasBinaryOperator(QString name)const;
 
                /** \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*/
index eb5781e..d87a3f1 100644 (file)
@@ -81,6 +81,7 @@ static void printtokenlist(QDebug&dbg,const QList<Token>&tok,int llevel,int tlev
        for(int i=0;i<tok.size();i++){
                dbg<<"\n";
                printtoken(dbg,tok[i],tlevel+1);
+               dbg.nospace();
        }
        dbg<<")";
 }
@@ -95,7 +96,10 @@ static void printtoken(QDebug&dbg,const Token&tok,int level)
                case Token::ParClose:dbg<<"ClosingParenthesis";break;
                case Token::ParOpen:dbg<<"OpeningParenthesis";break;
                case Token::Comma:dbg<<"Comma";break;
-               case Token::Literal:dbg<<"LiteralValue,value="<<tok.literalValue();break;
+               case Token::Literal:
+                       dbg<<"LiteralValue,value="<<tok.literalValue();
+                       dbg.nospace();
+                       break;
                case Token::Whitespace:dbg<<"WhiteSpace";break;
                case Token::Parentheses:dbg<<"Parentheses";break;
                case Token::Function:dbg<<"Function";break;
@@ -106,7 +110,7 @@ static void printtoken(QDebug&dbg,const Token&tok,int level)
                case Token::AssignmentOp:dbg<<"Assignment";break;
                default:dbg<<"Unknown:"<<(int)tok.type();break;
        }
-       dbg<<",pos="<<tok.position();
+       dbg<<",pos="<<tok.position();dbg.nospace();
        QList<Token> sub=tok.subTokens();
        if(sub.size()>0){
                dbg<<",sub-tokens:";
@@ -114,14 +118,14 @@ static void printtoken(QDebug&dbg,const Token&tok,int level)
        }
        dbg<<")";
 }
-QDebug&operator<<(QDebug&dbg,const Token&tok)
+QDebug&operator<<(QDebug dbg,const Token&tok)
 {
        dbg.nospace();
        printtoken(dbg,tok,0);
        return dbg.space();
 }
 
-QDebug&operator<<(QDebug&dbg,const QList<Token>&tok)
+QDebug&operator<<(QDebug dbg,const QList<Token>&tok)
 {
        dbg.nospace();
        printtokenlist(dbg,tok,0,0);
@@ -267,6 +271,7 @@ inline Exception Expression::scanForError(const QList< Token >& toks)
 
 Expression::Expression(Engine* parent, const QList< Token >& toks)
 {
+       d->parent=parent;
        //check for simple errors
        ELAM::Exception ex=scanForError(toks);
        if(ex.errorType()!=ELAM::Exception::NoError){
@@ -275,9 +280,8 @@ Expression::Expression(Engine* parent, const QList< Token >& toks)
                d->tokens=toks;
                return;
        }
-       d->parent=parent;
        d->tokens=reduceTokens(toks);
-       qDebug()<<"tokens:"<<d->tokens;
+//     qDebug()<<"tokens:"<<d->tokens;
        //check for nothing and complain
        if(d->tokens.size()==0){
                d->type=Exception;
@@ -288,12 +292,12 @@ Expression::Expression(Engine* parent, const QList< Token >& toks)
        if(d->tokens.size()==1){
                switch(d->tokens[0].type()){
                        case Token::Function:
+                               d->type=Function;
                                functionInit();
-                               d->type=(Type)d->tokens[0].type();
                                break;
                        case Token::Parentheses:
                                d->subexpr<<Expression(parent,d->tokens[0].subTokens());
-                               d->type=(Type)d->tokens[0].type();
+                               d->type=Parentheses;
                                break;
                        case Token::Constant:
                        case Token::Variable:
@@ -330,6 +334,9 @@ Expression::Expression(Engine* parent, const QList< Token >& toks)
                                return;
                        }
                        //ok
+                       d->type=AssignmentOp;
+                       d->oppos=i;
+                       d->subexpr<<Expression(parent,d->tokens.mid(0,1));
                        d->subexpr<<Expression(parent,d->tokens.mid(2));
                        return;
                }
@@ -359,23 +366,28 @@ Expression::Expression(Engine* parent, const QList< Token >& toks)
        //split operation
        d->oppos=cpos;
        if(d->tokens[cpos].type()==Token::BinaryOp){
+               d->type=BinaryOp;
                d->subexpr<<Expression(parent,d->tokens.mid(0,cpos));
                d->subexpr<<Expression(parent,d->tokens.mid(cpos+1));
        }else{
+               d->type=UnaryOp;
                d->subexpr<<Expression(parent,d->tokens.mid(cpos+1));
        }
 }
 
 void Expression::functionInit()
 {
-       QList<Token>sub=d->tokens[0].subTokens();
+       //create hierarchy
+       QList<Token>sub=reduceTokens(d->tokens[0].subTokens());
+       //scan for commas
        QList<Token>par;
        for(int i=0;i<sub.size();i++){
                if(sub[i].type()==Token::Comma){
                        d->subexpr<<Expression(d->parent,par);
                        par.clear();
-               }
+               }else par<<sub[i];
        }
+       //last component
        if(par.size()>0){
                d->subexpr<<Expression(d->parent,par);
        }
@@ -384,43 +396,195 @@ void Expression::functionInit()
 
 QVariant Expression::evaluate()
 {
-       if(d->type==Exception)return d->excep;
-       if(d->parent.isNull())return ELAM::Exception(ELAM::Exception::OperationError,"Lost engine context, cannot evaluate.");
-       return QVariant();
+       //basic checks
+       if(d->type==Exception)
+               return d->excep;
+       if(d->parent.isNull())
+               return ELAM::Exception(ELAM::Exception::OperationError,"Lost engine context, cannot evaluate.");
+       //check type
+       switch(d->type){
+               case Literal:return d->tokens[0].literalValue();
+               case Variable:
+               case Constant:{
+                       QString n=d->tokens[0].content();
+                       if(d->parent->hasValue(n))
+                               return d->parent->getValue(n);
+                       else
+                               return ELAM::Exception(ELAM::Exception::UnknownValueError, "unknown variable or constant", d->tokens[0].position());
+               }
+               case Parentheses:return d->subexpr[0].evaluate();
+               case Function:return evalFunction();
+               case UnaryOp:return evalUnary();
+               case BinaryOp:
+                       return evalBinary();
+               case AssignmentOp:
+                       return evalAssign();
+               default:
+                       return ELAM::Exception(ELAM::Exception::OperationError, "internal error: unknown expression type", position());
+       }
 }
 
-void printExpression(QDebug&dbg,const Expression&ex,int level)
+QVariant Expression::evalBinary()
+{
+       QString un=d->tokens.value(d->oppos).content();
+       if(d->type==AssignmentOp)
+               un=d->parent->characterClasses().toOperator(un);
+       if(!d->parent->hasBinaryOperator(un))
+               return ELAM::Exception(ELAM::Exception::UnknownOperatorError,"unknown operator", position());
+       //get sub-expression and check for exception
+       QVariant sub1=d->subexpr[0].evaluate();
+       if(sub1.userType()==ELAM::Exception::metaTypeId())
+               return sub1;
+       QVariant sub2=d->subexpr[1].evaluate();
+       if(sub2.userType()==ELAM::Exception::metaTypeId())
+               return sub2;
+       //get operator
+       BinaryOperator binop=d->parent->binaryOperator(un);
+       //perform operation
+       QVariant r=binop.execute(sub1,sub2);
+       if(r.userType()==Exception::metaTypeId()){
+               ELAM::Exception ex=r;
+               if(!ex.position().isValid())
+                       ex.setPosition(position());
+       }
+       return r;
+}
+
+QVariant Expression::evalAssign()
+{
+       QString un=d->tokens.value(d->oppos).content();
+       QVariant r;
+       //execute operations to gain result
+       if(d->parent->characterClasses().isSimpleAssignment(un))
+               r=d->subexpr[1].evaluate();
+       else
+               r=evalBinary();
+       //check for exception
+       if(r.userType()!=ELAM::Exception::metaTypeId())
+               d->parent->setVariable(d->tokens[0].content(),r);
+       //return result
+       return r;
+}
+
+
+QVariant Expression::evalFunction()
 {
+       //basic checks
+       QString fn=d->tokens[0].content();
+       ELAM::Function func=d->parent->getFunction(fn);
+       if(func==0)
+               return ELAM::Exception(ELAM::Exception::UnknownFunctionError, "unknown function", position());
+       //gather arguments
+       QList<QVariant>args;
+       for(int i=0;i<d->subexpr.size();i++)
+               args<<d->subexpr[i].evaluate();
+       //execute
+       return func(args);
+}
+
+QVariant Expression::evalUnary()
+{
+       QString un=d->tokens.value(d->oppos).content();
+       if(!d->parent->hasUnaryOperator(un))
+               return ELAM::Exception(ELAM::Exception::UnknownOperatorError,"unknown operator", position());
+       //get sub-expression
+       QVariant sub=d->subexpr[0].evaluate();
+       //check for exception
+       if(sub.userType()==ELAM::Exception::metaTypeId())
+               return sub;
+       //get operator
+       UnaryOperator unop=d->parent->unaryOperator(un);
+       //perform operation
+       return unop.execute(sub);
+}
+
+
+Position Expression::position() const
+{
+       switch(d->type){
+               case Expression::Literal:
+               case Expression::Variable:
+               case Expression::Constant:
+               case Expression::Function:
+               case Expression::Parentheses:
+                       return d->tokens.value(0).position();
+               case Expression::UnaryOp:
+               case Expression::BinaryOp:
+               case Expression::AssignmentOp:
+                       return d->tokens.value(d->oppos).position();
+               case Expression::Exception:
+                       return d->excep.errorPos();
+               default:
+                       return Position();
+       }
+}
+
+
+void Expression::printExpression(QDebug&dbg,const Expression&ex,int level)
+{
+       dbg.nospace();
        printspaces(dbg,level);
        dbg<<"Expression(type=";
+       int ptok=-1;
        switch(ex.d->type){
-               case Expression::Literal:dbg<<"Literal";break;
-               case Expression::Variable:dbg<<"Variable";break;
-               case Expression::Constant:dbg<<"Constant";break;
-               case Expression::Function:dbg<<"Function";break;
-               case Expression::Parentheses:dbg<<"Parentheses";break;
-               case Expression::UnaryOp:dbg<<"UnaryOperator";break;
-               case Expression::BinaryOp:dbg<<"BinaryOperator";break;
-               case Expression::AssignmentOp:dbg<<"Assignment";break;
-               case Expression::Exception:dbg<<"Exception";break;
-               default:dbg<<"Unknown:"<<(int)ex.d->type;break;
+               case Expression::Literal:
+                       dbg<<"Literal";
+                       ptok=0;
+                       break;
+               case Expression::Variable:
+                       dbg<<"Variable";
+                       ptok=0;
+                       break;
+               case Expression::Constant:
+                       dbg<<"Constant";
+                       ptok=0;
+                       break;
+               case Expression::Function:
+                       dbg<<"Function";
+                       ptok=0;
+                       break;
+               case Expression::Parentheses:
+                       dbg<<"Parentheses";
+                       break;
+               case Expression::UnaryOp:
+                       dbg<<"UnaryOperator:" <<ex.d->tokens.value(ex.d->oppos).content() <<"sub1";
+                       break;
+               case Expression::BinaryOp:
+                       dbg<<"BinaryOperator:sub1" <<ex.d->tokens.value(ex.d->oppos).content() <<"sub2";
+                       break;
+               case Expression::AssignmentOp:
+                       dbg<<"Assignment:sub1" <<ex.d->tokens.value(ex.d->oppos).content() <<"sub2";
+                       break;
+               case Expression::Exception:
+                       dbg<<"Exception";
+                       break;
+               default:
+                       dbg<<"Unknown:id="<<(int)ex.d->type;
+                       break;
        }
+       dbg<<","<<ex.d->tokens.size()<<" tokens,pos="<<ex.position();dbg.nospace();
        if(ex.d->excep.errorType()!=ELAM::Exception::NoError)
                dbg<<",exception="<<ex.d->excep;
+       if(ptok>=0){
+               dbg<<",token "<<ptok<<":\n";
+               printtoken(dbg,ex.d->tokens.value(0),level+1);
+               dbg.nospace();
+       }
        if(ex.d->subexpr.size()>0){
                dbg<<",subexpressions:";
                for(int i=0;i<ex.d->subexpr.size();i++){
                        dbg<<"\n";
                        printExpression(dbg,ex.d->subexpr[i],level+1);
+                       dbg.nospace();
                }
        }
        dbg<<")";
 }
 
-QDebug& operator<<(QDebug&dbg,const Expression&ex)
+QDebug& operator<<(QDebug dbg,const Expression&ex)
 {
        dbg.nospace();
-       printExpression(dbg,ex,0);
+       Expression::printExpression(dbg,ex,0);
        return dbg.space();
 }
 
index fb8af15..cde9d65 100644 (file)
@@ -99,12 +99,18 @@ class Token
                inline bool isLiteral()const{return type()&LiteralMask;}
        protected:
                friend class Expression;
+               ///makes the tokens type more specialized
                void setSubType(Type);
+               ///adds a token to the sub-token list
                void addSubToken(const Token&);
+               ///overrides the list of sub-tokens
                void setSubTokens(const QList<Token>&);
 };
 
-QDebug& operator<<(QDebug&,const Token&);
+///makes tokens accessable to qDebug()
+QDebug& operator<<(QDebug,const Token&);
+///makes tokens accessable to qDebug()
+QDebug& operator<<(QDebug,const QList<Token>&);
 
 class Engine;
 /**Represents an expression in the context of its engine.
@@ -133,8 +139,13 @@ class Expression
                
                ///evaluates the expression and returns the result of the evaluation
                QVariant evaluate();
+               
+               ///returns the position of the main element of this expression
+               Position position()const;
        private:
-               friend  void printExpression(QDebug&,const Expression&,int);
+               friend QDebug& operator<<(QDebug,const Expression&);
+               ///helper for qDebug()
+               static void printExpression(QDebug&,const Expression&,int);
                ///scan tokens and decide what specific sub-type they are
                QList<Token>classifyTokens(QList<Token> toks);
                /**pushes parentheses and function arguments into the sub-tokens of their parents;
@@ -146,9 +157,18 @@ class Expression
                QList<Token>simplifyTokens(QList<Token> toks);
                ///parses tokens and splits them by comma
                void functionInit();
+               ///helper for evaluate - evaluates a function
+               QVariant evalFunction();
+               ///helper for evaluate - evaluates an unary operator
+               QVariant evalUnary();
+               ///helper for evaluate - evaluates an binary operator
+               QVariant evalBinary();
+               ///helper for evaluate - evaluates an assignment and an optional binary operator
+               QVariant evalAssign();
 };
 
-QDebug& operator<<(QDebug&,const Expression&);
+///makes expressions accessable to qDebug()
+QDebug& operator<<(QDebug,const Expression&);
 
 //end of namespace
 };
index ff828b2..6556baf 100644 (file)
@@ -45,15 +45,15 @@ UnaryOperator::~UnaryOperator()
 QVariant UnaryOperator::execute(const QVariant& op) const
 {
        if(d->callmap.size()==0)
-               return qVariantFromValue<Exception>(Exception(Exception::UnknownOperatorError));
+               return Exception(Exception::UnknownOperatorError);
        //search for direct match
-       if(d->callmap.contains(op.type()))
-               return d->callmap[op.type()](op);
+       if(d->callmap.contains(op.userType()))
+               return d->callmap[op.userType()](op);
        //search for fallback
        int any=AnyType::metaTypeId();
        if(d->callmap.contains(any))
                return d->callmap[any](op);
-       return qVariantFromValue<Exception>(Exception(Exception::TypeMismatchError));
+       return Exception(Exception::TypeMismatchError);
 }
 
 QList< int > UnaryOperator::getTypeIds() const
@@ -117,6 +117,11 @@ void UnaryOperator::removeCallback(int t)
        d->callmap.remove(t);
 }
 
+bool UnaryOperator::isNull() const
+{
+       return d->callmap.isEmpty();
+}
+
 
 //end of namespace
 };
\ No newline at end of file
index 5976319..d331dbc 100644 (file)
@@ -67,6 +67,9 @@ class UnaryOperator
                
                ///calls the callback function associated with the type of the argument, throws an exception if there is no callback
                QVariant execute(const QVariant&)const;
+               
+               ///true if this operator has no callbacks
+               bool isNull()const;
 };
 
 //end of namespace
index 1a9b63c..2b88537 100644 (file)
@@ -13,6 +13,19 @@ Exception::Exception(const Exception& e)
 {
        operator=(e);
 }
+
+Exception::Exception(const QVariant& v)
+{
+       operator=(v.value<Exception>());
+}
+Exception::Exception(Exception::ErrorType type, Position pos, QString errorText)
+{
+       mtype=type;
+       merr=errorText;
+       mpos=pos;
+}
+
+
 Exception::Exception(ErrorType tp,QString errText, Position pos)
 {
        mtype=tp;
@@ -25,6 +38,7 @@ int Exception::metaTypeId()
 {
        return Exception_metaid;
 }
+
 Exception& Exception::operator=(const Exception& e)
 {
        mtype=e.mtype;
@@ -39,13 +53,35 @@ int AnyType::metaTypeId()
        return AnyType_metaid;
 }
 
-QDebug& operator<< ( QDebug& dbg, const ELAM::Position& pos)
+QDebug& operator<< ( QDebug dbg, const ELAM::Position& pos)
 {
        if(!pos.isValid())dbg.nospace()<<"Position(invalid)";
        else dbg.nospace()<<"Position(line="<<pos.line()<<",col="<<pos.column()<<")";
        return dbg.space();
 }
 
+QDebug&operator<<(QDebug dbg,const Exception&ex)
+{
+       dbg.nospace()<<"Exception(type=";
+       switch(ex.errorType()){
+               case Exception::NoError:dbg<<"NoError";break;
+               case Exception::ParserError:dbg<<"ParserError";break;
+               case Exception::UnknownOperatorError:dbg<<"UnknownOperation";break;
+               case Exception::UnknownFunctionError:dbg<<"UnknownFunction";break;
+               case Exception::UnknownValueError:dbg<<"UnknownValue";break;
+               case Exception::TypeMismatchError:dbg<<"TypeMismatch";break;
+               case Exception::ArgumentListError:dbg<<"ArgumentList";break;
+               case Exception::OperationError:dbg<<"Operation";break;
+               default:dbg<<"Unknown:"<<(int)ex.errorType();break;
+       }
+       if(ex.errorText()!="")
+               dbg<<",text="<<ex.errorText();
+       dbg<<",pos="<<ex.errorPos();
+       dbg.nospace()<<")";
+       return dbg.space();
+}
+QDebug&operator<<(QDebug dbg,const AnyType&){dbg.nospace()<<"AnyType";return dbg.space();}
+
 
 //end of namespace
 };
\ No newline at end of file
index 3c3df79..f6a1fa9 100644 (file)
@@ -52,7 +52,7 @@ class Position
        private:
                int mline,mcol;
 };
-QDebug&operator<<(QDebug&,const Position&);
+QDebug&operator<<(QDebug,const Position&);
 
 /**Objects of this class represent an exception in the evaluation of an ELAM expression.*/
 class Exception
@@ -68,6 +68,8 @@ class Exception
                        UnknownOperatorError,
                        ///the function is not known
                        UnknownFunctionError,
+                       ///unknown variable or constant
+                       UnknownValueError,
                        ///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
@@ -78,6 +80,8 @@ class Exception
                
                ///instantiates a copy of an exception
                Exception(const Exception&);
+               ///instantiate a copy from a matching variant
+               Exception(const QVariant&);
                /**instantiates an exception
                \param type the type of exception
                \param errorText some human readable text describing the problem
@@ -98,11 +102,16 @@ class Exception
                ErrorType errorType()const{return mtype;}
                ///the position in the original expression where the error originated
                Position errorPos()const{return mpos;}
+               ///the position in the original expression where the error originated
+               Position position()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();}
                
+               ///overrides the exceptions position
+               void setPosition(Position p){mpos=p;}
+               
                ///converts the exception to a variant
                operator QVariant()const{return QVariant::fromValue<Exception>(*this);}
                
@@ -115,6 +124,8 @@ class Exception
                Position mpos;
 };
 
+QDebug&operator<<(QDebug,const Exception&);
+
 /**this type is not actually used, its ID is used as a fallback to tell operators, functions and engines that any supported type can be used*/
 class AnyType
 {
@@ -122,10 +133,12 @@ class AnyType
                ///returns the meta type ID of the ANY type
                static int metaTypeId();
 };
+QDebug&operator<<(QDebug,const AnyType&);
 
 //end of namespace
 };
 
+
 Q_DECLARE_METATYPE(ELAM::Exception);
 Q_DECLARE_METATYPE(ELAM::AnyType);
 
index d0d000a..abf866c 100644 (file)
@@ -13,10 +13,64 @@ void ElamTest::evaltest()
 {
        IntEngine ie;
        FloatEngine::configureFloatEngine(ie);
-       QString exs="a=b+=345*int(3.5)+ - -(+65/(5))";
+       ie.setVariable("b",(qlonglong)0);
+       QString exs="a=b+=345*int(3.15)+ - -(+65/(5))";
        Expression ex=ie.expression(exs);
-       qDebug()<<"expression:"<<ex;
+//     qDebug()<<"expression:\n"<<ex;
        QVariant v=ie.evaluate(ex);
+       QVariant xv=Exception();
+//     if(v.userType()==Exception::metaTypeId())qDebug()<<"exception:"<<Exception(v);
+       QVariant cmp=(qlonglong)(345*3+65/5);
+       QCOMPARE(v,cmp);
+       QCOMPARE(ie.getVariable("b"),cmp);
+       QCOMPARE(ie.getVariable("a"),cmp);
+       QCOMPARE(ie.evaluate("-2"),QVariant((qlonglong)-2));
+       ie.setVariable("v",(qlonglong)2);
+       ie.setConstant("c",(qlonglong)-3);
+       QCOMPARE(ie.evaluate("-v"),QVariant((qlonglong)-2));
+       QCOMPARE(ie.evaluate("-c"),QVariant((qlonglong)3));
+       
+}
+
+static QVariant mycount(const QList<QVariant>&args)
+{
+       return (qlonglong)args.size();
+}
+static QVariant mysum(const QList<QVariant>&args)
+{
+       qlonglong r=0;
+       for(int i=0;i<args.size();i++)
+               r+=args[i].toLongLong();
+       return r;
+}
+
+void ElamTest::excepttest()
+{
+       IntEngine ie;
+       QVariant v=ie.evaluate("-novar");
+       QCOMPARE(v.userType(),Exception::metaTypeId());
+       Exception ex=v;
+//     qDebug()<<ex;
+       QCOMPARE(ex.errorType(),Exception::UnknownValueError);
+       ex=ie.evaluate("9/0");
+       QCOMPARE(ex.errorType(),Exception::OperationError);
+       ex=ie.evaluate("(novar)");
+       QCOMPARE(ex.errorType(),Exception::UnknownValueError);
+}
+
+void ElamTest::counttest()
+{
+       IntEngine ie;
+       ie.setFunction("count",mycount);
+       QCOMPARE(ie.evaluate("count(1,2,3,count(4,5,6))"),QVariant((qlonglong)4));
+}
+
+void ElamTest::functest()
+{
+       IntEngine ie;
+       ie.setFunction("count",mycount);
+       ie.setFunction("sum",mysum);
+       QCOMPARE(ie.evaluate("sum(1,2,3,count(1,x,n))"),QVariant((qlonglong)9));
 }
 
 
index 0ab9f81..11986f6 100644 (file)
@@ -5,4 +5,7 @@ class ElamTest:public QObject
        Q_OBJECT
        private slots:
                void evaltest();
+               void excepttest();
+               void counttest();
+               void functest();
 };
index 9e54a90..7cbe8e6 100644 (file)
@@ -4,7 +4,7 @@
 
 #include <QtCore>
 #include <QtTest>
-#include <QDebug>
+// #include <QDebug>
 
 using namespace ELAM;