From: Konrad Rosenbaum Date: Tue, 12 Jul 2016 16:23:32 +0000 (+0200) Subject: expand ELAM to understand pipes X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=d69844f3a7fc86a4a1e3d4d82c2c2f9eb2bf286d;p=web%2Fkonrad%2Ftaurus.git expand ELAM to understand pipes --- diff --git a/.gitignore b/.gitignore index 20615c6..affd0a8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,8 @@ Makefile* core core.* .kdev* +*.o +*.obj +moc_* +elam/tests/eval/evaltest* +elam/tests/parser/parsertest* diff --git a/elam/elam.pro b/elam/elam.pro index f751469..6d2d687 100644 --- a/elam/elam.pro +++ b/elam/elam.pro @@ -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 } diff --git a/elam/include/elamcharclass.h b/elam/include/elamcharclass.h index 5790921..fba121e 100644 --- a/elam/include/elamcharclass.h +++ b/elam/include/elamcharclass.h @@ -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 diff --git a/elam/include/elamengine.h b/elam/include/elamengine.h index 81480d0..6c4a545 100644 --- a/elam/include/elamengine.h +++ b/elam/include/elamengine.h @@ -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,QListorigin,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); diff --git a/elam/include/elamexpression.h b/elam/include/elamexpression.h index ed67a25..7802ee9 100644 --- a/elam/include/elamexpression.h +++ b/elam/include/elamexpression.h @@ -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() diff --git a/elam/src/elamcharclass.cpp b/elam/src/elamcharclass.cpp index b3dc05d..224550d 100644 --- a/elam/src/elamcharclass.cpp +++ b/elam/src/elamcharclass.cpp @@ -20,11 +20,12 @@ class DPTR_CLASS_NAME(CharacterClassSettings):public SharedDPtr DPTR_NAME(){ operatorClass= "~!@#$%^&*-+=:<>?/"; literalClass="0123456789\'\""; - nameClass= QPair( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); + nameClass= QPair( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."); whitespaceClass=" \t\r\n\v"; parenthesesChars=QPair('(',')'); assignmentChars=QPair(0,'='); commaChar=','; + dotChar='.'; } QString operatorClass; QString literalClass; @@ -32,7 +33,7 @@ class DPTR_CLASS_NAME(CharacterClassSettings):public SharedDPtr QString whitespaceClass; QPair parenthesesChars; QPair 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 +}; diff --git a/elam/src/elamengine.cpp b/elam/src/elamengine.cpp index 77b9715..f4f8738 100644 --- a/elam/src/elamengine.cpp +++ b/elam/src/elamengine.cpp @@ -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 }; QListcasts; QListprimtypes; + 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&args,Engine&) { if(args.size()!=1) @@ -479,4 +510,4 @@ void Engine::configureReflection(Engine& e) //end of namespace -}; \ No newline at end of file +}; diff --git a/elam/src/elamexpression.cpp b/elam/src/elamexpression.cpp index e868454..13e1f49 100644 --- a/elam/src/elamexpression.cpp +++ b/elam/src/elamexpression.cpp @@ -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 QListExpression::classifyTokens(QList 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;itokens.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<tokens.mid(0,cpos)); d->subexpr<tokens.mid(cpos+1)); + }else if(d->tokens[cpos].type()==Token::PipeOp){ + d->type=PipeOp; + d->subexpr<tokens.mid(0,cpos)); + d->subexpr<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<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 QListargs; + if(doinsert)args<subexpr.size();i++) args<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" <tokens.value(ex.d->oppos).content() <<"sub2"; break; + case Expression::PipeOp: + dbg<<"PipeOperator:sub1" <tokens.value(ex.d->oppos).content() <<"sub2"; + break; case Expression::AssignmentOp: dbg<<"Assignment:sub1" <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 +}; diff --git a/elam/tests/eval/eval.pro b/elam/tests/eval/eval.pro index 1d1eb8f..4176897 100644 --- a/elam/tests/eval/eval.pro +++ b/elam/tests/eval/eval.pro @@ -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 } diff --git a/elam/tests/parser/parser.cpp b/elam/tests/parser/parser.cpp index 449b31b..5df125f 100644 --- a/elam/tests/parser/parser.cpp +++ b/elam/tests/parser/parser.cpp @@ -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&lf,Engine&)->QVariant{call+="i";return lf[0].toInt()+1;}); + ie.setFunction("double",[&](const QList&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()<