expand ELAM to understand pipes
authorKonrad Rosenbaum <konrad@silmor.de>
Tue, 12 Jul 2016 16:23:32 +0000 (18:23 +0200)
committerKonrad Rosenbaum <konrad@silmor.de>
Tue, 12 Jul 2016 16:23:32 +0000 (18:23 +0200)
12 files changed:
.gitignore
elam/elam.pro
elam/include/elamcharclass.h
elam/include/elamengine.h
elam/include/elamexpression.h
elam/src/elamcharclass.cpp
elam/src/elamengine.cpp
elam/src/elamexpression.cpp
elam/tests/eval/eval.pro
elam/tests/parser/parser.cpp
elam/tests/parser/parser.h
elam/tests/parser/parser.pro

index 20615c6..affd0a8 100644 (file)
@@ -8,3 +8,8 @@ Makefile*
 core
 core.*
 .kdev*
+*.o
+*.obj
+moc_*
+elam/tests/eval/evaltest*
+elam/tests/parser/parsertest*
index f751469..6d2d687 100644 (file)
@@ -1,12 +1,12 @@
 TEMPLATE = lib
 TARGET = elam
-CONFIG += dll create_prl hide_symbols separate_debug_info
+CONFIG += dll create_prl hide_symbols separate_debug_info c++11
 QT -= gui
 OBJECTS_DIR = .ctmp
 MOC_DIR = .ctmp
 RCC_DIR = .ctmp
 DEFINES += ELAM_LIBRARY_BUILD
-VERSION = 
+VERSION = 0.4
 DESTDIR = $$PWD/../lib
 
 HEADERS += \
@@ -36,5 +36,3 @@ SOURCES += \
 
 INCLUDEPATH += . src include ../include/elam ../chester
 DEPENDPATH += $$INCLUDEPATH
-
-gcc { QMAKE_CXXFLAGS+=-std=c++11 }
index 5790921..fba121e 100644 (file)
@@ -119,6 +119,13 @@ class ELAM_EXPORT CharacterClassSettings
                
                The character must not be part of any other class.*/
                void setCommaChar(QChar);
+
+               ///returns the character used as "dot" - separator of struct members
+               QChar dotChar()const;
+               ///sets the character used as "dot" - separator if struct members
+               ///
+               ///the character must be part of the name class
+               void setDotChar(QChar);
                
                /**true if the settings are internally consistent
                
index 81480d0..6c4a545 100644 (file)
@@ -136,7 +136,7 @@ class ELAM_EXPORT Engine:public QObject
                
                /** \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
-               \param prio the priority that should be set for the operator
+               \param prio the priority that should be set for the operator; higher value means stronger binding
                \param match how to behave if the operator already exists and priorities do not match, see PriorityMatch
                \returns a reference to the operator, or to a dummy operator if priority matching failed
                */
@@ -148,6 +148,9 @@ class ELAM_EXPORT Engine:public QObject
                ///returns true if the name represents an assignment operator
                Q_INVOKABLE bool isAssignment(QString name)const;
 
+               ///returns true if the name represents a pipe
+               Q_INVOKABLE bool isPipe(QString name)const;
+
                /**performs automatic casting
                \returns the casted value or the original value if it is of a primary type or if there is no known cast for it*/
                Q_INVOKABLE QVariant autoCast(const QVariant&)const;
@@ -162,6 +165,10 @@ class ELAM_EXPORT Engine:public QObject
                Q_INVOKABLE QStringList binaryOperatorNames()const;
                ///returns the names of all currently existing unary operators
                Q_INVOKABLE QStringList unaryOperatorNames()const;
+               ///returns the name of the pipe operator or empty string if it does not exist
+               Q_INVOKABLE QString pipeOperator()const;
+               ///returns the priority of the pipe operator - per default 0 (lowest binding)
+               Q_INVOKABLE int pipePrio()const;
 
        public slots:
                ///returns the value of the named variable or constant
@@ -225,7 +232,7 @@ class ELAM_EXPORT Engine:public QObject
                
                ///sets/overrides the priority of an operator, creating the operator if it does not exist yet
                void setBinaryOperatorPrio(QString name,int prio);
-               
+
                /**registers a type as primary, this means it is not cast into another type when encountered
                \param typeId the QVariant ID of the type to register*/
                void registerType(int typeId);
@@ -238,6 +245,11 @@ class ELAM_EXPORT Engine:public QObject
                \param castfunc the function that converts these types
                \param prio the priority of the cast, if a cast function for the same origin, but a higher priority exists the one with the higher priority is used; the priority must be a positive value*/
                void setAutoCast(int target,QList<int>origin,TypeCast castfunc,int prio=50);
+
+               ///sets the pipe operator
+               void setPipeOperator(QString pipe);
+               ///sets the prio of the pipe operator
+               void setPipePrio(int);
                
                ///simply parses an expression string into an  object
                Expression expression(QString);
index ed67a25..7802ee9 100644 (file)
@@ -39,6 +39,8 @@ class ELAM_EXPORT Token
                        BinaryOp = 0x200,
                        ///the token represents an assignment (with optional implicit binary op)
                        AssignmentOp = 0x400,
+                       ///the token represents a pipe operator
+                       PipeOp = 0x800,
                        ///an operator (unary or binary)
                        Operator=0xff00,
                        ///mask for operator tokens
@@ -129,6 +131,7 @@ class ELAM_EXPORT Expression
                        Parentheses=Token::Parentheses,
                        UnaryOp=Token::UnaryOp,
                        BinaryOp=Token::BinaryOp,
+                       PipeOp=Token::PipeOp,
                        AssignmentOp=Token::AssignmentOp,
                        Exception=Token::IgnoredTokenMask,
                };
@@ -158,13 +161,15 @@ class ELAM_EXPORT Expression
                ///parses tokens and splits them by comma
                void functionInit();
                ///helper for evaluate - evaluates a function
-               QVariant evalFunction();
+               QVariant evalFunction(bool doinsert=false,QVariant insert=QVariant());
                ///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();
+               ///helper for evaluate - evaluates a pipe
+               QVariant evalPipe();
 };
 
 ///makes expressions accessable to qDebug()
index b3dc05d..224550d 100644 (file)
@@ -20,11 +20,12 @@ class DPTR_CLASS_NAME(CharacterClassSettings):public SharedDPtr
                DPTR_NAME(){
                        operatorClass= "~!@#$%^&*-+=:<>?/";
                        literalClass="0123456789\'\"";
-                       nameClass= QPair<QString,QString>( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_",  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
+                       nameClass= QPair<QString,QString>( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_",  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.");
                        whitespaceClass=" \t\r\n\v";
                        parenthesesChars=QPair<QChar,QChar>('(',')');
                        assignmentChars=QPair<QChar,QChar>(0,'=');
                        commaChar=',';
+                       dotChar='.';
                }
                QString operatorClass;
                QString literalClass;
@@ -32,7 +33,7 @@ class DPTR_CLASS_NAME(CharacterClassSettings):public SharedDPtr
                QString whitespaceClass;
                QPair<QChar,QChar> parenthesesChars;
                QPair<QChar,QChar> assignmentChars;
-               QChar commaChar;
+               QChar commaChar,dotChar;
 };
 DEFINE_SHARED_DPTR(CharacterClassSettings);
 
@@ -83,6 +84,17 @@ void CharacterClassSettings::setCommaChar(QChar c)
        d->commaChar=c;
 }
 
+QChar CharacterClassSettings::dotChar() const
+{
+       return d->dotChar;
+}
+
+void CharacterClassSettings::setDotChar ( QChar c)
+{
+       d->dotChar=c;
+}
+
+
 QPair< QChar, QChar > CharacterClassSettings::parenthesesChars() const
 {
        return d->parenthesesChars;
@@ -147,6 +159,9 @@ bool CharacterClassSettings::isConsistent() const
        //check parentheses are different
        if(d->parenthesesChars.first==d->parenthesesChars.second)
                return false;
+       //check dot is part of the name class
+       if(!d->dotChar.isNull() && !d->nameClass.second.contains(d->dotChar))
+               return false;
        //check remaining special chars are not in any other class
        any+=d->operatorClass;
        if(any.contains(d->parenthesesChars.first) ||
@@ -232,4 +247,4 @@ bool CharacterClassSettings::isAssignment(QString op)const
 }
 
 //end of namespace
-};
\ No newline at end of file
+};
index 77b9715..f4f8738 100644 (file)
@@ -11,7 +11,7 @@
 
 namespace ELAM {
 
-ELAM_EXPORT QString versionInfo(){return "0.3 alpha";}
+ELAM_EXPORT QString versionInfo(){return "0.4 alpha";}
 ///////////////////////////////////////////////////////////////////////////////
 // Engine
 
@@ -48,6 +48,8 @@ class DPTR_CLASS_NAME(Engine):public DPtr
                };
                QList<CastFunc_s>casts;
                QList<int>primtypes;
+               QString pipe;
+               int pipeprio=0;
                Private():nextparserid(1){}
 };
 DEFINE_DPTR(Engine);
@@ -365,6 +367,15 @@ bool Engine::isAssignment(QString name) const
        return d->cclass.isAssignment(name);
 }
 
+bool Engine::isPipe ( QString name ) const
+{
+       if(name.isEmpty())return false;
+       //is it composed of operator chars?
+       if(!d->cclass.isOperator(name))return false;
+       //is it pipe?
+       return name==d->pipe;
+}
+
 bool Engine::hasBinaryOperator(QString name) const
 {
        if(!d->binary.contains(name))return false;
@@ -440,6 +451,26 @@ QStringList Engine::variableNames() const
        return d->vars.keys();
 }
 
+QString Engine::pipeOperator() const
+{
+       return d->pipe;
+}
+
+void Engine::setPipeOperator ( QString pipe )
+{
+       d->pipe=pipe;
+}
+
+int Engine::pipePrio() const
+{
+       return d->pipeprio;
+}
+
+void Engine::setPipePrio ( int p)
+{
+       d->pipeprio=p;
+}
+
 static QVariant reflectTypeId(const QList<QVariant>&args,Engine&)
 {
        if(args.size()!=1)
@@ -479,4 +510,4 @@ void Engine::configureReflection(Engine& e)
 
 
 //end of namespace
-};
\ No newline at end of file
+};
index e868454..13e1f49 100644 (file)
@@ -113,6 +113,7 @@ static void printtoken(QDebug&dbg,const Token&tok,int level)
                case Token::Variable:dbg<<"Variable";break;
                case Token::UnaryOp:dbg<<"Unary";break;
                case Token::BinaryOp:dbg<<"Binary";break;
+               case Token::PipeOp:dbg<<"Pipe";break;
                case Token::AssignmentOp:dbg<<"Assignment";break;
                default:dbg<<"Unknown:"<<(int)tok.type();break;
        }
@@ -208,6 +209,8 @@ inline QList<Token>Expression::classifyTokens(QList<Token> toks)
                        else{
                                if(eng->isAssignment(toks[i].content()))
                                        t.setSubType(Token::AssignmentOp);
+                               else if(eng->isPipe(toks[i].content()))
+                                       t.setSubType(Token::PipeOp);
                                else
                                        t.setSubType(Token::BinaryOp);
                        }
@@ -351,8 +354,8 @@ Expression::Expression(Engine* parent, const QList< Token >& toks)
        //search for lowest operator on the right
        int cprio=1000,cpos=-1;
        for(int i=0;i<d->tokens.size();i++){
-               if(d->tokens[i].type()==Token::BinaryOp){
-                       int oprio=d->parent->binaryOperatorPrio(d->tokens[i].content());
+               if(d->tokens[i].type()==Token::BinaryOp || d->tokens[i].type()==Token::PipeOp){
+                       int oprio=d->tokens[i].type()==Token::PipeOp?d->parent->pipePrio():d->parent->binaryOperatorPrio(d->tokens[i].content());
                        if(oprio<=cprio){
                                cprio=oprio;
                                cpos=i;
@@ -376,6 +379,15 @@ Expression::Expression(Engine* parent, const QList< Token >& toks)
                d->type=BinaryOp;
                d->subexpr<<Expression(parent,d->tokens.mid(0,cpos));
                d->subexpr<<Expression(parent,d->tokens.mid(cpos+1));
+       }else if(d->tokens[cpos].type()==Token::PipeOp){
+               d->type=PipeOp;
+               d->subexpr<<Expression(parent,d->tokens.mid(0,cpos));
+               d->subexpr<<Expression(parent,d->tokens.mid(cpos+1));
+               //for pipe the right part must be a function
+               if(d->subexpr[1].d->type!=Function){
+                       d->type=Exception;
+                       d->excep=ELAM::Exception(ELAM::Exception::OperationError, "right side of pipe must be a function", d->tokens[cpos].position());
+               }
        }else{
                d->type=UnaryOp;
                d->subexpr<<Expression(parent,d->tokens.mid(cpos+1));
@@ -426,6 +438,8 @@ QVariant Expression::evaluate()
                        return evalBinary();
                case AssignmentOp:
                        return evalAssign();
+               case PipeOp:
+                       return evalPipe();
                default:
                        return ELAM::Exception(ELAM::Exception::OperationError, "internal error: unknown expression type", position());
        }
@@ -459,6 +473,17 @@ QVariant Expression::evalBinary()
        return r;
 }
 
+QVariant Expression::evalPipe()
+{
+       //evaluate left subexpression
+       QVariant sub1=d->subexpr[0].evaluate();
+       if(sub1.userType()==ELAM::Exception::metaTypeId())
+               return sub1;
+       //manipulate right subexpression to insert argument, then return result
+       QVariant sub2=d->subexpr[1].evalFunction(true,sub1);
+       return sub2;
+}
+
 QVariant Expression::evalAssign()
 {
        QString un=d->tokens.value(d->oppos).content();
@@ -476,7 +501,7 @@ QVariant Expression::evalAssign()
 }
 
 
-QVariant Expression::evalFunction()
+QVariant Expression::evalFunction(bool doinsert,QVariant insert)
 {
        //basic checks
        QString fn=d->tokens[0].content();
@@ -485,6 +510,7 @@ QVariant Expression::evalFunction()
                return ELAM::Exception(ELAM::Exception::UnknownFunctionError, "unknown function", position());
        //gather arguments
        QList<QVariant>args;
+       if(doinsert)args<<insert;
        for(int i=0;i<d->subexpr.size();i++)
                args<<d->parent->autoCast(d->subexpr[i].evaluate());
        //execute
@@ -520,6 +546,7 @@ Position Expression::position() const
                        return d->tokens.value(0).position();
                case Expression::UnaryOp:
                case Expression::BinaryOp:
+               case Expression::PipeOp:
                case Expression::AssignmentOp:
                        return d->tokens.value(d->oppos).position();
                case Expression::Exception:
@@ -562,6 +589,9 @@ void Expression::printExpression(QDebug&dbg,const Expression&ex,int level)
                case Expression::BinaryOp:
                        dbg<<"BinaryOperator:sub1" <<ex.d->tokens.value(ex.d->oppos).content() <<"sub2";
                        break;
+               case Expression::PipeOp:
+                       dbg<<"PipeOperator: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;
@@ -599,4 +629,4 @@ QDebug& operator<<(QDebug dbg,const Expression&ex)
 }
 
 
-};
\ No newline at end of file
+};
index 1d1eb8f..4176897 100644 (file)
@@ -1,13 +1,11 @@
 TEMPLATE = app
 TARGET = evaltest
 QT -= gui
-CONFIG += testlib debug link_prl
+CONFIG += testlib debug link_prl c++11
 QT += testlib
-INCLUDEPATH += . ../../include
+INCLUDEPATH += . ../../include ../../../chester
 DEPENDPATH += $$INCLUDEPATH ../.. ../../src
-LIBS += -L../.. -lelam
+LIBS += -L../../../lib -lelam
 
 SOURCES += eval.cpp
 HEADERS += eval.h
-
-gcc { QMAKE_CXXFLAGS+=-std=c++11 }
index 449b31b..5df125f 100644 (file)
@@ -123,4 +123,22 @@ void ElamTest::emptyList()
        QCOMPARE(tl.size(),0);
 }
 
-QTEST_MAIN(ElamTest)
\ No newline at end of file
+void ElamTest::expression()
+{
+       IntEngine ie;
+       ie.setPipeOperator("$");
+       QString call;
+       ie.setFunction("incr",[&](const QList<QVariant>&lf,Engine&)->QVariant{call+="i";return lf[0].toInt()+1;});
+       ie.setFunction("double",[&](const QList<QVariant>&lf,Engine&)->QVariant{call+="d";return lf[0].toInt()*2;});
+       QString exs="a=346-345 $ incr $ double";
+       auto toks=ie.tokenize(exs);
+       Expression ex(&ie,toks);
+//     qDebug()<<toks;
+//     qDebug()<<ex;
+       QCOMPARE(ex.evaluate(),QVariant(4));
+       QCOMPARE(call,QString("id"));
+
+}
+
+
+QTEST_MAIN(ElamTest)
index 215de9d..a0d0269 100644 (file)
@@ -8,4 +8,5 @@ class ElamTest:public QObject
                void tokenizer();
                void stringLiteral();
                void emptyList();
+               void expression();
 };
index 50a126c..0ce73c2 100644 (file)
@@ -1,13 +1,11 @@
 TEMPLATE = app
 TARGET = parsertest
 QT -= gui
-CONFIG += testlib debug link_prl
+CONFIG += testlib debug link_prl c++11
 QT += testlib
-INCLUDEPATH += . ../../include
+INCLUDEPATH += . ../../include ../../../chester
 DEPENDPATH += $$INCLUDEPATH ../.. ../../src
-LIBS += -L../.. -lelam
+LIBS += -L../../../lib -lelam
 
 SOURCES += parser.cpp
 HEADERS += parser.h
-
-gcc { QMAKE_CXXFLAGS+=-std=c++11 }