From 4dffd5dcc9740067a129cd590bff735238b5e30d Mon Sep 17 00:00:00 2001 From: konrad Date: Sun, 14 Nov 2010 21:39:24 +0000 Subject: [PATCH] tokenizer, test of tokenizer git-svn-id: https://silmor.de/svn/softmagic/elam/trunk@627 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- src/Doxyfile | 8 +- src/elam.h | 8 ++ src/elam.pro | 3 +- src/elamengine.cpp | 284 ++++++++++++++++++++++++++++++++++++++++++++++- src/elamengine.h | 218 ++++++++++++++++++++++++++++++++++-- src/elamexpression.cpp | 19 +++ src/elamexpression.h | 5 + src/elamintengine.cpp | 48 ++++++++- src/elamvalue.cpp | 11 ++ src/elamvalue.h | 41 ++++++-- tests/parser/parser.cpp | 82 ++++++++++++++ tests/parser/parser.h | 9 ++ tests/parser/parser.pro | 10 ++ 13 files changed, 718 insertions(+), 28 deletions(-) create mode 100644 tests/parser/parser.cpp create mode 100644 tests/parser/parser.h create mode 100644 tests/parser/parser.pro diff --git a/src/Doxyfile b/src/Doxyfile index af2e34b..68a7a23 100644 --- a/src/Doxyfile +++ b/src/Doxyfile @@ -308,7 +308,7 @@ EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. -EXTRACT_STATIC = YES +EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. @@ -598,7 +598,7 @@ INPUT_ENCODING = UTF-8 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 -FILE_PATTERNS = +FILE_PATTERNS = *.h # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -742,7 +742,7 @@ USE_HTAGS = NO # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. -VERBATIM_HEADERS = YES +VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index @@ -1460,7 +1460,7 @@ HIDE_UNDOC_RELATIONS = YES # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will diff --git a/src/elam.h b/src/elam.h index 8df3bdf..e62e380 100644 --- a/src/elam.h +++ b/src/elam.h @@ -9,4 +9,12 @@ #include "elamengine.h" #include "elamintengine.h" +/** \mainpage ELAM - Elementary Logic and Arithmetic Machine + +Please see the main docu for build instructions and a tutorial on how to use ELAM.

+ +See the ELAM namespace for member documentation. + +*/ + #endif diff --git a/src/elam.pro b/src/elam.pro index 2082bfa..c3b2961 100644 --- a/src/elam.pro +++ b/src/elam.pro @@ -19,7 +19,8 @@ HEADERS += \ SOURCES += \ elamvalue.cpp \ elamengine.cpp \ - elamintengine.cpp + elamintengine.cpp \ + elamexpression.cpp INCLUDEPATH += . DEPENDPATH += . diff --git a/src/elamengine.cpp b/src/elamengine.cpp index 1ec523c..d622a3f 100644 --- a/src/elamengine.cpp +++ b/src/elamengine.cpp @@ -6,6 +6,8 @@ #include "elamengine.h" #include "elamvalue.h" +#include + namespace ELAM { /////////////////////////////////////////////////////////////////////////////// // Engine @@ -14,6 +16,18 @@ class DPTR_CLASS_NAME(Engine):public DPtr { public: CharacterClassSettings cclass; + QMap vars,consts; + QMap funcs; + struct LiteralParser_s + { + LiteralParser parser; + QString start; + int prio; + //this operator is reversed, so that highest prio is in element 0 after qSort + bool operator<(const LiteralParser_s&l2)const + {return prio>l2.prio;} + }; + QListparsers; }; DEFINE_DPTR(Engine); @@ -47,31 +61,73 @@ Expression Engine::expression(QList< Token > ) QList< Token > Engine::tokenize(QString ex) { + //presets Token::Type ctype=Token::Invalid; QString ctok; QList ret; int cline=1,ccol=0; int sline=cline,scol=ccol; + int litend=-1; + //go through expression for(int i=0;ii)continue; + //get type of current character Token::Type ntype=d->cclass.charType(ex[i],ctype); //check for invalid stuff if(ntype==Token::Invalid) - return QList()<()<lt=parseLiteral(ex,i); + //check for failure + if(lt.first=="") + return QList()<cclass; } +QVariant Engine::getConstant(QString n) const +{ + if(d->consts.contains(n))return d->consts[n]; + return QVariant(); +} + +QVariant Engine::getVariable(QString n) const +{ + if(d->vars.contains(n))return d->vars[n]; + return QVariant(); +} + +QVariant Engine::getValue(QString n) const +{ + if(d->consts.contains(n))return d->consts[n]; + if(d->vars.contains(n))return d->vars[n]; + return QVariant(); +} + +bool Engine::hasConstant(QString n) const +{ + return d->consts.contains(n); +} + +bool Engine::hasVariable(QString n) const +{ + return d->vars.contains(n); +} + +bool Engine::hasValue(QString n) const +{ + return d->vars.contains(n) || d->consts.contains(n); +} + +void Engine::removeConstant(QString n) +{ + d->consts.remove(n); +} + +void Engine::removeVariable(QString n) +{ + d->vars.remove(n); +} + +void Engine::removeValue(QString n) +{ + d->consts.remove(n); + d->vars.remove(n); +} + +bool Engine::setConstant(QString n, QVariant v) +{ + if(!d->cclass.isName(n))return false; + if(d->funcs.contains(n))return false; + d->vars.remove(n); + d->consts.insert(n,v); + return true; +} + +bool Engine::setVariable(QString n, QVariant v) +{ + if(!d->cclass.isName(n))return false; + if(d->consts.contains(n))return false; + if(d->funcs.contains(n))return false; + d->vars.insert(n,v); + return true; +} + +bool Engine::hasFunction(QString n) const +{ + return d->funcs.contains(n); +} + +Function Engine::getFunction(QString n) const +{ + if(d->funcs.contains(n))return d->funcs[n]; + return 0; +} + +bool Engine::setFunction(QString n, ELAM::Function p) +{ + if(p==0)return false; + if(!d->cclass.isName(n))return false; + if(d->consts.contains(n))return false; + if(d->vars.contains(n))return false; + d->funcs.insert(n,p); + return true; +} + +void Engine::removeFunction(QString n) +{ + d->funcs.remove(n); +} + +bool Engine::setLiteralParser ( ELAM::LiteralParser parser, QString startchars, int prio ) +{ + if(parser==0 || startchars=="" || prio<0 || prio>100) + return false; + for(int i=0;iparsers.size();i++){ + if(d->parsers[i].parser==parser){ + d->parsers[i].start=startchars; + d->parsers[i].prio=prio; + return true; + } + } + Private::LiteralParser_s s; + s.parser=parser; + s.prio=prio; + s.start=startchars; + d->parsers<parsers.size();i++) + if(d->parsers[i].parser==parser){ + d->parsers.removeAt(i); + return; + } +} + +QPair< QString, QVariant > Engine::parseLiteral ( QString ex, int start) +{ + QChar sc=ex[start]; + //find any parser that matches the start char + QListcand; + for(int i=0;iparsers.size();i++) + if(d->parsers[i].start.contains(sc)) + cand<parsers[i]; + if(cand.size()<1) + return QPair(); + //sort them + qSort(cand); + //execute + return cand[0].parser(ex,*this,start); +} + ///////////////////////////////////////////////////////////////// // character classes @@ -214,6 +408,9 @@ bool CharacterClassSettings::isConsistent() const 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) || @@ -224,6 +421,78 @@ bool CharacterClassSettings::isConsistent() const 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; +} + ///////////////////////////////////////////////////////////////// // Unary Operator @@ -241,6 +510,13 @@ UnaryOperator::UnaryOperator(const UnaryOperator& op) { } +UnaryOperator& UnaryOperator::UnaryOperator::operator=(const ELAM::UnaryOperator& op) +{ + d=op.d; + return *this; +} + + UnaryOperator::UnaryOperator() { } diff --git a/src/elamengine.h b/src/elamengine.h index 872114f..2c58557 100644 --- a/src/elamengine.h +++ b/src/elamengine.h @@ -3,8 +3,8 @@ // (c) Konrad Rosenbaum, 2010 // protected under the GNU LGPL v3 or at your option any newer -#ifndef _ENGINE_H -#define _ENGINE_H +#ifndef ELAM_ENGINE_H +#define ELAM_ENGINE_H #include #include @@ -16,19 +16,30 @@ namespace ELAM { -/**pointer to a function wrapping an unary operator +/** \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 @@ -60,12 +71,16 @@ class UnaryOperator QVariant execute(const QVariant&)const; }; -/**pointer to a function wrapping a binary operator +/** \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); @@ -77,37 +92,157 @@ class BinaryOperator /**pointer to a function wrapping a mathematical function \param args the list of arguments -\returns the result of the function*/ +\returns the result of the function or ELAM::Exception in case or errors + +Functions must check their arguments for validity before they start calculating. On error a function should returns an ELAM::Exception. +*/ typedef QVariant (*Function)(const QList&args); + +class Engine; + +/**wraps the parser routine for a literal +\param expr the original expression string +\param engine the engine that is calling this parser +\param start the character (index of expr) at which the literal starts +\returns the parsed literal (first=string representation, second=value represented by it + +The string representation of the literal must be verbatim from expr, so that the calling engine can determine where to continue parsing in expr. + +If the parser does not find a valid literal according to its rules it must return an empty string. +*/ +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; - void setParentheses(QChar,QChar); + /**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; - void setAssignmentChars(QChar,QChar); + /** 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; - Token::Type charType(QChar,Token::Type)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 . @@ -124,6 +259,70 @@ class Engine:public QObject ///instantiates an engine object Engine(QObject*parent=0); + ///true if the named variable exists in this engine + bool hasVariable(QString)const; + ///true if the named constant exists in this engine + bool hasConstant(QString)const; + ///true if a variable or constant of that name exists in this engine + bool hasValue(QString)const; + + ///returns the value of the named variable or constant + QVariant getValue(QString)const; + ///returns the value of the named variable (does not return constants) + QVariant getVariable(QString)const; + ///returns the value of the named constant (does not return variables) + QVariant getConstant(QString)const; + + /**sets a variable + + \returns true on success or false if: + - the name is not valid + - a function or constant of that name already exists + */ + bool setVariable(QString,QVariant); + /**sets a constant + + \returns true on success or false if: + - the name is not valid + - a function of that name exists + + Constants overwrite variables - if a variable of the same name exists, it is transparently deleted before the constant is created.*/ + bool setConstant(QString,QVariant); + + ///deletes a variable (does not affect constants) + void removeVariable(QString); + ///deletes a constant (does not affect variables) + void removeConstant(QString); + ///deletes a variable or constant + void removeValue(QString); + + ///returns true if the named function exists + bool hasFunction(QString)const; + ///returns the pointer to the function + Function getFunction(QString)const; + /**sets the function + \returns true on success or false if: + - the name is not valid + - the function is null + - a constant or variable of the same name exists already*/ + bool setFunction(QString,Function); + ///removes a function + void removeFunction(QString); + + /**sets the parser routine for a literal value + \param parser pointer to the parser routine + \param startchars characters that the literal can start with, all of those characters must be part of the literalStart class + \param prio a value between 0 and 100, parsers with higher values are preferred over those with lower values if they share a start character + \returns true if the parser is (re-)registered successfully, or false if: + - the parser is null + - the start characters are empty + - the priority is outside the allowed range (0<=prio<=100) + + If a parser function is registered a second time the new registration overwrites the old registration. + */ + bool setLiteralParser(LiteralParser parser,QString startchars,int prio=50); + ///removes a parser function + void removeLiteralParser(LiteralParser parser); public slots: ///simply parses an expression string into an object Expression expression(QString); @@ -137,6 +336,9 @@ class Engine:public QObject QVariant evaluate(Expression); ///gives access to the character classes settings CharacterClassSettings characterClasses(); + private: + ///parse a literal + QPairparseLiteral(QString,int); }; //end of namespace diff --git a/src/elamexpression.cpp b/src/elamexpression.cpp index 927683e..358cd81 100644 --- a/src/elamexpression.cpp +++ b/src/elamexpression.cpp @@ -1,5 +1,7 @@ #include "elamexpression.h" +#include + namespace ELAM { class DPTR_CLASS_NAME(Token):public DPtr @@ -37,4 +39,21 @@ 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) +{ + dbg.nospace()<<"Token(str="< IntLiteralParser(const QString&expr,Engine&engine,int start) +{ + QString ls; + //shortcut: single char? + if(start==(expr.size()-1)){ + ls=expr.mid(start,1); + return QPair(ls,ls.toInt()); + } + //check type + if(expr[start]>'0' && expr[start]<='9'){ + //decimal + for(int i=start;i'9')break; + ls+=expr[i]; + } + }else //oct or hex + if(expr[start+1]=='x'){ + //hex + ls="0x"; + for(int i=start+2;i='0' && expr[i]<='9')&& + (expr[i]>='a' && expr[i]<='f')&& + (expr[i]>='A' && expr[i]<='F')) + ls+=expr[i]; + else + break; + } + }else{ + //oct + for(int i=start;i'7')break; + ls+=expr[i]; + } + } + return QPair(ls,ls.toLongLong(0,0)); +} + +void IntEngine::configureIntEngine(ELAM::Engine& eng) { //TODO: implement + eng.setLiteralParser(IntLiteralParser,"0123456789",40); } diff --git a/src/elamvalue.cpp b/src/elamvalue.cpp index e60429b..943b10d 100644 --- a/src/elamvalue.cpp +++ b/src/elamvalue.cpp @@ -5,6 +5,8 @@ #include "elamvalue.h" +#include + namespace ELAM { Exception::Exception() @@ -20,6 +22,7 @@ Exception::Exception(ErrorType tp,QString errText, Position pos) merr=errText; mpos=pos; } + static int Exception_metaid=qRegisterMetaType(); int Exception::metaTypeId() { @@ -38,5 +41,13 @@ int AnyType::metaTypeId() return AnyType_metaid; } +QDebug& operator<< ( QDebug& dbg, const ELAM::Position& pos) +{ + if(!pos.isValid())dbg.nospace()<<"Position(invalid)"; + else dbg.nospace()<<"Position(line="<&p){mline=p.first;mcol=p.second;} - Position(const QPoint &p){mline=p.y();mcol=p.x();} - Position(int line,int col){mline=line;mcol=col;} - Position(int col){mline=1;mcol=col;} - Position(){mline=mcol=-1;} + ///converts a pair of ints to a position + inline Position(const QPair&p){mline=p.first;mcol=p.second;} + ///converts a QPoint to a position - x is interpreted as column, y as line + inline Position(const QPoint &p){mline=p.y();mcol=p.x();} + ///instantiates a position from line and column + inline Position(int line,int col){mline=line;mcol=col;} + ///instantiates a position from column only, line is assumed to be "1" + inline Position(int col){mline=1;mcol=col;} + ///instantiates an invalid position + inline Position(){mline=mcol=-1;} + ///copy constructor + inline Position(const Position&p){mline=p.mline;mcol=p.mcol;} + + ///copies a position + inline Position& operator=(const Position&p){mline=p.mline;mcol=p.mcol;return *this;} + + ///returns the line of this position or -1 if the position is invalid + inline int line()const{if(mcol>=0)return mline;else return -1;} + ///returns the column of this position or -1 if the position is invalid + inline int column()const{if(mline>=0)return mcol;else return -1;} - int line()const{return mline;} - int column()const{return mcol;} + ///true if this position is valid + inline bool isValid()const{return mline>=0 && mcol >=0;} - operator QPair()const{return QPair(mline,mcol);} - operator QPoint()const{return QPoint(mcol,mline);} + ///transparently converts to a pair of ints + inline operator QPair()const{return QPair(mline,mcol);} + ///transparently converts to a QPoint, with x=column and y=line + inline operator QPoint()const{return QPoint(mcol,mline);} private: int mline,mcol; }; +QDebug&operator<<(QDebug&,const Position&); /**Objects of this class represent an exception in the evaluation of an ELAM expression.*/ class Exception diff --git a/tests/parser/parser.cpp b/tests/parser/parser.cpp new file mode 100644 index 0000000..857f639 --- /dev/null +++ b/tests/parser/parser.cpp @@ -0,0 +1,82 @@ +#include "elam.h" + +#include "parser.h" + +#include +#include +#include + +using namespace ELAM; + +void ElamTest::charClass() +{ + CharacterClassSettings def; + //consistency of default + QCOMPARE(def.isConsistent(),true); + //name class + QCOMPARE(def.isName("halloWelt0_x"),true); + QCOMPARE(def.isName("_halloWelt0"),true); + QCOMPARE(def.isName("0halloWelt0_x"),false); + //operator class + QCOMPARE(def.isOperator("=*+-"),true); + QCOMPARE(def.isOperator("a-+"),false); + //right side assigment + QCOMPARE(def.isAssignment("="),true); + QCOMPARE(def.isAssignment("+="),true); + QCOMPARE(def.isAssignment("=+"),false); + QCOMPARE(def.isSimpleAssignment("="),true); + QCOMPARE(def.isSimpleAssignment("+="),false); + //left side assigment + def.setAssignmentChars('=',0); + QCOMPARE(def.isAssignment("="),true); + QCOMPARE(def.isAssignment("=+"),true); + QCOMPARE(def.isAssignment("+="),false); + QCOMPARE(def.isSimpleAssignment("="),true); + QCOMPARE(def.isSimpleAssignment("=+"),false); + //two sided assigment + def.setAssignmentChars(':','='); + QCOMPARE(def.isAssignment(":="),true); + QCOMPARE(def.isAssignment("="),false); + QCOMPARE(def.isAssignment(":"),false); + QCOMPARE(def.isAssignment(":+="),true); + QCOMPARE(def.isAssignment(":=+"),false); + QCOMPARE(def.isAssignment("+:="),false); + QCOMPARE(def.isSimpleAssignment(":="),true); + QCOMPARE(def.isSimpleAssignment(":+="),false); + QCOMPARE(def.isSimpleAssignment(":"),false); + QCOMPARE(def.isSimpleAssignment("="),false); + //collision between name/operator + def.setOperatorClass("+-abcd"); + QCOMPARE(def.isConsistent(),false); + //collision between name/literal + def=CharacterClassSettings(); + def.setLiteralStartClass("abcd"); + QCOMPARE(def.isConsistent(),false); + //collision between literal/operator + def.setLiteralStartClass("+-"); + QCOMPARE(def.isConsistent(),false); + //collision between parentheses and names/ops/literals/itself + def=CharacterClassSettings(); + def.setParentheses('a','b'); + QCOMPARE(def.isConsistent(),false); + def.setParentheses('+','-'); + QCOMPARE(def.isConsistent(),false); + def.setParentheses('0','1'); + QCOMPARE(def.isConsistent(),false); + def.setParentheses('(','('); + QCOMPARE(def.isConsistent(),false); +} + +void ElamTest::tokenizer() +{ + IntEngine ie; + QString ex="a= bcd +345*efg*(65/(5))"; + QList tl=ie.tokenize(ex); + /*qDebug()<<"expression:"< + +class ElamTest:public QObject +{ + Q_OBJECT + private slots: + void charClass(); + void tokenizer(); +}; diff --git a/tests/parser/parser.pro b/tests/parser/parser.pro new file mode 100644 index 0000000..87012c4 --- /dev/null +++ b/tests/parser/parser.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = parsertest +QT -= gui +CONFIG += qtestlib debug +INCLUDEPATH += . ../../src +DEPENDPATH += $$INCLUDEPATH +LIBS += -L../.. -lelam + +SOURCES += parser.cpp +HEADERS += parser.h \ No newline at end of file -- 1.7.2.5