core
core.*
.kdev*
+*.o
+*.obj
+moc_*
+elam/tests/eval/evaltest*
+elam/tests/parser/parsertest*
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 += \
INCLUDEPATH += . src include ../include/elam ../chester
DEPENDPATH += $$INCLUDEPATH
-
-gcc { QMAKE_CXXFLAGS+=-std=c++11 }
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
/** \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
*/
///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;
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
///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);
\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);
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
Parentheses=Token::Parentheses,
UnaryOp=Token::UnaryOp,
BinaryOp=Token::BinaryOp,
+ PipeOp=Token::PipeOp,
AssignmentOp=Token::AssignmentOp,
Exception=Token::IgnoredTokenMask,
};
///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()
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;
QString whitespaceClass;
QPair<QChar,QChar> parenthesesChars;
QPair<QChar,QChar> assignmentChars;
- QChar commaChar;
+ QChar commaChar,dotChar;
};
DEFINE_SHARED_DPTR(CharacterClassSettings);
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;
//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) ||
}
//end of namespace
-};
\ No newline at end of file
+};
namespace ELAM {
-ELAM_EXPORT QString versionInfo(){return "0.3 alpha";}
+ELAM_EXPORT QString versionInfo(){return "0.4 alpha";}
///////////////////////////////////////////////////////////////////////////////
// Engine
};
QList<CastFunc_s>casts;
QList<int>primtypes;
+ QString pipe;
+ int pipeprio=0;
Private():nextparserid(1){}
};
DEFINE_DPTR(Engine);
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;
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)
//end of namespace
-};
\ No newline at end of file
+};
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;
}
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);
}
//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;
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));
return evalBinary();
case AssignmentOp:
return evalAssign();
+ case PipeOp:
+ return evalPipe();
default:
return ELAM::Exception(ELAM::Exception::OperationError, "internal error: unknown expression type", position());
}
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();
}
-QVariant Expression::evalFunction()
+QVariant Expression::evalFunction(bool doinsert,QVariant insert)
{
//basic checks
QString fn=d->tokens[0].content();
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
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:
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;
}
-};
\ No newline at end of file
+};
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 }
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)
void tokenizer();
void stringLiteral();
void emptyList();
+ void expression();
};
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 }