From 073a9c13cc1bd99ee687a0970b6667cee7b7407c Mon Sep 17 00:00:00 2001 From: konrad Date: Wed, 17 Nov 2010 09:23:38 +0000 Subject: [PATCH] split engine files to make them easier to maintain git-svn-id: https://silmor.de/svn/softmagic/elam/trunk@632 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- src/elam.pro | 6 + src/elambinary.cpp | 98 ++++++++++ src/elambinary.h | 71 +++++++ src/elamcharclass.cpp | 233 +++++++++++++++++++++++ src/elamcharclass.h | 154 ++++++++++++++++ src/elamengine.cpp | 466 +++++------------------------------------------ src/elamengine.h | 268 +++------------------------ src/elamexpression.cpp | 27 +++ src/elamexpression.h | 9 +- src/elamfloatengine.cpp | 8 +- src/elamintengine.cpp | 44 ++++- src/elamunary.cpp | 122 ++++++++++++ src/elamunary.h | 75 ++++++++ 13 files changed, 911 insertions(+), 670 deletions(-) create mode 100644 src/elambinary.cpp create mode 100644 src/elambinary.h create mode 100644 src/elamcharclass.cpp create mode 100644 src/elamcharclass.h create mode 100644 src/elamunary.cpp create mode 100644 src/elamunary.h diff --git a/src/elam.pro b/src/elam.pro index e428bd5..4a2709d 100644 --- a/src/elam.pro +++ b/src/elam.pro @@ -11,6 +11,9 @@ RCC_DIR = .ctmp HEADERS += \ dptr.h \ elam.h \ + elamunary.h \ + elambinary.h \ + elamcharclass.h \ elamengine.h \ elamexpression.h \ elamvalue.h \ @@ -19,6 +22,9 @@ HEADERS += \ SOURCES += \ elamvalue.cpp \ + elamunary.cpp \ + elambinary.cpp \ + elamcharclass.cpp \ elamengine.cpp \ elamintengine.cpp \ elamexpression.cpp \ diff --git a/src/elambinary.cpp b/src/elambinary.cpp new file mode 100644 index 0000000..85a16f4 --- /dev/null +++ b/src/elambinary.cpp @@ -0,0 +1,98 @@ +// engine definition implementation +// +// (c) Konrad Rosenbaum, 2010 +// protected under the GNU LGPL v3 or at your option any newer + +#include "elamengine.h" +#include "elamvalue.h" + +#include + +namespace ELAM { +///////////////////////////////////////////////////////////////// +// Binary Operator + +class DPTR_CLASS_NAME(BinaryOperator):public SharedDPtr +{ + public: + QMap,BinaryOperatorCall>callmap; +}; +DEFINE_SHARED_DPTR(BinaryOperator) + + +BinaryOperator::BinaryOperator(const BinaryOperator& op) + :d(op.d) +{ +} + +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 diff --git a/src/elambinary.h b/src/elambinary.h new file mode 100644 index 0000000..055b749 --- /dev/null +++ b/src/elambinary.h @@ -0,0 +1,71 @@ +// main header +// +// (c) Konrad Rosenbaum, 2010 +// protected under the GNU LGPL v3 or at your option any newer + +#ifndef ELAM_ENGINE_BINARY_H +#define ELAM_ENGINE_BINARY_H + +#include +#include +#include + +#include "dptr.h" + +namespace ELAM { + +/** \brief pointer to a function wrapping a binary operator +\param op1 the left operand +\param op2 the right operand +\returns the result of the operation*/ +typedef QVariant (*BinaryOperatorCall)(const QVariant&op1,const QVariant&op2); + +/** \brief Wraps a particular binary operator. + +You can use the methods of this class to change the routines that handle the operator and the types on which it operates. Instances of this class are implicitly shared - meaning calls on a copy of an instance are also visible on the original and all other copies. +*/ +class BinaryOperator +{ + DECLARE_SHARED_DPTR(d); + public: + 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 > getTypeNames()const; + ///returns all combinations of type IDs that have a valid callback in this operator + QList > 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; +}; + +//end of namespace +}; + +#endif diff --git a/src/elamcharclass.cpp b/src/elamcharclass.cpp new file mode 100644 index 0000000..ed2f337 --- /dev/null +++ b/src/elamcharclass.cpp @@ -0,0 +1,233 @@ +// engine definition implementation +// +// (c) Konrad Rosenbaum, 2010 +// protected under the GNU LGPL v3 or at your option any newer + +#include "elamengine.h" +#include "elamvalue.h" + +#include + +namespace ELAM { + +///////////////////////////////////////////////////////////////// +// character classes + +class DPTR_CLASS_NAME(CharacterClassSettings):public SharedDPtr +{ + public: + DPTR_NAME(){ + operatorClass= "~!@#$%^&*-+=:<>?/"; + literalClass="0123456789\'\""; + nameClass= QPair( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); + whitespaceClass=" \t\r\n\v"; + parenthesesChars=QPair('(',')'); + assignmentChars=QPair(0,'='); + commaChar=','; + } + QString operatorClass; + QString literalClass; + QPair nameClass; + QString whitespaceClass; + QPair parenthesesChars; + QPair assignmentChars; + QChar commaChar; +}; +DEFINE_SHARED_DPTR(CharacterClassSettings); + +QString CharacterClassSettings::operatorClass() const +{ + return d->operatorClass; +} +void CharacterClassSettings::setOperatorClass(QString c) +{ + d->operatorClass=c; +} + +QPair CharacterClassSettings::nameClass() const +{ + return d->nameClass; +} +void CharacterClassSettings::setNameClass(QString f,QString s) +{ + d->nameClass.first=f; + d->nameClass.second=s; +} + +QString CharacterClassSettings::whitespaceClass() const +{ + return d->whitespaceClass; +} +void CharacterClassSettings::setWhitespaceClass(QString c) +{ + d->whitespaceClass=c; +} + +QPair< QChar, QChar > CharacterClassSettings::assignmentChars() const +{ + return d->assignmentChars; +} +void CharacterClassSettings::setAssignmentChars(QChar f, QChar s) +{ + d->assignmentChars.first=f; + d->assignmentChars.second=s; +} + +QChar CharacterClassSettings::commaChar() const +{ + return d->commaChar; +} +void CharacterClassSettings::setCommaChar(QChar c) +{ + d->commaChar=c; +} + +QPair< QChar, QChar > CharacterClassSettings::parenthesesChars() const +{ + return d->parenthesesChars; +} +void CharacterClassSettings::setParentheses(QChar f, QChar s) +{ + d->parenthesesChars.first=f; + d->parenthesesChars.second=s; +} + +QString CharacterClassSettings::literalStartClass() const +{ + return d->literalClass; +} +void CharacterClassSettings::setLiteralStartClass(QString c) +{ + d->literalClass=c; +} + +static bool strContainedIn(QString n,QString h) +{ + for(int i=0;inameClass.first,d->nameClass.second)) + return false; + //check that major classes do not overlap + //literals: operators, whitespace, start of names (may partially overlap with names) + if(strOverlaps(d->literalClass,d->operatorClass+d->whitespaceClass+d->nameClass.first)) + return false; + //operators: whitespace, literals, all of names + if(strOverlaps(d->operatorClass,d->literalClass+d->whitespaceClass+d->nameClass.second)) + return false; + //start of names: literals, operators, whitespace + if(strOverlaps(d->nameClass.first,d->operatorClass+d->whitespaceClass+d->literalClass)) + return false; + //all of names: operators, whitespace + if(strOverlaps(d->nameClass.second,d->operatorClass+d->whitespaceClass)) + return false; + //check assigment chars are not in any class (except operators) + QString any=d->literalClass+d->whitespaceClass+d->nameClass.second+d->literalClass; + if(any.contains(d->assignmentChars.first) || any.contains(d->assignmentChars.second)) + return false; + if(d->assignmentChars.first!=0 && !d->operatorClass.contains(d->assignmentChars.first)) + return false; + if(d->assignmentChars.second!=0 && !d->operatorClass.contains(d->assignmentChars.second)) + return false; + //check parentheses are different + if(d->parenthesesChars.first==d->parenthesesChars.second) + return false; + //check remaining special chars are not in any other class + any+=d->operatorClass; + if(any.contains(d->parenthesesChars.first) || + any.contains(d->parenthesesChars.second) || + any.contains(d->commaChar)) + return false; + //all ok + return true; +} + +Token::Type CharacterClassSettings::charType(QChar c, ELAM::Token::Type otype) const +{ + //special char? + if(c==d->parenthesesChars.first)return Token::ParOpen; + if(c==d->parenthesesChars.second)return Token::ParClose; + if(c==d->commaChar)return Token::Comma; + //is it a name? + if(otype==Token::Name){ + //is this a continuation + if(d->nameClass.second.contains(c))return Token::Name; + }else{ + //is this the start of a name + if(d->nameClass.first.contains(c))return Token::Name; + } + //is it the start of a literal? + if(d->literalClass.contains(c))return Token::Literal; + //operator? + if(d->operatorClass.contains(c))return Token::Operator; + //whitespace? + if(d->whitespaceClass.contains(c))return Token::Whitespace; + //must be invalid noise + return Token::Invalid; +} + +bool CharacterClassSettings::isAssignment(QString op)const +{ + //sanity checks: size + if(op.size()<1)return false; + if(d->assignmentChars.first!=0 && d->assignmentChars.second!=0) + if(op.size()<2)return false; + //check we have assignments at all + if(d->assignmentChars.first==0 && d->assignmentChars.second==0)return false; + //check it is assignment + if(d->assignmentChars.first!=0 && op[0]!=d->assignmentChars.first) + return false; + if(d->assignmentChars.second!=0 && op[op.size()-1]!=d->assignmentChars.second) + return false; + //check it is an operator + for(int i=0;ioperatorClass.contains(op[i])) + return false; + //passed everything + return true; +} + +bool CharacterClassSettings::isSimpleAssignment ( QString op) const +{ + if(op.size()<1)return false; + QString c; + if(d->assignmentChars.first!=0)c+=d->assignmentChars.first; + if(d->assignmentChars.second!=0)c+=d->assignmentChars.second; + return op==c; +} + + +bool CharacterClassSettings::isName(QString n) const +{ + if(n.size()<1)return false; + if(!d->nameClass.first.contains(n[0]))return false; + for(int i=0;inameClass.second.contains(n[i]))return false; + return true; +} + +bool CharacterClassSettings::isOperator(QString op) const +{ + for(int i=0;ioperatorClass.contains(op[i])) + return false; + return true; +} + + +//end of namespace +}; \ No newline at end of file diff --git a/src/elamcharclass.h b/src/elamcharclass.h new file mode 100644 index 0000000..56e4886 --- /dev/null +++ b/src/elamcharclass.h @@ -0,0 +1,154 @@ +// main header +// +// (c) Konrad Rosenbaum, 2010 +// protected under the GNU LGPL v3 or at your option any newer + +#ifndef ELAM_ENGINE_CCLASS_H +#define ELAM_ENGINE_CCLASS_H + +#include +#include +#include + +#include "elamexpression.h" + +#include "dptr.h" + +namespace ELAM { + +/** \brief This class holds the character classes used by an Engine. + +\see Engine::characterClasses() + +There are three major classes of chracters: + - names + - names can be variables, constants, functions + - the names class has two sub-classes + - start-of-names is characters that may start a name, usually letters and underscores + - all-of-names is characters that may be contained anywhere in names, this class must include the complete start of names class + - operators + - any sequence of these characters is interpreted as unary or binary operator + - whitespace + - whitespace can separate other tokens, but is ignored itself + +None of these three classes may overlap. There are several more minor classes: + - literals + - the character class actually only contains characters that can start a literal, the continuation and end of literals is determines by a callback routine + - characters in this class may overlap with names, but must not overlap with operators, whitespace and start-of-names + - parentheses + - are two distinct characters that group expressions, none of them must be contained in any other class + - comma + - a single character separating arguments in functions, the comma character must not be contained in any other class + - assignment + - one or two characters that denote an assignment + - both are optional, both must be contained in operators + - one of the characters, if it exists, is the first character of an assignment + - the other of the characters, if it exists, is the last character of an assignment + - just those two characters are the simple assignment operator + - both surrounding another operator combine the operation with assignment (e.g. a+=b is equivalent to a=a+b) + +*/ +class CharacterClassSettings +{ + DECLARE_SHARED_DPTR(d) + public: + ///class of operator characters + QString operatorClass()const; + ///sets the class of operator characters + void setOperatorClass(QString); + + /**returns the two name sub-classes + nameClass().first refers to start characters, + nameClass().second refers to all characters that can turn up anywhere in names*/ + QPair nameClass()const; + /**sets the two sub-classes of names + \param startchars are characters that can start a name + \param allchars are characters that can turn up anywhere in names, allchars must include all characters from startchars*/ + void setNameClass(QString startchars,QString allchars); + + ///returns all characters that are regarded as whitespace + QString whitespaceClass()const; + ///sets all characters that are regarded as whitespace + void setWhitespaceClass(QString); + + ///returns all characters that start a literal, normally digits ' and " + QString literalStartClass()const; + ///sets characters that can start a literal + void setLiteralStartClass(QString); + + ///returns the opening (first) and closing (second) character of parentheses + QPair parenthesesChars()const; + /**sets the characters used for parentheses + \param open the character that opens/begins a parentheses structure, normally "(" + \param close the character that closes/ends a parentheses structure, normally ")" + + The parentheses characters must not be included in any other class.*/ + void setParentheses(QChar open,QChar close); + + /** \brief returns the characters that designate an assignment + + - assignmentChars().first is the character that starts an assignment operator, + - assignmentChars().second is the character that ends it.*/ + QPair assignmentChars()const; + + /** sets the characters used for assignment operators + \param start if not '\0' the character that marks the start of an assignment + \param end if not '\0' the character that marks the end of an assignment + + The combination of both characters along (without whitespace) is the direct assignment operator. In automatic assignment operator mode any operator that starts with the start character and ends with end character is regarded as an implicit assignment. + + In the default configuration the start character is not set ('\0') and the end character is '=', so "a=1" will assign the value "1" to the variable "a" and "a += 1" is equivalent to "a = a + 1". + + You can turn this automatism around by defining a start character only (e.g. start='=' and end='\0'). Then the assignment would still be "a = 1", but the combination of assignment and "+" would become "a =+ 1". + + If you define both characters then both must be present in assignments. For example with start=':' and end='=' then assignment becomes "a:=1" and assignment with "+" becomes "a :+= 1". + + If set, both characters must be part of the operator class. + + If both start and end are '\0' it will be impossible to make assignments*/ + void setAssignmentChars(QChar start,QChar end); + + ///returns the character used as a comma (separator of function arguments) + QChar commaChar()const; + /**sets the character used as comma + + The character must not be part of any other class.*/ + void setCommaChar(QChar); + + /**true if the settings are internally consistent + + The character class settings are consistent if all constraints of class inclusion and exclusion are fullfilled.*/ + bool isConsistent()const; + + /**returns the type of token the character belongs to + \param ch the character to be checked + \param oldtype the type the previous character belongs to (set to Invalid if this is the first character) + + Compares the character with the known settings for character classes and returns its probable token type. The old type of the previous character is necessary to check for context sensitive rules. + + \returns the type of this character: + - Invalid is returned if the character does not match any class, parsing should stop here + - If Literal is returned the calling engine must use specialized literal checking functions to find the end of the literal, the next character checke with this routine should be the first one behind the end of the literal + - Assignment characters are returned as Operator class + - Whitespace class characters should be ignored + - Special character types (Par*, Comma) must be tokenized separately + - Any other type (Name, Operator) must be concatenated until the return type changes + */ + Token::Type charType(QChar ch,Token::Type oldtype)const; + + ///returns true if the string contains any assignment operator + bool isAssignment(QString)const; + ///returns true if the string contains exactly the simple assignment operator + bool isSimpleAssignment(QString)const; + + ///returns true if the string can be interpreted as name + bool isName(QString)const; + + ///returns true if the string can be interpreted as operator + bool isOperator(QString)const; +}; + +//end of namespace +}; + +#endif diff --git a/src/elamengine.cpp b/src/elamengine.cpp index 7cb5de8..560e889 100644 --- a/src/elamengine.cpp +++ b/src/elamengine.cpp @@ -29,7 +29,13 @@ class DPTR_CLASS_NAME(Engine):public DPtr }; QListparsers; QMapunary; - QMapbinary; + struct BinaryOperator_s { + BinaryOperator_s(int p=1){prio=p;} + BinaryOperator_s(BinaryOperator o,int p){op=o;prio=p;} + BinaryOperator op; + int prio; + }; + QMapbinary; }; DEFINE_DPTR(Engine); @@ -45,20 +51,17 @@ QVariant Engine::evaluate(QString ex) QVariant Engine::evaluate(Expression ex) { - Q_UNUSED(ex); - //TODO: implement - return QVariant(); + return ex.evaluate(); } Expression Engine::expression(QString s) { - return Expression(tokenize(s)); + return Expression(this,tokenize(s)); } -Expression Engine::expression(QList< Token > ) +Expression Engine::expression(QList< Token > tok) { - //TODO: implement parser - return Expression(); + return Expression(this,tok); } QList< Token > Engine::tokenize(QString ex) @@ -285,433 +288,56 @@ 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 - -class DPTR_CLASS_NAME(CharacterClassSettings):public SharedDPtr -{ - public: - DPTR_NAME(){ - operatorClass= "~!@#$%^&*-+=:<>?/"; - literalClass="0123456789\'\""; - nameClass= QPair( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); - whitespaceClass=" \t\r\n\v"; - parenthesesChars=QPair('(',')'); - assignmentChars=QPair(0,'='); - commaChar=','; - } - QString operatorClass; - QString literalClass; - QPair nameClass; - QString whitespaceClass; - QPair parenthesesChars; - QPair assignmentChars; - QChar commaChar; -}; -DEFINE_SHARED_DPTR(CharacterClassSettings); - -QString CharacterClassSettings::operatorClass() const -{ - return d->operatorClass; -} -void CharacterClassSettings::setOperatorClass(QString c) -{ - d->operatorClass=c; -} - -QPair CharacterClassSettings::nameClass() const +BinaryOperator Engine::binaryOperator ( QString name,int prio,PriorityMatch match ) { - return d->nameClass; -} -void CharacterClassSettings::setNameClass(QString f,QString s) -{ - d->nameClass.first=f; - d->nameClass.second=s; -} - -QString CharacterClassSettings::whitespaceClass() const -{ - return d->whitespaceClass; -} -void CharacterClassSettings::setWhitespaceClass(QString c) -{ - d->whitespaceClass=c; -} - -QPair< QChar, QChar > CharacterClassSettings::assignmentChars() const -{ - return d->assignmentChars; -} -void CharacterClassSettings::setAssignmentChars(QChar f, QChar s) -{ - d->assignmentChars.first=f; - d->assignmentChars.second=s; -} - -QChar CharacterClassSettings::commaChar() const -{ - return d->commaChar; -} -void CharacterClassSettings::setCommaChar(QChar c) -{ - d->commaChar=c; -} - -QPair< QChar, QChar > CharacterClassSettings::parenthesesChars() const -{ - return d->parenthesesChars; -} -void CharacterClassSettings::setParentheses(QChar f, QChar s) -{ - d->parenthesesChars.first=f; - d->parenthesesChars.second=s; -} - -QString CharacterClassSettings::literalStartClass() const -{ - return d->literalClass; -} -void CharacterClassSettings::setLiteralStartClass(QString c) -{ - d->literalClass=c; -} - -static bool strContainedIn(QString n,QString h) -{ - for(int i=0;inameClass.first,d->nameClass.second)) - return false; - //check that major classes do not overlap - //literals: operators, whitespace, start of names (may partially overlap with names) - if(strOverlaps(d->literalClass,d->operatorClass+d->whitespaceClass+d->nameClass.first)) - return false; - //operators: whitespace, literals, all of names - if(strOverlaps(d->operatorClass,d->literalClass+d->whitespaceClass+d->nameClass.second)) - return false; - //start of names: literals, operators, whitespace - if(strOverlaps(d->nameClass.first,d->operatorClass+d->whitespaceClass+d->literalClass)) - return false; - //all of names: operators, whitespace - if(strOverlaps(d->nameClass.second,d->operatorClass+d->whitespaceClass)) - return false; - //check assigment chars are not in any class (except operators) - QString any=d->literalClass+d->whitespaceClass+d->nameClass.second+d->literalClass; - if(any.contains(d->assignmentChars.first) || any.contains(d->assignmentChars.second)) - return false; - if(d->assignmentChars.first!=0 && !d->operatorClass.contains(d->assignmentChars.first)) - return false; - if(d->assignmentChars.second!=0 && !d->operatorClass.contains(d->assignmentChars.second)) - return false; - //check parentheses are different - if(d->parenthesesChars.first==d->parenthesesChars.second) - return false; - //check remaining special chars are not in any other class - any+=d->operatorClass; - if(any.contains(d->parenthesesChars.first) || - any.contains(d->parenthesesChars.second) || - any.contains(d->commaChar)) - return false; - //all ok - return true; -} - -Token::Type CharacterClassSettings::charType(QChar c, ELAM::Token::Type otype) const -{ - //special char? - if(c==d->parenthesesChars.first)return Token::ParOpen; - if(c==d->parenthesesChars.second)return Token::ParClose; - if(c==d->commaChar)return Token::Comma; - //is it a name? - if(otype==Token::Name){ - //is this a continuation - if(d->nameClass.second.contains(c))return Token::Name; - }else{ - //is this the start of a name - if(d->nameClass.first.contains(c))return Token::Name; + //correct prio + if(prio<1)prio=1; + if(prio>99)prio=99; + //if new: declare success + if(!d->binary.contains(name)){ + d->binary.insert(name,Private::BinaryOperator_s(prio)); + return d->binary[name].op; } - //is it the start of a literal? - if(d->literalClass.contains(c))return Token::Literal; - //operator? - if(d->operatorClass.contains(c))return Token::Operator; - //whitespace? - if(d->whitespaceClass.contains(c))return Token::Whitespace; - //must be invalid noise - return Token::Invalid; -} - -bool CharacterClassSettings::isAssignment(QString op)const -{ - //sanity checks: size - if(op.size()<1)return false; - if(d->assignmentChars.first!=0 && d->assignmentChars.second!=0) - if(op.size()<2)return false; - //check we have assignments at all - if(d->assignmentChars.first==0 && d->assignmentChars.second==0)return false; - //check it is assignment - if(d->assignmentChars.first!=0 && op[0]!=d->assignmentChars.first) - return false; - if(d->assignmentChars.second!=0 && op[op.size()-1]!=d->assignmentChars.second) - return false; - //check it is an operator - for(int i=0;ioperatorClass.contains(op[i])) - return false; - //passed everything - return true; -} - -bool CharacterClassSettings::isSimpleAssignment ( QString op) const -{ - if(op.size()<1)return false; - QString c; - if(d->assignmentChars.first!=0)c+=d->assignmentChars.first; - if(d->assignmentChars.second!=0)c+=d->assignmentChars.second; - return op==c; -} - - -bool CharacterClassSettings::isName(QString n) const -{ - if(n.size()<1)return false; - if(!d->nameClass.first.contains(n[0]))return false; - for(int i=0;inameClass.second.contains(n[i]))return false; - return true; -} - -bool CharacterClassSettings::isOperator(QString op) const -{ - for(int i=0;ioperatorClass.contains(op[i])) - return false; - return true; -} - - -///////////////////////////////////////////////////////////////// -// Unary Operator - -class DPTR_CLASS_NAME(UnaryOperator):public SharedDPtr -{ - public: - QMapcallmap; -}; -DEFINE_SHARED_DPTR(UnaryOperator) - - -UnaryOperator::UnaryOperator(const UnaryOperator& op) - :d(op.d) -{ -} - -UnaryOperator& UnaryOperator::UnaryOperator::operator=(const ELAM::UnaryOperator& op) -{ - d=op.d; - return *this; -} - - -UnaryOperator::UnaryOperator() -{ -} - -UnaryOperator::~UnaryOperator() -{ -} - - -QVariant UnaryOperator::execute(const QVariant& op) const -{ - if(d->callmap.size()==0) - return qVariantFromValue(Exception(Exception::UnknownOperatorError)); - //search for direct match - if(d->callmap.contains(op.type())) - return d->callmap[op.type()](op); - //search for fallback - int any=AnyType::metaTypeId(); - if(d->callmap.contains(any)) - return d->callmap[any](op); - return qVariantFromValue(Exception(Exception::TypeMismatchError)); -} - -QList< int > UnaryOperator::getTypeIds() const -{ - return d->callmap.keys(); -} - -QStringList UnaryOperator::getTypeNames() const -{ - QStringList ret; - QListti=d->callmap.keys(); - for(int i=0;icallmap.contains(type)) - return d->callmap[type]; - if(d->callmap.contains(AnyType::metaTypeId())) - return d->callmap[AnyType::metaTypeId()]; - return 0; -} - -void UnaryOperator::setCallback(UnaryOperatorCall callback, int type) -{ - if(type==QVariant::Invalid)return; - if(callback==0){ - d->callmap.remove(type); - return; + //check whether prio matches + if(d->binary[name].prio==prio) + return d->binary[name].op; + //check matching mode + switch(match){ + case OverridePrio: + //change prio + d->binary[name].prio=prio; + //fall through + case IgnoreMismatch: + //return original op + return d->binary[name].op; + break; + default://FailIfMismatch + //return fake + return BinaryOperator(); + break; } - d->callmap.insert(type,callback); -} - -void UnaryOperator::setCallback(UnaryOperatorCall callback, QString type) -{ - setCallback(callback,QVariant::nameToType(type.toAscii().data())); -} - -void UnaryOperator::removeCallback(UnaryOperatorCall cb) -{ - if(cb==0)return; - QListk=d->callmap.keys(cb); - for(int i=0;icallmap.remove(k[i]); } -void UnaryOperator::removeCallback(QString t) +int Engine::binaryOperatorPrio(QString name) { - removeCallback(QVariant::nameToType(t.toAscii().data())); + if(d->binary.contains(name)) + return d->binary[name].prio; + return -1; } -void UnaryOperator::removeCallback(int t) +void Engine::setBinaryOperatorPrio(QString name, int prio) { - d->callmap.remove(t); + binaryOperator(name,prio,OverridePrio); } -///////////////////////////////////////////////////////////////// -// Binary Operator - -class DPTR_CLASS_NAME(BinaryOperator):public SharedDPtr -{ - public: - QMap,BinaryOperatorCall>callmap; -}; -DEFINE_SHARED_DPTR(BinaryOperator) - -BinaryOperator::BinaryOperator(const BinaryOperator& op) - :d(op.d) -{ -} - -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 ) +UnaryOperator Engine::unaryOperator ( QString name ) { - //TODO: implement - + if(!d->unary.contains(name)) + d->unary.insert(name,UnaryOperator()); + return d->unary[name]; } - //end of namespace }; \ No newline at end of file diff --git a/src/elamengine.h b/src/elamengine.h index 572c206..1a4de00 100644 --- a/src/elamengine.h +++ b/src/elamengine.h @@ -11,116 +11,14 @@ #include #include "elamexpression.h" +#include "elamunary.h" +#include "elambinary.h" +#include "elamcharclass.h" #include "dptr.h" namespace ELAM { -/** \brief pointer to a function wrapping an unary operator -\param op the operand to be worked on -\returns the result of the operation*/ -typedef QVariant (*UnaryOperatorCall)(const QVariant&op); - -/** \brief Wraps a particular unary operator. - -You can use the methods of this class to change the routines that handle the operator and the types on which it operates. Instances of this class are implicitly shared - meaning calls on a copy of an instance are also visible on the original and all other copies. -*/ -class UnaryOperator -{ - DECLARE_SHARED_DPTR(d); - public: - /**copy constructor, - the instance will access the exact same operator as the original, any setting that is done in the copy is also done in the original and all other copies.*/ - UnaryOperator(const UnaryOperator&); - ///instantiates an empty operator - UnaryOperator(); - ///deletes and operator - ~UnaryOperator(); - - /**the operator becomes a shared copy of op and abandones its old link*/ - UnaryOperator& operator=(const UnaryOperator&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(UnaryOperatorCall callback,QString type); - /**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(UnaryOperatorCall callback,int type); - /**returns the callback function attached to the type or NULL if there is none*/ - UnaryOperatorCall getCallback(QString type)const; - /**returns the callback function attached to the type or NULL if there is none*/ - UnaryOperatorCall getCallback(int type)const; - - /**removes all types attached to this callback from the operator*/ - void removeCallback(UnaryOperatorCall); - ///removes the type from this operators list - void removeCallback(QString); - ///removes the type from this operators list - void removeCallback(int); - - ///returns all type names that have a valid callback in this operator - QStringList getTypeNames()const; - ///returns all type IDs that have a valid callback in this operator - QList 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; -}; - -/** \brief pointer to a function wrapping a binary operator -\param op1 the left operand -\param op2 the right operand -\returns the result of the operation*/ -typedef QVariant (*BinaryOperatorCall)(const QVariant&op1,const QVariant&op2); - -/** \brief Wraps a particular binary operator. - -You can use the methods of this class to change the routines that handle the operator and the types on which it operates. Instances of this class are implicitly shared - meaning calls on a copy of an instance are also visible on the original and all other copies. -*/ -class BinaryOperator -{ - DECLARE_SHARED_DPTR(d); - public: - 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 > getTypeNames()const; - ///returns all combinations of type IDs that have a valid callback in this operator - QList > 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 \param args the list of arguments @@ -145,138 +43,6 @@ If the parser does not find a valid literal according to its rules it must retur */ typedef QPair (*LiteralParser)(const QString&expr,Engine&engine,int start); -/** \brief This class holds the character classes used by an Engine. - -\see Engine::characterClasses() - -There are three major classes of chracters: - - names - - names can be variables, constants, functions - - the names class has two sub-classes - - start-of-names is characters that may start a name, usually letters and underscores - - all-of-names is characters that may be contained anywhere in names, this class must include the complete start of names class - - operators - - any sequence of these characters is interpreted as unary or binary operator - - whitespace - - whitespace can separate other tokens, but is ignored itself - -None of these three classes may overlap. There are several more minor classes: - - literals - - the character class actually only contains characters that can start a literal, the continuation and end of literals is determines by a callback routine - - characters in this class may overlap with names, but must not overlap with operators, whitespace and start-of-names - - parentheses - - are two distinct characters that group expressions, none of them must be contained in any other class - - comma - - a single character separating arguments in functions, the comma character must not be contained in any other class - - assignment - - one or two characters that denote an assignment - - both are optional, both must be contained in operators - - one of the characters, if it exists, is the first character of an assignment - - the other of the characters, if it exists, is the last character of an assignment - - just those two characters are the simple assignment operator - - both surrounding another operator combine the operation with assignment (e.g. a+=b is equivalent to a=a+b) - -*/ -class CharacterClassSettings -{ - DECLARE_SHARED_DPTR(d) - public: - ///class of operator characters - QString operatorClass()const; - ///sets the class of operator characters - void setOperatorClass(QString); - - /**returns the two name sub-classes - nameClass().first refers to start characters, - nameClass().second refers to all characters that can turn up anywhere in names*/ - QPair nameClass()const; - /**sets the two sub-classes of names - \param startchars are characters that can start a name - \param allchars are characters that can turn up anywhere in names, allchars must include all characters from startchars*/ - void setNameClass(QString startchars,QString allchars); - - ///returns all characters that are regarded as whitespace - QString whitespaceClass()const; - ///sets all characters that are regarded as whitespace - void setWhitespaceClass(QString); - - ///returns all characters that start a literal, normally digits ' and " - QString literalStartClass()const; - ///sets characters that can start a literal - void setLiteralStartClass(QString); - - ///returns the opening (first) and closing (second) character of parentheses - QPair parenthesesChars()const; - /**sets the characters used for parentheses - \param open the character that opens/begins a parentheses structure, normally "(" - \param close the character that closes/ends a parentheses structure, normally ")" - - The parentheses characters must not be included in any other class.*/ - void setParentheses(QChar open,QChar close); - - /** \brief returns the characters that designate an assignment - - - assignmentChars().first is the character that starts an assignment operator, - - assignmentChars().second is the character that ends it.*/ - QPair assignmentChars()const; - - /** sets the characters used for assignment operators - \param start if not '\0' the character that marks the start of an assignment - \param end if not '\0' the character that marks the end of an assignment - - The combination of both characters along (without whitespace) is the direct assignment operator. In automatic assignment operator mode any operator that starts with the start character and ends with end character is regarded as an implicit assignment. - - In the default configuration the start character is not set ('\0') and the end character is '=', so "a=1" will assign the value "1" to the variable "a" and "a += 1" is equivalent to "a = a + 1". - - You can turn this automatism around by defining a start character only (e.g. start='=' and end='\0'). Then the assignment would still be "a = 1", but the combination of assignment and "+" would become "a =+ 1". - - If you define both characters then both must be present in assignments. For example with start=':' and end='=' then assignment becomes "a:=1" and assignment with "+" becomes "a :+= 1". - - If set, both characters must be part of the operator class. - - If both start and end are '\0' it will be impossible to make assignments*/ - void setAssignmentChars(QChar start,QChar end); - - ///returns the character used as a comma (separator of function arguments) - QChar commaChar()const; - /**sets the character used as comma - - The character must not be part of any other class.*/ - void setCommaChar(QChar); - - /**true if the settings are internally consistent - - The character class settings are consistent if all constraints of class inclusion and exclusion are fullfilled.*/ - bool isConsistent()const; - - /**returns the type of token the character belongs to - \param ch the character to be checked - \param oldtype the type the previous character belongs to (set to Invalid if this is the first character) - - Compares the character with the known settings for character classes and returns its probable token type. The old type of the previous character is necessary to check for context sensitive rules. - - \returns the type of this character: - - Invalid is returned if the character does not match any class, parsing should stop here - - If Literal is returned the calling engine must use specialized literal checking functions to find the end of the literal, the next character checke with this routine should be the first one behind the end of the literal - - Assignment characters are returned as Operator class - - Whitespace class characters should be ignored - - Special character types (Par*, Comma) must be tokenized separately - - Any other type (Name, Operator) must be concatenated until the return type changes - */ - Token::Type charType(QChar ch,Token::Type oldtype)const; - - ///returns true if the string contains any assignment operator - bool isAssignment(QString)const; - ///returns true if the string contains exactly the simple assignment operator - bool isSimpleAssignment(QString)const; - - ///returns true if the string can be interpreted as name - bool isName(QString)const; - - ///returns true if the string can be interpreted as operator - bool isOperator(QString)const; -}; - /**The calculation engine of . Instances of this class can be configured to represent a specific system of functions and operators. @@ -360,9 +126,33 @@ class Engine:public QObject \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); + /**Matching mode for priority when overwriting an operator, see binaryOperator + + The matching mode has no effect if the operator is new or the priorities match. In those cases binaryOperator always returns the operator object corresponding to the given name for this engine. + + The matching mode changes behavior if the operator exists and its priority is different from the one given in the call. + */ + enum PriorityMatch{ + ///if the operator is new: use the given priority, otherwise leave priority as is ignoring the one given in the call, this is the default + IgnoreMismatch=0, + ///override the priority of the existing operator, using the priority given in the call + OverridePrio=1, + ///returns a dummy operator making it impossible to overwrite the existing operator + FailIfMismatch=2, + }; + /** \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); + \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 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 + */ + BinaryOperator binaryOperator(QString name,int prio=1,PriorityMatch match=IgnoreMismatch); + + ///returns the priority of the operator, or -1 if the operator does not exist + int binaryOperatorPrio(QString name); + ///sets/overrides the priority of an operator, creating the operator if it does not exist yet + void setBinaryOperatorPrio(QString name,int prio); public slots: ///simply parses an expression string into an object Expression expression(QString); diff --git a/src/elamexpression.cpp b/src/elamexpression.cpp index e847457..60e4ca5 100644 --- a/src/elamexpression.cpp +++ b/src/elamexpression.cpp @@ -4,6 +4,9 @@ namespace ELAM { +/////////////////////////////////////////////////////////////////////////////// +// Token + class DPTR_CLASS_NAME(Token):public DPtr { public: @@ -58,4 +61,28 @@ QDebug&operator<<(QDebug&dbg,const Token&tok) return dbg.space(); } +/////////////////////////////////////////////////////////////////////////////// +// Expression + +class DPTR_CLASS_NAME(Expression):public SharedDPtr +{ +}; +DEFINE_SHARED_DPTR(Expression); + +Expression::Expression() +{ + +} +Expression::Expression(Engine* parent, const QList< Token >& tokens) +{ + +} + +QVariant Expression::evaluate() +{ + return QVariant(); +} + + + }; \ No newline at end of file diff --git a/src/elamexpression.h b/src/elamexpression.h index d885ab8..75650e7 100644 --- a/src/elamexpression.h +++ b/src/elamexpression.h @@ -55,8 +55,10 @@ class Token QDebug& operator<<(QDebug&,const Token&); +class Engine; class Expression { + DECLARE_SHARED_DPTR(d) public: enum Type { Literal, @@ -66,9 +68,12 @@ class Expression Parentheses, UnaryOp, BinaryOp, + Exception, }; - Expression(){} - Expression(const QList&){} + Expression(); + Expression(Engine*parent,const QList&tokens); + + QVariant evaluate(); }; //end of namespace diff --git a/src/elamfloatengine.cpp b/src/elamfloatengine.cpp index b2bc0ea..5d8a904 100644 --- a/src/elamfloatengine.cpp +++ b/src/elamfloatengine.cpp @@ -123,16 +123,16 @@ void FloatEngine::configureFloatEngine(ELAM::Engine& eng) eng.unaryOperator("-").setCallback(floatMinus,fid); eng.unaryOperator("+").setCallback(floatPlus,fid); //binary - eng.binaryOperator("-").setCallback(floatMinus,fid,fid); + eng.binaryOperator("-",80).setCallback(floatMinus,fid,fid); eng.binaryOperator("-").setCallback(floatMinus,iid,fid); eng.binaryOperator("-").setCallback(floatMinus,fid,iid); - eng.binaryOperator("+").setCallback(floatAdd,fid,fid); + eng.binaryOperator("+",80).setCallback(floatAdd,fid,fid); eng.binaryOperator("+").setCallback(floatAdd,iid,fid); eng.binaryOperator("+").setCallback(floatAdd,fid,iid); - eng.binaryOperator("*").setCallback(floatMult,fid,fid); + eng.binaryOperator("*",90).setCallback(floatMult,fid,fid); eng.binaryOperator("*").setCallback(floatMult,iid,fid); eng.binaryOperator("*").setCallback(floatMult,fid,iid); - eng.binaryOperator("/").setCallback(floatDiv,fid,fid); + eng.binaryOperator("/",90).setCallback(floatDiv,fid,fid); eng.binaryOperator("/").setCallback(floatDiv,iid,fid); eng.binaryOperator("/").setCallback(floatDiv,fid,iid); //cast diff --git a/src/elamintengine.cpp b/src/elamintengine.cpp index 35e1e42..9ab9f3c 100644 --- a/src/elamintengine.cpp +++ b/src/elamintengine.cpp @@ -44,16 +44,17 @@ static QVariant intFunc(const QList&lf) return lf[0].toLongLong(); } +//unary static QVariant intPlus(const QVariant&o) { return o; } - static QVariant intMinus(const QVariant&o) { return -o.toLongLong(); } +//additive static QVariant intAdd(const QVariant&o1,const QVariant&o2) { return o1.toLongLong()+o2.toLongLong(); @@ -62,6 +63,8 @@ static QVariant intMinus(const QVariant&o1,const QVariant&o2) { return o1.toLongLong()-o2.toLongLong(); } + +//multiplicative static QVariant intMult(const QVariant&o1,const QVariant&o2) { return o1.toLongLong()*o2.toLongLong(); @@ -73,6 +76,32 @@ static QVariant intDiv(const QVariant&o1,const QVariant&o2) return Exception(Exception::OperationError,"division by zero"); return o1.toLongLong()/l2; } +static QVariant intMod(const QVariant&o1,const QVariant&o2) +{ + qlonglong l2=o2.toLongLong(); + if(l2==0) + return Exception(Exception::OperationError,"division by zero"); + return o1.toLongLong()%l2; +} + +//bitwise +static QVariant intAnd(const QVariant&o1,const QVariant&o2) +{ + return o1.toLongLong()&o2.toLongLong(); +} +static QVariant intOr(const QVariant&o1,const QVariant&o2) +{ + return o1.toLongLong()|o2.toLongLong(); +} +static QVariant intXor(const QVariant&o1,const QVariant&o2) +{ + return o1.toLongLong()^o2.toLongLong(); +} +static QVariant intNot(const QVariant&o) +{ + return ~o.toLongLong(); +} + int IntEngine::intParserPrio() { @@ -89,9 +118,14 @@ void IntEngine::configureIntEngine(ELAM::Engine& eng) //unaries eng.unaryOperator("-").setCallback(intMinus,iid); eng.unaryOperator("+").setCallback(intPlus,iid); + eng.unaryOperator("~").setCallback(intNot,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); + eng.binaryOperator("-",80).setCallback(intMinus,iid,iid); + eng.binaryOperator("+",80).setCallback(intAdd,iid,iid); + eng.binaryOperator("*",90).setCallback(intMult,iid,iid); + eng.binaryOperator("/",90).setCallback(intDiv,iid,iid); + eng.binaryOperator("%",90).setCallback(intMod,iid,iid); + eng.binaryOperator("&",50).setCallback(intAnd,iid,iid); + eng.binaryOperator("|",40).setCallback(intOr,iid,iid); + eng.binaryOperator("^",45).setCallback(intXor,iid,iid); } diff --git a/src/elamunary.cpp b/src/elamunary.cpp new file mode 100644 index 0000000..ff828b2 --- /dev/null +++ b/src/elamunary.cpp @@ -0,0 +1,122 @@ +// engine definition implementation +// +// (c) Konrad Rosenbaum, 2010 +// protected under the GNU LGPL v3 or at your option any newer + +#include "elamengine.h" +#include "elamvalue.h" + +#include + +namespace ELAM { + +///////////////////////////////////////////////////////////////// +// Unary Operator + +class DPTR_CLASS_NAME(UnaryOperator):public SharedDPtr +{ + public: + QMapcallmap; +}; +DEFINE_SHARED_DPTR(UnaryOperator) + + +UnaryOperator::UnaryOperator(const UnaryOperator& op) + :d(op.d) +{ +} + +UnaryOperator& UnaryOperator::UnaryOperator::operator=(const ELAM::UnaryOperator& op) +{ + d=op.d; + return *this; +} + + +UnaryOperator::UnaryOperator() +{ +} + +UnaryOperator::~UnaryOperator() +{ +} + + +QVariant UnaryOperator::execute(const QVariant& op) const +{ + if(d->callmap.size()==0) + return qVariantFromValue(Exception(Exception::UnknownOperatorError)); + //search for direct match + if(d->callmap.contains(op.type())) + return d->callmap[op.type()](op); + //search for fallback + int any=AnyType::metaTypeId(); + if(d->callmap.contains(any)) + return d->callmap[any](op); + return qVariantFromValue(Exception(Exception::TypeMismatchError)); +} + +QList< int > UnaryOperator::getTypeIds() const +{ + return d->callmap.keys(); +} + +QStringList UnaryOperator::getTypeNames() const +{ + QStringList ret; + QListti=d->callmap.keys(); + for(int i=0;icallmap.contains(type)) + return d->callmap[type]; + if(d->callmap.contains(AnyType::metaTypeId())) + return d->callmap[AnyType::metaTypeId()]; + return 0; +} + +void UnaryOperator::setCallback(UnaryOperatorCall callback, int type) +{ + if(type==QVariant::Invalid)return; + if(callback==0){ + d->callmap.remove(type); + return; + } + d->callmap.insert(type,callback); +} + +void UnaryOperator::setCallback(UnaryOperatorCall callback, QString type) +{ + setCallback(callback,QVariant::nameToType(type.toAscii().data())); +} + +void UnaryOperator::removeCallback(UnaryOperatorCall cb) +{ + if(cb==0)return; + QListk=d->callmap.keys(cb); + for(int i=0;icallmap.remove(k[i]); +} + +void UnaryOperator::removeCallback(QString t) +{ + removeCallback(QVariant::nameToType(t.toAscii().data())); +} + +void UnaryOperator::removeCallback(int t) +{ + d->callmap.remove(t); +} + + +//end of namespace +}; \ No newline at end of file diff --git a/src/elamunary.h b/src/elamunary.h new file mode 100644 index 0000000..5976319 --- /dev/null +++ b/src/elamunary.h @@ -0,0 +1,75 @@ +// main header +// +// (c) Konrad Rosenbaum, 2010 +// protected under the GNU LGPL v3 or at your option any newer + +#ifndef ELAM_ENGINE_UNARY_H +#define ELAM_ENGINE_UNARY_H + +#include +#include +#include + +#include "dptr.h" + +namespace ELAM { + +/** \brief pointer to a function wrapping an unary operator +\param op the operand to be worked on +\returns the result of the operation*/ +typedef QVariant (*UnaryOperatorCall)(const QVariant&op); + +/** \brief Wraps a particular unary operator. + +You can use the methods of this class to change the routines that handle the operator and the types on which it operates. Instances of this class are implicitly shared - meaning calls on a copy of an instance are also visible on the original and all other copies. +*/ +class UnaryOperator +{ + DECLARE_SHARED_DPTR(d); + public: + /**copy constructor, + the instance will access the exact same operator as the original, any setting that is done in the copy is also done in the original and all other copies.*/ + UnaryOperator(const UnaryOperator&); + ///instantiates an empty operator + UnaryOperator(); + ///deletes and operator + ~UnaryOperator(); + + /**the operator becomes a shared copy of op and abandones its old link*/ + UnaryOperator& operator=(const UnaryOperator&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(UnaryOperatorCall callback,QString type); + /**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(UnaryOperatorCall callback,int type); + /**returns the callback function attached to the type or NULL if there is none*/ + UnaryOperatorCall getCallback(QString type)const; + /**returns the callback function attached to the type or NULL if there is none*/ + UnaryOperatorCall getCallback(int type)const; + + /**removes all types attached to this callback from the operator*/ + void removeCallback(UnaryOperatorCall); + ///removes the type from this operators list + void removeCallback(QString); + ///removes the type from this operators list + void removeCallback(int); + + ///returns all type names that have a valid callback in this operator + QStringList getTypeNames()const; + ///returns all type IDs that have a valid callback in this operator + QList 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; +}; + +//end of namespace +}; + +#endif -- 1.7.2.5