From cd6e404b318e2fbc0ee94072dc9fbf02c1c48640 Mon Sep 17 00:00:00 2001 From: hama Date: Wed, 25 Jun 2014 00:26:35 +0200 Subject: [PATCH] dayly work --- rplcore/rplconfig.hpp | 4 +- rplcore/rpllogger.cpp | 2 +- rplcore/rpllogger.hpp | 4 +- rplcore/rplstring.cpp | 4 +- rplcore/rplstring.hpp | 2 +- rplcore/rpltest.cpp | 10 +- rplcore/rpltest.hpp | 4 +- rplexpr/rplasclasses.cpp | 367 +++++++++++++++++---- rplexpr/rplasclasses.hpp | 118 +++++-- rplexpr/rplastree.cpp | 607 +++++++++++++++++++++++++++++------ rplexpr/rplastree.hpp | 237 ++++++++------ rplexpr/rplexpr.hpp | 3 +- rplexpr/rpllexer.cpp | 36 ++- rplexpr/rpllexer.hpp | 16 +- rplexpr/rplmfparser.cpp | 295 +++++++++++++++++ rplexpr/rplmfparser.hpp | 62 ++++ rplexpr/rplsource.cpp | 4 +- rplexpr/rplvm.cpp | 13 + rplexpr/rplvm.hpp | 34 ++ rplstatic/rplstatic.pro | 8 +- unittests/main.cpp | 8 +- unittests/rplastree_test.cpp | 102 ++++++ unittests/rplstring_test.cpp | 2 +- unittests/unittests.pro | 3 +- 24 files changed, 1644 insertions(+), 301 deletions(-) create mode 100644 rplexpr/rplmfparser.cpp create mode 100644 rplexpr/rplmfparser.hpp create mode 100644 rplexpr/rplvm.cpp create mode 100644 rplexpr/rplvm.hpp create mode 100644 unittests/rplastree_test.cpp diff --git a/rplcore/rplconfig.hpp b/rplcore/rplconfig.hpp index 46af106..abf3a71 100644 --- a/rplcore/rplconfig.hpp +++ b/rplcore/rplconfig.hpp @@ -18,7 +18,7 @@ public: bool read(const char* file); bool write(const char* file); void clear(); - const QVector& getLines() const; + const QList& getLines() const; virtual bool asBool(const char* key, bool defaultValue) const; virtual int asInt(const char* key, int defaultValue) const; @@ -27,7 +27,7 @@ private: void initLogger(); private: const char* m_file; - QVector m_lineList; + QList m_lineList; bool m_readOnly; RplLogger* m_logger; // true: the logger must be destroyed in the destructor diff --git a/rplcore/rpllogger.cpp b/rplcore/rpllogger.cpp index e044b97..0499c1a 100644 --- a/rplcore/rpllogger.cpp +++ b/rplcore/rpllogger.cpp @@ -624,7 +624,7 @@ void RplMemoryAppender::log(RplLoggerLevel level, int location, * * @return the line list */ -const QVector& RplMemoryAppender::getLines() const { +const QList& RplMemoryAppender::getLines() const { return m_lines; } diff --git a/rplcore/rpllogger.hpp b/rplcore/rpllogger.hpp index 37e56eb..791d14c 100644 --- a/rplcore/rpllogger.hpp +++ b/rplcore/rpllogger.hpp @@ -155,10 +155,10 @@ public: public: virtual void log(RplLoggerLevel level, int location, const char* message, RplLogger* logger); - const QVector& getLines() const; + const QList& getLines() const; void clear(); private: - QVector m_lines; + QList m_lines; // maximum count of m_lines. If larger the oldest lines will be deleted. int m_maxLines; // true: standard prefix (level + datetime) will be stored too. diff --git a/rplcore/rplstring.cpp b/rplcore/rplstring.cpp index 6a2b3e7..da2c2c9 100644 --- a/rplcore/rplstring.cpp +++ b/rplcore/rplstring.cpp @@ -208,10 +208,10 @@ QByteArray RplString::replaceNode(const char* source, const char* newNode){ * @param separator the separator between the items to split * @return an array with the splitted source */ -QVector RplString::toArray(const char* source, +QList RplString::toArray(const char* source, const char* separator) { const char* end = source; - QVector rc; + QList rc; rc.reserve(count(source, separator) + 1); int lengthItem = strlen(separator); while(*end != '\0') { diff --git a/rplcore/rplstring.hpp b/rplcore/rplstring.hpp index d287ff0..ab12169 100644 --- a/rplcore/rplstring.hpp +++ b/rplcore/rplstring.hpp @@ -24,7 +24,7 @@ public: static QByteArray replaceNode(const char* source, const char* newNode); static bool write(const char* file, const char* content = NULL, const char* mode = "w"); - static QVector toArray(const char* source, const char* separator); + static QList toArray(const char* source, const char* separator); static QByteArray toCString(const char* source, int maxLength = -1); static QByteArray toNumber(int value, const char* format = "%d"); static int lengthOfNumber(const char* text, bool skipTrailingSpaces = false); diff --git a/rplcore/rpltest.cpp b/rplcore/rpltest.cpp index 7cce846..abb952f 100644 --- a/rplcore/rpltest.cpp +++ b/rplcore/rpltest.cpp @@ -172,8 +172,8 @@ bool RplTest::assertEquals(const char* expected, const char* current, bool equal = strcmp(expected, current) == 0; if(! equal) { if(strchr(expected, '\n') != NULL || strchr(current, '\n')) { - QVector exp = RplString::toArray(expected, "\n"); - QVector cur = RplString::toArray(current, "\n"); + QList exp = RplString::toArray(expected, "\n"); + QList cur = RplString::toArray(current, "\n"); equal = assertEquals(exp, cur, file, lineNo); } else { int ix = 0; @@ -211,8 +211,8 @@ bool RplTest::assertEquals(const char* expected, const char* current, * @param lineNo the line number containing the test * @return true: equal */ -bool RplTest::assertEquals(const QVector& expected, - const QVector& current, const char* file, int lineNo) { +bool RplTest::assertEquals(const QList& expected, + const QList& current, const char* file, int lineNo) { int nMax = expected.size(); bool rc = true; if(current.size() < nMax) @@ -370,7 +370,7 @@ bool RplTest::error(const char* format, ...) { */ bool RplTest::logContains(const char* pattern) { - const QVector& lines = m_memoryAppender.getLines(); + const QList& lines = m_memoryAppender.getLines(); QRegularExpression rexpr(pattern); bool rc = false; QRegularExpressionMatch match; diff --git a/rplcore/rpltest.hpp b/rplcore/rpltest.hpp index e5cbde5..ed7caed 100644 --- a/rplcore/rpltest.hpp +++ b/rplcore/rpltest.hpp @@ -36,8 +36,8 @@ public: const char* file, int lineNo); bool assertEquals(const char* expected, const QByteArray& current, const char* file, int lineNo); - bool assertEquals(const QVector& expected, - const QVector& current, const char* file, int lineNo); + bool assertEquals(const QList& expected, + const QList& current, const char* file, int lineNo); bool assertTrue(bool condition, const char* file, int lineNo); bool assertFalse(bool condition, const char* file, int lineNo); bool assertNull(const void* ptr, const char* file, int lineNo); diff --git a/rplexpr/rplasclasses.cpp b/rplexpr/rplasclasses.cpp index f37101d..4887b5a 100644 --- a/rplexpr/rplasclasses.cpp +++ b/rplexpr/rplasclasses.cpp @@ -10,105 +10,223 @@ #include "rplcore/rplcore.hpp" #include "rplexpr/rplexpr.hpp" -/** @class RplASList rplastree.hpp "rplexpr/rplastree.hpp" +RplASList RplASList::m_instance; +RplASMap RplASMap::m_instance; +RplASFloat RplASFloat::m_instance; +RplASInteger RplASInteger::m_instance; +RplASString RplASString::m_instance; +RplASBoolean RplASBoolean::m_instance; + +/** @class RplSymbolSpace rplastree.hpp "rplexpr/rplastree.hpp" * - * @brief Implements the class of a list. + * @brief Implements a symbol space for the parser. * - * A list is a container of any values. Values can be selected by the index. + * A symbol space is a container of the classes and variables which can be used + * at a given moment while compiling. + * + * A symbol space could have a parent which is a symbol space too and extends + * the visible classes / variables. + * + * If an entry exists more than one time in a symbol space chain only the first + * can be seen. + * + * The "last" parent is named "global" and exists always. It contains the + * core classes like String, List and Map and methods like print. + * + * Each module hit by the compiler builds its own symbol space with global as parent. + * + * Each class defines a symbol space and takes the symbol space of the superclass + * as its parent. If there is no superclass the module's symbol space will be taken. + * + * Each method defines its own symbol space. The parent may be the symbol space of + * the module or of the class. */ + /** * @brief Constructor. + * + * @param name the symbol space's name + * @param parent the parent of the symbol space */ -RplASList::RplASList() : - RplASClass("List") +RplSymbolSpace::RplSymbolSpace(RplSymbolSpace::SymbolSpaceType type, + const QString &name, RplSymbolSpace *parent) : + m_type(type), + m_name(name), + m_variables(), + m_classes(), + m_parent(parent), + m_body(NULL) { + if (type == SST_GLOBAL){ + m_classes[RplASInteger::m_instance.name()] = &RplASInteger::m_instance; + m_classes[RplASBoolean::m_instance.name()] = &RplASBoolean::m_instance; + m_classes[RplASFloat::m_instance.name()] = &RplASBoolean::m_instance; + m_classes[RplASString::m_instance.name()] = &RplASString::m_instance; + m_classes[RplASList::m_instance.name()] = &RplASList::m_instance; + m_classes[RplASMap::m_instance.name()] = &RplASMap::m_instance; + } + } +/** + * @brief Destructor. + */ +RplSymbolSpace::~RplSymbolSpace() +{ + QMap::iterator it; + for (it = m_variables.begin(); it != m_variables.end(); it++){ + delete it.value(); + } + for (it = m_variables.begin(); it != m_variables.end(); it++){ + delete it.value(); + } +} +/** + * @brief Returns the name of the symbol space. + * + * @return the name + */ +QString RplSymbolSpace::name() const +{ + return m_name; +} + + +/** @class RplASBoolean rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a Boolean. + * + * A Boolean is a real value. + */ +/** + * @brief Constructor. + */ +RplASBoolean::RplASBoolean() : + RplASClass("Boolean") +{ +} /** * @brief Creates a value object (used in RplASVariant). * + * For Booleans nothing is to do! + * * @param source NULL or a source to copy - * @return a new value object (specific for the class) + * @return NULL */ -void* RplASList::newValueInstance(void* source) +void* RplASBoolean::newValueInstance(void* source) const { - QList* rc = new QList(); - if (source != NULL){ - QList* source2 = (QList*) source; - rc->reserve(source2->size()); - QList::iterator it; - for (it = source->begin(); - it != source->end(); - it++){ - // deleting in destroyValue(): - rc->append(new RplASValue(*it)); - } - } - return (void*) rc; + return NULL; } /** - * @brief Destroyes the given object. + * @brief Destroys the given object. * - * The object must be created by newValueInstance(). + * For Booleans nothing is to do! * * @param object object to destroy */ -void RplASList::destroyValueInstance(void* object) +void RplASBoolean::destroyValueInstance(void* object) const { - delete (QList*) object; } -/** @class RplASMap rplastree.hpp "rplexpr/rplastree.hpp" +/** + * @brief Calculates the boolean value of an class specific object. * - * @brief Implements the class of a map. + * This method should never be called. * - * A map is a container of (key, value) pairs. - * Values can be selected by the key. + * @param object the object to test (with type QList*) + * @return false + */ +bool RplASBoolean::boolValueOf(void* object) const +{ + return false; +} + +/** @class RplASNumber rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a Boolean. + * + * A Boolean is a one of the values true and false. */ /** * @brief Constructor. */ -RplASMap::RplASMap() : - RplASClass("Map") +RplASFloat::RplASFloat() : + RplASClass("Boolean") { } + +RplASFloat::RplASFloat(const QString& name) : + RplASClass(name) +{ + m_superClass = &RplASFloat::m_instance; +} /** * @brief Creates a value object (used in RplASVariant). * + * For Booleans nothing is to do! + * * @param source NULL or a source to copy - * @return a new value object (specific for the class) + * @return NULL */ -void* RplASMap::newValueInstance(void* source) +void* RplASFloat::newValueInstance(void* source) const { - QMap* rc = new QMap(); - if (source != NULL){ - QMap* source2 = - (QMap*) source; - rc->reserve(source2->size()); - QMap::iterator it; - for (it = source->begin(); - it != source->end(); - it++){ - // deleting in destroyValue(): - rc->append(new RplASValue(*it)); - } - } - return (void*) rc; + return NULL; } /** - * @brief Destroyes the given object. + * @brief Destroys the given object. * - * The object must be created by newValueInstance(). + * For Booleans nothing is to do! * * @param object object to destroy */ -void RplASMap::destroyValueInstance(void* object) +void RplASFloat::destroyValueInstance(void* object) const +{ +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * This method should never be called. + * + * @param object the object to test + * @return false + */ +bool RplASFloat::boolValueOf(void* object) const { - delete (QMap*) object; + return false; } +/** @class RplASInteger rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a Boolean. + * + * A Boolean is a one of the values true and false. + */ +/** + * @brief Constructor. + */ +RplASInteger::RplASInteger() : + RplASFloat("Integer") +{ +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * This method should never be called. + * + * @param object the object to test + * @return false + */ +bool RplASInteger::boolValueOf(void* object) const +{ + return false; +} + + + /** @class RplASString rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements the class of a string. @@ -128,56 +246,179 @@ RplASString::RplASString() : * @param source NULL or a source to copy * @return a new value object (specific for the class) */ -void* RplASString::newValueInstance(void* source) +void* RplASString::newValueInstance(void* source) const { QString* rc = source == NULL ? new QString() : new QString(*(QString*) source); return (void*) rc; } /** - * @brief Destroyes the given object. + * @brief Destroys the given object. * * The object must be created by newValueInstance(). * * @param object object to destroy */ -void RplASString::destroyValueInstance(void* object) +void RplASString::destroyValueInstance(void* object) const { delete (QString*) object; } -/** @class RplASNumber rplastree.hpp "rplexpr/rplastree.hpp" +/** + * @brief Calculates the boolean value of an class specific object. + * + * This method should never be called. + * + * @param object the object to test (a QString* instance) + * @return false: the string is empty + * true: otherwise + */ +bool RplASString::boolValueOf(void* object) const +{ + bool rc = false; + if (object != NULL){ + QString* string = static_cast(object); + if (string == NULL) + throw RplException("RplASString.boolValueOf(): not a string"); + rc = ! string->isEmpty(); + } + return rc; +} + + +/** @class RplASList rplastree.hpp "rplexpr/rplastree.hpp" * - * @brief Implements the class of a Number. + * @brief Implements the class of a list. * - * A Number is a real value. + * A list is a container of any values. Values can be selected by the index. */ /** * @brief Constructor. */ -RplASNumber::RplASNumber() : - RplASClass("Number") +RplASList::RplASList() : + RplASClass("List") { } + /** * @brief Creates a value object (used in RplASVariant). * * @param source NULL or a source to copy - * @return NULL + * @return a new value object (specific for the class) + */ +void* RplASList::newValueInstance(void* source) const +{ + ListValueType* rc = new ListValueType(); + if (source != NULL){ + ListValueType* source2 = (ListValueType*) source; + rc->reserve(source2->size()); + ListValueType::iterator it; + for (it = source2->begin(); + it != source2->end(); + it++){ + // deleting in destroyValue(): + rc->append(new RplASVariant(*(*it))); + } + } + return (void*) rc; +} + +/** + * @brief Destroys the given object. + * + * The object must be created by newValueInstance(). + * + * @param object object to destroy + */ +void RplASList::destroyValueInstance(void* object) const +{ + delete static_cast(object); +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * @param object the object to test (with type QList*) + * @return false: the list is empty
+ * true: otherwise + */ +bool RplASList::boolValueOf(void* object) const +{ + bool rc = false; + if (object != NULL){ + ListValueType* list = static_cast(object); + if (list == NULL) + throw RplException("RplASList.boolValueOf(): not a list"); + rc = ! list->empty(); + } + return rc; +} + +/** @class RplASMap rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a map. + * + * A map is a container of (key, value) pairs. + * Values can be selected by the key. + */ +/** + * @brief Constructor. */ -void* RplASNumber::newValueInstance(void* source) +RplASMap::RplASMap() : + RplASClass("Map") { - return null; +} +/** + * @brief Creates a value object (used in RplASVariant). + * + * @param source NULL or a source to copy + * @return a new value object (specific for the class) + */ +void* RplASMap::newValueInstance(void* source) const +{ + MapValueType* rc = new MapValueType(); + if (source != NULL){ + MapValueType* source2 = + static_cast(source); + // rc->reserve(source2->size()); + MapValueType::iterator it; + for (it = source2->begin(); it != source2->end(); it++){ + // deleting in destroyValue(): + const QString& key = it.key(); + RplASVariant* value = new RplASVariant(*it.value()); + (*rc)[key] = value; + } + } + return (void*) rc; } /** - * @brief Destroyes the given object. + * @brief Destroys the given object. * * The object must be created by newValueInstance(). * * @param object object to destroy */ -void RplASNumber::destroyValueInstance(void* object) +void RplASMap::destroyValueInstance(void* object) const { + delete (MapValueType*) object; +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * @param object the object to test (with type QMap*) + * @return + */ +bool RplASMap::boolValueOf(void* object) const +{ + bool rc = false; + if (object != NULL){ + MapValueType* map = reinterpret_cast(object); + if (map == NULL) + throw RplException("RplASMap.boolValueOf(): not a map"); + rc = map->empty() > 0; + } + return rc; } diff --git a/rplexpr/rplasclasses.hpp b/rplexpr/rplasclasses.hpp index c456b5e..b79a51b 100644 --- a/rplexpr/rplasclasses.hpp +++ b/rplexpr/rplasclasses.hpp @@ -10,40 +10,122 @@ #ifndef RPLASCLASSES_HPP #define RPLASCLASSES_HPP -class RplASList : public RplASClass { +class RplSymbolSpace; +class RplVariable { public: - RplASList(); - ~RplASList(); + RplVariable(); +protected: + QString m_name; + // NULL for "simple" variables (int, float, bool) + RplSymbolSpace* m_namespace; + RplASVariant m_value; + RplASClass* m_class; +}; + +class RplSymbolSpace +{ +public: + enum SymbolSpaceType { + SST_UNDEF, + SST_GLOBAL, + SST_MODULE, + SST_CLASS, + SST_METHOD + }; + +public: + typedef QMap VariableMap; + typedef QMap ClassMap; +public: + RplSymbolSpace(SymbolSpaceType type, const QString& name, + RplSymbolSpace* parent); + ~RplSymbolSpace(); +public: + RplVariable* findVariable(const QString& name) const; + RplASClass* findClass(const QString& name) const; +public: + static void initGlobal(RplSymbolSpace& global); +private: + SymbolSpaceType m_type; + QString m_name; + VariableMap m_variables; + ClassMap m_classes; + RplSymbolSpace* m_parent; + RplASItem* m_body; public: - void* newValueInstance(void* source = NULL); - void destroyValueInstance(void* object); + static RplSymbolSpace m_global; + QString name() const; }; -class RplASMap : public RplASClass { +class RplASBoolean : public RplASClass { public: - RplASMap(); - ~RplASMap(); + RplASBoolean(); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; public: - void* newValueInstance(void* source = NULL); - void destroyValueInstance(void* object); + static RplASBoolean m_instance; }; -class RplASNumber : public RplASClass { +class RplASFloat : public RplASClass { +public: + RplASFloat(); + RplASFloat(const QString& name); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; +public: + static RplASFloat m_instance; +}; + +class RplASInteger : public RplASFloat { +public: + RplASInteger(); public: - RplASNumber(); - ~RplASNumber(); + virtual bool boolValueOf(void* object) const; public: - void* newValueInstance(void* source = NULL); - void destroyValueInstance(void* object); + static RplASInteger m_instance; }; class RplASString : public RplASClass { public: RplASString(); - ~RplASString(); public: - void* newValueInstance(void* source = NULL); - void destroyValueInstance(void* object); + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; +public: + static RplASString m_instance; +}; + +class RplASList : public RplASClass { +public: + typedef QList ListValueType; +public: + RplASList(); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; +public: + static RplASList m_instance; }; +class RplASMap : public RplASClass { +public: + typedef QMap MapValueType; +public: + RplASMap(); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; +public: + static RplASMap m_instance; +}; + + + #endif // RPLASCLASSES_HPP diff --git a/rplexpr/rplastree.cpp b/rplexpr/rplastree.cpp index 9edb194..3519946 100644 --- a/rplexpr/rplastree.cpp +++ b/rplexpr/rplastree.cpp @@ -9,6 +9,37 @@ #include "rplcore/rplcore.hpp" #include "rplexpr/rplexpr.hpp" +/** @class RplASException rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a specific exception for the Abstract Syntax Tree. + */ + +/** + * @brief RplASException::RplASException + * @param message + */ +/** + * @brief Constructor. + * + * @param position describes the position of the error/warning + * @param format the reason of the exception + * @param ... the values for the placeholders in the format. + */ +RplASException::RplASException(const RplSourcePosition* position, + const char* format, ...) : + RplException("") +{ + char buffer[64000]; + if (position != NULL) + m_message = position->toString().toUtf8(); + va_list ap; + va_start(ap, format); + vsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + m_message += buffer; +} + + /** @class RplASVariant rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements a class which can be one of the basic types. @@ -38,6 +69,22 @@ RplASVariant::RplASVariant(const RplASVariant& source): // m_value m_class(source.m_class) { + copyValue(source); +} + +/** + * @brief Assignment operator. + * + * @param source the source to copy + * @return the instance itself + */ +RplASVariant&RplASVariant::operator=(const RplASVariant& source) +{ + destroyValue(); + m_dataType = source.m_dataType; + m_class = source.m_class; + copyValue(source); + return *this; } /** @@ -48,36 +95,19 @@ void RplASVariant::copyValue(const RplASVariant& source) { switch(source.m_dataType) { - case DT_NUMBER: - m_value.m_number = source.m_value.m_number; + case DT_BOOL: + m_value.m_bool = source.m_value.m_bool; break; - case DT_STRING: - // deleting in destroyValue(): - m_value.m_string = new QString(source.m_value.m_string); + case DT_FLOAT: + m_value.m_float = source.m_value.m_float; break; - case DT_LIST: - { + case DT_INTEGER: + m_value.m_int = source.m_value.m_int; break; - } - case DT_MAP: - { - m_value.m_map = new QList(); - m_value.m_map.reserve(source.m_value.m_list.size()); - QList::iterator it; - for (it = source.m_value.m_list.begin(); - it != source.m_value.m_list.end(); - it++){ - // deleting in destroyValue(): - m_value.m_list.append(new RplASValue(*it)); - } - break - } - case DT_OBJECT: - // deleting in destroyValue(): - m_value.m_object = m_class->newValueInstance(source.m_value.m_object); + case DT_UNDEF: break; default: - break; + m_value.m_object = m_class->newValueInstance(source.m_value.m_object); } } @@ -86,32 +116,164 @@ void RplASVariant::copyValue(const RplASVariant& source) */ void RplASVariant::destroyValue() { - switch(source.m_dataType) + switch(m_dataType) { - case DT_NUMBER: - break; - case DT_STRING: - // deleting in destroyValue(): - m_value.m_string = delete m_value.m_string; - m_value.m_string = NULL; - break; - case DT_LIST: - delete m_value.m_list; - m_value.m_list = NULL; - break; - case DT_MAP: - delete m_value.m_map; - m_value.m_map = NULL; - break; - case DT_OBJECT: - m_map.destroyValueInstance(m_value.m_object); - m_value.m_object = NULL; + case DT_BOOL: + case DT_FLOAT: + case DT_INTEGER: + case DT_UNDEF: break; default: + m_class->destroyValueInstance(m_value.m_object); + m_value.m_object = NULL; break; } } +/** + * @brief Returns the numeric value. + * + * @return the numeric value + * @throw RplException the instance is not a numberic value + * + */ +qreal RplASVariant::asFloat() const +{ + if (m_dataType != DT_FLOAT) + throw RplException("RplASVariant::asNumber: not a number: %d", + m_dataType); + return m_value.m_float; +} +/** + * @brief Returns the numeric value. + * + * @return the numeric value + * @throw RplException the instance is not a numberic value + * + */ +int RplASVariant::asInt() const +{ + if (m_dataType != DT_INTEGER) + throw RplException("RplASVariant::asInt: not an integer: %d", + m_dataType); + return m_value.m_int; +} + +/** + * @brief Returns the boolean value. + * + * @return the boolean value + * @throw RplException the instance is not a boolean value + * + */ +bool RplASVariant::asBool() const +{ + if (m_dataType != DT_BOOL) + throw RplException("RplASVariant::asBool: not a boolean: %d", + m_dataType); + return m_value.m_bool; +} + +/** + * @brief Returns the class specific value. + * + * @param clazz OUT: the class of the instance. May be NULL + * @return the class specific value + * @throw RplException the instance is not a boolean value + * + */ +void* RplASVariant::asObject(const RplASClass** clazz) const +{ + if (m_dataType != DT_OBJECT) + throw RplException("RplASVariant::asObject: not an object: %d", + m_dataType); + if (clazz != NULL) + *clazz = m_class; + return m_value.m_object; +} + +/** + * @brief Returns the value as string. + * + * @return the value as string + * @throw RplException the instance is not a string value + */ +const QString* RplASVariant::asString() const +{ + const RplASClass* clazz; + const QString* rc = static_cast(asObject(&clazz)); + if (clazz->name() != "String"){ + const QString& name = clazz->name(); + throw RplException("RplASVariant::asString: not an string: %s", + name.toUtf8().constData()); + } + return rc; +} + +/** + * @brief Make the instance to a numeric value. + * + * @param number the numeric value. + */ +void RplASVariant::setFloat(qreal number) +{ + destroyValue(); + m_dataType = DT_FLOAT; + m_value.m_float = number; + m_class = &RplASFloat::m_instance; +} + +/** + * @brief Make the instance to an integer value. + * + * @param integer the numeric value. + */ +void RplASVariant::setInt(int integer) +{ + destroyValue(); + m_dataType = DT_INTEGER; + m_value.m_int = integer; + m_class = &RplASInteger::m_instance; +} + +/** + * @brief Make the instance to a boolean value. + * + * @param value the boolean value. + */ +void RplASVariant::setBool(bool value) +{ + destroyValue(); + m_dataType = DT_BOOL; + m_value.m_bool = value; + m_class = &RplASBoolean::m_instance; +} + +/** + * @brief Make the instance to a boolean value. + * + * @param value the string value. + */ +void RplASVariant::setString(const QString& string) +{ + // deletion in RplASVariant::destroyValue(): + setObject(new QString(string), &RplASString::m_instance); +} + +/** + * @brief Make the instance to an object. + * + * @param object the class specific value object. + */ +void RplASVariant::setObject(void* object, const RplASClass* clazz) +{ + destroyValue(); + m_dataType = DT_OBJECT; + m_value.m_object = object; + m_class = clazz; +} + + /** @class RplASItem rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements the abstract base class of all entries of an AST. @@ -137,12 +299,22 @@ RplASItem::~RplASItem() { } -RplSourcePosition* RplASItem::position() const +/** + * @brief Returns the position of the item in the source code. + * + * @return the position of the item + */ +const RplSourcePosition* RplASItem::position() const { return m_position; } -void RplASItem::setPosition(RplSourcePosition* position) +/** + * @brief Stores the position in the source code. + * + * @param position the position to store + */ +void RplASItem::setPosition(const RplSourcePosition* position) { m_position = position; } @@ -152,66 +324,86 @@ void RplASItem::setPosition(RplSourcePosition* position) * @brief Implements the abstract base class of value containing items. * */ -RplASExpr::RplASExpr() +RplASCalculable::RplASCalculable() { } -/** @class RplASValue rplastree.hpp "rplexpr/rplastree.hpp" +/** @class RplASConstant rplastree.hpp "rplexpr/rplastree.hpp" * - * @brief Implements the abstract base class of all named values, e.g. variables. + * @brief Implements a constant for the Abstract Syntax Tree. * */ /** * @brief Constructor. * - * @param type the type of the instance - * @param name the name of the instance - * @param isConst true: the value is constant: It is not allowed to change it */ -RplASValue::RplASValue(RplASItemType type, const QString& name, boolean isConst) : - RplASItem(type), - m_name(name), - m_isConst(isConst) +RplASConstant::RplASConstant() : + RplASItem(AST_CONSTANT), + m_value() { } + /** - * @brief Destructor. + * @brief Calculates the expression. + * + * @param value OUT: the calculated value */ -RplASValue::~RplASValue() +void RplASConstant::calc(RplASVariant& value) { + value = m_value; } +/** + * @brief Returns the value of the constant. + * + * This method will be used to set the value of the constant: + *
RplASConstant constant;
+ * constant.value().setString("Jonny");
+ *
+ * + * @return the internal value + */ +RplASVariant& RplASConstant::value() +{ + return m_value; +} -/** @class RplASScalar rplastree.hpp "rplexpr/rplastree.hpp" +/** @class RplASNamedValue rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the abstract base class of all named values, e.g. variables. * - * @brief Implements a variable/constant with exactly one value. */ + /** - * @brief Constructor - * @param name - * @param isConst + * @brief Constructor. + * + * @param name the name of the instance + * @param attr a bitmask of Attribute values, e.g. A_CONST */ -RplASScalar::RplASScalar(const QString& name, boolean isConst) : - RplASValue(AST_SCALAR, name, isConst), - m_value() +RplASNamedValue::RplASNamedValue(const QString& name, int attributes) : + RplASItem(AST_NAMED_VALUE), + m_name(name), + m_attributes(attributes) { } - /** - * @brief Destructor. + * @brief Returns the name. + * + * @return the name */ -RplASScalar::~RplASScalar() +QString RplASNamedValue::name() const { + return m_name; } - /** - * @brief RplASScalar::calc - * @return the value of the variable/constant + * @brief Calculates the value. + * + * @param value OUT: the value of the instance */ -QVariant RplASScalar::calc() +void RplASNamedValue::calc(RplASVariant& value) { - return m_value; + } /** @class RplASNode1 rplastree.hpp "rplexpr/rplastree.hpp" @@ -239,6 +431,13 @@ RplASNode1::~RplASNode1() delete m_child; m_child = NULL; } +/** + * @brief Sets the child. + */ +void RplASNode1::setChild(RplASItem* child) +{ + m_child = child; +} /** @class RplASNode2 rplastree.hpp "rplexpr/rplastree.hpp" * @@ -292,50 +491,58 @@ RplASNode3::~RplASNode3() m_child3 = NULL; } -/** @class RplASBinOp rplastree.hpp "rplexpr/rplastree.hpp" +/** @class RplASNode4 rplastree.hpp "rplexpr/rplastree.hpp" * - * @brief Implements a binary operator. + * @brief Implements a inner node of the abstract syntax tree with 3 childs. + * + * This class is an abstract class. */ + /** - * @brief Constructor. - * - * @param op the operator + * @brief RplASNode4::RplASNode4 + * @param type */ -RplASBinOp::RplASBinOp(RplASBinaryOperator op) : - RplASNode2(type), - m_op(op) +RplASNode4::RplASNode4(RplASItemType type) : + RplASNode3(type), + m_child4(NULL) { } + /** * @brief Destructor. */ -RplASUnaryOp::~RplASBinOp() +RplASNode4::~RplASNode4() { + delete m_child4; + m_child4 = NULL; } /** @class RplASUnaryOp rplastree.hpp "rplexpr/rplastree.hpp" * - * @brief Implements a unary operator. + * @brief Implements a unary operation. + * + * This is a operation with one operand, e.g. the boolean not operation. */ /** * @brief Constructor. - * - * @param op operator */ -RplASUnaryOp::RplASUnaryOp(RplASUnaryOperator op) : - RplASItem(AST_UNOP), - m_op(op) +RplASUnary::RplASUnaryOp(int op) : + m_operator(op) { } + /** - * @brief Destructor. + * @brief Returns the operator of the unary operation. + * + * @return the operator */ -RplASUnaryOp::~RplASUnaryOp() +int RplASUnary::getOperator() const { - + return m_operator; } + /** @class RplASStatement rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements a base class for all statements. @@ -359,6 +566,59 @@ RplASStatement::~RplASStatement() m_successor = NULL; } + +/** @class RplASCondition rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a condition. + * + * The condition is a statement and a RplASExpr and will be used in other + * statements like for and while. + * + */ +/** + * @brief Constructor. + */ +RplASCondition::RplASCondition() : + RplASNode1(AST_CONDITION), + RplASCalculable() +{ +} + +void RplASCondition::calc(RplASVariant& value) +{ + value.setBool(false); + RplASCalculable* node = dynamic_cast(m_child); + if (node == NULL) + throw RplASException(m_position, "child of condition is not calculable"); + node->calc(value); +} +/** + * @brief Calculates the boolean value and returns it. + * + * @return the boolean value of the expression + */ +bool RplASCondition::calcAsBool() +{ + bool rc = false; + RplASVariant value; + calc(value); + switch(value.m_dataType){ + case RplASVariant::DT_FLOAT: + rc = value.m_value.m_float == 0; + break; + case RplASVariant::DT_BOOL: + rc = value.m_value.m_bool; + break; + case RplASVariant::DT_OBJECT: + rc = value.m_class->boolValueOf(value.m_value.m_object); + break; + default: + rc = false; + break; + } + return rc; +} + /** @class RplASFor rplastree.hpp "rplexpr/rplastree.hpp" * * @brief Implements a for statement. @@ -374,7 +634,7 @@ RplASStatement::~RplASStatement() * @brief Constructor. */ RplASFor::RplASFor() : - RplASNode3(AST_FOR), + RplASNode4(AST_FOR), RplASStatement() { } @@ -385,10 +645,34 @@ RplASFor::~RplASFor() { } +/** + * @brief Executes the statement. + * + * Meaning of the childs: + * m_child: initialization + * m_child2: condition + * m_child3: forwarding statement + * m_child4: body + */ void RplASFor::execute() { ((RplASStatement*) m_child)->execute(); - RplASTerm* condition = (RplASTerm* condition)RplASTerm + RplASCondition* condition = dynamic_cast(m_child2); + if (condition == NULL) + throw RplASException(m_child2 == NULL ? m_position : m_child2->position(), + "for statement: not a condition"); + RplASStatement* forwarding = dynamic_cast(m_child3); + if (forwarding == NULL) + throw RplASException(m_child3 == NULL ? m_position : m_child3->position(), + "for statement: forwarding is not a statement"); + RplASStatement* body = dynamic_cast(m_child4); + if (body == NULL) + throw RplASException(m_child4 == NULL ? m_position : m_child4->position(), + "for statement: body is not a statement"); + while(condition->calcAsBool()){ + body->execute(); + forwarding->execute(); + } } /** @class RplASWhile rplastree.hpp "rplexpr/rplastree.hpp" @@ -426,4 +710,139 @@ RplASClass::RplASClass(const QString& name) : { } +/** + * @brief Return the class name. + * + * @return the class name + */ +const QString& RplASClass::name() const +{ + return m_name; +} + +/** @class RplASTree rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a manager for all parts of an Abstract Syntax Tree. + * + * It contains the global symbol space and maintainance a list of used modules. + */ + +/** + * @brief Constructor. + */ +RplASTree::RplASTree() : + // freed in ~RplASTree() + m_global(new RplSymbolSpace(RplSymbolSpace::SST_GLOBAL, "global", NULL)), + m_modules(), + m_symbolSpaces(), + m_currentSpace(m_global) +{ + m_symbolSpaces.append(m_global); +} + +/** + * @brief Destructor. + */ +RplASTree::~RplASTree() +{ + SymbolSpaceMap::iterator it; + for (it = m_symbolSpaceHeap.begin(); it != m_symbolSpaceHeap.end(); it++){ + delete it.value(); + } + m_symbolSpaceHeap.clear(); +} + +/** + * @brief Handles the start of a new module. + * + * @param name the module's name + * @return true: the module is new
+ * false: the module is yet known + */ +bool RplASTree::startModule(const QString& name) +{ + bool rc = m_modules.contains(name); + if (! rc){ + // m_modules[0] is the "global" symbol space. + // freed in ~RplASTree() + RplSymbolSpace* space = new RplSymbolSpace(RplSymbolSpace::SST_MODULE, + name, m_symbolSpaces[0]); + m_symbolSpaceHeap[name] = space; + m_modules[name] = space; + m_symbolSpaces.append(space); + m_currentSpace = space; + } + return rc; +} + +/** + * @brief Handles the end of a module. + * @param name the module's name + */ +void RplASTree::finishModule(const QString& name) +{ + RplSymbolSpace* top = m_symbolSpaces.at(m_symbolSpaces.size() - 1); + if (top->name() != name) + throw RplException("RplASTree::finishModule(): module is not top: %s", + name.toUtf8().constData()); + else { + m_symbolSpaces.removeLast(); + // "global" is always the bottom: + m_currentSpace = m_symbolSpaces.at(m_symbolSpaces.size() - 1); + } +} + +/** + * @brief Handles the start of a new class definition. + * @param name + */ +void RplASTree::startClassOrMethod(const QString& name, + RplSymbolSpace::SymbolSpaceType type) +{ + // the stack m_modules is never empty because of "global" and modules. + RplSymbolSpace* parent = m_symbolSpaces[m_symbolSpaces.size() - 1]; + QString fullName = parent->name() + "." + name; + // freed in ~RplASTree() + RplSymbolSpace* space = new RplSymbolSpace(type, fullName, parent); + m_symbolSpaceHeap[fullName] = space; + m_symbolSpaces.append(space); + m_currentSpace = space; +} + +/** + * @brief Handles the end of a class definition. + * + * @param name the name of the class (short form) + */ +void RplASTree::finishClassOrMethod(const QString& name) +{ + RplSymbolSpace* top = m_symbolSpaces.at(m_symbolSpaces.size() - 1); + if (! top->name().endsWith("." + name)) + throw RplException("RplASTree::finishModule(): class is not top: %s", + name.toUtf8().constData()); + else { + m_symbolSpaces.removeLast(); + // "global" is the bottom always! + m_currentSpace = m_symbolSpaces.at(m_symbolSpaces.size() - 1); + } +} + +/** + * @brief Returns the stack of the symbol spaces. + * + * @return the stack with the active symbol spaces + */ +RplASTree::SymbolSpaceStack& RplASTree::symbolSpaces() +{ + return m_symbolSpaces; +} +/** + * @brief Returns the current symbol space (top of the stack). + * + * @return the current symbol space + */ +RplSymbolSpace* RplASTree::currentSpace() const +{ + return m_currentSpace; +} diff --git a/rplexpr/rplastree.hpp b/rplexpr/rplastree.hpp index 690136c..012197c 100644 --- a/rplexpr/rplastree.hpp +++ b/rplexpr/rplastree.hpp @@ -12,83 +12,68 @@ enum RplASItemType { AST_UNDEF, - AST_SCALAR, - AST_MATRIX, - AST_LIST, - AST_MAP, - AST_BINOP, - AST_UNOP, - AST_FUNCTION, - AST_CALL, + AST_CONSTANT, + AST_NAMED_VALUE, + AST_METHOD, + AST_INTRINSIC_METHOD, + AST_METHOD_CALL, AST_WHILE, AST_IF, + AST_CONDITION, AST_FOR, + AST_COUNTED_FOR, AST_SWITCH, - AST_STATEMENT - + AST_LEAVE, + AST_CONTINUE }; -enum RplASBinaryOperator { - ASOP_UNDEF, - ASOP_PLUS, - ASOP_MINUS, - ASOP_TIMES, - ASOP_DIV, - ASOP_MOD, - ASOP_OR, - ASOP_AND, - ASOP_GT, - ASOP_LT, - ASOP_GE, - ASOP_LE, - ASOP_EQ, - ASOP_NE, - ASOP_ASSIGN, - ASOP_ASSIGN_PLUS, - ASOP_ASSIGN_MINUS, - ASOP_ASSIGN_TIMES, - ASOP_ASSIGN_DIV, - ASOP_ASSIGN_MOD -}; -enum RplASUnaryOperator { - ASUOP_UNDEF, - ASUOP_PLUS, - ASOUP_MINUS, - ASUOP_POST_DECREMENT, - ASUOP_PRE_DECREMENT, - ASUOP_POST_INCREMENT, - ASUOP_PRE_INCREMENT +class RplASException : public RplException { +public: + RplASException(const RplSourcePosition* position, const char* message, ...); }; + class RplASClass; +class RplASNamedValue; +class RplASItem; +class RplASCondition; + class RplASVariant { public: enum DataType { DT_UNDEF, - DT_NUMBER, - DT_STRING, - DT_MAP, - DT_LIST, + DT_FLOAT, + DT_INTEGER, + DT_BOOL, DT_OBJECT }; - + friend class RplASCondition; public: RplASVariant(); ~RplASVariant(); RplASVariant(const RplASVariant& source); RplASVariant& operator=(const RplASVariant& source); + qreal asFloat() const; + int asInt() const; + bool asBool() const; + void* asObject(const RplASClass** clazz) const; + const QString* asString() const; + void setFloat(qreal number); + void setInt(int integer); + void setBool(bool value); + void setObject(void* object, const RplASClass* clazz); + void setString(const QString& string); protected: void copyValue(const RplASVariant& source); void destroyValue(); private: DataType m_dataType; union { - qreal m_number; - QString* m_string; - QMap m_map; - QList m_list; + qreal m_float; + int m_int; + bool m_bool; void* m_object; } m_value; - RplASClass* m_class; + const RplASClass* m_class; }; class RplASTree; @@ -98,39 +83,55 @@ public: friend class RplASTree; RplASItem(RplASItemType type); virtual ~RplASItem(); - +public: + const RplSourcePosition* position() const; + void setPosition(const RplSourcePosition* position); protected: - RplSourcePosition* m_position; + RplASItemType m_type; + const RplSourcePosition* m_position; }; -class RplASExpr +class RplASCalculable { public: - RplASExpr(); + RplASCalculable(); public: - virtual QVariant calc() = 0; + virtual void calc(RplASVariant& value) = 0; }; -class RplASValue : public RplASItem, public RplASExpr +class RplASConstant : public RplASItem, public RplASCalculable { public: - RplASValue(RplASItemType type, const QString& name, - boolean isConst); - virtual ~RplASValue(); + RplASConstant(); +public: + virtual void calc(RplASVariant& value); +public: + RplASVariant& value(); private: - QString m_name; - boolean m_isConst; + RplASVariant m_value; }; -class RplASScalar : public RplASValue +class RplASNamedValue : public RplASItem, public RplASCalculable { public: - RplASScalar(const QString& name, boolean isConst); - virtual ~RplASScalar(); + enum Attributes { + A_NONE, + /// the value cannot be changed. + A_CONST = 1<<1, + /// the variable/constant is found in the global namespace, not in a method + A_GLOBAL = 1<<2, + /// the variable/constant is found in the module namespace, not in a method + A_MODULE_STATIC = 1<<3 + }; + +public: + RplASNamedValue(const QString& name, int attributes = A_NONE); + QString name() const; public: - virtual QVariant calc(); + virtual void calc(RplASVariant& value); private: - QVariant m_value; + QString m_name; + int m_attributes; }; class RplASNode1 : public RplASItem @@ -138,6 +139,8 @@ class RplASNode1 : public RplASItem public: RplASNode1(RplASItemType type); virtual ~RplASNode1(); +public: + void setChild(RplASItem* child); protected: RplASItem* m_child; }; @@ -160,22 +163,31 @@ protected: RplASItem* m_child3; }; -class RplASBinOp : public RplASNode2 +class RplASNode4 : public RplASNode3 { public: - RplASBinOp(RplASBinaryOperator op); - virtual ~RplASBinOp(); -private: - RplASBinaryOperator m_op; + RplASNode4(RplASItemType type); + virtual ~RplASNode4(); +protected: + RplASItem* m_child4; }; -class RplASUnaryOp : public RplASNode1 +class RplASUnary : public RplASNode1 +{ +public: + RplASUnaryOp(int op); +public: + int getOperator() const; + +private: + int m_operator; +}; +class RplASBinaryOp : public RplASNode2 { public: - RplASUnaryOp(RplASUnaryOperator op); - virtual ~RplASUnaryOp(); + RplASBinaryOp(); private: - RplASUnaryOperator m_op; + int m_operator; }; class RplASStatement @@ -189,15 +201,22 @@ private: RplASItem* m_successor; }; -class RplASFor : public RplASNode3, public RplASStatement +class RplASCondition : public RplASNode1, public RplASCalculable +{ +public: + RplASCondition(); +public: + void calc(RplASVariant& value); + bool calcAsBool(); +}; + +class RplASFor : public RplASNode4, public RplASStatement { public: RplASFor(); virtual ~RplASFor(); public: virtual void execute(); -private: - RplASStatement* m_body; }; class RplASWhile : public RplASNode2, public RplASStatement @@ -207,20 +226,21 @@ public: virtual ~RplASWhile(); }; -class RplArgument : public RplAsNode2, public RplASStatement +class RplArgument : public RplASNode2, public RplASStatement { public: RplArgument(); virtual ~RplArgument(); }; +class RplASMethod; class RplAsMethodCall : public RplASNode2, public RplASStatement { public: - RplArgument(); - virtual ~RplArgument(); + RplAsMethodCall(); + virtual ~RplAsMethodCall(); private: - RplASFunction* m_function; + RplASMethod* m_method; RplArgument* m_arg1; }; @@ -231,12 +251,12 @@ public: virtual ~RplParameter(); private: QString m_name; - RplASValue* m_default; + RplASNamedValue* m_default; }; class RplASClass; -class RplASMethod : public RplASNode2, public RplASStatement +class RplASMethod : public RplASNode2 { public: RplASMethod(); @@ -256,26 +276,63 @@ public: * @param source NULL or a source to copy * @return a new value object (specific for the class) */ - void* newValueInstance(void* source = NULL) = 0; + virtual void* newValueInstance(void* source = NULL) const = 0; /** - * @brief Destroyes the given object. + * @brief Destroys the given object. * * The object must be created by newValueInstance(). * * @param object object to destroy */ - void destroyValueInstance(void* object) = 0; + virtual void destroyValueInstance(void* object) const = 0; + /** + * @brief Returns the boolean value of a class specific value. + * + * Example: the boolean value of an the empty string is false + * + * @param object object to test + * @return false: the object represents the false value
+ * true: otherwise + */ + virtual bool boolValueOf(void* object) const = 0; +public: + const QString& name() const; protected: QString m_name; - QMap m_methods; - RplASClass* m_superClass; + QMap m_methods; + const RplASClass* m_superClass; }; - +#include "rplexpr/rplasclasses.hpp" +class RplSymbolSpace; class RplASTree { +public: + typedef QMap SymbolSpaceMap; + typedef QList SymbolSpaceStack; public: RplASTree(); + ~RplASTree(); +public: + bool startModule(const QString& name); + void finishModule(const QString& name); + void startClassOrMethod(const QString& name, + RplSymbolSpace::SymbolSpaceType type); + void finishClassOrMethod(const QString& name); + SymbolSpaceStack& symbolSpaces(); + RplSymbolSpace* currentSpace() const; + +private: + // the mother of all symbol spaces. + RplSymbolSpace* m_global; + // contains all hit modules + SymbolSpaceMap m_modules; + // nested modules (import), classes and methods build this stack: + SymbolSpaceStack m_symbolSpaces; + // top of the stack: + RplSymbolSpace* m_currentSpace; + // contain all ever built symbol spaces: + SymbolSpaceMap m_symbolSpaceHeap; }; #endif // RPLASTREE_HPP diff --git a/rplexpr/rplexpr.hpp b/rplexpr/rplexpr.hpp index 64b2ceb..18a9cf6 100644 --- a/rplexpr/rplexpr.hpp +++ b/rplexpr/rplexpr.hpp @@ -21,6 +21,7 @@ #include "rplexpr/rplsource.hpp" #include "rplexpr/rpllexer.hpp" #include "rplexpr/rplastree.hpp" -#include "rplexpr/rplasclasses.hpp" +#include "rplexpr/rplvm.hpp" +#include "rplexpr/rplmfparser.hpp" #endif // RPLEXPR_HPP diff --git a/rplexpr/rpllexer.cpp b/rplexpr/rpllexer.cpp index 1bf42b5..a2d7e29 100644 --- a/rplexpr/rpllexer.cpp +++ b/rplexpr/rpllexer.cpp @@ -10,9 +10,10 @@ #include "rplexpr/rplexpr.hpp" #define CHAR_INFO_SIZE (int(sizeof m_charInfo / sizeof m_charInfo[0])) + /** @class RplToken rpllexer.hpp "rplexpr/rpllexer.hpp" * - * @brief Implements a token which is the smallest unit for a parser. + * @brief Implements specific exception for the lexer. * */ @@ -40,6 +41,11 @@ RplLexException::RplLexException(RplSourcePosition& position, m_message += buffer; } +/** @class RplToken rpllexer.hpp "rplexpr/rpllexer.hpp" + * + * @brief Implements a token which is the smallest unit for a parser. + * + */ /** * @brief Constructor. * @param type token type @@ -184,7 +190,7 @@ void RplToken::clear() * */ -static void itemsToVector(const char* items, QVector& vector, +static void itemsToVector(const char* items, RplLexer::StringList& vector, int firstCharFlag, int secondCharFlag, int thirdCharFlag, int restCharFlag, int charInfo[]) @@ -376,7 +382,7 @@ void RplLexer::initializeComments(const char* comments) * @param id the id of the entry in the vector. Only set if found * @return */ -int RplLexer::findInVector(int tokenLength, const QVector& vector) +int RplLexer::findInVector(int tokenLength, const StringList& vector) { int id = 0; int lbound = 0; @@ -456,7 +462,7 @@ bool RplLexer::fillInput() * otherwise: the token */ RplToken* RplLexer::findTokenWithId(RplTokenType tokenType, int flag2, - QVector& names) + StringList& names) { int length = 1; int inputLength = m_input.size(); @@ -795,6 +801,28 @@ RplToken* RplLexer::nextToken() } return rc; } +/** + * @brief Makes that nextToken() returns the current token again. + */ +void RplLexer::undoLastToken() +{ + m_waitingToken = m_currentToken; + m_currentToken = m_currentToken == &m_token1 ? &m_token2 : &m_token1; + m_currentPosition = m_currentPosition == &m_position1 + ? &m_position2 : &m_position1; + +} + +/** + * @brief Returns the next relevant token, but the token remains "unread". + * + * @return the next token which is not a space/comment + */ +RplToken*RplLexer::peekNonSpaceToken() +{ + nextNonSpaceToken(); + undoLastToken(); +} /** * @brief Returns the maximal length of a token * @return the maximal length of a token diff --git a/rplexpr/rpllexer.hpp b/rplexpr/rpllexer.hpp index 8d87710..e9ff9d7 100644 --- a/rplexpr/rpllexer.hpp +++ b/rplexpr/rpllexer.hpp @@ -69,6 +69,7 @@ class RplSource; class RplLexer { public: + typedef QList StringList; enum NumericType { NUMTYPE_UNDEF, NUMTYPE_DECIMAL = 1 << 0, @@ -125,7 +126,7 @@ public: SF_QUOTE = 1 << 2, /// character escaping like in C: \ is SF_C_ESCAPING = 1 << 3, - /// special characters like in C: \r \f \r \t + /// special characters like in C: \r \f \n \t \a SF_C_SPECIAL = 1 << 4, /// characters can be written in hexadecimal notation: \x20 is ' ' SF_C_HEX_CHARS = 1 << 5, @@ -162,6 +163,7 @@ public: virtual ~RplLexer(); public: RplToken* nextToken(); + void undoLastToken(); RplToken* peekNonSpaceToken(); RplToken* nextNonSpaceToken(); size_t maxTokenLength() const; @@ -172,23 +174,23 @@ private: void prepareOperators(const char* operators); void initializeComments(const char* comments); bool fillInput(); - int findInVector(int tokenLength, const QVector& vector); + int findInVector(int tokenLength, const StringList& vector); const char* nextText(int& length, bool &isLast); RplToken* findTokenWithId(RplTokenType tokenType, int flag2, - QVector& names); + StringList& names); RplToken* scanNumber(); RplToken* scanString(); void scanComment(); protected: RplSource* m_source; /// sorted, string ends with the id of the keyword - QVector m_keywords; + StringList m_keywords; // sorted, string ends with the id of the operator - QVector m_operators; + StringList m_operators; // sorted, each entry ends with the id of the comment start - QVector m_commentStarts; + StringList m_commentStarts; // index: id content: comment_end - QVector m_commentEnds; + StringList m_commentEnds; // index: ord(char) content: a sum of CharClassTags int m_charInfo[128]; // a list of QChars with ord(cc) > 127 and which can be the first char diff --git a/rplexpr/rplmfparser.cpp b/rplexpr/rplmfparser.cpp new file mode 100644 index 0000000..57d3898 --- /dev/null +++ b/rplexpr/rplmfparser.cpp @@ -0,0 +1,295 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#include "rplcore/rplcore.hpp" +#include "rplexpr/rplexpr.hpp" + +/** @class RplMFParser rpllexer.hpp "rplexpr/rplmfparser.hpp" + * + * @brief Implements a parser for the language MF. + * + * MF stands for Mathe Fan or Multiple Faces or .... + * This is an universal object oriented programming language with extension + * for matrix operations, simulation and graphics. + */ + +RplMFParser::RplMFParser(RplSource& source) : + m_lexer(&source, + MF_KEYWORDS, MF_OPERATORS, + "/* */ // \n", + "a-zA-Z_", "a-zA-Z0-9_", + NUMTYPE_ALL, SF_C_ALL + ) +{ +} + +/** + * @brief Parses a function or a generator. + */ +void RplMFParser::parseFunc() +{ + +} +/** + * @brief Parses an if statement. + */ +void RplMFParser::parseIf() +{ + +} +/** + * @brief Parses a while statement. + */ + +void RplMFParser::parseWhile() +{ + +} + +/** + * @brief Parses a repeat statement. + */ +void RplMFParser::parseRepeat() +{ + +} + +/** + * @brief Parses a for statement. + */ +void RplMFParser::parseFor() +{ + +} + +/** + * @brief Parses a variable definition. + * + * @param clazz NULL or the type of the variable + * @param attribute 0 or attribute of the variable: K_CONST or K_LAZY + */ +void RplMFParser::parseDefinition(RplASClass* clazz, Keyword attribute = K_UNDEF) +{ + +} + +/** + * @brief Parses an operand. + * An operand is the first and the third part of an binary operation. + * Examples: constant, variable, method call + * + * @return the node with the operand + */ +RplASItem* RplMFParser::parseOperand(int level) +{ + RplToken* token = m_lexer->nextNonSpaceToken(); + RplASItem* rc = NULL; + switch(token->tokenType()){ + case TOKEN_OPERATOR: + rc = new RplASUnary(token->id()); + rc->setChild(parseOperand); + break; + case TOKEN_STRING: + case TOKEN_NUMBER: + case TOKEN_REAL: + { + RplASConstant constant = new RplASConstant(); + item = constant; + switch(token->tokenType()){ + case TOKEN_STRING: + constant.m_value.setString(token->toString()); + break; + case TOKEN_NUMBER: + constant.m_value.setInt(token->asInteger()); + break; + case TOKEN_REAL: + constant.m_value.setFloat(token->asReal()); + break; + default: + break; + } + break; + } + case TOKEN_ID: + { + QString name = token->toString(); + token = m_lexer->nextNonSpaceToken(); + if (token->tokenType() != TOKEN_OPERATOR){ + RplASNamedValue* var = new RplASNamedValue(name); + item = var; + m_lexer->undoLastToken(); + } else { + if (token->id() == m_tokenParenthesis){ + RplASItem* args = parseArguments(); + RplAsMethodCall = + } else if (token->id() == m_tokenDecrement){ + RplASNamedValue* var = new RplASNamedValue(name); + item = var; + + } + } + break; + } + default: + break; + } + return item; +} + +/** + * @brief Parses a term. + * + * A term is a part of an expression with the same parenthesis level. + * Example: a + (3 * 7 - 2)
+ * first term: 3*7-2
+ * second term: a + term1
+ * + * @param depth the level of the parenthesis + * @return + */ +RplASItem* RplMFParser::parseTerm(int depth){ + RplToken* token; + RplSourcePosition* start = m_lexer->currentSourcePosition(); + RplASItem* top = NULL; + RplASItem* item; + bool again = true; + do { + item = parseOperand(level); + token = m_lexer->nextNonSpaceToken(); + switch(token->tokenType()){ + case TOKEN_OPERATOR: + break; + case TOKEN_STRING: + case TOKEN_NUMBER: + case TOKEN_REAL: + break; + case TOKEN_KEYWORD: + case TOKEN_OPERATOR: + case TOKEN_ID: + case TOKEN_END_OF_SOURCE: + again = false; + break; + default: + break; + } + } while(again); + +} + +/** + * @brief Parses an expression. + * + * @precond the nextNonSpaceToken() will return the first token of the expr. + * @postcond all tokens belonging to the expr are read + * + * @return the token behind the expr + */ +RplToken* RplMFParser::parseExpr() +{ + RplASItem* item = parseTerm(0); +} + +/** + * @brief Parses the body. + * + * A body is a module, a class region or a method body. + */ +void RplMFParser::parseBody() +{ + RplToken token = m_lexer->nextNonSpaceToken(); + switch(token.tokenType()) + { + case TOKEN_STRING: + case TOKEN_NUMBER: + case TOKEN_REAL: + case TOKEN_OPERATOR: + m_lexer->undoNextToken(); + token = parseExpr(); + break; + case TOKEN_KEYWORD: + switch (token.id()){ + case K_IF: + parseIf(); + break; + case K_WHILE: + parseWhile(); + break; + case K_REPEAT: + parseRepeat(); + break; + case K_FOR: + parseFor(); + break; + case K_CLASS: + parseClass(); + break; + case K_FUNCTION: + case K_GENERATOR: + parseMethod(); + break; + case K_IMPORT: + parseImport(); + break; + case K_CONST: + case K_LAZY: + parseDefinition(NULL, token.id()); + break; + case K_INT: + parseDefinition(&RplASInteger.m_instance); + break; + case K_FLOAT: + parseDefinition(&RplASFloat.m_instance); + break; + case K_BOOL: + parseDefinition(&RplASBoolean.m_instance); + break; + default: + break; + } + break; + case TOKEN_ID: + { + RplASClass* clazz = m_tree.currentSpace()->findClass(); + if (clazz != NULL){ + parseDefinition(clazz); + } else { + m_lexer->undoNextToken(); + parseExpr(); + } + break; + } + case TOKEN_END_OF_SOURCE: + break; + default: + break; + } +} + +/** + * @brief Parses a module. + * + * @precond the first char of the module is the next char to read. + * @postcond the total module is read + * + * @param name the name of the module (without path) + */ +void RplMFParser::parseModule(const QString& name) +{ + m_tree.startModule(name); + parseBody(); + m_tree.finishModule(name); +} +/** + * @brief Parse the input given by the source. + */ +void RplMFParser::parse() +{ + +} + diff --git a/rplexpr/rplmfparser.hpp b/rplexpr/rplmfparser.hpp new file mode 100644 index 0000000..73bc712 --- /dev/null +++ b/rplexpr/rplmfparser.hpp @@ -0,0 +1,62 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#ifndef RPLMFPARSER_HPP +#define RPLMFPARSER_HPP + +class RplMFParser +{ +public: + enum Keyword { + K_UNDEF, K_IF, K_THEN, K_ELSE, K_FI, K_WHILE, K_DO, K_OD, K_REPEAT, K_UNTIL, + K_FOR, K_FROM, K_TO, K_STEP, K_CASE, K_OF, K_ESAC, K_LEAVE, K_CONTINUE, K_PASS, + K_CLASS, K_END, K_FUNCTION, K_GENERATOR, K_IMPORT, + K_CONST, K_LAZY, K_INT, K_FLOAT, K_BOOL, K_NONE, K_TRUE, K_FALSE + }; +#define MF_KEYWORDS "if then else fi while do od repeat until" \ + " for from to step case of esac leave continue pass" \ + " class end function generator import" \ + " const lazy int float bool None True False" + enum Operator { + O_UNDEF, O_SEMI_SEMICOLON, O_SEMICOLON, O_COMMA, O_COLON, O_QUESTION, + O_OR, O_AND, + O_XOR, O_BIT_OR, O_BIT_AND, O_LSHIFT, O_RSHIFT, O_RSHIFT2, + O_LT, O_GT, O_LE, O_GE, O_EQ, O_NE, + O_PLUS, O_MINUS, O_DIV, O_MOD, O_TIMES, O_POWER, O_INC, O_DEC, + O_DOT + }; + +#define MF_OPERATORS ";; ; , : ?" \ + " || && " \ + " ^ | & << >> >>>" \ + " < > <= >= == !=" \ + " + - / % * ** ++ --" \ + " ." +public: + RplMFParser(RplSource& source, RplASTree& ast); +public: + void parseFunc(); + void parseIf(); + void parseWhile(); + void parseRepeat(); + void parseFor(); + void parseDefinition(RplASClass* clazz); + RplToken* parseExpr(); + void parseBody(); + void parseClass(); + void parseModule(const QString& name); + void parse(); + RplASItem* parseOperand(); + RplASItem* parseTerm(int depth); +private: + RplLexer m_lexer; + RplASTree& m_tree; +}; + +#endif // RPLMFPARSER_HPP diff --git a/rplexpr/rplsource.cpp b/rplexpr/rplsource.cpp index 05b0943..5e147ff 100644 --- a/rplexpr/rplsource.cpp +++ b/rplexpr/rplsource.cpp @@ -153,7 +153,9 @@ RplSourcePosition&RplSourcePosition::operator=(const RplSourcePosition& source) */ QString RplSourcePosition::toString() const { - QString rc = m_sourceUnit->name(); + QString rc; + if (m_sourceUnit != NULL) + rc = m_sourceUnit->name(); QTextStream stream(&rc); stream << "-" << m_lineNo << " (" << m_column << "): "; return rc; diff --git a/rplexpr/rplvm.cpp b/rplexpr/rplvm.cpp new file mode 100644 index 0000000..8e351c4 --- /dev/null +++ b/rplexpr/rplvm.cpp @@ -0,0 +1,13 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#include "rplcore/rplcore.hpp" +#include "rplexpr/rplexpr.hpp" + + diff --git a/rplexpr/rplvm.hpp b/rplexpr/rplvm.hpp new file mode 100644 index 0000000..c8f8d62 --- /dev/null +++ b/rplexpr/rplvm.hpp @@ -0,0 +1,34 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#ifndef RPLVM_HPP +#define RPLVM_HPP + +class RplStackFrame { +public: + RplStackFrame(int countArgs, int countLocals); + ~RplStackFrame(); +public: +private: + RplASVariant* m_variables; +}; + +class RPLVirtualMachine +{ +public: + RPLVirtualMachine(); +public: + void calc(); +private: + RplStackFrame m_global; + QMap m_modules; + QList m_stack; +}; + +#endif // RPLVM_HPP diff --git a/rplstatic/rplstatic.pro b/rplstatic/rplstatic.pro index ef9e871..78d1cd8 100644 --- a/rplstatic/rplstatic.pro +++ b/rplstatic/rplstatic.pro @@ -34,7 +34,9 @@ SOURCES += \ ../rplexpr/rplsource.cpp \ ../rplcore/rplqstring.cpp \ ../rplexpr/rplastree.cpp \ - ../rplexpr/rplasclasses.cpp + ../rplexpr/rplasclasses.cpp \ + ../rplexpr/rplmfparser.cpp \ + ../rplexpr/rplvm.cpp HEADERS += ../rplmodules.hpp \ ../rplcore/rplconfig.hpp \ @@ -60,7 +62,9 @@ HEADERS += ../rplmodules.hpp \ ../rplexpr/rplsource.hpp \ ../rplcore/rplqstring.hpp \ ../rplexpr/rplastree.hpp \ - ../rplexpr/rplasclasses.hpp + ../rplexpr/rplasclasses.hpp \ + ../rplexpr/rplmfparser.hpp \ + ../rplexpr/rplvm.hpp unix:!symbian { maemo5 { diff --git a/unittests/main.cpp b/unittests/main.cpp index 00d80d3..274a93c 100644 --- a/unittests/main.cpp +++ b/unittests/main.cpp @@ -22,9 +22,12 @@ void testCore(){ } void testExpr(){ + extern void testRplASTree(); + testRplASTree(); extern void testRplSource(); testRplSource(); - + extern void testRplLexer(); + testRplLexer(); } void testStandard(){ @@ -40,9 +43,6 @@ int main(int argc, char *argv[]) if (argc > 1) printf("not used: %s\n", argv[1]); - extern void testRplLexer(); - testRplLexer(); - testStandard(); } diff --git a/unittests/rplastree_test.cpp b/unittests/rplastree_test.cpp new file mode 100644 index 0000000..a225c84 --- /dev/null +++ b/unittests/rplastree_test.cpp @@ -0,0 +1,102 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ +#include "rplcore/rplcore.hpp" +#include "rplexpr/rplexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestRplASTree : public RplTest{ +private: + RplSource m_source; + RplStringReader m_reader; + RplStringSourceUnit m_unit; +public: + TestRplASTree() : + RplTest("RplASTree"), + m_source(), + m_reader(m_source), + m_unit("
", "", &m_reader) + {} +public: + void testRplASException() { + try{ + RplSourcePosition pos(&m_unit, 1, 2); + throw RplASException(&pos, "simple string: %s", "Hi"); + checkF(true); + } catch (RplASException exc){ + checkE("
-1 (2): simple string: Hi", exc.getMessage().constData()); + } + } + void testRplASVariant(){ + RplASVariant val1; + val1.setFloat(2.5E-2); + checkE(2.5E-2, val1.asFloat()); + RplASVariant val2(val1); + checkE(2.5E-2, val2.asFloat()); + + val1.setInt(4321); + checkE(4321, val1.asInt()); + val2 = val1; + checkE(4321, val2.asInt()); + + val1.setBool(false); + checkF(val1.asBool()); + val2 = val1; + checkF(val2.asBool()); + + val1.setBool(true); + checkT(val1.asBool()); + val2 = val1; + checkT(val2.asBool()); + + val1.setString("High noon!"); + checkE("High noon!", *val1.asString()); + val2 = val1; + val1.setString("Bye"); + checkE("High noon!", *val2.asString()); + RplASVariant val3(val1); + checkE("Bye", *val3.asString()); + } + void testRplASConstant(){ + RplASConstant constant; + constant.value().setString("Jonny"); + RplASVariant value; + constant.calc(value); + checkE("Jonny", *value.asString()); + } + void testRplASNamedValue(){ + RplASNamedValue value("gugo"); + checkE("gugo", value.name()); + } + void testRplASCondition(){ + RplASCondition cond; + RplASConstant* constant = new RplASConstant(); + constant->value().setString("True"); + cond.setChild(constant); + checkT(cond.calcAsBool()); + constant->value().setInt(0); + checkF(cond.calcAsBool()); + } + + virtual void doIt() { + testRplASCondition(); + testRplASNamedValue(); + testRplASConstant(); + testRplASException(); + testRplASVariant(); + } +}; +void testRplASTree() { + TestRplASTree test; + test.run(); +} + + + + + + diff --git a/unittests/rplstring_test.cpp b/unittests/rplstring_test.cpp index 9857e13..b32f701 100644 --- a/unittests/rplstring_test.cpp +++ b/unittests/rplstring_test.cpp @@ -62,7 +62,7 @@ public: } void testToArray() { - QVector array = RplString::toArray("1 abc 3", " "); + QList array = RplString::toArray("1 abc 3", " "); checkE(3, array.size()); checkE("1", array.at(0)); checkE("abc", array.at(1)); diff --git a/unittests/unittests.pro b/unittests/unittests.pro index 61f3e81..6edd0ec 100644 --- a/unittests/unittests.pro +++ b/unittests/unittests.pro @@ -32,5 +32,6 @@ SOURCES += main.cpp \ rpllexer_test.cpp \ rplqstring_test.cpp \ ../rplcore/rplqstring.cpp \ - ../rplexpr/rplasclasses.cpp + ../rplexpr/rplasclasses.cpp \ + rplastree_test.cpp -- 2.39.5