hierarchical tokens
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Wed, 17 Nov 2010 20:57:43 +0000 (20:57 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Wed, 17 Nov 2010 20:57:43 +0000 (20:57 +0000)
some docu for default lib
add TODO
add eval test draft

git-svn-id: https://silmor.de/svn/softmagic/elam/trunk@635 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

12 files changed:
TODO [new file with mode: 0644]
doc/library.html
doc/syntax.html
src/elam.h
src/elam.pro
src/elamboolengine.cpp [new file with mode: 0644]
src/elamboolengine.h [new file with mode: 0644]
src/elamexpression.cpp
src/elamexpression.h
tests/eval/eval.cpp [new file with mode: 0644]
tests/eval/eval.h [new file with mode: 0644]
tests/eval/eval.pro [new file with mode: 0644]

diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..44345e4
--- /dev/null
+++ b/TODO
@@ -0,0 +1,20 @@
+TODO for ELAM
+================
+
+
+Engine
+---------
+
+* Expression ordering and execution
+* function overloading
+
+Default Library
+------------------
+
+int: abs(...)
+
+float: abs, round, comparison operators
+
+bool
+
+string
index 7a713ed..c9bb4cc 100644 (file)
@@ -7,9 +7,62 @@
 
 <h2>Calling It</h2>
 
+<h2>Default Libray</h2>
+
+The default library defines functions and operators for integers, floats, booleans and strings. It uses exactly the operator precedence used in the <a href="syntax.html#oper_prece">Syntax</a> documentation.<p/>
+
+The type "any" is used below to denote any type that is supported by the current engine. Constraints are noted in the description. The following types are used in the default library:<br/>
+<table frame="1" border="1">
+<tr><td><b>ELAM type</b></td><td><b>QVariant</b></td><td><b>Description</b></td></tr>
+
+<tr><td>any</td><td>ELAM::AnyType</td><td>cannot be used directly, but tells the engine that any type is allowed</td></tr>
+
+<tr><td>exception</td><td>ELAM::Exception</td><td>thrown when something goes wrong (parser errors, syntactic errors, operational/conversion errors</td></tr>
+
+<tr><td>int</td><td>qlonglong</td><td>integer mathematics</td></tr>
+<tr><td>float</td><td>qreal/double</td><td>floating point mathematics</td></tr>
+<tr><td>bool</td><td>bool</td><td>boolean mathematics and logic</td></tr>
+<tr><td>string</td><td>QString</td><td>character strings</td></tr>
+</table><p/>
+
+Optional arguments are marked with [ and ].
+
+<h3>Integer Library</h3>
+
+The integer library defines some very basic integer functionality:
+<table frame="1" border="1">
+<tr><td><b>Function</b></td><td><b>Description</b></td></tr>
+<tr><td>int(any)</td><td>tries to convert the argument to integer</td><tr>
+<tr><td>int + int</td><td>adds two integers</td></tr>
+<tr><td>int - int</td><td>subtracts two integers</td></tr>
+<tr><td>int * int</td><td>multiplies two integers</td></tr>
+<tr><td>int / int</td><td>divides two integers</td></tr>
+<tr><td>int % int</td><td>calculates the modulo of two integers</td></tr>
+<tr><td>int &amp; int</td><td>calculates the bitwis AND of  two integers</td></tr>
+<tr><td>int | int</td><td>calculates the bitwis OR of  two integers</td></tr>
+<tr><td>int ^ int</td><td>calculates the bitwis XOR of  two integers</td></tr>
+<tr><td>+ int</td><td>returns the value as is</td></tr>
+<tr><td>- int</td><td>returns the integer negative of the number</td></tr>
+<tr><td>~ int</td><td>returns the bitwise negative of the integers</td></tr>
+</table>
+
+<h3>Floating Point Library</h3>
+
+The floating point library defines some very basic floating point functionality:
+<table frame="1" border="1">
+<tr><td><b>Function</b></td><td><b>Description</b></td></tr>
+<tr><td>float(any)</td><td>tries to convert the argument to floating point</td><tr>
+<tr><td>float + float</td><td>adds two floating points</td></tr>
+<tr><td>float - float</td><td>subtracts two floating points</td></tr>
+<tr><td>float * float</td><td>multiplies two floating points</td></tr>
+<tr><td>float / float</td><td>divides two floating points</td></tr>
+<tr><td>+ float</td><td>returns the value as is</td></tr>
+<tr><td>- float</td><td>returns the floating point negative of the number</td></tr>
+</table>
+
 <h2>Basic Types, Operators, Functions and Variables</h2>
 
-<h1>Extending The ElAM Libray</h1>
+<h1>Extending The ELAM Libray</h1>
 
 <h2>Redirecting Variables and Constants</h2>
 
index 73e410f..2411963 100644 (file)
@@ -1,5 +1,5 @@
 <html>
-<title>ELAM</title>
+<title>ELAM Syntax</title>
 <body>
 <h1>The ELAM Syntax</h1>
 
@@ -74,6 +74,7 @@ Parentheses group operations and may alter the order in which they are executed.
 
 The equals sign ("=") has a special meaning when used in operators. In the normal mode the direct assignment operator "=" cannot be overloaded with a user specific function. If an operator ends in "=" and there is no overloaded operator, then the remainder is interpreted as a binary operator and the result assigned to the left side of the operator, for example "a+=b" is interpreted as a short form of "a=a+b".
 
+<a name="oper_prece"/>
 <h3>Operator Precedence</h3>
 
 Operators are interpreted in a specific order of precedence. First the highest precedence operators are evaluated, then the next lower precedence level, etc. with assignments being executed last. Consecutive operations with the same precedence level are executed from left to right.<p>
@@ -83,7 +84,7 @@ The default library has the following precedence order:
 <table frame="1" border="1">
 <tr><td><b>Operator Class</b><td><b>Operators</b><td><b>Precedence Value</b></tr>
 <tr><td>Parentheses<td>(parentheses), functions(...)<td>1000</tr>
-<tr><td>All Unary Operators<td> ++, --, ~, !, -, +<td>100</tr>
+<tr><td>All Unary Operators<td> ~, !, -, +<td>100</tr>
 <tr><td>Maximum usable Precedence<td>&nbsp;<td>99</tr>
 <tr><td>Multiplicative<td> *, /, %<td>90</tr>
 <tr><td>Additive<td> +, -<td>80</tr>
@@ -93,7 +94,8 @@ The default library has the following precedence order:
 <tr><td>bitwise XOR<td> ^<td>45</tr>
 <tr><td>bitwise OR<td> |<td>40</tr>
 <tr><td>logical AND<td> &amp;&amp;<td>30</tr>
-<tr><td>logical OR<td>||<td>25</tr>
+<tr><td>logical XOR<td>^^<td>25</tr>
+<tr><td>logical OR<td>||<td>20</tr>
 <tr><td>Lowest usable Precedence<td>&nbsp;<td>1</tr>
 <tr><td>assignments<td>=, +=, -=, ...<td>0</tr>
 </table><p>
index 2c86388..165abc9 100644 (file)
@@ -9,6 +9,7 @@
 #include "elamengine.h"
 #include "elamintengine.h"
 #include "elamfloatengine.h"
+#include "elamboolengine.h"
 
 /** \mainpage ELAM - Elementary Logic and Arithmetic Machine
 
index 4a2709d..690e18f 100644 (file)
@@ -18,7 +18,8 @@ HEADERS += \
        elamexpression.h \
        elamvalue.h \
        elamintengine.h \
-       elamfloatengine.h
+       elamfloatengine.h \
+       elamboolengine.h
 
 SOURCES += \
        elamvalue.cpp \
@@ -28,7 +29,8 @@ SOURCES += \
        elamengine.cpp \
        elamintengine.cpp \
        elamexpression.cpp \
-       elamfloatengine.cpp
+       elamfloatengine.cpp \
+       elamboolengine.cpp
 
 INCLUDEPATH += .
 DEPENDPATH += .
diff --git a/src/elamboolengine.cpp b/src/elamboolengine.cpp
new file mode 100644 (file)
index 0000000..f577b20
--- /dev/null
@@ -0,0 +1,143 @@
+// ELAM int engine definition implementation
+//
+// (c) Konrad Rosenbaum, 2010
+// protected under the GNU LGPL v3 or at your option any newer
+
+#include "elamboolengine.h"
+
+#include<QDebug>
+
+using namespace ELAM;
+
+BoolEngine::BoolEngine()
+{
+       configureBoolEngine(*this);
+       configureLogicEngine(*this);
+}
+
+
+static QVariant boolFunc(const QList<QVariant>&lf)
+{
+       if(lf.size()!=1)
+               return Exception(Exception::ArgumentListError, "expecting exactly one argument");
+       if(!lf[0].canConvert<bool>())
+               return Exception(Exception::TypeMismatchError,"cannot convert to bool");
+       return lf[0].toBool();
+}
+
+//unary
+static QVariant boolNot(const QVariant&o)
+{
+       return !o.toBool();
+}
+
+//bitwise
+static QVariant boolAnd(const QVariant&o1,const QVariant&o2)
+{
+       return o1.toBool()&&o2.toBool();
+}
+static QVariant boolOr(const QVariant&o1,const QVariant&o2)
+{
+       return o1.toBool()||o2.toBool();
+}
+static QVariant boolXor(const QVariant&o1,const QVariant&o2)
+{
+       return o1.toBool()^o2.toBool();
+}
+
+
+void BoolEngine::configureBoolEngine(Engine&eng)
+{
+       int iid=QVariant::LongLong;
+       int bid=QVariant::Bool;
+       //cast
+       eng.setFunction("bool",boolFunc);
+       //constants
+       eng.setConstant("null",QVariant());
+       eng.setConstant("true",true);
+       eng.setConstant("false",false);
+       //unaries
+       eng.unaryOperator("!").setCallback(boolNot,iid);
+       eng.unaryOperator("!").setCallback(boolNot,bid);
+       eng.unaryOperator("~").setCallback(boolNot,bid);
+       //binaries adapted
+       eng.binaryOperator("&",50).setCallback(boolAnd,iid,bid);
+       eng.binaryOperator("&",50).setCallback(boolAnd,bid,iid);
+       eng.binaryOperator("&",50).setCallback(boolAnd,bid,bid);
+       eng.binaryOperator("|",40).setCallback(boolOr,bid,iid);
+       eng.binaryOperator("|",40).setCallback(boolOr,iid,bid);
+       eng.binaryOperator("|",40).setCallback(boolOr,bid,bid);
+       eng.binaryOperator("^",45).setCallback(boolXor,bid,iid);
+       eng.binaryOperator("^",45).setCallback(boolXor,iid,bid);
+       eng.binaryOperator("^",45).setCallback(boolXor,bid,bid);
+       //booleans
+       eng.binaryOperator("&&",30).setCallback(boolAnd,iid,iid);
+       eng.binaryOperator("&&",30).setCallback(boolAnd,iid,bid);
+       eng.binaryOperator("&&",30).setCallback(boolAnd,bid,iid);
+       eng.binaryOperator("&&",30).setCallback(boolAnd,bid,bid);
+       eng.binaryOperator("||",20).setCallback(boolOr,iid,iid);
+       eng.binaryOperator("||",20).setCallback(boolOr,bid,iid);
+       eng.binaryOperator("||",20).setCallback(boolOr,iid,bid);
+       eng.binaryOperator("||",20).setCallback(boolOr,bid,bid);
+       eng.binaryOperator("^^",25).setCallback(boolXor,iid,iid);
+       eng.binaryOperator("^^",25).setCallback(boolXor,bid,iid);
+       eng.binaryOperator("^^",25).setCallback(boolXor,iid,bid);
+       eng.binaryOperator("^^",25).setCallback(boolXor,bid,bid);
+}
+
+static QVariant ifFunc(const QList<QVariant>&lf)
+{
+       if(lf.size()<2 || lf.size()>3)
+               return Exception(Exception::ArgumentListError, "expecting 2 or 3 arguments");
+       if(!lf[0].canConvert<bool>())
+               return Exception(Exception::TypeMismatchError,"cannot convert argument 1 to bool");
+       if(lf[0].toBool())
+               return lf[1];
+       else
+               if(lf.size()>2)return lf[2];
+               return QVariant();
+}
+
+static QVariant isNullFunc(const QList<QVariant>&lf)
+{
+       if(lf.size()!=1)
+               return Exception(Exception::ArgumentListError, "expecting exactly one argument");
+       return lf[0].isNull();
+}
+
+static QVariant isExceptionFunc(const QList<QVariant>&lf)
+{
+       if(lf.size()!=1)
+               return Exception(Exception::ArgumentListError, "expecting exactly one argument");
+       return lf[0].type()==Exception::metaTypeId();
+}
+
+static QVariant isExceptionOrNullFunc(const QList<QVariant>&lf)
+{
+       if(lf.size()!=1)
+               return Exception(Exception::ArgumentListError, "expecting exactly one argument");
+       return lf[0].isNull() || lf[0].type()==Exception::metaTypeId();
+}
+
+static QVariant catchFunc(const QList<QVariant>&lf)
+{
+       if(lf.size()<1||lf.size()>3)
+               return Exception(Exception::ArgumentListError, "expecting 1-3 arguments");
+       if(lf[0].type()==Exception::metaTypeId()){
+               if(lf.size()>1)return lf[1];
+               else return true;
+       }else{
+               if(lf.size()>2)return lf[2];
+               else return false;
+       }
+}
+
+
+void BoolEngine::configureLogicEngine(Engine& eng)
+{
+       eng.setFunction("if",ifFunc);
+       eng.setFunction("isNull",isNullFunc);
+       eng.setFunction("isException",isExceptionFunc);
+       eng.setFunction("isExceptionOrNull",isExceptionOrNullFunc);
+       eng.setFunction("catch",catchFunc);
+}
diff --git a/src/elamboolengine.h b/src/elamboolengine.h
new file mode 100644 (file)
index 0000000..379bb32
--- /dev/null
@@ -0,0 +1,32 @@
+//ELAM integer engine header
+//
+// (c) Konrad Rosenbaum, 2010
+// protected under the GNU LGPL v3 or at your option any newer
+
+#ifndef ELAM_BOOLENGINE_H
+#define ELAM_BOOLENGINE_H
+
+#include "elamengine.h"
+
+namespace ELAM {
+
+/**A boolean and logic math enabled engine.
+
+This engine type plays nicely with ELAM::IntEngine
+*/
+class BoolEngine:public Engine
+{
+       public:
+               ///instantiates a pre-configured engine
+               BoolEngine();
+               
+               ///configures any engine to support basic boolean math
+               static void configureBoolEngine(Engine&);
+               
+               ///configures any engine to support logic functions
+               static void configureLogicEngine(Engine&);
+};
+
+};
+
+#endif
index 60e4ca5..65d0a7b 100644 (file)
@@ -1,21 +1,26 @@
 #include "elamexpression.h"
 
 #include <QDebug>
+#include <QPointer>
+#include "elamengine.h"
 
 namespace ELAM {
 
 ///////////////////////////////////////////////////////////////////////////////
 // Token
 
-class DPTR_CLASS_NAME(Token):public DPtr
+class DPTR_CLASS_NAME(Token):public SharedDPtr
 {
        public:
+               DPTR_NAME(){type=Invalid;subtype=None;}
                QString cont;
                Type type;
+               SubType subtype;
                QVariant val;
                Position pos;
+               QList<Token>subtok;
 };
-DEFINE_DPTR(Token)
+DEFINE_SHARED_DPTR(Token)
 
 
 Token::Token(Position pos)
@@ -44,9 +49,38 @@ Token::Type Token::type()const{return d->type;}
 QVariant Token::literalValue()const{return d->val;}
 Position Token::position()const{return d->pos;}
 
-QDebug&operator<<(QDebug&dbg,const Token&tok)
+QList< Token > Token::subTokens() const{return d->subtok;}
+Token::SubType Token::subType() const{return d->subtype;}
+void Token::setSubType(Token::SubType s){d->subtype=s;}
+void Token::addSubToken(const ELAM::Token& t)
+{
+       d->subtok<<t;
+}
+void Token::setSubTokens(const QList< Token >& t)
+{
+       d->subtok=t;
+}
+
+static void printspaces(QDebug&dbg,int level)
+{
+       for(int i=0;i<level;i++)dbg<<"   ";
+}
+
+static void printtoken(QDebug&dbg,const Token&tok,int level);
+static void printtokenlist(QDebug&dbg,const QList<Token>&tok,int llevel,int tlevel)
+{
+       printspaces(dbg,llevel);
+       dbg<<"TokenList(";
+       for(int i=0;i<tok.size();i++){
+               dbg<<"\n";
+               printtoken(dbg,tok[i],tlevel+1);
+       }
+       dbg<<")";
+}
+static void printtoken(QDebug&dbg,const Token&tok,int level)
 {
-       dbg.nospace()<<"Token(str="<<tok.content()<<",type=";
+       printspaces(dbg,level);
+       dbg<<"Token(str="<<tok.content()<<",type=";
        switch(tok.type()){
                case Token::Invalid:dbg<<"Invalid";break;
                case Token::Name:dbg<<"Name";break;
@@ -54,18 +88,66 @@ QDebug&operator<<(QDebug&dbg,const Token&tok)
                case Token::ParClose:dbg<<"ClosingParenthesis";break;
                case Token::ParOpen:dbg<<"OpeningParenthesis";break;
                case Token::Comma:dbg<<"Comma";break;
-               case Token::Literal:dbg<<"LiteralValue"<<",value="<<tok.literalValue();break;
+               case Token::Literal:dbg<<"LiteralValue,value="<<tok.literalValue();break;
                case Token::Whitespace:dbg<<"WhiteSpace";break;
+               case Token::Parentheses:dbg<<"Parentheses";break;
        }
-       dbg<<",pos="<<tok.position()<<")";
+       if(tok.subType()!=Token::None){
+               dbg<<",subtype=";
+               switch(tok.subType()){
+                       case Token::Function:dbg<<"Function";break;
+                       case Token::Constant:dbg<<"Constant";break;
+                       case Token::Variable:dbg<<"Variable";break;
+                       case Token::UnaryOp:dbg<<"Unary";break;
+                       case Token::BinaryOp:dbg<<"Binary";break;
+                       default:break;
+               }
+       }
+       dbg<<",pos="<<tok.position();
+       QList<Token> sub=tok.subTokens();
+       if(sub.size()>0){
+               dbg<<",sub-tokens:";
+               printtokenlist(dbg,sub,0,level);
+       }
+       dbg<<")";
+}
+QDebug&operator<<(QDebug&dbg,const Token&tok)
+{
+       dbg.nospace();
+       printtoken(dbg,tok,0);
        return dbg.space();
 }
 
+QDebug&operator<<(QDebug&dbg,const QList<Token>&tok)
+{
+       dbg.nospace();
+       printtokenlist(dbg,tok,0,0);
+       return dbg.space();
+}
+///////////////////////////////////////////////////////////////////////////////
+// TokenBundle
+
+class TokenBundle
+{
+       public:
+               TokenBundle(const Token&);
+               TokenBundle(const TokenBundle&);
+               TokenBundle(const QList<Token>&);
+       private:
+               QList<TokenBundle>&mtoks;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Expression
 
 class DPTR_CLASS_NAME(Expression):public SharedDPtr
 {
+       public:
+               DPTR_NAME(){type=Exception;}
+               QPointer<Engine>parent;
+               QList<Token>tokens;
+               Type type;
+               QVariant value;
 };
 DEFINE_SHARED_DPTR(Expression);
 
@@ -73,9 +155,169 @@ Expression::Expression()
 {
 
 }
-Expression::Expression(Engine* parent, const QList< Token >& tokens)
+
+//reduce surrounding parentheses and whitespace
+static inline QList<Token>simplifyTokens(Engine*eng,QList<Token> toks)
 {
+       Q_UNUSED(eng);
+       QList<Token>ret;
+       int min=0,max=toks.size()-1;
+       //eliminate redundant parentheses
+       while(toks[min].type()==Token::ParOpen && toks[max].type()==Token::ParClose){
+               min++;max--;
+       }
+       //reduce whitespace
+       for(int i=min;i<=max;i++){
+               //eliminate whitespace
+               if(toks[i].type()==Token::Whitespace)
+                       continue;
+               ret<<toks[i];
+       }
+       return ret;
+}
 
+//reduce surrounding parentheses and whitespace
+static inline QList<Token>classifyTokens(Engine*eng,QList<Token> toks)
+{
+       QList<Token>ret;
+       if(toks.size()<1)return toks;
+       //check token 0
+       Token t=toks[0];
+       if(t.type()==Token::Name){
+               if(eng->hasFunction(t.content()))t.setSubType(Token::Function);else
+               if(eng->hasConstant(t.content()))t.setSubType(Token::Constant);
+               else t.setSubType(Token::Variable);
+       }else if(t.type()==Token::Operator)t.setSubType(Token::UnaryOp);
+       ret<<t;
+       //check remaining tokens
+       for(int i=1;i<toks.size();i++){
+               t=toks[i];
+               //define names
+               if(t.type()==Token::Name){
+                       if(eng->hasFunction(t.content()))t.setSubType(Token::Function);else
+                       if(eng->hasConstant(t.content()))t.setSubType(Token::Constant);
+                       else t.setSubType(Token::Variable);
+               }else
+               //define operators
+               if(t.type()==Token::Operator){
+                       switch(toks[i-1].type()){
+                               case Token::ParOpen:
+                               case Token::Operator:
+                                       t.setSubType(Token::UnaryOp);
+                                       break;
+                               default:
+                                       t.setSubType(Token::BinaryOp);
+                                       break;
+                       }
+               }
+               //add
+               ret<<t;
+       }
+       return ret;
+}
+//reduce surrounding parentheses and whitespace
+static inline QList<Token>reduceTokens(Engine*eng,QList<Token> toks)
+{
+       toks=classifyTokens(eng,simplifyTokens(eng,toks));
+       QList<Token>ret,sub;
+       //copy and create hierarchy
+       int pcnt=0;
+       for(int i=0;i<toks.size();i++){
+               //count parentheses
+               if(toks[i].type()==Token::ParOpen){
+                       if(!pcnt){
+                               if(i==0 || toks.value(i-1).subType()!=Token::Function)
+                                       ret<<Token("",Token::Parentheses,toks[i].position());
+                               sub.clear();
+                       }
+                       pcnt++;
+                       continue;
+               }else
+               if(toks[i].type()==Token::ParClose){
+                       pcnt--;
+                       if(!pcnt){
+                               ret[ret.size()-1].setSubTokens(sub);
+                               sub.clear();
+                       }
+                       continue;
+               }
+               //collect tokens
+               if(pcnt)
+                       sub<<toks[i];
+               else
+                       ret<<toks[i];
+       }
+       return ret;
+}
+//scan for simple errors, return exception (NoError if no error found)
+static inline Exception scanForError(const QList< Token >& toks)
+{
+       //check for invalid tokens
+       for(int i=0;i<toks.size();i++)
+               if(toks[i].type()==Token::Invalid){
+                       return Exception(Exception::ParserError, "invalid token", toks[i].position());
+               }
+       //scan for parentheses mismatch
+       int pcnt=0;
+       for(int i=0;i<toks.size();i++){
+               //check parentheses
+               if(toks[i].type()==Token::ParOpen)pcnt++;else
+               if(toks[i].type()==Token::ParClose)pcnt--;
+               if(pcnt<0){
+                       return Exception(Exception::ParserError, "parentheses mismatch", toks[i].position());
+               }
+       }
+       if(pcnt!=0){
+               return Exception(Exception::ParserError, "parentheses mismatch", toks[0].position());
+       }
+       //nothing found
+       return Exception(Exception::NoError);
+}
+
+Expression::Expression(Engine* parent, const QList< Token >& toks)
+{
+       //check for simple errors
+       ELAM::Exception ex=scanForError(toks);
+       if(ex.errorType()!=ELAM::Exception::NoError){
+               d->type=Exception;
+               d->value=ex;
+               d->tokens=toks;
+               return;
+       }
+       d->parent=parent;
+       d->tokens=reduceTokens(parent,toks);
+       qDebug()<<"expression:"<<d->tokens;
+       //check for nothing and complain
+       if(d->tokens.size()==0){
+               d->type=Exception;
+               d->value=ELAM::Exception(ELAM::Exception::ParserError,"no tokens", (toks.size()>0?toks[0].position():Position()));
+               return;
+       }
+       //check for simplicity (literals, vars, consts)
+       if(d->tokens.size()==1){
+               switch(d->tokens[0].type()){
+                       case Token::Name:
+                               if(parent->hasFunction(d->tokens[0].content())){
+                                       d->type=Exception;
+                                       d->value=ELAM::Exception(ELAM::Exception::ParserError, "function call incomplete", d->tokens[0].position());
+                               }else if(parent->hasConstant(d->tokens[0].content())){
+                                       d->type=Constant;
+                                       d->value=parent->getConstant(d->tokens[0].content());
+                               }else{
+                                       d->type=Variable;
+                               }
+                               break;
+                       case Token::Literal:
+                               d->type=Literal;
+                               d->value=d->tokens[0].literalValue();
+                               break;
+                       default:
+                               d->type=Exception;
+                               d->value=ELAM::Exception(ELAM::Exception::ParserError, "unexpected token", d->tokens[0].position());
+                               break;
+               }
+               return;
+       }
 }
 
 QVariant Expression::evaluate()
index 75650e7..dff4853 100644 (file)
@@ -16,26 +16,36 @@ namespace ELAM {
 Tokens are pretty stupid themselves - they just know their type, position, their original piece of text and an optional value (literals). They are used by the engine and expressions to transform text into executable expressions.*/
 class Token
 {
-       DECLARE_DPTR(d)
+       DECLARE_SHARED_DPTR(d)
        public:
                ///The type of token
                enum Type {
                        ///invalid token
-                       Invalid,
+                       Invalid=0,
                        ///a name: function, variable, or constant
-                       Name,
+                       Name=7,
                        ///an operator (unary or binary)
-                       Operator,
+                       Operator=24,
+                       ///meta-type used for parsed sub-tokens
+                       Parentheses=96,
                        ///opening parenthese
-                       ParOpen,
+                       ParOpen=32,
                        ///closing parenthese
-                       ParClose,
+                       ParClose=64,
                        ///a comma - separating expressions in function calls
-                       Comma,
+                       Comma=128,
                        ///a literal value
-                       Literal,
+                       Literal=256,
                        ///white space chars, this is actually not used for tokens, but for parsing
-                       Whitespace,
+                       Whitespace=512,
+               };
+               enum SubType{
+                       None = 0,
+                       Function = 1,
+                       Constant = 2,
+                       Variable = 4,
+                       UnaryOp = 8,
+                       BinaryOp = 16,
                };
                ///creates an empty/invalid token
                Token(Position pos=Position(-1,-1));
@@ -51,6 +61,11 @@ class Token
                QVariant literalValue()const;
                ///returns the original position of the token
                Position position()const;
+               SubType subType()const;
+               void setSubType(SubType);
+               QList<Token>subTokens()const;
+               void addSubToken(const Token&);
+               void setSubTokens(const QList<Token>&);
 };
 
 QDebug& operator<<(QDebug&,const Token&);
@@ -61,14 +76,14 @@ class Expression
        DECLARE_SHARED_DPTR(d)
        public:
                enum Type {
-                       Literal,
-                       Variable,
-                       Constant,
-                       Function,
-                       Parentheses,
-                       UnaryOp,
-                       BinaryOp,
-                       Exception,
+                       Literal=Token::Literal,
+                       Variable=Token::Variable,
+                       Constant=Token::Constant,
+                       Function=Token::Function,
+                       Parentheses=96,
+                       UnaryOp=Token::UnaryOp,
+                       BinaryOp=Token::BinaryOp,
+                       Exception=0x8000,
                };
                Expression();
                Expression(Engine*parent,const QList<Token>&tokens);
diff --git a/tests/eval/eval.cpp b/tests/eval/eval.cpp
new file mode 100644 (file)
index 0000000..bc5e706
--- /dev/null
@@ -0,0 +1,21 @@
+#include "elam.h"
+
+#include "eval.h"
+
+#include <QtCore>
+#include <QtTest>
+#include <QDebug>
+
+using namespace ELAM;
+
+
+void ElamTest::evaltest()
+{
+       IntEngine ie;
+       FloatEngine::configureFloatEngine(ie);
+       QString ex="a= 345*(65.3/(5))";
+       QVariant v=ie.evaluate(ex);
+}
+
+
+QTEST_MAIN(ElamTest)
\ No newline at end of file
diff --git a/tests/eval/eval.h b/tests/eval/eval.h
new file mode 100644 (file)
index 0000000..0ab9f81
--- /dev/null
@@ -0,0 +1,8 @@
+#include <QObject>
+
+class ElamTest:public QObject
+{
+       Q_OBJECT
+       private slots:
+               void evaltest();
+};
diff --git a/tests/eval/eval.pro b/tests/eval/eval.pro
new file mode 100644 (file)
index 0000000..9900a09
--- /dev/null
@@ -0,0 +1,10 @@
+TEMPLATE = app
+TARGET = evaltest
+QT -= gui
+CONFIG += qtestlib debug link_prl
+INCLUDEPATH += . ../../src
+DEPENDPATH += $$INCLUDEPATH ../..
+LIBS += -L../.. -lelam
+
+SOURCES += eval.cpp
+HEADERS += eval.h
\ No newline at end of file