From: hama Date: Fri, 3 Apr 2015 23:06:57 +0000 (+0200) Subject: Refactoring: names similar to crepublib X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=59e0f0942a45ef3176c0eb1d4d442384b455da28;p=reqt Refactoring: names similar to crepublib --- diff --git a/appl/qfilesearch b/appl/qfilesearch new file mode 160000 --- /dev/null +++ b/appl/qfilesearch @@ -0,0 +1 @@ +Subproject commit 0000000000000000000000000000000000000000 diff --git a/base/ReByteStorage.cpp b/base/ReByteStorage.cpp new file mode 100644 index 0000000..033c048 --- /dev/null +++ b/base/ReByteStorage.cpp @@ -0,0 +1,169 @@ +/* + * 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. +*/ + +/** @file + * + * @brief A very efficient storage for bytes and C strings. + */ +/** @file rplcore/rplbytestorage.hpp + * + * @brief Definitions for a very efficient storage for bytes and C strings. + */ + +#include "base/rebase.hpp" +/** @class ReByteStorage ReByteStorage.hpp "base/ReByteStorage.hpp" + * + * @brief Implements a very efficient byte storage. + * + * Efficiency: Allocation of one block needs mostly only 1 comparison + * and 2 assignments. + * + * Restriction: The blocks can be returned (freed) only all together, not block by block. + * This can be an advantage! + * + * Process: + * The storage manages large buffers. Allocation can be done only in the + * last buffer. If the buffer has too little space for the new block a new + * buffer will be allocated and linked into the buffer list. + * One buffer can store dozens or hundreds of blocks. Therefore allocation and + * freeing is much cheeper than allocation by new(). + */ + +/** + * @brief Constructor. + * + * @param bufferSize the size of one buffer + */ +ReByteStorage::ReByteStorage(int bufferSize) : + m_bufferSize(bufferSize), + m_buffer(NULL), + m_rest(0), + m_freePosition(NULL), + m_summarySize(0), + m_buffers(0) +{ +} + +/** + * @brief Destructor. + */ +ReByteStorage::~ReByteStorage() +{ + const uint8_t* ptr = m_buffer; + while(ptr != NULL){ + const uint8_t* old = ptr; + ptr = *(const uint8_t**)(ptr); + delete[] old; + m_buffers--; + } + assert(m_buffers == 0); +} + +/** + * @brief Allocates a block in a new allocated buffer. + * + * This method will be called if the buffer has too little space. + * A new buffer will be allocated and the block will be allocated + * in this new block. + * + * @note The block address is returned, but the allocation must be done outside! + * + * @param size of the new block (inclusive the trailing '\0') + * @return a new block with the size bytes + */ +char* ReByteStorage::allocBuffer(int size) +{ + m_rest = size + sizeof(uint8_t*) <= (size_t) m_bufferSize + ? m_bufferSize : size + sizeof(uint8_t*); + m_summarySize += m_rest; + m_buffers++; + uint8_t* rc = new uint8_t[m_rest]; + * (uint8_t**)rc = m_buffer; + m_buffer = rc; + rc += sizeof(uint8_t*); + // the block allocation will be done outside! + m_freePosition = rc; + m_rest -= sizeof(uint8_t*); + return reinterpret_cast(rc); +} + +/** + * @brief Duplicates a string into a new allocated block. + * + * @param source the source string + * @param size the length of the string. + * If < 0 strlen(source) will be used + * @return a copy of the source string. The copy ends always with '\0' + */ +const char* ReByteStorage::allocateChars(const char* source, int size) +{ + if (size < 0) + size = strlen(source); + const char* rc = allocateChars(size + 1); + memcpy((void*) rc, source, size); + ((char*)rc)[size] = '\0'; + return rc; +} + +/** + * @brief Duplicates a string into a new allocated block. + * + * The unicode string will be converted into an UTF-8 string. + * + * @param source the source string + * @return a copy of the source string. The copy ends always with '\0' + */ +const char* ReByteStorage::allocUtf8(const ReString& source) +{ + const char* rc = allocateChars(source.toUtf8().constData()); + return rc; +} + +/** + * @brief Allocates a byte block without initialization. + * + * @param size the size of the block to allocate + * + * @return a byte block (without a trailing '\0') + */ +uint8_t* ReByteStorage::allocateBytes(int size) +{ + uint8_t* rc = size <= m_rest ? m_freePosition + : reinterpret_cast(allocBuffer(size)); + m_freePosition += size; + m_rest -= size; + return rc; +} + +/** + * @brief Allocates a byte block initialized by '\0'. + * + * @param size the size of the block to allocate + * + * @return a byte block (without a trailing '\0') + */ +uint8_t* ReByteStorage::allocateZeros(int size) +{ + uint8_t* rc = allocateBytes(size); + memset(rc, 0, size); + return rc; +} + +/** + * @brief Copy a byte block to a new allocated byte block. + * + * @param source the source to copy + * @param size the size of the block to allocate + * @return a byte block (without a trailing '\0') + */ +uint8_t* ReByteStorage::allocateBytes(void* source, int size) +{ + uint8_t* rc = allocateBytes(size); + memcpy(rc, source, size); + return rc; +} diff --git a/base/ReByteStorage.hpp b/base/ReByteStorage.hpp new file mode 100644 index 0000000..51496b6 --- /dev/null +++ b/base/ReByteStorage.hpp @@ -0,0 +1,46 @@ +/* + * 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 RECHARSTORAGE_HPP +#define RECHARSTORAGE_HPP + +class ReByteStorage +{ +public: + ReByteStorage(int blockSize); + ~ReByteStorage(); +public: + char* allocBuffer(int size); + /** + * @brief Allocates a char block. + * + * @return a new block + */ + inline char* allocateChars(int size){ + char* rc = size <= m_rest ? (char*) m_freePosition + : allocBuffer(size); + m_freePosition += size; + m_rest -= size; + return rc; + } + const char* allocateChars(const char* source, int size = -1); + const char* allocUtf8(const ReString& source); + uint8_t* allocateBytes(int size); + uint8_t* allocateZeros(int size); + uint8_t*allocateBytes(void* source, int size); +private: + int m_bufferSize; + uint8_t* m_buffer; + int m_rest; + uint8_t* m_freePosition; + int64_t m_summarySize; + int m_buffers; +}; + +#endif // RECHARSTORAGE_HPP diff --git a/base/ReCharPtrMap.cpp b/base/ReCharPtrMap.cpp new file mode 100644 index 0000000..d421b62 --- /dev/null +++ b/base/ReCharPtrMap.cpp @@ -0,0 +1,66 @@ +/* + * 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 "base/rebase.hpp" + +/** @class ReKeyCharPtr ReCharPtrMap.hpp "base/ReCharPtrMap.hpp" + * + * @brief Allows using char* pointers as key in QMap. + * + * The template QMap uses the operator < to search in the map. + * But the char* pointers have no such an operator. + * The solution is the class RplMapCharPtr which implements + * this operator. + * + * strcmp() is used to implement the '<' operator. + */ + +/** + * @brief Constructor. + * + * @param ptr the key used in the map. + * @note the pointer will be stored in the map as a key, + * not the content + */ +ReKeyCharPtr::ReKeyCharPtr(const char* ptr) : + m_ptr(ptr) +{ +} + + +/** @class ReCharPtrMap ReCharPtrMap.hpp "base/ReCharPtrMap.hpp" + * + * @brief A template for a map using const char* as keys. + * + * The value type is dynamic (a parameter type of the template). + * + * Usage: + *

+ * ReCharPtrMap ids;
+ * if (! id.contains("jonny"))
+ *    ids["jonny"] = 1;
+ * 
+ * + * Important:
+ * Keys used with this class must be unchangable and must live during the + * whole life of the map. + * + * Wrong example: + *

+ * ReCharPtrMap ids;
+ * void init(int keyNo, int value){
+ *    char key[10];
+ *    qsnprintf(buffer, sizeof buffer, "key%d", keyNo);
+ *    ids[key] = value;
+ * }
+ * init(1, 100);
+ * 
+ * The lifetime of the key is the body of the function init(). + * The key becomes invalid after the call. + */ diff --git a/base/ReCharPtrMap.hpp b/base/ReCharPtrMap.hpp new file mode 100644 index 0000000..f76bcfb --- /dev/null +++ b/base/ReCharPtrMap.hpp @@ -0,0 +1,37 @@ +/* + * 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 RECHARPTRMAP_HPP +#define RECHARPTRMAP_HPP + +class ReKeyCharPtr { + friend bool operator <(ReKeyCharPtr const& op1, ReKeyCharPtr const& op2); +public: + ReKeyCharPtr(const char* ptr); +private: + const char* m_ptr; +}; +/** + * @brief Compares two instances of the class ReKeyCharPtr. + * @param op1 1st operand + * @param op2 2nd operand + * @return true: op1 < op2
+ * false: op1 >= op2 + */ +inline bool operator <(ReKeyCharPtr const& op1, ReKeyCharPtr const& op2){ + bool rc = strcmp(op1.m_ptr, op2.m_ptr) < 0; + return rc; +} + +template +class ReCharPtrMap : public QMap +{ +}; + +#endif // RECHARPTRMAP_HPP diff --git a/base/ReConfig.cpp b/base/ReConfig.cpp new file mode 100644 index 0000000..b605572 --- /dev/null +++ b/base/ReConfig.cpp @@ -0,0 +1,191 @@ +/* + * 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 "base/rebase.hpp" + +/** @file + * + * @brief Reading/writing configuration files. + */ +/** @file rplcore/rplconfig.hpp + * + * @brief Definitions for reading/writing configuration files. + */ + +/** @class ReConfig ReConfig.hpp "base/ReConfig.hpp" + * + * @brief Imports and exports a configuration file into a QHash instance. + * + * The format of the file:
+ * DEFS or COMMENTS + * + * DEFS ::= KEY=VALUE + * + * KEY is a string starting with an alphanumeric character and does not contain a '=' + * + * VALUE is a string + */ + +enum Locations { + LOC_WRITE_1 = LOC_FIRST_OF(LOC_CONFIG), // 10201 + LOC_WRITE_2, + LOC_READ_1, + LOC_READ_2, +}; + +/** + * Constructor. + * + * Initializes the logger and reads the configuration file + * + * @param file name of the configuration file. May be NULL + * @param readOnly true: the configuration must not be written + * @param logger NULL or a logger + */ +ReConfig::ReConfig(const char* file, bool readOnly, ReLogger* logger) : + m_file(file), + m_lineList(), + m_readOnly(readOnly), + m_logger(logger), + m_ownLogger(logger == NULL) { + if(logger == NULL) { + initLogger(); + } + if(file != NULL) + read(file); +} + +/** + * Destructor. + * + * Frees the resources. + */ +ReConfig::~ReConfig() { + if(m_ownLogger) + delete m_logger; + m_logger = NULL; +} + +/** + * Inititializes a logger. + */ +void ReConfig::initLogger() { + m_logger = new ReLogger(); + ReMemoryAppender* appender = new ReMemoryAppender(); + appender->setAutoDelete(true); + m_logger->addAppender(appender); + + ReStreamAppender* appender2 = new ReStreamAppender(stdout); + appender2->setAutoDelete(true); + m_logger->addAppender(appender2); +} + +/** + * Returns configuration value as an integer. + * + * @param key key of the wanted value + * @param defaultValue if the key does not exist this is the result + * @return defaultValue: key does not exist + * otherwise: the value assigned to key + */ +int ReConfig::asInt(const char* key, int defaultValue) const { + int rc = defaultValue; + if(contains(key)) { + rc = atoi((*this)[key]); + } + return rc; +} + +/** + * Returns the configuration value as a boolean. + * + * @param key key of the wanted value + * @param defaultValue if the key does not exist this is the result + * @return defaultValue: key does not exist + * otherwise: the value assigned to key + */ +bool ReConfig::asBool(const char* key, bool defaultValue) const { + bool rc = defaultValue; + if(contains(key)) { + QByteArray value = (*this)[key].toLower(); + rc = value == "1" || value == "y" || value == "yes" || value == "t" + || value == "true"; + } + + return rc; +} + +/** + * Returns a configuration value. + * + * @param key key of the wanted value + * @param defaultValue if the key does not exist this is the result + * @return defaultValue: key does not exist + */ +QByteArray ReConfig::asString(const char* key, const char* defaultValue) { + QByteArray rc = defaultValue; + if(contains(key)) { + rc = (*this)[key]; + } + return rc; +} + +/** + * Reads a configuration file. + * + * @param file file to read. + * @return true: OK
+ * false: error occurred + */ +bool ReConfig::read(const char* file) { + bool rc = true; + m_lineList.reserve(1024); + FILE* fp = fopen(file, "r"); + if(fp == NULL) { + m_logger->logv(LOG_ERROR, LOC_READ_1, "cannot read: %s", file); + rc = false; + } else { + char line[64000]; + char* separator; + int lineNo = 0; + while(fgets(line, sizeof line, fp) != NULL) { + lineNo++; + m_lineList.append(line); + if(isalnum(line[0]) && (separator = strchr(line, '=')) != NULL) { + QByteArray key(line, separator - line); + QByteArray value(separator + 1); + key = key.trimmed(); + value = value.trimmed(); + if(contains(key)) + m_logger->logv(LOG_WARNING, LOC_READ_2, + "defined more than once: %s-%d: %s", file, lineNo, line); + else + insert(key, value); + } + } + } + return rc; +} + +/** + * Writes a configuration file. + * + * @param file file to write. + * @return true: OK
+ * false: error occurred + */ +bool ReConfig::write(const char* file) { + bool rc = false; + if(m_readOnly) + m_logger->log(LOG_ERROR, LOC_WRITE_1, "cannot write: (readonly"); + else { + m_logger->logv(LOG_ERROR, LOC_WRITE_2, "not implemented: write(%s)", + file); + } + return rc; +} + diff --git a/base/ReConfig.hpp b/base/ReConfig.hpp new file mode 100644 index 0000000..70fa36e --- /dev/null +++ b/base/ReConfig.hpp @@ -0,0 +1,37 @@ +/* + * 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 RECONFIG_HPP +#define RECONFIG_HPP + +class ReConfig : public ReConfigurator, public QHash { +public: + ReConfig(const char* file = NULL, bool readOnly = true, + ReLogger* logger = NULL); + virtual ~ReConfig(); + +public: + bool read(const char* file); + bool write(const char* file); + void clear(); + const QList& getLines() const; + + virtual bool asBool(const char* key, bool defaultValue) const; + virtual int asInt(const char* key, int defaultValue) const; + virtual QByteArray asString(const char* key, const char* defaultValue); +private: + void initLogger(); +private: + const char* m_file; + QList m_lineList; + bool m_readOnly; + ReLogger* m_logger; + // true: the logger must be destroyed in the destructor + bool m_ownLogger; +}; + +#endif // RECONFIG_HPP diff --git a/base/ReConfigurator.hpp b/base/ReConfigurator.hpp new file mode 100644 index 0000000..ab201bc --- /dev/null +++ b/base/ReConfigurator.hpp @@ -0,0 +1,18 @@ +/* + * 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 RECONFIGURATOR_HPP +#define RECONFIGURATOR_HPP + +class ReConfigurator { +public: + virtual int asInt(const char* key, int defaultValue) const = 0; + virtual bool asBool(const char* key, bool defaultValue) const = 0; + virtual QByteArray asString(const char* key, const char* defaultValue) = 0; +}; + +#endif // RECONFIGURATOR_HPP diff --git a/base/ReContainer.cpp b/base/ReContainer.cpp new file mode 100644 index 0000000..9e8e251 --- /dev/null +++ b/base/ReContainer.cpp @@ -0,0 +1,453 @@ +/* + * 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 "base/rebase.hpp" + +/** @file + * @brief Implements a portable data container. + */ + +/** @file recore/ReContainer.hpp + * + * @brief Definition for a portable data container. + */ + +/** @class ReContainer ReContainer.hpp "base/ReContainer.hpp" + * + * @brief Implements a portable data container. + * + * The container contains a list of "bags". + * Each bag contains a sequence of items (with a simple data type). + * The items are portable: transported to another architecture + * the item is restored correct (independent of big/little endian). + * + * Format: + * container ::= magic header_size_hex2 container_size_hex '[' list_count ']' bag_descr list_of_bags
+ * list_of_bag ::= bag1 bag2 ...
+ * bag_descr ::= bag_type1 bag_type2 ... ':'
+ * bag_types := i(nteger) ' ' | s(tring) | b(indata255) | B(indata64k) X(blob4G)
+ * item_list ::= item1 item2...
+ * The binary items (bBX) are byte sequences with a starting size element. + * The size element can be a byte (255) or a word (64k) or a double word(4G). + * The order of the size element is big endian. + * + * Example (additional blanks for readibility): + *
+ * Rpl&1 09 36[2]cis:!7b Nirwana <0> Y -ab34 A long string with an trailing '0' <0>
+ * 
+ * magic header: Rpl&1 length: 09h data length: 46h bag count: 2 item types: char int string + * + */ + +enum { + // 11000 + LOC_FILL_1 = LOC_CONTAINER * 1000, + LOC_FILL_2, + LOC_FILL_3, + LOC_NEXT_BAG_1, + LOC_NEXT_ITEM_1, + LOC_NEXT_ITEM_2 = 11005, + LOC_NEXT_INT_1, + LOC_NEXT_ITEM_3, + LOC_NEXT_BAG_2, +}; + +const char* ReContainer::MAGIC_1 = "Rpl&1"; + +/** + * @brief Constructor. + * + * @param sizeHint Probable length of the container + */ +ReContainer::ReContainer(size_t sizeHint) : + m_data(""), + m_countBags(0), + m_typeList(""), + m_ixItem(0), + m_ixBag(0), + m_readPosition(NULL) { + if (sizeHint > 0) + m_data.reserve(sizeHint); +} + +/** + * @brief Destructor. + */ +ReContainer::~ReContainer() { +} + +/** + * @brief Adds an type to the type list. + * + * @param tag the type tag + */ +void ReContainer::addType(type_tag_t tag) { + if(m_countBags == 0) + startBag(); + if(m_countBags == 1) + m_typeList.append((char) tag); +} + +/** + * @brief Starts a new bag. + */ +void ReContainer::startBag() { + m_countBags++; + m_ixBag = 0; +} +/** + * @brief Adds a character to the current bag. + * + * @param value value to insert + */ +void ReContainer::addChar(char value) { + addType(TAG_CHAR); + //if (m_typeList.at(m_ixBag) != TAG_INT) + // ReLogger::logAndThrow(LOG_ERROR, __FILE__, __LINE__, 1, "unexpected type: %c instead of c", m_typeList.at(m_ixBag)); + m_data.append(value); +} +/** + * @brief Adds an integer to the current bag. + * + * @param value value to add + */ +void ReContainer::addInt(int value) { + addType(TAG_INT); + char buffer[64]; + char* ptr = buffer; + if(value < 0) { + *ptr++ = '-'; + value = - value; + } + qsnprintf(ptr, sizeof buffer - 1, "%x ", value); + m_data.append(buffer); +} +/** + * @brief Adds an integer to the current bag. + * + * @param value value to add + */ +void ReContainer::addInt(int64_t value) { + addType(TAG_INT); + char buffer[128]; + qsnprintf(buffer, sizeof buffer, "%llx ", value); + m_data.append(buffer); +} + +/** + * @brief Adds a string to the current bag.n + * + * @param value value to add + */ +void ReContainer::addString(const char* value) { + addType(TAG_STRING); + // store with trailing '\0' + m_data.append(value, strlen(value) + 1); +} +/** + * @brief Adds binary data to the current bag. + * + * @param value binary data + * @param size size of the binary data in bytes + */ +void ReContainer::addData(uint8_t* value, size_t size) { + if(size <= 255) { + addType(TAG_DATA255); + m_data.append((char) size); + } else if(size <= 0xffff) { + addType(TAG_DATA64K); + m_data.append((char)(size / 256)); + m_data.append((char)(size % 256)); + m_data.append((const char*) value, size); + } else { + addType(TAG_DATA4G); + m_data.append((char)(size / 256 / 256 / 256)); + m_data.append((char)(size / 256 / 256 % 256)); + m_data.append((char)(size / 256 % 256)); + m_data.append((char)(size % 256)); + m_data.append((const char*) value, size); + } + addType(TAG_DATA255); + +} + +/** + * @brief Returns the container byte buffer. + * + * @return the container as a byte array + */ +const QByteArray& ReContainer::getData() { + if(m_typeList.length() != 0) { + char buffer[128]; + // RPL&1 0a b5[2]cis: !12 + qsnprintf(buffer, sizeof buffer, "%x[%d]%s:", (unsigned int) m_data.length(), + m_countBags, m_typeList.data()); + char header[128+8]; + qsnprintf(header, sizeof header, "%s%02x%s", MAGIC_1, + (unsigned int) strlen(buffer), buffer); + m_data.insert(0, header); + } + return m_data; +} + +/** + * @brief Fills the container with an byte array. + * + * @param data the container as a byte array + */ +void ReContainer::fill(const QByteArray& data) { + m_data = data; + const char* ptr = m_data.data(); + if(strncmp(ptr, MAGIC_1, strlen(MAGIC_1)) != 0) + throw RplInvalidDataException(LOG_ERROR, LOC_FILL_1, "container has no magic", + data.data(), data.length()); + ptr += strlen(MAGIC_1); + unsigned int headerSize = 0; + if(sscanf(ptr, "%02x", &headerSize) != 1) + throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2, + "container has no header size", ptr, 2); + ptr += 2; + + unsigned int dataSize = 0; + unsigned int countBags = 0; + if(sscanf(ptr, "%x[%x]", &dataSize, &countBags) != 2) + throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2, + "container has no data_size[bag_count]", ptr, 16); + m_countBags = countBags; + ptr = strchr(ptr, ']') + 1; + const char* end = ptr + strspn(ptr, "cisdDX!"); + if(end == ptr || *end != ':') { + throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2, + "container has no valid typelist", ptr, 16); + } + m_typeList.clear(); + m_typeList.append(ptr, end - ptr); + m_ixBag = -1; + m_readPosition = (uint8_t*) end + 1; + + +} +/** + * @brief Returns the number of bags in the container. + * + * @return the number of bags + */ +int ReContainer::getCountBags() const { + return m_countBags; +} +/** + * @brief Sets the begin of the new bag. + */ +void ReContainer::nextBag() { + if(m_ixItem < m_typeList.length() && m_ixItem != -1) + throw ReException(LOG_ERROR, LOC_NEXT_BAG_1, NULL, + "end of bag not reached: remaining items: %s", + m_typeList.data() + m_ixItem); + m_ixItem = 0; + m_ixBag++; + if(m_ixBag >= m_countBags) + throw ReException(LOG_ERROR, LOC_NEXT_BAG_2, NULL, + "no more bags: %d", m_ixBag); +} +/** + * @brief Sets the next item. + * + * @param expected the expected data type + */ +void ReContainer::nextItem(type_tag_t expected) { + if(m_ixBag < 0) { + m_ixBag = 0; + m_ixItem = 0; + } + if(m_ixItem >= m_typeList.length()) + throw ReException(LOG_ERROR, LOC_NEXT_ITEM_1, "no more items in the bag"); + type_tag_t current = (type_tag_t) m_typeList.at(m_ixItem); + // Unify all data types: + if(current == TAG_DATA4G || current == TAG_DATA64K) + current = TAG_DATA255; + if(current != expected) + throw ReException(LOG_ERROR, LOC_NEXT_ITEM_2, NULL, + "current item is a %c, not a %c", + (char) m_typeList.at(m_ixItem), (char) expected); + m_ixItem++; + if(m_readPosition > (uint8_t*)(m_data.data() + m_data.length())) + throw ReException(LOG_ERROR, LOC_NEXT_ITEM_3, NULL, + "container size too small. Bag: %d of %d Item: %d of %d", + 1 + m_ixBag, m_countBags, 1 + m_ixItem, m_typeList.length()); +} + +/** + * @brief Reads the next character from the current item in the current bag. + * + * @return the next char from the container + */ +char ReContainer::nextChar() { + nextItem(TAG_CHAR); + char rc = *m_readPosition++; + return rc; +} + +/** + * @brief Reads the next integer from the current item in the current bag. + * + * @return the next integer from the container + */ +int ReContainer::nextInt() { + nextItem(TAG_INT); + bool isNegativ = *m_readPosition == '-'; + if(isNegativ) + m_readPosition++; + unsigned int value = 0; + if(sscanf((const char*) m_readPosition, "%x ", &value) != 1) + throw RplInvalidDataException(LOG_ERROR, LOC_NEXT_INT_1, + "not a hex_number", m_readPosition, 16); + m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1; + if(isNegativ) + value = - value; + return value; +} +/** + * @brief Reads the next integer from the current item in the current bag. + * + * @return the next integer from the container + */ +int64_t ReContainer::nextInt64() { + nextItem(TAG_INT); + bool isNegativ = *m_readPosition == '-'; + if(isNegativ) + m_readPosition++; + uint64_t value = 0; + if(sscanf((const char*) m_readPosition, "%llx ", &value) != 1) + throw RplInvalidDataException(LOG_ERROR, LOC_NEXT_INT_1, + "not a hex_number", m_readPosition, 16); + m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1; + if(isNegativ) + value = - value; + return (int64_t) value; +} + +/** + * @brief Reads the next string from the current item in the current bag. + * + * @return the next '\0' delimited string from the container + */ +const char* ReContainer::nextString() { + nextItem(TAG_STRING); + const char* rc = (const char*) m_readPosition; + m_readPosition += strlen(rc) + 1; + return rc; +} + +/** + * @brief Reads the next string from the current item in the current bag. + * + * @param data OUT: the next data item from the container + * @param append true: the item data will be appended to data
+ * false: data contains the item data only + * @return the size of the read data + */ +size_t ReContainer::nextData(QByteArray& data, bool append) { + nextItem(TAG_DATA255); + type_tag_t tag = (type_tag_t) m_typeList.at(m_ixItem - 1); + size_t length = 0; + switch(tag) { + case TAG_DATA4G: + for(int ix = 3; ix >= 0; ix--) { + length = 256 * length + m_readPosition[ix]; + } + m_readPosition += 4; + break; + case TAG_DATA64K: + length = *m_readPosition++ * 256; + length += *m_readPosition++; + break; + case TAG_DATA255: + length = *m_readPosition++; + break; + default: + break; + } + if(! append) + data.clear(); + data.append((const char*) m_readPosition, length); + m_readPosition += length; + return length; +} + + + +/** + * @brief Dumps a container as a human readable string. + * + * @param title will be used in the first line + * @param maxBags if there are more bags they will be ignored + * @param maxStringLength if strings are longer the will be cut + * @param maxBlobLength maximum count of bytes which will be dumped + * @param separatorItems separator between two items, e.g. '\\n' or '|' + * @return a human readable string describing the container + */ +QByteArray ReContainer::dump(const char* title, + int maxBags, int maxStringLength, int maxBlobLength, + char separatorItems) { + QByteArray rc; + rc.reserve(64000); + rc.append("=== ").append(title).append('\n'); + rc.append("Bags: ").append(ReStringUtil::toNumber(m_countBags)); + rc.append(" Types: ").append(m_typeList).append('\n'); + // save the current state: + int safeIxBag = m_ixBag; + int safeIxItem = m_ixItem; + m_ixBag = -1; + m_ixItem = 0; + int iValue; + QByteArray sValue; + if(maxBags > m_countBags) + maxBags = m_countBags; + for(int ixBag = 0; ixBag < maxBags; ixBag++) { + rc.append("--- bag ").append(ReStringUtil::toNumber(ixBag)).append(":\n"); + nextBag(); + QByteArray item; + int maxLength; + for(int ixItem = 0; ixItem < m_typeList.length(); ixItem++) { + type_tag_t currentType = (type_tag_t) m_typeList.at(ixItem); + switch(currentType) { + case TAG_CHAR: + rc.append(" c: ").append(nextChar()).append(separatorItems); + break; + case TAG_INT: + iValue = nextInt(); + rc.append(" i: ").append(ReStringUtil::toNumber(iValue)).append(" / "); + rc.append(ReStringUtil::toNumber(iValue, "%x")).append(separatorItems); + break; + case TAG_STRING: + sValue = nextString(); + if(sValue.length() > maxStringLength) + sValue = sValue.left(maxStringLength); + rc.append(" s: ").append(sValue).append(separatorItems); + break; + case TAG_DATA255: + case TAG_DATA64K: + case TAG_DATA4G: + nextData(item, false); + rc.append(' ').append((char) currentType).append(": ["); + rc.append(ReStringUtil::toNumber(item.length())).append("] "); + maxLength = item.length() < maxBlobLength ? item.length() : maxBlobLength; + rc.append(ReStringUtil::hexDump(item.data(), maxLength, + 16)).append(separatorItems); + break; + default: + break; + } + } + } + + // restore the current state: + m_ixBag = safeIxBag; + m_ixItem = safeIxItem; + return rc; +} + diff --git a/base/ReContainer.hpp b/base/ReContainer.hpp new file mode 100644 index 0000000..5a08aca --- /dev/null +++ b/base/ReContainer.hpp @@ -0,0 +1,82 @@ +/* + * 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 RECONTAINER_HPP +#define RECONTAINER_HPP + +// the sources generated from QT include this file directly: +#ifndef RPLCORE_HPP +#include +#include +#endif +class ReContainer { +public: + typedef enum { + + TAG_CHAR = 'c', ///< one character + TAG_INT = 'i', ///< an integer number, up to 64 bit + TAG_STRING = 's', ///< a string ending with a '\\0' + TAG_DATA255 = 'd', ///< binary data, up to 255 bytes long + TAG_DATA64K = 'D', ///< binary data, up to 64 KiBytes long + TAG_DATA4G = 'X', ///< binary data, up to 4 GiBytes long + TAG_CONTAINER = '!' ///< a container (recursion) + } type_tag_t; + static const char* MAGIC_1; +public: + ReContainer(size_t sizeHint); + virtual ~ReContainer(); +private: + // No copy constructor: no implementation! + ReContainer(const ReContainer& source); + // Prohibits assignment operator: no implementation! + ReContainer& operator =(const ReContainer& source); +public: + // Building the container: + void addType(type_tag_t tag); + void startBag(); + void addChar(char cc); + void addInt(int value); + void addInt(int64_t value); + void addString(const char* value); + void addData(uint8_t* value, size_t size); + const QByteArray& getData(); + + // Getting data from the container: + void fill(const QByteArray& data); + int getCountBags() const; + const char* getTypeList() const; + void nextBag(); + char nextChar(); + int nextInt(); + int64_t nextInt64(); + const char* nextString(); + size_t nextData(QByteArray& data, bool append = false); + + QByteArray dump(const char* title, + int maxBags, int maxStringLength = 80, int maxBlobLength = 16, + char separatorItems = '\n'); +private: + void nextItem(type_tag_t expected); +private: + // the complete data of the container + QByteArray m_data; + // the number of elements in the container + int m_countBags; + // a string with the data types of a bag + QByteArray m_typeList; + + // Getting data from the container: + + // current read position in m_typeList + int m_ixItem; + // the index of the current current bag: + int m_ixBag; + // read position in m_data: + const uint8_t* m_readPosition; +}; + +#endif // RECONTAINER_HPP diff --git a/base/ReException.cpp b/base/ReException.cpp new file mode 100644 index 0000000..0af373d --- /dev/null +++ b/base/ReException.cpp @@ -0,0 +1,204 @@ +/* + * 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. + */ +/** @mainpage + * + * @note A real public library for QT. + * + * This library contains the module groups + *
    + *
  • rplcore: basic definitions, used in all other module groups
  • + *
  • rplmath: mathematic definitions and tools
  • + *
  • rplexpr: definition for parsing and interpretation of languages
  • + *
  • rplnet: definitions and tools for tcp/udp communication
  • + *
+ * + * Each module group has a central include file, which organizes the necessary + * include files. You had to include only the central include file. + * + * Example: + *

+ * #include "base/rebase.hpp"
+ * #include "expr/reexpr.hpp"
+ * 
+ * In this case all definitions of rplcore and rplexpr are available. + */ +/** @file + * @brief Generally usable exceptions. + */ +/** @file rplcore/rplexception.hpp + * + * @brief Definitions for a generally usable exceptions. + */ +#include "base/rebase.hpp" + +/** @class ReException ReException.hpp "base/ReException.hpp" + * + * @brief A generally usable exception with or without logging. + * + * Note: If the logger is not given by parameter + * the usage of the global logger is not threadsafe. + */ +class ReException; + + +/** + * @brief Constructor. + * + * For derived classes only! + */ +ReException::ReException() : + m_message("") +{ +} + +/** + * @brief Constructor. + * + * @param format the reason of the exception + * @param ... the values for the placeholders in the format. + */ +ReException::ReException(const char* format, ...) : + m_message("") { + char buffer[64000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + m_message = buffer; +} + +/** + * @brief Constructor. + * + * This constructor automatically logs the given data. + * + * @param level the logging level, e.g. LOG_ERROR + * @param location an unique identifier for the location + * where the exception was thrown + * @param format the reason of the exception. + * Can contain placeholders (@see + * std::printf()) + * @param ... the values of the placeholders + * in format + * @param logger if NULL the global logger will be used + */ +ReException::ReException(ReLoggerLevel level, int location, + ReLogger* logger, const char* format, ...) : + m_message("") { + char buffer[64000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + m_message = buffer; + if(logger == NULL) + logger = ReLogger::globalLogger(); + logger->log(level, location, buffer); +} + + +/** @class RplRangeException rplexception.hpp "rplcore/rplexception.hpp" + * + * @brief An exception for integer range errors. + * + * The error will be logged. + * + * Note: If the logger is not given by parameter + * the usage of the global logger is not threadsafe. + */ + +/** + * @brief Constructor. + * + * This exception can be used if a value does not be + * inside a given range. + * + * This constructor automatically logs the given data. + * + * @param level the logging level, e.g. LOG_ERROR + * @param location an unique identifier for the location + * where the exception was thrown + * @param current the current value + * @param lbound the minimum of the allowed values + * @param ubound the maximum of the allowed values + * @param message the reason. If NULL a generic + * message will be used + * @param logger if NULL the global logger will be used + */ + +ReRangeException::ReRangeException(ReLoggerLevel level, int location, + size_t current, + size_t lbound, size_t ubound, const char* message, ReLogger* logger) : + ReException("") { + char buffer[64000]; + if(message == NULL) + message = "value outside limits"; + qsnprintf(buffer, sizeof buffer, "%s: %lu [%lu, %lu]", + message == NULL ? "" : message, + current, lbound, ubound); + if(logger == NULL) + logger = ReLogger::globalLogger(); + logger->log(level, location, buffer); +} + +/** @class RplInvalidDataException rplexception.hpp "rplcore/rplexception.hpp" + * + * @brief An exception usable if binary data have the wrong structure. + * + * The data will be dumped as hex and ASCII dump. + * + * Note: If the logger is not given by parameter + * the usage of the global logger is not threadsafe. + */ + +/** + * @brief Constructor. + * + * This exception can be used if data does not have a given fomat. + * + * This constructor automatically logs the given data. This data + * will be dumped (hexadecimal dump and ASCII interpretation). + * + * @param level the logging level, e.g. LOG_ERROR + * @param location an unique identifier for the location + * where the exception was thrown + * @param message the reason + * @param data pointer to binary data + * @param dataSize the size of the data which should be dumped + * @param logger if NULL the global logger will be used + */ +RplInvalidDataException::RplInvalidDataException(ReLoggerLevel level, + int location, + const char* message, const void* data, + size_t dataSize, ReLogger* logger) : + ReException("") { + char buffer[64000]; + if(message == NULL) + message = "invalid data: "; + if(data == NULL) + data = ""; + if(dataSize > 16) + dataSize = 16; + size_t ix; + char* ptr = buffer + strlen(buffer); + for(ix = 0; ix < dataSize; ix++) { + qsnprintf(ptr, sizeof(buffer) - (ptr - buffer) - 1, "%02x ", + ((unsigned char*) data)[ix]); + ptr += strlen(ptr); + } + for(ix = 0; ix < dataSize; ix++) { + char cc = ((char*) data)[ix]; + if(cc > ' ' && cc <= '~') + *ptr++ = cc; + else + *ptr++ = '.'; + } + if(logger == NULL) + logger = ReLogger::globalLogger(); + logger->log(level, location, buffer); +} diff --git a/base/ReException.hpp b/base/ReException.hpp new file mode 100644 index 0000000..8fb8113 --- /dev/null +++ b/base/ReException.hpp @@ -0,0 +1,45 @@ +/* + * 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 REEXCEPTION_HPP +#define REEXCEPTION_HPP + +// the sources generated from QT include this file directly: +#ifndef RPLCORE_HPP +#include +#endif + +class ReException { +protected: + ReException(); +public: + ReException(const char* message, ...); + ReException(ReLoggerLevel level, int location, const char* message, + ReLogger* logger = NULL); + ReException(ReLoggerLevel level, int location, ReLogger* logger, + const char* message, ...); + const QByteArray& getMessage() const { + return m_message; + } +protected: + QByteArray m_message; +}; + +class ReRangeException : public ReException { +public: + ReRangeException(ReLoggerLevel level, int location, size_t current, + size_t lbound, size_t ubound, + const char* message = NULL, ReLogger* logger = NULL); +}; + +class RplInvalidDataException : public ReException { +public: + RplInvalidDataException(ReLoggerLevel level, int location, const char* message, + const void* data = NULL, size_t dataSize = 0, ReLogger* logger = NULL); +}; + +#endif // REEXCEPTION_HPP diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp new file mode 100644 index 0000000..0e913f7 --- /dev/null +++ b/base/ReLogger.cpp @@ -0,0 +1,649 @@ +/* + * 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. + */ + +/** @file + * A configurable logger for different output media. + */ +/** @file rplcore/rpllogger.hpp + * + * Definitions for a configurable logger for different output media. + */ +#include "base/rebase.hpp" +#include +#include + +enum { + LOC_ADD_APPENDER_1 = LOC_FIRST_OF(LOC_LOGGER), // 10101 +}; + +ReLogger* ReLogger::m_globalLogger = NULL; + +/** + * @brief Returns the global logger. + * + * If it does not exist it will be created (singleton). + * + * @return the global logger + */ +ReLogger* ReLogger::globalLogger() { + if(m_globalLogger == NULL) { + m_globalLogger = new ReLogger(); + m_globalLogger->buildStandardAppender("globallogger"); + } + return m_globalLogger; +} +/** + * @brief Frees the resources of the global logger. + */ +void ReLogger::destroyGlobalLogger() { + delete m_globalLogger; + m_globalLogger = NULL; +} + +/** @class ReAppender rpllogger.hpp "rplcore/rpllogger.hpp" + * + * @brief Puts the logging info to a medium (e.g. a file). + * + * This is an abstract base class. + */ + +/** + * @brief Constructor. + * + * @param name identifies the logger. Useful for ReLogger::findLogger() + */ +ReAppender::ReAppender(const QByteArray& name) : + m_name(name), + m_level(LOG_INFO) { + +} +/** + * @brief Destructor. + */ +ReAppender::~ReAppender() { +} + +/** + * Returns the name. + * + * @return the name of the instance + */ +const char* ReAppender::getName() const { + return m_name.data(); +} + +/** + * @brief Sets the level. + * + * @param level + */ +void ReAppender::setLevel(ReLoggerLevel level) { + m_level = level; +} +/** + * @brief Returns the level. + * + * @return the level + */ +ReLoggerLevel ReAppender::getLevel() const { + return m_level; +} +/** + * @brief Checks whether the current location should be logged. + * + * @param level the level of the location. + * @return true: the location level is greater or equals to the appender's level + */ +bool ReAppender::isActive(ReLoggerLevel level) { + return level <= m_level; +} + +/** + * @brief Sets or clears the automatic deletions. + * + * @param onNotOff the state of the auto deletion + */ +void ReAppender::setAutoDelete(bool onNotOff) { + m_autoDelete = onNotOff; +} + +/** + * @brief Returns the state of the auto deletion. + * + * @return true: the logger destroys the instance + */ +bool ReAppender::isAutoDelete() const { + return m_autoDelete; +} + +/** @class ReLogger rpllogger.hpp "rplcore/rpllogger.hpp" + * + * @brief Implements a logger. + * + * The logger takes the call from the calling location. + * But the output assumes the class ReAppender, + * more exactly: a subclass from the abstract class + * ReAppender, + * + * For single threaded applications there is a possability of + * a global logger. In this case the logger can be got with the static + * method ReLogger::globalLogger(). + * + * Note: using the global logger is not threadsafe! + * + * Each call of the logger should be provided by a unique identifier + * named the location. This allows to find the error quickly. + */ + +/** + * @brief Constructor. + */ +ReLogger::ReLogger() : + // m_appenders(), + m_countAppenders(0), + m_stdPrefix(), + m_mutex(), + m_withLocking(false) { + memset(m_appenders, 0, sizeof m_appenders); +} + +/** + * @brief Destructor. + */ +ReLogger::~ReLogger() { + for(size_t ix = 0; ix < m_countAppenders; ix++) { + ReAppender* appender = m_appenders[ix]; + if(appender->isAutoDelete()) { + delete appender; + } + m_appenders[ix] = NULL; + } +} +/** + * @brief Returns the first char of a logging line displaying the logging level. + * + * @param level the level to "convert" + * @return the assigned prefix char + */ +char ReLogger::getPrefixOfLevel(ReLoggerLevel level) const { + char rc = ' '; + switch(level) { + case LOG_ERROR: + rc = '!'; + break; + case LOG_WARNING: + rc = '+'; + break; + case LOG_INFO: + rc = ' '; + break; + case LOG_DEBUG: + rc = '='; + break; + default: + rc = '?'; + break; + } + return rc; +} + +/** + * @brief Tests whether at least one appender is active for a given level. + * + * @param level level to test + * @return false: all appenders are not activated by this level
+ * true: otherwise + */ +bool ReLogger::isActive(ReLoggerLevel level) const { + bool rc = false; + for(size_t ix = 0; ix < m_countAppenders; ix++) { + ReAppender* appender = m_appenders[ix]; + if(appender->isActive(level)) { + rc = true; + break; + } + } + return rc; +} + +/** + * @brief Sets the log level for all appenders. + * + * @param level level to set + */ +void ReLogger::setLevel(ReLoggerLevel level) { + for(size_t ix = 0; ix < m_countAppenders; ix++) { + ReAppender* appender = m_appenders[ix]; + appender->setLevel(level); + } +} + +/** + * @brief Sets or clears the state "with locking". + * + * @param onNotOff true: the logger is thread save.
+ * false: not thread save + */ +void ReLogger::setWithLocking(bool onNotOff) { + m_withLocking = onNotOff; +} + +/** + * @brief Returns the standard prefix of a logging line. + * + * If it does not exist it will be created. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @return the standard logging line prefix + */ +const QByteArray& ReLogger::getStdPrefix(ReLoggerLevel level, int location) { + if(m_stdPrefix.isEmpty()) + m_stdPrefix = buildStdPrefix(level, location); + return m_stdPrefix; +} + +/** + * @brief Logs (or not) the calling location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param message the logging message + * @return true: for chaining + */ +bool ReLogger::log(ReLoggerLevel level, int location, const char* message) { + m_stdPrefix = ""; + bool first = true; + for(size_t ix = 0; ix < m_countAppenders; ix++) { + ReAppender* appender = m_appenders[ix]; + if(appender->isActive(level)) { + if(first && m_withLocking) + m_mutex.lock(); + appender->log(level, location, message, this); + } + } + if(! first && m_withLocking) + m_mutex.unlock(); + return true; +} +/** + * @brief Logs (or not) the calling location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param message the logging message + * @return true: for chaining + */ +bool ReLogger::log(ReLoggerLevel level, int location, + const QByteArray& message) { + return log(level, location, message.data()); +} + +/** + * @brief Logs (or not) the calling location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param message the logging message + * @return true: for chaining + */ +bool ReLogger::log(ReLoggerLevel level, int location, + const ReString& message) { + return log(level, location, message.toUtf8().data()); +} + +/** + * @brief Logs (or not) the calling location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param format the logging message with placeholders (like printf). + * @param ... the values of the placeholders (varargs) + * @return true: for chaining + */ +bool ReLogger::logv(ReLoggerLevel level, int location, const char* format, + ...) { + char buffer[64000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + return log(level, location, buffer); +} + +/** + * @brief Logs (or not) the calling location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param format the logging message with placeholders (like printf). + * @param ... the values of the placeholders (varargs) + * @return true: for chaining + */ +bool ReLogger::logv(ReLoggerLevel level, int location, + const QByteArray& format, ...) { + char buffer[64000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + return log(level, location, buffer); +} + +/** + * @brief Logs (or not) the calling location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param format the logging message with placeholders (like printf). + * @param varlist variable arguments + * @return true: for chaining + */ +bool ReLogger::log(ReLoggerLevel level, int location, const char* format, + va_list& varlist) { + char buffer[64000]; + qvsnprintf(buffer, sizeof buffer, format, varlist); + return log(level, location, buffer); +} + +/** + * @brief Builds the standard prefix of a logging line. + * + * @param level the level of the location + * @param location an unique identifier of the location + */ +QByteArray ReLogger::buildStdPrefix(ReLoggerLevel level, int location) { + time_t now = time(NULL); + struct tm* now2 = localtime(&now); + char buffer[64]; + qsnprintf(buffer, sizeof buffer, "%c%d.%02d.%02d %02d:%02d:%02d (%d): ", + getPrefixOfLevel(level), + now2->tm_year + 1900, + now2->tm_mon + 1, + now2->tm_mday, + now2->tm_hour, + now2->tm_min, + now2->tm_sec, + location); + return QByteArray(buffer); +} + +/** + * @brief Adds an appender. + * + * @param appender appender to add + */ +void ReLogger::addAppender(ReAppender* appender) { + if(m_countAppenders < sizeof m_appenders / sizeof m_appenders[0]) { + m_appenders[m_countAppenders++] = appender; + } else { + log(LOG_ERROR, LOC_ADD_APPENDER_1, "too many appenders"); + } +} + +/** + * @brief Returns the appender with a given name. + * + * @param name the appender's name + * + * @return NULL: no appender with this name is registered
+ * otherwise: the wanted appender + */ +ReAppender* ReLogger::findAppender(const char* name) const { + ReAppender* rc = NULL; + for(size_t ix = 0; ix < m_countAppenders; ix++) { + ReAppender* current = m_appenders[ix]; + if(strcmp(name, current->getName()) == 0) { + rc = current; + break; + } + } + return rc; +} + +/** + * @brief Builds the standard appender configured by a configuration file. + * + * @param config configuration file + * @param prefix the prefix of the key in the config file + * (in front of "name") + * @param defaultLogfilePrefix + * the prefix of the log file if no entry in the + * configuration file + */ +void ReLogger::buildStandardAppender(ReConfig* config, + const char* prefix, + const char* defaultLogfilePrefix) { + QByteArray sPrefix(prefix); + QByteArray logFilePrefix = config->asString(sPrefix + "name", + defaultLogfilePrefix); + + int maxSize = config->asInt( + "maxsize", 10100100); + int maxCount = config->asInt(sPrefix + "maxfiles", 5); + buildStandardAppender(logFilePrefix, maxSize, maxCount); + QByteArray sLevel = config->asString(sPrefix + "level", "info"); + ReLoggerLevel level = LOG_INFO; + if (strcasecmp(sLevel.constData(), "error") == 0) + level = LOG_ERROR; + else if (strcasecmp(sLevel, "warning") == 0) + level = LOG_WARNING; + else if (strcasecmp(sLevel, "debug") == 0) + level = LOG_DEBUG; + setLevel(level); +} + +/** + * @brief Builds the standard appender for the instance: a console logger and a file logger. + * + * @param prefix the prefix of the log file name, e.g. /var/log/server + * @param maxSize the maximum of the file size + * @param maxCount the maximal count of files. If neccessary the oldest file will be deleted + */ +void ReLogger::buildStandardAppender(const QByteArray& prefix, int maxSize, + int maxCount) { + ReStreamAppender* streamAppender = new ReStreamAppender(stderr); + streamAppender->setAutoDelete(true); + addAppender((ReAppender*) streamAppender); + ReFileAppender* fileAppender = new ReFileAppender(prefix, maxSize, maxCount); + fileAppender->setAutoDelete(true); + addAppender((ReAppender*) fileAppender); +} + +/** @class ReStreamAppender rpllogger.hpp "rplcore/rpllogger.hpp" + * + * @brief Puts the logging info to a standard output stream. + * + * The possible streams are std::stdout or std::stderr + */ + + +/** + * @brief Constructor. + */ +ReStreamAppender::ReStreamAppender(FILE* file, const char* appenderName) : + ReAppender(QByteArray(appenderName)), + m_fp(file) { +} + +/** + * @brief Destructor. + */ +ReStreamAppender::~ReStreamAppender() { + fflush(m_fp); +} + + +/** + * @brief Logs (or not) the current location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param message the logging message + * @param logger the calling logger + */ +void ReStreamAppender::log(ReLoggerLevel level, int location, + const char* message, ReLogger* logger) { + const QByteArray& prefix = logger->getStdPrefix(level, location); + fputs(prefix, m_fp); + fputs(message, m_fp); + fputc('\n', m_fp); + fflush(m_fp); +} +#pragma GCC diagnostic warning "-Wunused-parameter" + + +/** @class ReFileAppender rpllogger.hpp "rplcore/rpllogger.hpp" + * + * @brief Puts the logging info to a file. + * + * The appender creates a collection of files to limit the used disk space. + * Each logfile is limited to a given size. And the number of files is limited. + * If the count exceeds the oldest file will be deleted. + * + * Each logfile's name has a given name prefix, a running number + * and the suffix ".log", e.g. "globallogger.003.log". + */ + +/** + * @brief Constructor. + * + * @param prefix the prefix of the log file name, e.g. /var/log/server + * @param maxSize the maximum of the file size + * @param maxCount the maximal count of files. If neccessary the oldest file will be deleted + * @param appenderName the name of the appender. @see ReLogger::findAppender() + */ +ReFileAppender::ReFileAppender(const QByteArray& prefix, int maxSize, + int maxCount, const char* appenderName) : + ReAppender(QByteArray(appenderName)), + m_prefix(prefix), + m_maxSize(maxSize), + m_maxCount(maxCount), + m_currentSize(0), + m_currentNo(0), + m_fp(NULL) { + open(); +} + +/** + * @brief Destructor. + */ +ReFileAppender::~ReFileAppender() { + if(m_fp != NULL) { + fclose(m_fp); + m_fp = NULL; + } +} + +/** + * @brief Opens the next log file. + */ +void ReFileAppender::open() { + if(m_fp != NULL) + fclose(m_fp); + char fullName[512]; + qsnprintf(fullName, sizeof fullName, "%s.%03d.log", m_prefix.data(), + ++m_currentNo); + m_fp = fopen(fullName, "a"); + if(m_fp == NULL) + fprintf(stderr, "cannot open: %s\n", fullName); + else { + //@ToDo + m_currentSize = 0; + } +} + +/** + * @brief Logs (or not) the current location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param message the logging message + * @param logger the calling logger + */ +#pragma GCC diagnostic ignored "-Wunused-parameter" +void ReFileAppender::log(ReLoggerLevel level, int location, + const char* message, + ReLogger* logger) { + if(m_fp != NULL) { + const QByteArray& prefix = logger->getStdPrefix(level, location); + fputs(prefix, m_fp); + fputs(message, m_fp); + fputc('\n', m_fp); + fflush(m_fp); + } +} +#pragma GCC diagnostic warning "-Wunused-parameter" + +/** @class ReMemoryAppender rpllogger.hpp "rplcore/rpllogger.hpp" + * + * @brief Puts the logging info to an internal buffer. + * + * This line list can be required: getLines(). + */ + +/** + * @brief Constructor. + * + * @param maxLines the maximum of lines. + * If the buffer is full the oldest lines will be deleted + * @param appenderName NULL or the name of the appender + */ +ReMemoryAppender::ReMemoryAppender(int maxLines, const char* appenderName) : + ReAppender(appenderName), + m_lines(), + m_maxLines(maxLines), + m_addPrefix(true) +{ + m_lines.reserve(maxLines); +} + +/** + * @brief Destructor. + */ +ReMemoryAppender::~ReMemoryAppender() { +} + +/** + * Logs (or not) the current location. + * + * @param level the level of the location + * @param location an unique identifier of the location + * @param message the logging message + * @param logger the calling logger + */ +#pragma GCC diagnostic ignored "-Wunused-parameter" +void ReMemoryAppender::log(ReLoggerLevel level, int location, + const char* message, + ReLogger* logger) { + if(m_lines.size() >= m_maxLines) + m_lines.removeFirst(); + if (! m_addPrefix) + m_lines.append(message); + else { + QByteArray msg(logger->getStdPrefix(level, location)); + msg += message; + m_lines.append(msg); + } +} +#pragma GCC diagnostic warning "-Wunused-parameter" + +/** + * @brief Returns the list of lines. + * + * @return the line list + */ +const QList& ReMemoryAppender::getLines() const { + return m_lines; +} + +/** + * @brief Deletes all log lines. + */ +void ReMemoryAppender::clear() { + m_lines.clear(); +} diff --git a/base/ReLogger.hpp b/base/ReLogger.hpp new file mode 100644 index 0000000..125a6f6 --- /dev/null +++ b/base/ReLogger.hpp @@ -0,0 +1,168 @@ +/* + * 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 RELOGGER_HPP +#define RELOGGER_HPP + +/** + * + */ +class ReLogger; +class ReConfig; +/** + * @brief Logging level: for controlling of the logging. + * + * Each logging location defines one of the following level. + * If the level of an appender is lower or equals to this level + * the logging is done. + */ +enum ReLoggerLevel { + LOG_ERROR = 10, ///< marks an error. + LOG_WARNING = 15, ///< marks a warning + LOG_INFO = 20, ///< marks an information + LOG_DEBUG = 25 ///< for debug purpose only +}; + +class ReAppender { +public: + ReAppender(const QByteArray& name); + virtual ~ReAppender(); +private: + // No copy constructor: no implementation! + ReAppender(const ReAppender& source); + // Prohibits assignment operator: no implementation! + ReAppender& operator =(const ReAppender& source); +public: + virtual void log(ReLoggerLevel level, int location, const char* message, + ReLogger* logger) = 0; + bool isActive(ReLoggerLevel level); + void setLevel(ReLoggerLevel level); + void setAutoDelete(bool onNotOff); + bool isAutoDelete() const; + ReLoggerLevel getLevel() const; + const char* getName() const; + +private: + // Name of the appender. Used to find the appender in a list of appenders + QByteArray m_name; + // only locations with a lower or equal level will be logged + ReLoggerLevel m_level; + // true: the logger destroys the instance. false: the deletion must be done outside of the logger + bool m_autoDelete; +}; + +class ReLogger { +public: + static ReLogger* globalLogger(); + static void destroyGlobalLogger(); +private: + // the standard logger, can be called (with globalLogger()) from each location + static ReLogger* m_globalLogger; +public: + ReLogger(); + virtual ~ReLogger(); +private: + // No copy constructor: no implementation! + ReLogger(const ReLogger& source); + // Prohibits assignment operator: no implementation! + ReLogger& operator =(const ReLogger& source); +public: + bool log(ReLoggerLevel level, int location, const char* message); + bool log(ReLoggerLevel level, int location, const QByteArray& message); + bool log(ReLoggerLevel level, int location, const ReString& message); + bool logv(ReLoggerLevel level, int location, const char* format, ...); + bool logv(ReLoggerLevel level, int location, const QByteArray& format, ...); + bool log(ReLoggerLevel level, int location, const char* format, + va_list& varlist); + void addAppender(ReAppender* appender); + ReAppender* findAppender(const char* name) const; + void buildStandardAppender(ReConfig* config, const char* prefix = "logfile.", + const char* defaultLoggerName = "logger"); + void buildStandardAppender(const QByteArray& prefix, int maxSize = 10*1024*1024, + int maxCount = 5); + QByteArray buildStdPrefix(ReLoggerLevel level, int location); + const QByteArray& getStdPrefix(ReLoggerLevel level, int location); + char getPrefixOfLevel(ReLoggerLevel level) const; + bool isActive(ReLoggerLevel level) const; + void setLevel(ReLoggerLevel level); + void setWithLocking(bool onNotOff); +private: + // the assigned appenders: + ReAppender* m_appenders[16]; + // the number of appenders in m_appenders: + size_t m_countAppenders; + // "" or the cache of the prefix of the current logging line: This can be reused by any appender. + QByteArray m_stdPrefix; + QMutex m_mutex; + bool m_withLocking; +}; + +/** + * Implements an appender which puts the messages to a standard stream: stdout or stderr + */ +class ReStreamAppender : public ReAppender { +public: + ReStreamAppender(FILE* stream, const char* appenderName = "FileAppender"); + virtual ~ReStreamAppender(); +public: + virtual void log(ReLoggerLevel level, int location, const char* message, + ReLogger* logger); +private: + // stdout or stderr: + FILE* m_fp; +}; + +/** + * Implements an appender which puts the messages to a file + */ +class ReFileAppender : public ReAppender { +public: + ReFileAppender(const QByteArray& name, int maxSize, int maxCount, + const char* appenderName = "FileAppender"); + virtual ~ReFileAppender(); +public: + void open(); + virtual void log(ReLoggerLevel level, int location, const char* message, + ReLogger* logger); + +private: + // prefix of the log file name. Will be appended by "..log" + QByteArray m_prefix; + // maximal size of a logging file: + int m_maxSize; + // maximal count of logging files. If neccessary the oldest file will be deleted. + int m_maxCount; + // the size of the current log file: + int m_currentSize; + // the number of the current log file: + int m_currentNo; + // the current log file: + FILE* m_fp; +}; + +/** + * Stores the log messages in a list. + */ +class ReMemoryAppender : public ReAppender { +public: + ReMemoryAppender(int maxLines = 1024, + const char* appenderName = "MemoryAppender"); + ~ReMemoryAppender(); +public: + virtual void log(ReLoggerLevel level, int location, const char* message, + ReLogger* logger); + const QList& getLines() const; + void clear(); +private: + 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. + bool m_addPrefix; +}; + +#endif // RELOGGER_HPP diff --git a/base/ReQString.cpp b/base/ReQString.cpp new file mode 100644 index 0000000..171401b --- /dev/null +++ b/base/ReQString.cpp @@ -0,0 +1,182 @@ +/* + * 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. +*/ + +/** @file + * @brief Missed operation for ReStrings. + */ +/** @file rplcore/rplqstring.hpp + * + * @brief Definitions for missed operation for ReStrings. + */ +#include "base/rebase.hpp" + + +/** + * @brief Determines the length and vlaue of an integer. + * + * @param text the number as text + * @param start the first index to inspect + * @param radix the base of the number sytem: 8 (octal), 10 or 16 + * @param pValue OUT: the value of the integer. May be NULL + * + * @return <=0: no integer found + * otherwise: the length of the integer + */ +int ReQString::lengthOfUInt64(const ReString& text, int start, + int radix, quint64* pValue) +{ + int inputLength = text.size(); + int64_t value = 0; + int ix = start; + int cc; + if (radix == 10){ + while (ix < inputLength){ + if ( (cc = text[ix].unicode()) >= '0' && cc <= '9') + value = value * 10 + cc - '0'; + else + break; + ix++; + } + } else if (radix == 16){ + while (ix < inputLength){ + if ( (cc = text[ix].unicode()) >= '0' && cc <= '9') + value = value * 16 + cc - '0'; + else if (cc >= 'A' && cc <= 'F') + value = value * 16 + cc - 'A' + 10; + else if (cc >= 'a' && cc <= 'f') + value = value * 16 + cc - 'a' + 10; + else + break; + ix++; + } + } else if (radix == 8){ + while (ix < inputLength){ + if ( (cc = text[ix].unicode()) >= '0' && cc <= '7') + value = value * 8 + cc - '0'; + else + break; + ix++; + } + } else { + throw ReException("ReQString::lengthOfInt(): wrong radix: %d", radix); + } + if (pValue != NULL) + *pValue = value; + return ix - start; +} +/** + * @brief Determines the length and value of an unsigned integer. + * + * @param text the number as text + * @param start the first index to inspect + * @param radix the base of the number sytem: 8 (octal), 10 or 16 + * @param pValue OUT: the value of the integer. May be NULL + * + * @return 0: no integer found + * otherwise: the length of the integer + */ +int ReQString::lengthOfUInt(const ReString& text, int start, + int radix, uint* pValue) +{ + quint64 value; + int rc = lengthOfUInt64(text, start, radix, &value); + if (pValue != NULL) + *pValue = (uint) value; + return rc; +} + +/** + * @brief Determines the length and value of a floting point number. + * + * @param text the number as text + * @param start the first index to inspect + * @param pValue OUT: the value of the integer. May be NULL + * + * @return <=0: no real number found + * otherwise: the length of the floating point number + */ +int ReQString::lengthOfReal(const ReString& text, int start, qreal* pValue) +{ + int inputLength = text.size(); + qreal value = 0.0; + int cc; + int ix = start; + while (ix < inputLength){ + if ( (cc = text[ix].unicode()) >= '0' && cc <= '9') + value = value * 10 + (cc - '0'); + else + break; + ix++; + } + // found: a digit has been found (in front of or behind the '.' + bool found = ix > start; + if (ix < inputLength && text[ix].unicode() == '.'){ + ix++; + } + if (ix < inputLength && text[ix].isDigit()){ + found = true; + qreal divisor = 1; + qreal precision = 0; + while ( ix < inputLength && (cc = text[ix].unicode()) >= '0' && cc <= '9'){ + divisor *= 10; + precision = precision*10 + cc - '0'; + ix++; + } + value += precision / divisor; + } else if (! found){ + ix = start; + } + if (found && ix + 1 < inputLength && toupper(text[ix].unicode()) == 'E'){ + int savePoint = ix; + ix++; + bool negative = false; + if ( (cc = text[ix].unicode()) == '+') + ix++; + else if (cc == '-'){ + ix++; + negative = true; + } + if (ix >= inputLength || ! text[ix].isDigit()) + ix = savePoint; + else{ + int exponent = 0; + while (ix < inputLength && text[ix].isDigit()){ + exponent = exponent * 10 + text[ix].unicode() - '0'; + ix++; + } + if (negative) + value /= qPow(10, exponent); + else + value *= qPow(10, exponent); + } + } + if (pValue) + *pValue = value; + return found ? ix - start : 0; +} + +/** + * @brief Converts a ReString into an utf-8 string + * + * The expression qstring.toUtf8().constData() is not allowed + * in a variable argument list like sprintf. This is a thread save workaround. + * + * @param source string to convert + * @param buffer OUT: target buffer + * @param bufferSize size of the target buffer + * @return buffer + */ +char*ReQString::utf8(const ReString& source, char buffer[], size_t bufferSize) +{ + QByteArray val = source.toUtf8(); + if (val.length() < (int) bufferSize) + bufferSize = val.length() + 1; + memcpy(buffer, val.constData(), bufferSize - 1); + buffer[bufferSize - 1] = '\0'; + return buffer; +} diff --git a/base/ReQtring.hpp b/base/ReQtring.hpp new file mode 100644 index 0000000..3a3bcf5 --- /dev/null +++ b/base/ReQtring.hpp @@ -0,0 +1,37 @@ +/* + * 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 RPLQSTRING_HPP +#define RPLQSTRING_HPP + +class ReQString +{ +public: + static int lengthOfUInt64(const ReString& text, int start = 0, + int radix = 10, quint64* value = NULL); + static int lengthOfUInt(const ReString& text, int start, int radix, + uint* pValue); + static int lengthOfReal(const ReString& text, int start = 0, + qreal* value = NULL); + /** + * @brief Returns the value of a hexadecimal digit. + * + * @param digit a (unicode) character + * @return -1: not a hexadecimal digit
+ * otherwise: the value, e.g. 10 for 'a' + */ + inline static int valueOfHexDigit(int digit){ + return digit >= '0' && digit <= '9' ? digit - '0' + : digit >= 'A' && digit <= 'F' ? digit - 'A' + 10 + : digit >= 'a' && digit <= 'f' ? digit - 'a' + 10 : -1; + } + static char* utf8(const ReString& source, char buffer[], size_t bufferSize); +}; + +#endif // RPLQSTRING_HPP diff --git a/base/ReStringUtil.cpp b/base/ReStringUtil.cpp new file mode 100644 index 0000000..0afebec --- /dev/null +++ b/base/ReStringUtil.cpp @@ -0,0 +1,539 @@ +/* + * 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. + */ +/** @file + * @brief Missed operations for QByteArrays. + */ +/** @file base/ReStringUtil.cpp + * + * @brief Definitions for missed operations for QByteArrays. + */ +#include "base/rebase.hpp" + +/** @class ReStringUtil ReStringUtil.hpp "base/ReStringUtil.hpp" + * + * @brief Implements some services around strings. + * + * This is a class with static members only. + */ + +/** + * @brief Counts the occurrences of a given char in a string. + * + * @param line the text to inspect + * @param cc the char to count + * @return the number of cc in the text + */ +int ReStringUtil::countChar(const char* line, char cc) +{ + const char* ptr = line; + int rc = 0; + while( (ptr = strchr(ptr, cc)) != NULL){ + rc++; + ptr++; + } + return rc; +} +/** + * Counts the occurrences of a string in a string. + * + * @param source in this string will be searched + * @param item this item will be searched + * @return the count of occurrences + */ +int ReStringUtil::count(const char* source, const char* item) { + const char* end = source; + int rc = 0; + int lengthItem = strlen(item); + while(true) { + const char* start = end; + end = strstr(start, item); + if(end == NULL) + break; + else { + rc++; + end += lengthItem; + } + } + return rc; +} + +/** + * Returns a string with a given maximum length. + * + * @param source the source + * @param maxLength the maximum length of the result + * @param buffer Out: used if length of the result is shorter + * @param appendix if the result is cut this string will be appended.
+ * May be NULL. + * @return source: the source is enough short
+ * the prefix of source with the given length + */ +const QByteArray& ReStringUtil::cutString(const QByteArray& source, int maxLength, + QByteArray& buffer, const char* appendix) { + QByteArray& rc = source.length() <= maxLength ? (QByteArray&) source : buffer; + if(source.length() > maxLength) { + buffer = source.left(maxLength); + if(appendix != NULL && appendix[0] != '\0') + buffer.append(appendix); + } + return rc; +} +static char s_fileSeparator = 0; + +/** + * @brief Returns the os specific file path separator. + * @return the file path separator, e.g. "/" for linux + */ +const char* ReStringUtil::fileSeparator(){ + return fileSeparatorChar() == '/' ? "/" : "\\"; +} + +/** + * @brief Returns the os specific file path separator. + * @return the file path separator, e.g. '/' for linux + */ +char ReStringUtil::fileSeparatorChar(){ + if (s_fileSeparator == 0){ + const char* path = getenv("PATH"); + if (path != NULL){ + s_fileSeparator = strchr(path, ';') != NULL + || strchr(path, '\\') != NULL ? '\\' : '/'; + } else { + if (getenv("windows") != NULL) + s_fileSeparator = '\\'; + else + s_fileSeparator = '/'; + } + } + return s_fileSeparator; +} + +/** + * Builds a hexadecimal dump. + * + * Format: a sequence of hex digits followed by the ascii interpretation. + * + * Example: "42 30 61 B0a" + * + * @param data data to convert + * @param length length of data + * @param bytesPerLine one line containes so many bytes of data + * @return the hex dump + */ +QByteArray ReStringUtil::hexDump(uint8_t* data, int length, int bytesPerLine) { + QByteArray rc; + int fullLines = length / bytesPerLine; + int expectedLength = (bytesPerLine * 4 + 2) * (fullLines + 1); + rc.reserve(expectedLength + 100); + int ixData = 0; + int col; + char buffer[16]; + for(int lineNo = 0; lineNo < fullLines; lineNo++) { + for(col = 0; col < bytesPerLine; col++) { + qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]); + rc.append(buffer); + } + rc.append(' '); + for(col = 0; col < bytesPerLine; col++) { + uint8_t cc = data[ixData + col]; + rc.append(cc > ' ' && cc < 128 ? (char) cc : '.'); + } + ixData += bytesPerLine; + rc.append('\n'); + } + // incomplete last line: + int restBytes = length - ixData; + if(restBytes > 0) { + for(col = 0; col < restBytes; col++) { + qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]); + rc.append(buffer); + } + for(col = restBytes; col < bytesPerLine; col++) { + rc.append(" "); + } + rc.append(' '); + for(col = 0; col < restBytes; col++) { + uint8_t cc = data[ixData + col]; + rc.append(cc > ' ' && cc < 128 ? (char) cc : '.'); + } + rc.append('\n'); + } + return rc; +} + + +/** + * Reads a file into a string. + * + * @param file file to read + * @param removeLastNewline true: if the last character is a newline + * the result will not contain this + * @return the file's content + */ +QByteArray ReStringUtil::read(const char* file, bool removeLastNewline) { + QByteArray rc; + struct stat info; + size_t size; + if(stat(file, &info) == 0 && (size = info.st_size) > 0) { + FILE* fp = fopen(file, "r"); + if(fp != NULL) { + rc.resize(info.st_size); + fread(rc.data(), 1, size, fp); + fclose(fp); + if(removeLastNewline && rc.at(size - 1) == '\n') { + rc.resize(size - 1); + } + } + } + return rc; +} + +QByteArray ReStringUtil::replaceNode(const char* source, const char* newNode){ + char sep = fileSeparatorChar(); + const char* ptr = strrchr(source, sep); + QByteArray rc; + rc.reserve(strlen(source) + strlen(newNode) + 1); + if (ptr == NULL){ + rc.append(source).append(sep).append(newNode); + } else if (ptr[0] == '\0'){ + rc.append(source).append(newNode); + } else { + rc.append(source, ptr - source + 1).append(newNode); + } + return rc; +} + +/** + * Converts a string into an array of strings. + * + * @param source string to convert + * @param separator the separator between the items to split + * @return an array with the splitted source + */ +QList ReStringUtil::toArray(const char* source, + const char* separator) { + const char* end = source; + QList rc; + rc.reserve(count(source, separator) + 1); + int lengthItem = strlen(separator); + while(*end != '\0') { + const char* start = end; + end = strstr(start, separator); + if(end == NULL) { + end = start + strlen(start); + } + rc.append(QByteArray(start, end - start)); + if(end[0] != '\0') + end += lengthItem; + } + return rc; +} + +QByteArray ReStringUtil::toCString(const char* source, int maxLength){ + if (maxLength <= 0) + maxLength = strlen(source); + int binaries = 0; + int ix; + for (ix = 0; ix < maxLength; ix++) + if (source[ix] < ' '){ + binaries++; + } + QByteArray rc; + rc.reserve(maxLength + 3 * binaries + 1); + char cc; + for (ix = 0; ix < maxLength; ix++) + if ( (cc = source[ix]) >= ' '){ + rc += source[ix]; + } else { + switch(cc){ + case '\0': + // stop looping: + ix = maxLength; + break; + case '\n': + rc += "\\n"; + break; + case '\r': + rc += "\\r"; + break; + case '\t': + rc += "\\t"; + break; + default: + { + char buffer[5]; + qsnprintf(buffer, sizeof buffer, "\\x%02x", + ((unsigned int) cc) % 0xff); + rc += buffer; + break; + } + } + } + return rc; +} + +/** + * Return an integer as an QByteArray. + * + * @param value value to convert + * @param format format like in sprintf() + * @return the ascii form of the value + */ +QByteArray ReStringUtil::toNumber(int value, const char* format) { + char buffer[128]; + qsnprintf(buffer, sizeof buffer, format, value); + return QByteArray(buffer); +} + +/** + * Writes a string to a file. + * + * @param file the file's name + * @param content NULL or the file's content + * @param mode the file open mode: "w" for write, "a" for append + * @return true: successful
+ * false: error occurred + */ +bool ReStringUtil::write(const char* file, const char* content, const char* mode) { + FILE* fp = fopen(file, mode); + if(fp != NULL) { + fputs(content, fp); + fclose(fp); + } + return fp != NULL; +} +/** + * @brief Returns the length of the number string. + * + * @param text a text to inspect + * @param skipTrailingSpaces true: if spaces are behind the number + * the result contains the length of these + * @return 0: not a number
+ * otherwise: the length of the number string + */ +int ReStringUtil::lengthOfNumber(const char* text, bool skipTrailingSpaces){ + int rc = 0; + bool found = false; + const char* ptr = text; + while(isspace(*ptr)) + ptr++; + if ( (*ptr == '+' || *ptr == '-')) + ptr++; + found = isdigit(*ptr); + while(isdigit(*ptr)){ + ptr++; + } + if (*ptr == '.'){ + ptr++; + if (isdigit(*ptr)){ + found = true; + while(isdigit(*ptr)) + ptr++; + } + } + if (found && toupper(*ptr) == 'E'){ + const char* ptrToE = ptr; + ptr++; + if (*ptr == '+' || *ptr == '-') + ptr++; + if (! isdigit(*ptr)) + ptr = ptrToE; + else { + while(isdigit(*ptr)) + ptr++; + } + } + if (found && skipTrailingSpaces){ + while(isspace(*ptr)){ + ptr++; + } + } + rc = ! found ? 0 : ptr - text; + return rc; +} +/** + * @brief Adds the count of the possible separators. + * + * @param countCommas IN/OUT: number of ',' + * @param countSemicolons IN/OUT: number of ';' + * @param countPipes IN/OUT: number of '|' + * @param countBlanks IN/OUT: number of ' ' + */ +static void addSeparators(const char* line, int& commas, int& semicolons, + int& pipes, int& blanks) +{ + commas += ReStringUtil::countChar(line, ','); + semicolons += ReStringUtil::countChar(line, ';'); + pipes += ReStringUtil::countChar(line, '|'); + blanks += ReStringUtil::countChar(line, ' '); +} + +/** + * @brief Finds the separator of the CSV file. + * + * If the file contain TABs the result is TAB. + * If not: + * Inspects the first 5 lines and counts the possible separators. + * The most found separator will be returned. + * + * @param fp CSV file + * @param buffer a line buffer + * @param bufferSize the size of buffer[] + */ +char ReStringUtil::findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize){ + char rc = '\0'; + int lineNo = 0; + int maxLines = 5; + const char* line; + int commas = 0; + int semicolons = 0; + int pipes = 0; + int blanks = 0; + while(++lineNo < maxLines && (line = fgets(buffer, bufferSize, fp)) != NULL){ + if (strchr(line, '\t') != NULL){ + rc = '\t'; + break; + } + addSeparators(line, commas, semicolons, pipes, blanks); + } + fseek(fp, 0, SEEK_SET); + if (rc != '\t'){ + if (semicolons > 0 && commas > 0){ + // if ',' is decimal separator and ';' is the column separator: + // Add one semicolon per line because of number of values is + // 1 greater than the number of separators + semicolons += lineNo; + } + if (commas + semicolons + pipes == 0) { + rc = blanks > 0 ? ' ' : '\0'; + } else if (semicolons >= commas && semicolons >= pipes) + rc = ';'; + else if (commas > semicolons && commas > pipes) + rc = ','; + else if (pipes > commas && pipes > semicolons) + rc = '|'; + } + return rc; +} + +/** + * @brief Determines the length and vlaue of an integer. + * + * @param text the number as text + * @param radix the base of the number system: 8 (octal), 10 or 16 + * @param pValue OUT: the value of the integer. May be NULL + * + * @return <=0: no integer found + * otherwise: the length of the integer + */ +int ReStringUtil::lengthOfUInt64(const char* text, int radix, quint64* pValue) +{ + int64_t value = 0; + int length = 0; + int cc; + if (radix == 10){ + while ( (cc = text[length]) >= '0' && cc <= '9'){ + value = value * 10 + cc - '0'; + length++; + } + } else if (radix == 16){ + while (true){ + if ( (cc = text[length]) >= '0' && cc <= '9') + value = value * 16 + cc - '0'; + else if (cc >= 'A' && cc <= 'F') + value = value * 16 + cc - 'A' + 10; + else if (cc >= 'a' && cc <= 'f') + value = value * 16 + cc - 'a' + 10; + else + break; + length++; + } + } else if (radix == 8){ + while (true){ + if ( (cc = text[length]) >= '0' && cc <= '7') + value = value * 8 + cc - '0'; + else + break; + length++; + } + } else { + throw ReException("ReStringUtil::lengthOfInt(): wrong radix: %d", radix); + } + if (pValue != NULL) + *pValue = value; + return length; +} + +/** + * @brief Determines the length and value of a floting point number. + * + * @param text the number as text + * @param pValue OUT: the value of the integer. May be NULL + * + * @return <=0: no real number found + * otherwise: the length of the floating point number + */ +int ReStringUtil::lengthOfReal(const char* text, qreal* pValue) +{ + qreal value = 0.0; + int cc; + int length = 0; + while (true){ + if ( (cc = text[length]) >= '0' && cc <= '9') + value = value * 10 + (cc - '0'); + else + break; + length++; + } + // found: a digit has been found (in front of or behind the '.' + bool found = length > 0; + if (text[length] == '.'){ + length++; + } + if (isdigit(text[length])){ + found = true; + qreal divisor = 1; + qreal precision = 0; + while ( (cc = text[length]) >= '0' && cc <= '9'){ + divisor *= 10; + precision = precision*10 + cc - '0'; + length++; + } + value += precision / divisor; + } else if (! found){ + length = 0; + } + if (found && toupper(text[length]) == 'E'){ + int savePoint = length; + length++; + bool negative = false; + if ( (cc = text[length]) == '+') + length++; + else if (cc == '-'){ + length++; + negative = true; + } + if (! isdigit(text[length])) + length = savePoint; + else{ + int exponent = 0; + while (isdigit(text[length])){ + exponent = exponent * 10 + text[length] - '0'; + length++; + } + if (negative) + value /= qPow(10, exponent); + else + value *= qPow(10, exponent); + } + } + if (pValue) + *pValue = value; + return found ? length : 0; +} + diff --git a/base/ReStringUtil.hpp b/base/ReStringUtil.hpp new file mode 100644 index 0000000..831503c --- /dev/null +++ b/base/ReStringUtil.hpp @@ -0,0 +1,36 @@ +/* + * 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 RPLSTRING_HPP +#define RPLSTRING_HPP + +class ReStringUtil { +public: + static int countChar(const char* line, char cc); + static int count(const char* source, const char* item); + static const QByteArray& cutString(const QByteArray& source, int maxLength, + QByteArray& buffer, const char* appendix = "..."); + static const char* fileSeparator(); + static char fileSeparatorChar(); + static QByteArray hexDump(uint8_t* data, int length, int bytesPerLine = 16); + static QByteArray hexDump(const void* data, int length, int bytesPerLine = 16) { + return hexDump((uint8_t*) data, length, bytesPerLine); + } + static QByteArray read(const char* file, bool removeLastNewline = true); + static QByteArray replaceNode(const char* source, const char* newNode); + static bool write(const char* file, const char* content = NULL, + const char* mode = "w"); + 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); + static char findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize); + static int lengthOfUInt64(const char* text, int radix, quint64* pValue); + static int lengthOfReal(const char* text, qreal* pValue); +}; + +#endif // RPLSTRING_HPP diff --git a/base/ReTerminator.cpp b/base/ReTerminator.cpp new file mode 100644 index 0000000..45f46d1 --- /dev/null +++ b/base/ReTerminator.cpp @@ -0,0 +1,78 @@ +/* + * 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. + */ +/** @file + * @brief Implements a thread stopper. + */ +/** @file + * @brief Definitions for a thread stopper. + */ +#include "base/rebase.hpp" + +enum { + LOC_CAUSE_TERMINATION_1 = LOC_FIRST_OF(LOC_TERMINATOR), // 10901 +}; + +/** + * @class ReTerminator ReTerminator.hpp "rplcore/ReTerminator.hpp" + * + * @brief Implements a thread stopper. + * + * Allows to terminate a thread avoiding unfreeing resources, deadlocks etc. + * + * The application must create one instance of a ReTerminator. + * All threads get this instance and call them periodically if the application should stop. + * If yes they finish their work, free the resources and stop. + * + */ + +/** + * @brief Constructor. + * @param logger NULL or the logger. Used to protocol the termination + */ +ReTerminator::ReTerminator(ReLogger* logger) : + m_stop(false), + m_logger(logger) { +} + +/** + * @brief Destructor. + */ +ReTerminator::~ReTerminator() { +} + +/** + * @brief Defines the stop of all threads. + * + * @param reason the reason of the termination. Will be logged (if a logger is defined) + * @param file NULL or the file of the caller. Normally set with __FILE__ + * @param lineNo 0 or the line number of the caller. Normally set with __LINE__ + * @param level log level + * @param location 0 or the location of the caller + */ +void ReTerminator::causeTermination(const char* reason, const char* file, + int lineNo, ReLoggerLevel level, int location) { + if(m_logger != NULL) { + QByteArray message(reason); + if(file != NULL) { + message.append(" [").append(file).append(lineNo).append("]"); + } + m_logger->log(level, location == 0 ? LOC_CAUSE_TERMINATION_1 : location, + message); + } + m_stop = true; +} + +/** + * @brief Tests whether the thread should be stopped. + * @return true: the thread should be stopped.
+ * false: otherwise + */ +bool ReTerminator::isStopped() const { + return m_stop; +} + diff --git a/base/ReTerminator.hpp b/base/ReTerminator.hpp new file mode 100644 index 0000000..e96ff27 --- /dev/null +++ b/base/ReTerminator.hpp @@ -0,0 +1,29 @@ +/* + * 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 RPLTERMINATOR_HPP +#define RPLTERMINATOR_HPP + +class ReTerminator { +public: + ReTerminator(ReLogger* logger = NULL); + virtual ~ReTerminator(); +private: + // No copy constructor: no implementation! + ReTerminator(const ReTerminator& source); + // Prohibits assignment operator: no implementation! + ReTerminator& operator =(const ReTerminator& source); +public: + void causeTermination(const char* reason, const char* file = NULL, + int lineNo = 0, ReLoggerLevel level = LOG_ERROR, int location = 0); + bool isStopped() const; +private: + bool m_stop; + ReLogger* m_logger; +}; + +#endif // RPLTERMINATOR_HPP diff --git a/base/ReTest.cpp b/base/ReTest.cpp new file mode 100644 index 0000000..1bccfe3 --- /dev/null +++ b/base/ReTest.cpp @@ -0,0 +1,482 @@ +/* + * 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. + */ +/** @file + * @brief A testing tool like JUnit. + */ +/** @file rplcore/rpltest.hpp + * + * @brief Definitions for a testing tool like JUnit. + */ +#include "base/rebase.hpp" + +/** @class ReTest rpltest.hpp "rplcore/repltest" + * + * @brief Implements an unit test base class similar JUnit for java. + * + * Example for usage: + * + * @see rplexample.cpp + */ +class ReTest; + +/** + * @brief Constructor. + * + * @param name + */ +ReTest::ReTest(const char* name) : + m_errors(0), + m_name(name), + m_logger(), + m_memoryAppender(1024), + m_memoryLogger() +{ + m_memoryAppender.setAutoDelete(false); + m_logger.buildStandardAppender(getTempDir("rpltest")); + log(QByteArray("Start of ") + m_name); + m_memoryLogger.addAppender(&m_memoryAppender); + try { + run(); + } catch(ReException e) { + error("unexpected RplException: %s", e.getMessage().constData()); + } catch(...){ + error("unknown Exception"); + } + + if(m_errors > 0) { + error("Unit %s has %d error(s)", m_name.data(), m_errors); + // error() increments, we decrement: + m_errors--; + } +} + +/** + * @brief Destructor. + */ +ReTest::~ReTest() { +} + +/** + * Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(int expected, int current, const char* file, + int lineNo) { + if(expected != current) + error("%s-%d: error: %d != %d / %x != %x)", file, lineNo, expected, current, + (unsigned int) expected, (unsigned int) current); + return expected == current; +} + +/** + * Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(int64_t expected, int64_t current, const char* file, + int lineNo) { + if(expected != current) + error("%s-%d: error: %lld != %lld / %llx != %llx)", file, lineNo, + expected, current, (quint64) expected, (quint64) current); + return expected == current; +} + + +/** + * Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(qreal expected, qreal current, const char* file, + int lineNo) { + if(expected != current) + error("%s-%d: error: %d != %d / %x != %x)", file, lineNo, expected, current, + (unsigned int) expected, (unsigned int) current); + return expected == current; +} + +/** + * @brief Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(const char* expected, const ReString& current, + const char* file, int lineNo) { + bool equal = assertEquals(expected, current.toUtf8().constData(), file, + lineNo); + return equal; +} + +/** + * @brief Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(const ReString& expected, const ReString& current, + const char* file, int lineNo) { + bool equal = assertEquals(expected.toUtf8().constData(), + current.toUtf8().constData(), file, lineNo); + return equal; +} + + +/** + * @brief Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(const char* expected, const char* current, + const char* file, int lineNo) { + bool equal = strcmp(expected, current) == 0; + if(! equal) { + if(strchr(expected, '\n') != NULL || strchr(current, '\n')) { + QList exp = ReStringUtil::toArray(expected, "\n"); + QList cur = ReStringUtil::toArray(current, "\n"); + equal = assertEquals(exp, cur, file, lineNo); + } else { + int ix = 0; + while(expected[ix] == current[ix] && expected[ix] != '\0') + ix++; + char pointer[12+1]; + char* ptr = pointer; + int maxIx = ix > 10 ? 10 : ix; + for(int ii = 0; ii < maxIx - 1; ii++) + *ptr++ = '-'; + *ptr++ = '^'; + *ptr = '\0'; + if(ix < 10) + error("%s-%d: error: diff at index %d\n%s\n%s\n%s", + file, lineNo, ix, expected, current, pointer); + else + error("%s-%d: error: diff at index %d\n%s\n...%s\n...%s\n%s", + file, lineNo, ix, current, + expected + ix - 10 + 3, + current + ix - 10 + 3, + pointer); + } + } + return equal; +} + +/** + * @brief Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(const QList& expected, + const QList& current, const char* file, int lineNo) { + int nMax = expected.size(); + bool rc = true; + if(current.size() < nMax) + nMax = current.size(); + for(int ix = 0; ix < nMax; ix++) { + if(expected.at(ix) != current.at(ix)) { + error("%s-%d: difference in line %d", file, lineNo, ix+1); + m_errors--; + assertEquals(expected.at(ix).constData(), current.at(ix).constData(), + file, lineNo); + rc = false; + break; + } + } + if(rc) { + if(expected.size() > nMax) + error("%s-%d: less lines than expected (%d):\n%s", + file, lineNo, nMax, expected.at(nMax).constData()); + else if(expected.size() < nMax) + error("%s-%d: more lines than expected (%d):\n%s", + file, lineNo, nMax, current.at(nMax).constData()); + } + return rc; +} +/** + * @brief Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(const QByteArray& expected, + const QByteArray& current, const char* file, int lineNo) { + return assertEquals(expected.data(), current.data(), file, lineNo); +} + +/** + * @brief Tests the equality of two values. + * + * Differences will be logged. + * + * @param expected the expected value + * @param current the current value + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: equal + */ +bool ReTest::assertEquals(const char* expected, const QByteArray& current, + const char* file, int lineNo) { + return assertEquals(expected, current.constData(), file, lineNo); +} + +/** + * @brief Tests whether a value is true. + * + * A value of false will be logged. + * + * @param condition value to test + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return condition + */ +bool ReTest::assertTrue(bool condition, const char* file, int lineNo) { + if(! condition) + error("%s-%d: not TRUE", file, lineNo); + return condition; +} + +/** + * @brief Tests whether a value is false. + * + * A value of true will be logged. + * + * @param condition value to test + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return ! condition + */ +bool ReTest::assertFalse(bool condition, const char* file, int lineNo) { + if(condition) + error("%s-%d: not FALSE", file, lineNo); + return ! condition; +} + +/** + * @brief Tests whether a value is NULL. + * + * A value of not NULL will be logged. + * + * @param ptr value to test + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: ptr is NULL + */ +bool ReTest::assertNull(const void* ptr, const char* file, int lineNo) { + if(ptr != NULL) + error("%s-%d: not NULL", file, lineNo); + return ptr == NULL; +} + +/** + * @brief Tests whether a value is not NULL. + * + * A value of NULL will be logged. + * + * @param ptr value to test + * @param file the file containing the test + * @param lineNo the line number containing the test + * @return true: ptr is not NULL + */ +bool ReTest::assertNotNull(const void* ptr, const char* file, int lineNo) { + if(ptr == NULL) + error("%s-%d: is NULL", file, lineNo); + return ptr != NULL; +} + +/** + * @brief Compares two files line by line. + * + * @param expected the file with the expected content + * @param current the file with the current content + * @param file the source file (point of the comparison) + * @param lineNo the source position (point of the comparison) + * @return true: the files are equal
+ * false: otherwise + */ +bool ReTest::assertEqualFiles(const char* expected, const char* current, + const char* file, int lineNo) +{ + bool rc = false; + QByteArray expectedContent = ReStringUtil::read(expected, true); + QByteArray currentContent = ReStringUtil::read(current, true); + if (expectedContent.isEmpty()){ + char buffer[512]; + qsnprintf(buffer, sizeof buffer, "%s has no content. Does it exist?", + expected); + error(buffer); + } else if (currentContent.isEmpty()){ + char buffer[512]; + qsnprintf(buffer, sizeof buffer, "%s has no content. Does it exist?", + current); + error(buffer); + } else { + QList expLines = expectedContent.split('\n'); + QList curLines = currentContent.split('\n'); + rc = assertEquals(expLines, curLines, file, lineNo); + } + return rc; +} + +/** + * @brief Writes an info. + * + * @param message message to show + * @return true (for chaining) + */ +bool ReTest::log(const char* message) { + m_logger.log(LOG_INFO, 0, message); + return true; +} + + +/** + * @brief Writes an error. + * + * @param format message to show. With placeholders like std::printf() + * @param ... the values for the placeholders in format + * @return false (for chaining) + */ +bool ReTest::error(const char* format, ...) { + m_errors++; + va_list ap; + va_start(ap, format); + m_logger.log(LOG_ERROR, 0, format, ap); + va_end(ap); + return false; +} + +/** + * @brief Tests whether the m_memoryLogger has a message containing a given pattern. + * + * @param pattern regular expression to search + * @return true: pattern has been found
+ * false: otherwise + */ +bool ReTest::logContains(const char* pattern) +{ + const QList& lines = m_memoryAppender.getLines(); + QRegularExpression rexpr(pattern); + bool rc = false; + QRegularExpressionMatch match; + for (int ii = 0; ii < lines.size(); ii++){ + const QByteArray& line = lines.at(ii); + match = rexpr.match(line); + if (match.hasMatch()){ + rc = true; + break; + } + } + return rc; +} + +/** + * @brief Returns the name of a directory in the temp dir. + * + * If the named directory does not exist it will be created. + * + * @param node NULL or the node (name without path) + * @param parent NULL or a node of the parent + * @param withSeparator true: the result ends with slash/backslash + * @return the name of an existing directory + */ +QByteArray ReTest::getTempDir(const char* node, const char* parent, + bool withSeparator) { + QByteArray temp("c:\\temp"); + struct stat info; + const char* ptr; + if((ptr = getenv("TMP")) != NULL) + temp = ptr; + else if((ptr = getenv("TEMP")) != NULL) + temp = ptr; + else if(stat("/tmp", &info) == 0) + temp = "/tmp"; + char sep = m_separator = temp.indexOf('/') >= 0 ? '/' : '\\'; + if(temp.at(temp.length() - 1) != sep) + temp += sep; + if(parent != NULL) { + temp += parent; + if(stat(temp.constData(), &info) != 0) + mkdir(temp.constData(), (-1)); + temp += sep; + } + if(node != NULL) { + temp += node; + temp += sep; + if(stat(temp.data(), &info) != 0) + mkdir(temp.data(), -1); + } + if(! withSeparator) + temp.resize(temp.length() - 1); + return temp; +} + +/** + * @brief Returns a name of a file in a temporary directory. + * + * @param node the file's name without path + * @param parent NULL or the name of a subdirectory the file will be inside + * @param deleteIfExists true: if the file exists it will be removed + * @return the full name of a temporary file + */ +QByteArray ReTest::getTempFile(const char* node, const char* parent, + bool deleteIfExists) { + QByteArray dir = getTempDir(parent); + QByteArray rc = dir; + if (! rc.endsWith(m_separator)) + rc += m_separator; + rc += node; + struct stat info; + if(deleteIfExists && stat(rc.constData(), &info) == 0) + unlink(rc.constData()); + return rc; +} diff --git a/base/ReTest.hpp b/base/ReTest.hpp new file mode 100644 index 0000000..ec04340 --- /dev/null +++ b/base/ReTest.hpp @@ -0,0 +1,67 @@ +/* + * 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 RETEST_HPP +#define RETEST_HPP + +// the sources generated from QT include this file directly: +class ReTest { +public: + ReTest(const char* name); + virtual ~ReTest(); +private: + // No copy constructor: no implementation! + ReTest(const ReTest& source); + // Prohibits assignment operator: no implementation! + ReTest& operator =(const ReTest& source); +public: + bool assertEquals(int expected, int current, const char* file, int lineNo); + bool assertEquals(int64_t expected, int64_t current, const char* file, int lineNo); + bool assertEquals(qreal expected, qreal current, const char* file, int lineNo); + bool assertEquals(const char* expected, const ReString& current, + const char* file, int lineNo); + bool assertEquals(const ReString& expected, const ReString& current, + const char* file, int lineNo); + bool assertEquals(const char* expected, const char* current, + const char* file, int lineNo); + bool assertEquals(const QByteArray& expected, const QByteArray& current, + const char* file, int lineNo); + bool assertEquals(const char* expected, const QByteArray& 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); + bool assertNotNull(const void* ptr, const char* file, int lineNo); + bool assertEqualFiles(const char* expected, const char* current, + const char* file, int lineNo); + bool log(const char* message); + bool error(const char* message, ...); + QByteArray getTempDir(const char* node, const char* parent = NULL, + bool withSeparator = true); + QByteArray getTempFile(const char* node, const char* parent = NULL, + bool deleteIfExists = true); + bool logContains(const char* pattern); + virtual void run(void) = 0; + +protected: + int m_errors; + QByteArray m_name; + ReLogger m_logger; + // for testing of logging code: + ReMemoryAppender m_memoryAppender; + ReLogger m_memoryLogger; + char m_separator; +}; +#define checkEqu(expected, current) assertEquals(expected, current, __FILE__, __LINE__) +#define checkT(current) assertTrue(current, __FILE__, __LINE__) +#define checkF(current) assertFalse(current, __FILE__, __LINE__) +#define checkN(current) assertNull(current, __FILE__, __LINE__) +#define checkNN(current) assertNotNull(current, __FILE__, __LINE__) +#define checkFiles(expected, current) assertEqualFiles(expected, current, __FILE__, __LINE__) +#endif // RETEST_HPP diff --git a/base/ReWriter.cpp b/base/ReWriter.cpp new file mode 100644 index 0000000..ecfb73d --- /dev/null +++ b/base/ReWriter.cpp @@ -0,0 +1,200 @@ +/* + * 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. +*/ + +/** @file + * @brief A writer to an output media. + * + * Implementation of the abstract base class ReWriter and + * the concrete derivation ReFileWriter. + */ +/** @file rplcore/rplwriter.hpp + * + * @brief Definitions for a writer to an output media. + */ +#include "base/rebase.hpp" + +const char* ReWriter::m_tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; +int ReWriter::m_maxIndention = strlen(ReWriter::m_tabs); + +/** @class ReWriter ReWriter.hpp "rplcore/ReWriter.hpp" + * + * @brief Implements an abstract base class for producing text lines. + * + */ + +/** + * @brief Destructor. + * + * Closes the output medium. + * Ensures that the destructors of the derived classes are virtual. + */ +ReWriter::~ReWriter() +{ + close(); +} + +/** + * @brief Closes the output medium. + * + * This method does nothing, but overriding methods should free the resources. + * + * @note The method must be designed so that it can be called multiple times. + */ +void ReWriter::close() +{ +} +/** + * @brief Puts a given count of "\t" to the output medium. + * + * @param indent indention level, number of "\t" + */ +void ReWriter::indent(int indent) +{ + if (indent > m_maxIndention) + indent = m_maxIndention; + format("%.*s", indent, m_tabs); +} + +/** + * @brief Formats a string and write it to the output medium. + * + * @param format format string with placeholders like sprintf() + * @param ... variable arguments, values for the placeholders + */ +void ReWriter::format(const char* format, ...) +{ + va_list ap; + va_start(ap, format); + write(ap, format); + va_end(ap); +} +/** + * @brief Formats a line and write it to the output medium. + * + * @param format format string with placeholders like sprintf() + * @param ... variable arguments, values for the placeholders + */ +void ReWriter::formatLine(const char* format, ...) +{ + char buffer[64000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + writeLine(buffer); +} + +/** + * @brief Formats a message and writes it to the output medium. + * + * @param ap variable argument list (like in vsprintf) + * @param format format string with placeholders + */ +void ReWriter::write(va_list ap, const char* format) +{ + char buffer[64000]; + qvsnprintf(buffer, sizeof buffer, format, ap); + write(buffer); +} + +/** + * @brief Writes a line with indention to the output medium. + * + * @param indent indention level. Indention is limited to 20 + * @param line the line to write + */ +void ReWriter::writeIndented(int indent, const char* line) +{ + ReWriter::indent(indent); + writeLine(line); +} + +/** + * @brief Writes a line with indention to the output medium. + * + * @param indent indention level. Indention is limited to 20 + * @param format format string with placeholders like sprintf + * @param ... the values for the placeholders (variable arguments) + */ +void ReWriter::formatIndented(int indent, const char* format, ...) +{ + ReWriter::indent(indent); + + char buffer[64000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + writeLine(buffer); +} + +/** @class ReWriter rplwriter.hpp "rplcore/rplwriter.hpp" + * + * @brief Implements a class which writes lines into a file. + */ + +/** + * @brief Constructor. + * + * @param filename the file's name + * @param mode write mode, "w" for write or "a" for append + * @param additionalStream if not NULL the content will be written to this + * stream too. Normal usage: stdout or + * stderr + * @param eoln line end: "\n" or "\r\n" + */ +ReFileWriter::ReFileWriter(const char* filename, const char* mode, + FILE* additionalStream, const char* eoln) : + m_fp(fopen(filename, mode)), + m_name(filename), + m_eoln(eoln), + m_additionalStream(additionalStream) +{ +} + +/** + * @brief Writes a string to the file. + * @param message the string to write + */ +void ReFileWriter::write(const char* message) +{ + if (m_fp != NULL) + fputs(message, m_fp); + if (m_additionalStream != NULL) + fputs(message, m_additionalStream); +} + +/** + * @brief Writes a line to the file. + * @param line the line to write. If NULL an empty line will be written + */ +void ReFileWriter::writeLine(const char* line) +{ + if (m_fp != NULL){ + if (line != NULL) + fputs(line, m_fp); + fputs(m_eoln, m_fp); + } + if (m_additionalStream != NULL){ + if (line != NULL) + fputs(line, m_additionalStream); + fputc('\n', m_additionalStream); + } +} + +/** + * @brief Closes the output file. + */ +void ReFileWriter::close() +{ + if (m_fp != NULL){ + fclose(m_fp); + m_fp = NULL; + } + m_additionalStream = NULL; +} diff --git a/base/ReWriter.hpp b/base/ReWriter.hpp new file mode 100644 index 0000000..a53120b --- /dev/null +++ b/base/ReWriter.hpp @@ -0,0 +1,59 @@ +/* + * 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 REWRITER_HPP +#define REWRITER_HPP + +class ReWriter +{ +public: + virtual ~ReWriter(); +public: + /** + * @brief Writes a text to the output medium. + * + * @param message the message + */ + virtual void write(const char* message) = 0; + /** + * @brief Writes a text line to the output medium. + * + * @param line the text line. If NULL an empty line will be written + */ + virtual void writeLine(const char* line = NULL) = 0; + virtual void close(); +public: + void indent(int indent); + void format(const char* format, ...); + void formatLine(const char* format, ...); + void write(va_list ap, const char* format); + void writeIndented(int indent, const char* line); + void formatIndented(int indent, const char* format, ...); +protected: + static const char* m_tabs; + static int m_maxIndention; +}; + +class ReFileWriter : public ReWriter +{ +public: + ReFileWriter(const char* filename, const char* mode = "w", + FILE* additionalStream = NULL, const char* eoln = "\n"); +public: + virtual void write(const char* line); + virtual void writeLine(const char* line = NULL); + virtual void close(); +protected: + FILE* m_fp; + QByteArray m_name; + QByteArray m_eoln; + FILE* m_additionalStream; +}; + +#endif // REWRITER_HPP diff --git a/base/rebase.hpp b/base/rebase.hpp new file mode 100644 index 0000000..a415c98 --- /dev/null +++ b/base/rebase.hpp @@ -0,0 +1,56 @@ +/* + * 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 RECORE_HPP +#define RECORE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned char uint8_t; +//typedef qint64 int64_t; +typedef quint64 uint64_t; +typedef qint32 int32_t; +typedef quint32 uint32_t; +typedef qreal real_t; +typedef QString ReString; +#define RPL_UNUSED(x) (void)(x) + +#include "remodules.hpp" +#include "base/ReByteStorage.hpp" +#include "base/ReCharPtrMap.hpp" +#include "base/ReWriter.hpp" +#include "base/ReLogger.hpp" +#include "base/ReException.hpp" +#include "base/ReContainer.hpp" +#include "base/ReStringUtil.hpp" +#include "base/ReQtring.hpp" +#include "base/ReConfigurator.hpp" +#include "base/ReConfig.hpp" +#include "base/ReTerminator.hpp" +#include "base/ReTest.hpp" + +#endif // RECORE_HPP diff --git a/base/testrplexample.cpp b/base/testrplexample.cpp new file mode 100644 index 0000000..0c89591 --- /dev/null +++ b/base/testrplexample.cpp @@ -0,0 +1,50 @@ +/* + * 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 "project.hpp" + +#include "base/base.hpp" +// Code to test: +int add(int a, int b) { + return a+b; +} +QByteArray concat(const char* a, const char* b) { + return QByteArray(a) + " " + b; +} +const char* firstDot(const char* s) { + return strchr(s, '.'); +} +/** + * @brief Example for usage of the class ReTest. + */ +class TestRplExample : public ReTest { +public: + TestRplExample() : ReTest("RplExample") {} + +public: + void testInt() { + log("testing add..."); + // compare 2 integers: + checkEqu(2, add(1, 1)); + } + void testString() { + // compare 2 strings: + checkEqu("Be good", concat("Be", "good")); + // test for not NULL: + checkN(firstDot("Hi.")); + // test for NULL: + checkNN(firstDot("Hi")); + } + virtual void doIt() { + testInt(); + testString(); + } +}; +void testRplExample() { + TestRplExample test; + test.run(); +} diff --git a/cunit/cuReConfig.cpp b/cunit/cuReConfig.cpp new file mode 100644 index 0000000..76fa172 --- /dev/null +++ b/cunit/cuReConfig.cpp @@ -0,0 +1,60 @@ +/* + * cuReConfig.cpp + * + * 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 "base/rebase.hpp" +/** + * @brief Unit test of ReConfig. + */ +class TestReConfig: public ReTest { +public: + TestReConfig() : + ReTest("ReConfig") { + } + +public: + void testBasic() { + QByteArray fn = getTempFile("test.data", "config"); + ReStringUtil::write(fn, "#comment\na=1\nb.1==x\n#=\nB=zzz"); + ReConfig config(fn.constData()); + checkEqu(3, config.size()); + checkEqu("1", config["a"]); + checkEqu("=x", config["b.1"]); + checkEqu("zzz", config["B"]); + } + void testAsX() { + QByteArray fn = getTempFile("test.data", "config"); + ReStringUtil::write(fn, "i=123\nb=1\nb2=true\nb3=yes\ns=abc"); + ReConfig config(fn.constData()); + checkEqu(5, config.size()); + checkEqu(123, config.asInt("i", -1)); + checkEqu(-1, config.asInt("I", -1)); + checkT(config.asBool("b", false)); + checkT(config.asBool("b2", false)); + checkT(config.asBool("b3", false)); + checkT(config.asBool("-", true)); + checkF(config.asBool("-", false)); + checkEqu("abc", config.asString("s", "x")); + checkEqu("x", config.asString("S", "x")); + } + + virtual void run() { + testAsX(); + testBasic(); + + } +}; + +void testReConfig() { + TestReConfig test; +} + + + + diff --git a/cunit/cuReContainer.cpp b/cunit/cuReContainer.cpp new file mode 100644 index 0000000..8b3b38e --- /dev/null +++ b/cunit/cuReContainer.cpp @@ -0,0 +1,58 @@ +/* + * cuReContainer.cpp + * + * 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 "base/rebase.hpp" +/** + * @brief Unit test for ReContainer + */ +class TestRplContainer : public ReTest { +public: + TestRplContainer() : ReTest("RplContainer") {} + +public: + void testBasic() { + ReContainer container(256); + // Rpl&1 09 36[2]cis:!7b Nirwana <0> Y -ab34 A long string with an trailing '0' <0>
+ container.startBag(); + container.addChar('!'); + container.addInt(123); + container.addString("Nirwana"); + container.startBag(); + container.addChar('Y'); + container.addInt(-0xab34); + container.addString("A long string with an trailing '0'"); + + QByteArray data = container.getData(); + + ReContainer container2(256); + container2.fill(data); + checkEqu(2, container2.getCountBags()); + checkEqu('!', container2.nextChar()); + checkEqu(123, container2.nextInt()); + checkEqu("Nirwana", container2.nextString()); + container2.nextBag(); + checkEqu('Y', container2.nextChar()); + checkEqu(-0xab34, container2.nextInt()); + checkEqu("A long string with an trailing '0'", container2.nextString()); + + log(("Example: " + data).constData()); + } + + virtual void run() { + testBasic(); + } +}; + +void testRplContainer() { + TestRplContainer test; +} + + + + diff --git a/cunit/cuReEnigma.cpp b/cunit/cuReEnigma.cpp new file mode 100644 index 0000000..6688df5 --- /dev/null +++ b/cunit/cuReEnigma.cpp @@ -0,0 +1,112 @@ +/* + * 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 "base/rebase.hpp" +/** + * @brief Unit test for ReEnigma. + */ +class TestReEnigma : public ReTest { +public: + TestReEnigma() : ReTest("ReEnigma") {} + +public: + void testOneCharset(const char* value, const char* charSet, + const char* expected) { + ReEnigma enigma; + enigma.addByteSecret(QByteArray("Geheim")); + enigma.setSeed(0); + QByteArray encoded = value; + QByteArray booster; + enigma.encode(encoded.data(), encoded.length(), charSet, booster); + //printString(encoded.constData()); + QByteArray decoded = encoded; + enigma.setSeed(0); + enigma.decode(decoded.data(), decoded.length(), charSet, booster); + checkEqu(value, decoded.constData()); + checkEqu(expected, encoded); + } + + void printCharSets() { + QByteArray value; + value.reserve(256); + unsigned char cc; + for(cc = ' '; cc <= 127; cc++) { + if(cc == '"' || cc == '\\') + value.append('\\'); + value.append(cc); + } + printf("%s\n", value.constData()); + value.resize(0); + for(cc = 128; cc >= 128; cc++) { + char buf[10]; + if(cc % 32 == 0) + value.append("\n"); + sprintf(buf, "\\x%02x", cc); + value.append(buf); + } + printf("%s\n", value.constData()); + } + void printString(const char* value) { + QByteArray v; + unsigned char cc; + while((cc = (unsigned char) *value++) != 0) { + if(cc == '\\' || cc == '"') { + v.append('\\'); + v.append(cc); + } else if(cc >= 127) { + char buffer[10]; + sprintf(buffer, "\\x%02x", cc); + v.append(buffer); + } else { + v.append(cc); + } + } + printf("%s\n", v.constData()); + } + void testOneBytes(const char* bytes) { + ReEnigma enigma; + enigma.addByteSecret("Hello World"); + enigma.setSeed(0x1234); + + QByteArray encoded(bytes); + enigma.change(encoded); + + enigma.setSeed(0x1234); + QByteArray decoded(encoded); + enigma.change(decoded); + checkEqu(bytes, decoded); + } + + void testBytes() { + testOneBytes("abcdefg"); + testOneBytes("01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + } + + void testCharSet() { + //testOneCharset("&()[]{}Weiß der Geier/Kuckuck?", ReEnigma::SET_32_255, "2Kc\x9a\xfeQ\xd7\xa84sx)*\xfb\xd2z\xf4\"W\xb0\xee\xb0\xd1\x84\xace\xf8_u*T"); + testOneCharset("\\Weiß der Geier/Kuckuck?", ReEnigma::SET_32_127, + "(Z?hßaZ_#/QZ+Oi|SI^=<,)A"); + testOneCharset("01234567890abcdef", ReEnigma::SET_HEXDIGITS, + "c4c25b08735c53a63"); + testOneCharset("data$1%3.^~", ReEnigma::SET_FILENAME, "^voazo-n%$b"); + testOneCharset("Weiß der Geier!", ReEnigma::SET_ALPHANUM, "weyß BCk 19NoO!"); + testOneCharset("12345678901234567890", ReEnigma::SET_DECIMALS, + "97394833084815683977"); + testOneCharset("000000000000000000000000000", ReEnigma::SET_DECIMALS, + "850592651836811261879625929"); + } + + virtual void doIt() { + testBytes(); + testCharSet(); + } +}; + +void testReEnigma() { + TestReEnigma test; + test.run(); +} diff --git a/cunit/main.cpp b/cunit/main.cpp new file mode 100644 index 0000000..56bafc5 --- /dev/null +++ b/cunit/main.cpp @@ -0,0 +1,82 @@ +/* + * 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 "../rplmath/rplmath.hpp" + +#include + +void testCore(){ + extern void testReString(); + testReString(); + + extern void testReCharPtrMap(); + testReCharPtrMap(); + + extern void testRplWriter(); + testRplWriter(); + + extern void testRplByteStorage(); + testRplByteStorage(); + + extern void testRplQString(); + testRplQString(); + + extern void testReString(); + testReString(); + + extern void testRplException(); + testRplException(); +} + +void testExpr(){ + extern void testRplMFParser(); + testRplMFParser(); + + extern void testRplBenchmark(); + //testRplBenchmark(); + + extern void testReVM(); + testReVM(); + + extern void testReSource(); + testReSource(); + + extern void testRplLexer(); + testRplLexer(); + + extern void testRplMFParser(); + testRplMFParser(); + + extern void testReASTree(); + testReASTree(); + + extern void testReVM(); + testReVM(); + +} + +void testStandard(){ + testExpr(); + testCore(); + extern void testRplMatrix(); + testRplMatrix(); + +} + +void labor(){ +} + +int main(int argc, char *argv[]) +{ + //labor(); + if (argc > 1) + printf("not used: %s\n", argv[1]); + + testStandard(); + +} diff --git a/cunit/rplastree_test.cpp b/cunit/rplastree_test.cpp new file mode 100644 index 0000000..2f03043 --- /dev/null +++ b/cunit/rplastree_test.cpp @@ -0,0 +1,103 @@ +/* + * 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. +*/ + +/** @file + * @brief Unit test of the abstract syntax tree. + */ + + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestReASTree : public ReTest{ +private: + ReSource m_source; + ReStringReader m_reader; + ReStringSourceUnit m_unit; + ReASTree m_tree; +public: + TestReASTree() : + ReTest("ReASTree"), + m_source(), + m_reader(m_source), + m_unit("
", "", &m_reader), + m_tree() + {} +public: + void testReASException() { + try{ + m_reader.addSource("
", "12"); + m_source.addReader(&m_reader); + m_source.addSourceUnit(m_reader.currentSourceUnit()); + const ReSourcePosition* pos = m_source.newPosition(2); + throw ReASException(pos, "simple string: %s", "Hi"); + checkF(true); + } catch (ReASException exc){ + checkEqu("
:0:2: simple string: Hi", exc.getMessage().constData()); + } + } + void testReASVariant(){ + ReASVariant val1; + val1.setFloat(2.5E-2); + checkEqu(2.5E-2, val1.asFloat()); + ReASVariant val2(val1); + checkEqu(2.5E-2, val2.asFloat()); + + val1.setInt(4321); + checkEqu(4321, val1.asInt()); + val2 = val1; + checkEqu(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!"); + checkEqu("High noon!", *val1.asString()); + val2 = val1; + val1.setString("Bye"); + checkEqu("High noon!", *val2.asString()); + ReASVariant val3(val1); + checkEqu("Bye", *val3.asString()); + } + void testReASConstant(){ + ReASConstant constant; + //constant.value().setString("Jonny"); + ReASVariant value; + //constant.calc(value); + //checkEqu("Jonny", *value.asString()); + } + void testReASNamedValue(){ + ReASNamedValue value(NULL, m_tree.symbolSpaces()[0], "gugo", + ReASNamedValue::A_GLOBAL); + checkEqu("gugo", value.name()); + } + virtual void doIt() { + testReASNamedValue(); + testReASConstant(); + testReASException(); + testReASVariant(); + } +}; +void testReASTree() { + TestReASTree test; + test.run(); +} + + + + + + diff --git a/cunit/rplbench.cpp b/cunit/rplbench.cpp new file mode 100644 index 0000000..74dc740 --- /dev/null +++ b/cunit/rplbench.cpp @@ -0,0 +1,61 @@ +/* + * 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. +*/ + +/** @file + * @brief Unit test of the abstract syntax tree. + */ + + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestRplBenchmark : public ReTest{ +private: + const char* m_filename; + ReSource m_source; + ReFileReader m_reader; + ReASTree m_tree; +public: + TestRplBenchmark() : + ReTest("RplBenchmark"), + m_filename("/home/ws/qt/rplqt/bench/mfbench.mf"), + m_source(), + m_reader(m_source), + m_tree() + { + m_source.addReader(&m_reader); + m_reader.addSource(m_filename); + } +public: + void benchmark() { + time_t start = time(NULL); + ReMFParser parser(m_source, m_tree); + parser.parse(); + time_t end = time(NULL); + printf("compilation: %d sec\n", int(end - start)); + } + virtual void doIt() { + try{ + ReFileSourceUnit* unit = dynamic_cast + (m_reader.currentSourceUnit()); + if (unit != NULL && ! unit->isOpen()) + throw ReException("file not found: %s", m_filename); + benchmark(); + } catch(ReException ex){ + printf("%s\n", ex.getMessage().constData()); + } + } +}; +void testRplBenchmark() { + TestRplBenchmark test; + test.run(); +} + + + diff --git a/cunit/rplbytestorage_test.cpp b/cunit/rplbytestorage_test.cpp new file mode 100644 index 0000000..52437d2 --- /dev/null +++ b/cunit/rplbytestorage_test.cpp @@ -0,0 +1,77 @@ +/* + * 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. +*/ +/** @file + * @brief Unit test of the byte and C string storage. + */ + +#include "../base/rebase.hpp" +#include "../base/ReTest.hpp" + +class TestRplByteStorage : public ReTest{ +public: + TestRplByteStorage() : + ReTest("RplByteStorage") + {} +private: + void testChars(){ + ReByteStorage store(100); + char* s1 = store.allocateChars(4); + memcpy((void*) s1, "123", 4); + const char* s2 = store.allocateChars("abc"); + const char* s3 = store.allocateChars("defghij", 3); + checkEqu(s1, "123"); + checkEqu(s2, "abc"); + checkEqu(s3, "def"); + const char* ptr = s1 + 4; + checkT(ptr == s2); + ptr += 4; + checkT(ptr == s3); + } + + void testBytes(){ + ReByteStorage store(100); + uint8_t* s1 = store.allocateBytes(4); + memcpy((void*) s1, "1234", 4); + uint8_t* s2 = store.allocateBytes((void*) "abcd", 4); + uint8_t* s3 = store.allocateBytes((void*) "efghij", 4); + uint8_t* s4 = store.allocateZeros(4); + + checkEqu("1234abcdefgh", (const char*) s1); + uint8_t* ptr = s1 + 4; + checkT(ptr == s2); + ptr += 4; + checkT(ptr == s3); + ptr += 4; + checkT(ptr == s4); + for (int ii = 0; ii < 4; ii++) + checkEqu(0, (int) s4[ii]); + } + void testBufferChange(){ + ReByteStorage store(10); + char buffer[2]; + buffer[1] = '\0'; + for (int ii = 0; ii < 10000; ii++){ + buffer[1] = 'A' + ii % 26; + store.allocateBytes(buffer, 1); + } + int a = 1; + } + +public: + virtual void doIt() { + testBufferChange(); + testChars(); + testBytes(); + } +}; +void testRplByteStorage() { + TestRplByteStorage test; + test.run(); +} + + diff --git a/cunit/rplcharptrmap_test.cpp b/cunit/rplcharptrmap_test.cpp new file mode 100644 index 0000000..8ad1d78 --- /dev/null +++ b/cunit/rplcharptrmap_test.cpp @@ -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. +*/ + +#include "base/rebase.hpp" +#include "rplcore/rpltest.hpp" + +class TestReCharPtrMap : public ReTest{ +public: + TestReCharPtrMap() : + ReTest("ReCharPtrMap") + { + } +protected: + void testBasic(){ + ReCharPtrMap map; + map["x"] = "x1"; + checkT(map.contains("x")); + checkF(map.contains("y")); + checkEqu("x1", map["x"]); + } + + virtual void doIt(void) { + testBasic(); + } +}; +void testReCharPtrMap() { + TestReCharPtrMap test; + test.run(); +} diff --git a/cunit/rplexception_test.cpp b/cunit/rplexception_test.cpp new file mode 100644 index 0000000..57293cb --- /dev/null +++ b/cunit/rplexception_test.cpp @@ -0,0 +1,51 @@ +/* + * 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 "base/rebase.hpp" +#include "rplcore/rpltest.hpp" +/** @file + * @brief Unit test of the basic exceptions. + */ + +class TestRplException : public ReTest{ +public: + TestRplException() : ReTest("RplException") {} + +public: + void testBasic() { + try{ + throw ReException("simple"); + checkF(true); + } catch (ReException exc){ + checkEqu("simple", exc.getMessage().constData()); + } + try{ + throw ReException("String: %s and int %d", "Hi", -333); + checkF(true); + } catch (ReException exc){ + checkEqu("String: Hi and int -333", exc.getMessage().constData()); + } + try{ + throw ReException(LOG_INFO, 1234, &m_memoryLogger, + "String: %s and int %d", "Hi", -333); + checkF(true); + } catch (ReException exc){ + checkT(logContains("^ .*\\(1234\\): String: Hi and int -333")); + } + log("ok"); + } + virtual void doIt() { + testBasic(); + } +}; +void testRplException() { + TestRplException test; + test.run(); +} + + + diff --git a/cunit/rpllexer_test.cpp b/cunit/rpllexer_test.cpp new file mode 100644 index 0000000..060e824 --- /dev/null +++ b/cunit/rpllexer_test.cpp @@ -0,0 +1,280 @@ +/* + * 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. +*/ + +/** @file + * @brief Unit test of the syntax symbol extractor. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestRplLexer : public ReTest, public ReToken{ +public: + TestRplLexer() : + ReTest("ReLexer"), + ReToken(TOKEN_ID) + {} + +public: + void testRplToken(){ + // test constructor values: + checkEqu(TOKEN_ID, tokenType()); + checkEqu(0, m_value.m_id); + checkT(m_string.isEmpty()); + checkT(m_printableString.isEmpty()); + + m_value.m_id = 7422; + checkEqu(7422, ReToken::id()); + m_string = "Wow!"; + checkEqu("Wow!", ReToken::toString()); + m_printableString = "GooGoo"; + checkEqu("GooGoo", rawString()); + m_tokenType = TOKEN_NUMBER; + checkEqu(TOKEN_NUMBER, tokenType()); + + clear(); + checkEqu(TOKEN_UNDEF, tokenType()); + checkEqu(0, m_value.m_id); + checkT(m_string.isEmpty()); + checkT(m_printableString.isEmpty()); + + m_value.m_integer = 773322; + checkEqu(773322, asInteger()); + m_value.m_real = 0.25; + checkEqu(0.25, asReal()); + } + + ReToken* checkToken(ReToken* token, RplTokenType type, int id = 0, + const char* string = NULL){ + checkEqu(type, token->tokenType()); + if (id != 0) + checkEqu(id, token->id()); + if (string != NULL) + checkEqu(string, token->toString()); + return token; + } + enum { KEY_UNDEF, KEY_IF, KEY_THEN, KEY_ELSE, KEY_FI + }; +# define KEYWORDS "if then else fi" + enum { OP_UNDEF, OP_PLUS, OP_TIMES, OP_DIV, OP_GT, + OP_LT, OP_GE, OP_LE, OP_EQ, OP_ASSIGN, OP_PLUS_ASSIGN, OP_DIV_ASSIGN, + OP_TIMES_ASSIGN + }; +# define OPERATORS "+\n* /\n> < >= <= ==\n= += /= *=" + enum { COMMENT_UNDEF, COMMENT_1, COMMENT_MULTILINE, COMMENT_2 + }; +# define COMMENTS "/* */ // \n" + void testSpace(){ + ReSource source; + ReStringReader reader(source); +# define BLANKS1 "\t\t \n" +# define BLANKS2 " \n" + reader.addSource("
", BLANKS1 BLANKS2); + source.addReader(&reader); + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_DECIMAL, + ReLexer::SF_TICK, ReLexer::STORE_ALL); + checkToken(lex.nextToken(), TOKEN_SPACE, 0, BLANKS1); + checkToken(lex.nextToken(), TOKEN_SPACE, 0, BLANKS2); + } + void testNumeric(){ + ReSource source; + ReStringReader reader(source); + const char* blanks = "321 0x73 7.8e+5"; + reader.addSource("
", blanks); + source.addReader(&reader); + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_TICK, ReLexer::STORE_ALL); + ReToken* token = checkToken(lex.nextToken(), TOKEN_NUMBER); + checkEqu(321, token->asInteger()); + token = checkToken(lex.nextNonSpaceToken(), TOKEN_NUMBER); + checkEqu(0x73, token->asInteger()); + token = checkToken(lex.nextNonSpaceToken(), TOKEN_REAL); + checkEqu(7.8e+5, token->asReal()); + } + + void testOperators(){ + ReSource source; + ReStringReader reader(source); + const char* ops = "<< < <<< <= == = ( ) [ ]"; + reader.addSource("
", ops); + source.addReader(&reader); + enum { UNDEF, SHIFT, LT, SHIFT2, LE, EQ, ASSIGN, + LPARENT, RPARENT, LBRACKET, RBRACKET }; + ReLexer lex(&source, KEYWORDS, ops, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_TICK, ReLexer::STORE_ALL); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, SHIFT); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LT); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, SHIFT2); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LE); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, EQ); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, ASSIGN); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LPARENT); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, RPARENT); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LBRACKET); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, RBRACKET); + checkToken(lex.nextNonSpaceToken(), TOKEN_END_OF_SOURCE); + reader.addSource("", "(([["); + lex.startUnit(""); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LPARENT); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LPARENT); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LBRACKET); + checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LBRACKET); + checkToken(lex.nextNonSpaceToken(), TOKEN_END_OF_SOURCE); + } + + void testComments(){ + ReSource source; + ReStringReader reader(source); + + reader.addSource("
", "/**/9//\n8/***/7// wow\n/*\n*\n*\n**/"); + source.addReader(&reader); + + enum { COMMENT_UNDEF, COMMENT_MULTILINE, COMMENT_1 + }; + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_LIKE_C, ReLexer::STORE_ALL); + checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_MULTILINE, + "/**/"); + checkToken(lex.nextToken(), TOKEN_NUMBER); + checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_1, + "//\n"); + checkToken(lex.nextToken(), TOKEN_NUMBER); + checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_MULTILINE, + "/***/"); + checkToken(lex.nextToken(), TOKEN_NUMBER); + checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_1, + "// wow\n"); + checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_MULTILINE, + "/*\n*\n*\n**/"); + } + void testStrings(){ + ReSource source; + ReStringReader reader(source); + + reader.addSource("
", "\"abc\\t\\r\\n\\a\\v\"'1\\x9Z\\x21A\\X9'"); + source.addReader(&reader); + + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_LIKE_C, ReLexer::STORE_ALL); + checkToken(lex.nextToken(), TOKEN_STRING, '"', "abc\t\r\n\a\v"); + checkToken(lex.nextToken(), TOKEN_STRING, '\'', "1\tZ!A\t"); + } + void testKeywords(){ + ReSource source; + ReStringReader reader(source); + + reader.addSource("
", "if\n\tthen else\nfi"); + source.addReader(&reader); + + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_LIKE_C, ReLexer::STORE_ALL); + checkToken(lex.nextToken(), TOKEN_KEYWORD, KEY_IF); + checkToken(lex.nextNonSpaceToken(), TOKEN_KEYWORD, KEY_THEN); + checkToken(lex.nextNonSpaceToken(), TOKEN_KEYWORD, KEY_ELSE); + checkToken(lex.nextNonSpaceToken(), TOKEN_KEYWORD, KEY_FI); + checkToken(lex.nextNonSpaceToken(), TOKEN_END_OF_SOURCE); + } + + void testIds(){ + ReSource source; + ReStringReader reader(source); + + reader.addSource("
", "i\n\tifs\n" + "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + source.addReader(&reader); + + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_LIKE_C, ReLexer::STORE_ALL); + checkToken(lex.nextToken(), TOKEN_ID, 0, "i"); + checkToken(lex.nextNonSpaceToken(), TOKEN_ID, 0, + "ifs"); + checkToken(lex.nextNonSpaceToken(), TOKEN_ID, 0, + "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + } + + void testBasic(){ + ReSource source; + ReStringReader reader(source); + source.addReader(&reader); + reader.addSource("
", "if i>1 then i=1+2*_x9 fi"); + ReLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_LIKE_C, ReLexer::STORE_ALL); + ReToken* token; + checkToken(lex.nextToken(), TOKEN_KEYWORD, KEY_IF); + checkToken(lex.nextToken(), TOKEN_SPACE, 0); + checkToken(lex.nextToken(), TOKEN_ID, 0, "i"); + checkToken(lex.nextToken(), TOKEN_OPERATOR, OP_GT); + token = checkToken(lex.nextToken(), TOKEN_NUMBER); + checkEqu(1, token->asInteger()); + checkToken(lex.nextToken(), TOKEN_SPACE, 0); + checkToken(lex.nextToken(), TOKEN_KEYWORD, KEY_THEN); + checkToken(lex.nextToken(), TOKEN_SPACE, 0); + + } + void testPrio(){ + ReSource source; + ReStringReader reader(source); + source.addReader(&reader); + reader.addSource("x", ""); + enum { O_UNDEF, O_ASSIGN, O_PLUS, O_MINUS, O_TIMES, O_DIV + }; + ReLexer lex(&source, KEYWORDS, + "=\n+ -\n* /", "=", + COMMENTS, + "A-Za-z_", + "A-Za-z0-9_", + ReLexer::NUMTYPE_ALL, + ReLexer::SF_LIKE_C, ReLexer::STORE_ALL); + checkT(lex.prioOfOp(O_ASSIGN) < lex.prioOfOp(O_PLUS)); + checkEqu(lex.prioOfOp(O_PLUS), lex.prioOfOp(O_MINUS)); + checkT(lex.prioOfOp(O_MINUS) < lex.prioOfOp(O_TIMES)); + checkEqu(lex.prioOfOp(O_TIMES), lex.prioOfOp(O_DIV)); + } + + virtual void doIt(void) { + testPrio(); + testBasic(); + testIds(); + testKeywords(); + testComments(); + testStrings(); + testOperators(); + testNumeric(); + testSpace(); + testRplToken(); + } +}; +void testRplLexer() { + TestRplLexer test; + test.run(); +} diff --git a/cunit/rplmatrix_test.cpp b/cunit/rplmatrix_test.cpp new file mode 100644 index 0000000..d1507e4 --- /dev/null +++ b/cunit/rplmatrix_test.cpp @@ -0,0 +1,372 @@ +/* + * Matrix_test.cpp + * + * Created on: 29.05.2014 + * Author: hm + */ + +/** @file + * @brief Unit test of the matrices. + */ + +#include "base/rebase.hpp" +#include "rplmath/rplmath.hpp" +#include "rplcore/rpltest.hpp" + +class TestRplMatrix : public ReTest{ +public: + TestRplMatrix() : ReTest("RplMatrix") {} + +public: + void fillMatrix(RplMatrix& mx, MatVal offset = 0){ + for(int row = 0; row < mx.getRows(); row++){ + for (int col = 0; col < mx.getCols(); col++){ + mx.set(row, col, 100.0*row + col + offset); + } + } + } + void checkMatrix(const RplMatrix& mx, MatVal offset = 0){ + int count = 0; + for(int row = 0; row < mx.getRows(); row++){ + for (int col = 0; col < mx.getCols(); col++){ + checkEqu(100.0*row + col + offset, mx.get(row, col)); + count++; + } + } + checkEqu(mx.getCols()*mx.getRows(), count); + } + void fillConst(RplMatrix& mx, MatVal value){ + for(int row = 0; row < mx.getRows(); row++){ + for (int col = 0; col < mx.getCols(); col++){ + mx.set(row, col, value); + } + } + } + void checkConst(const RplMatrix& mx, MatVal value){ + int count = 0; + for(int row = 0; row < mx.getRows(); row++){ + for (int col = 0; col < mx.getCols(); col++){ + checkEqu(value, mx.get(row, col)); + count++; + } + } + checkEqu(mx.getCols()*mx.getRows(), count); + } + + void testBasic() { + Tuple2 tuple(-2.0, 0.5); + checkEqu(-2.0, tuple.m_value1); + checkEqu(0.5, tuple.m_value2); + RplMatrix mat("mx"); + try{ + throw RplMatrixException(mat, "String: %s and int %d", "Hi", -333); + checkF(true); + } catch (RplMatrixException exc){ + checkEqu("mx: String: Hi and int -333", exc.getMessage()); + } + RplMatrix mat2; + try{ + throw RplMatrixException(mat2, "String: %s and int %d", "Hi", -333); + checkF(true); + } catch (RplMatrixException exc){ + checkEqu("String: Hi and int -333", exc.getMessage()); + } + checkEqu("mx", mat.getName()); + checkEqu("", mat2.getName()); + + RplMatrix m2x3(2, 3, "m2x3"); + checkEqu("m2x3", m2x3.getName()); + checkEqu(2, m2x3.getRows()); + checkEqu(3, m2x3.getCols()); + fillMatrix(m2x3); + checkMatrix(m2x3); + + RplMatrix mxCopy(m2x3); + checkEqu("m2x3-copy", mxCopy.getName()); + checkEqu(2, mxCopy.getRows()); + checkEqu(3, mxCopy.getCols()); + checkMatrix(mxCopy); + + RplMatrix mxCopy2("mxCopy2"); + mxCopy2 = m2x3; + checkEqu("mxCopy2", mxCopy2.getName()); + checkEqu(2, mxCopy2.getRows()); + checkEqu(3, mxCopy2.getCols()); + checkMatrix(mxCopy2); + } + void testAddOperators(){ + RplMatrix m1(3, 2, "m1"); + fillMatrix(m1); + checkMatrix(m1); + RplMatrix m2(3, 2, "m2"); + fillMatrix(m2, 42); + checkMatrix(m2, 42); + RplMatrix m3(3, 2, "m3"); + fillMatrix(m3, -42); + checkMatrix(m3, -42); + + m1 += 42; + checkMatrix(m1, 42); + + checkT(m1 == m2); + checkF(m1 == m3); + + m1 -= 42; + checkMatrix(m1); + m1 -= m1; + checkConst(m1, 0); + + fillMatrix(m1); + m1 -= m3; + checkConst(m1, 42); + m1 += m2; + checkMatrix(m1, 42*2); + } + void testCompareOperators(){ + RplMatrix m1(3, 2, "m1"); + fillMatrix(m1); + checkMatrix(m1); + RplMatrix m2(3, 2, "m2"); + fillMatrix(m2); + + checkT(m1 == m2); + checkF(m1 != m2); + // modify each element, comparism must return false: + int row, col; + for (row = 0; row < m2.getRows(); row++) + for (col = 0; col < m2.getCols(); col++){ + fillMatrix(m2); + m2.set(row, col, -1); + checkF(m1 == m2); + checkT(m1 != m2); + } + + fillConst(m1, 42); + checkT(m1 == 42); + checkF(m1 == 43); + checkT(m1 != 43); + for (row = 0; row < m1.getRows(); row++) + for (col = 0; col < m1.getCols(); col++){ + fillMatrix(m1, 42); + m1.set(row, col, -1); + checkF(m1 == 42); + checkT(m1 != 42); + } + } + + void testCheckDefinition(){ + RplMatrix m1(3, 2, "m1"); + fillMatrix(m1); + checkMatrix(m1); + RplMatrix m2(3, 2, "m2"); + fillMatrix(m2); + + m1.checkDefinition(1, 1); + m1.checkDefinition(1000, 1000); + m1.checkDefinition(0, 0); + try { + m1.checkDefinition(-1, 1); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: row number negative: -1", exc.getMessage()); + } + try { + m1.checkDefinition(1, -1); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: column number negative: -1", exc.getMessage()); + } + + } + void testCheck(){ + RplMatrix m1(3, 2, "m1"); + fillMatrix(m1); + checkMatrix(m1); + + m1.check(0, 0); + m1.check(3-1, 2-1); + try { + m1.check(-1, 1); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: invalid row: -1 not in [0,3[", exc.getMessage()); + } + try { + m1.check(3, 1); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: invalid row: 3 not in [0,3[", exc.getMessage()); + } + try { + m1.check(1, -1); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: invalid column: -1 not in [0,2[", exc.getMessage()); + } + try { + m1.check(1, 2); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: invalid column: 2 not in [0,2[", exc.getMessage()); + } + } + void testCheckSameDimension(){ + RplMatrix m1(3, 2, "m1"); + RplMatrix m2(3, 2, "m2"); + + m1.checkSameDimension(m2); + + m2.resize(2, 2); + try { + m1.checkSameDimension(m2); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: m2 has a different row count: 3 / 2", exc.getMessage()); + } + m2.resize(3, 3); + try { + m1.checkSameDimension(m2); + checkT(false); + } catch(RplMatrixException exc){ + checkEqu("m1: m2 has a different column count: 2 / 3", exc.getMessage()); + } + } + void testResize(){ + RplMatrix m1(3, 2, "m1"); + fillMatrix(m1); + checkMatrix(m1); + RplMatrix m2(2, 4, "m2"); + fillConst(m2, 0); + checkConst(m2, 0); + + m1.resize(2, 4); + checkEqu(2, m1.getRows()); + checkEqu(4, m1.getCols()); + checkT(m1 == m2); + } + + void testMinMax(){ + RplMatrix m1(4, 5, "m1"); + fillMatrix(m1); + checkMatrix(m1); + m1.set(0, 0, -98); + m1.set(3, 4, 9999); + Tuple2 miniMax = m1.minMax(); + checkEqu(-98.0, miniMax.m_value1); + checkEqu(9999.0, miniMax.m_value2); + + fillMatrix(m1); + checkMatrix(m1); + m1.set(1, 1, 7777); + m1.set(3, 4, -987); + miniMax = m1.minMax(); + checkEqu(-987.0, miniMax.m_value1); + checkEqu(7777.0, miniMax.m_value2); + } + + void testTranspose() + { + RplMatrix m1(1, 5, "m1"); + fillMatrix(m1); + RplMatrix m2(m1.transpose()); + + checkEqu(5, m2.getRows()); + checkEqu(1, m2.getCols()); + + int row, col; + col = 0; + for (row = 0; row < 5; row++){ + checkEqu(qreal(col*100+row), m2.get(row, 0)); + } + + m1.resize(35, 73); + fillMatrix(m1); + m2 = m1.transpose(); + + checkEqu(73, m2.getRows()); + checkEqu(35, m2.getCols()); + + int count = 0; + for (row = 0; row < m2.getRows(); row++){ + for (col = 0; col < m2.getCols(); col++){ + checkEqu(qreal(col*100+row), m2.get(row, col)); + count++; + } + } + checkEqu(73*35, count); + } + void testToString(){ + RplMatrix m1(1, 1, "m1"); + m1.set(0, 0, 2.34); + checkEqu("[2.340000,\n]", m1.toString().constData()); + checkEqu("jonny[2.34000 |]", m1.toString("jonny", "%.5f", "|", " ").constData()); + + m1.resize(2, 1); + m1.set(0, 0, 2.34); + m1.set(1, 0, 55.5); + + checkEqu("[2.340000,\n55.500000,\n]", m1.toString().constData()); + checkEqu("jonny[2.34000 |55.50000 |]", m1.toString("jonny", "%.5f", "|", " ").constData()); + log(""); + } + void testReadCsv(){ + QByteArray fn = getTempFile("rplmatrixtest.csv"); + const char* content; + RplMatrix m1(1,1,"m1"); + + fillMatrix(m1); + content = ",Port0,Port1,Port2\n" + "element1,5, -3E-99 , 0.5\n" + "element2,7,-22.3,44\n" + "\n" + "2 Elements, 3, Ports"; + ReStringUtil::write(fn, content); + m1.readFromCvs(fn, 256); + checkEqu(2, m1.getRows()); + checkEqu(3, m1.getCols()); + + checkEqu(5.0, m1.get(0, 0)); + checkEqu(-3.0E-99, m1.get(0, 1)); + checkEqu(0.5, m1.get(0, 2)); + + checkEqu(7.0, m1.get(1, 0)); + checkEqu(-22.3, m1.get(1, 1)); + checkEqu(44.0, m1.get(1, 2)); + + fillMatrix(m1); + content = "Port0,Port1,Port2\n" + "5, -3E-99 , 0.5\n"; + ReStringUtil::write(fn, content); + m1.readFromCvs(fn, 256); + checkEqu(1, m1.getRows()); + checkEqu(3, m1.getCols()); + checkEqu(5.0, m1.get(0, 0)); + checkEqu(-3.0E-99, m1.get(0, 1)); + checkEqu(0.5, m1.get(0, 2)); + + +/* + void readFromCvs(const char* filename, int maxLineLength = 1024*1024); + void readFromXml(const char* filename, const char* tagCol, + const char* tagRow, const char* tagTable, + int maxLineLength = 1024*1024); +*/ + } + virtual void doIt(void) { + testBasic(); + testAddOperators(); + testCompareOperators(); + testCheckDefinition(); + testCheck(); + testCheckSameDimension(); + testResize(); + testMinMax(); + testTranspose(); + testToString(); + testReadCsv(); + } +}; +void testRplMatrix() { + TestRplMatrix test; + test.run(); +} diff --git a/cunit/rplmfparser_test.cpp b/cunit/rplmfparser_test.cpp new file mode 100644 index 0000000..6cb4ede --- /dev/null +++ b/cunit/rplmfparser_test.cpp @@ -0,0 +1,226 @@ +/* + * 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. +*/ +/** @file + * @brief Unit test of the parser for the language "MF". + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestRplMFParser : public ReTest{ +private: + ReSource m_source; + ReASTree m_tree; + ReStringReader m_reader; + ReFileReader m_fileReader; + QByteArray m_currentSource; +public: + TestRplMFParser() : + ReTest("ReMFParser"), + m_source(), + m_tree(), + m_reader(m_source), + m_fileReader(m_source) + { + m_source.addReader(&m_reader); + } +protected: + void setSource(const char* content){ + ReASItem::reset(); + m_currentSource = content; + m_tree.clear(); + m_source.clear(); + m_reader.clear(); + m_reader.addSource("", content); + m_source.addReader(&m_reader); + m_source.addSourceUnit(m_reader.currentSourceUnit()); + } + void setFileSource(const char* filename){ + ReASItem::reset(); + m_currentSource = ReStringUtil::read(filename); + m_tree.clear(); + m_source.clear(); + m_fileReader.clear(); + m_fileReader.addSource(filename); + m_source.addReader(&m_fileReader); + m_source.addSourceUnit(m_fileReader.currentSourceUnit()); + } + +private: + void checkAST(const char* fileExpected, int lineNo){ + QByteArray fnExpected = "test"; + fnExpected += QDir::separator().toLatin1(); + fnExpected += "mfparser"; + fnExpected += (char) QDir::separator().toLatin1(); + fnExpected += fileExpected; + QByteArray fnCurrent = getTempFile(fileExpected, "rplmfparser"); + m_tree.dump(fnCurrent, ReASTree::DMP_NO_GLOBALS, m_currentSource); + assertEqualFiles(fnExpected.constData(), fnCurrent.constData(), + __FILE__, lineNo); + } + +public: + void fileClassTest(){ + setFileSource("test/rplmfparser/string1.mf"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("string1.txt", __LINE__); + } + + void baseTest(){ + setSource("2+3*4"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("baseTest.txt", __LINE__); + } + + void varDefTest(){ + setSource("const lazy Str s = 'Hi';\nconst List l;\nInt i = 3;"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("varDefTest.txt", __LINE__); + } + + void ifTest(){ + setSource("Int a;\nInt b;\na = b = 2;\nif 11 < 12\nthen a = 13 * 14\nelse a = 15 / 16\nfi"); + // setSource("Int a; if 11 < 12 then a = 13 * 14 else a = 15 / 16 fi"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("ifTest1.txt", __LINE__); + setSource("Str x;\nif 7 < 6\nthen x = '123';\nfi"); + parser.parse(); + checkAST("ifTest2.txt", __LINE__); + } + void whileTest(){ + setSource("Int a = 20;\nwhile 3 < 5 do\n a = 7\nod"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("whileTest.txt", __LINE__); + } + + void repeatTest(){ + setSource("Int a;\nrepeat\na++;\nuntil a != 2 * 3;"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("repeatTest.txt", __LINE__); + } + void forCTest(){ + setSource("Int a;\nfor b from 10 to 1 step -2 do\na += 1;\nod"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("forC1.txt", __LINE__); + setSource("Int a; for to 10 do a += 1 od"); + parser.parse(); + checkAST("forC2.txt", __LINE__); + } + void opTest(){ + checkEqu(25, ReMFParser::O_QUESTION); + checkEqu(37, ReMFParser::O_RSHIFT2); + checkEqu(41, ReMFParser::O_DEC); + checkEqu(48, ReMFParser::O_RBRACE); + setSource("Int a = 1;\nInt b = 100;\n--a;\nb++;\na--*++b**(8-3);\na=b=(a+(b-2)*3)"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("opTest1.txt", __LINE__); + } + void forItTest(){ + setSource("Map a;\nfor x in a do\na += 1;\nod"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("forIt1.txt", __LINE__); + } + void listTest(){ + ReMFParser parser(m_source, m_tree); + setSource("List b = [];"); + parser.parse(); + checkAST("list1.txt", __LINE__); + setSource("List a = [2+3, 3.14, 7, 'hi', a]; List b = [];"); + parser.parse(); + checkAST("list2.txt", __LINE__); + } + void mapTest(){ + setSource("Map a = {};"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("map1.txt", __LINE__); + setSource("Map a = {'a': 2+3,'bcd':3.14,'ccc':7, 'hi':'world'};\nMap b = {};"); + parser.parse(); + checkAST("map2.txt", __LINE__); + } + void methodCallTest(){ + //setSource("max(4,3.14);"); + setSource("rand();\nsin(a);\nmax(1+2*3,4**(5-4));"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("methc1.txt", __LINE__); + } + void fieldTest(){ + setSource("file.find('*.c')[0].name;\n[1,2,3].join(' ');\n3.14.trunc;"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("field1.txt", __LINE__); + } + + void methodTest(){ + setSource("func Float pi: 3.1415; endf func Str delim(): '/' endf;"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("meth1.txt", __LINE__); + setSource("func Int fac(const Int n):\n" + "Int rc; if rc <= 1 then rc = 1 else rc = n*fac(n-1) fi\n" + "rc endf"); + parser.parse(); + checkAST("meth2.txt", __LINE__); + setSource("func Int max(Int a, Int b):\n Int rc = a;\n" + "if a < b then rc = b; fi\nrc\n" + "endf\n" + "func Int max(const Int a, Int b, Int c):\n" + "max(a, max(b, c))\n" + "endf"); + parser.parse(); + checkAST("meth3.txt", __LINE__); + setSource("func Int max(const Int a, Int b, Int c):\n" + "func Int max(Int a, Int b):\n Int rc = a;\n" + "if a < b then rc = b; fi\nrc\n" + "endf\n" + "max(a, max(b, c))\n" + "endf"); + parser.parse(); + checkAST("meth4.txt", __LINE__); + } + void mainTest(){ + setSource("Int a=2+3*4;\nfunc Void main():\na;\nendf"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + checkAST("main1.txt", __LINE__); + } + + virtual void doIt(void) { + mainTest(); + varDefTest(); + repeatTest(); + baseTest(); + whileTest(); + methodTest(); + fieldTest(); + methodCallTest(); + mapTest(); + forItTest(); + forCTest(); + listTest(); + opTest(); + fileClassTest(); + } +}; +void testRplMFParser() { + TestRplMFParser test; + test.run(); +} + + diff --git a/cunit/rplqstring_test.cpp b/cunit/rplqstring_test.cpp new file mode 100644 index 0000000..8f9db55 --- /dev/null +++ b/cunit/rplqstring_test.cpp @@ -0,0 +1,125 @@ +/* + * 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. +*/ +/** @file + * @brief Unit test of the ReString tools. + */ + +#include "base/rebase.hpp" +#include "rplcore/rpltest.hpp" + +class TestRplQString : public ReTest { +public: + TestRplQString() : + ReTest("ReQString") + {} + +public: + void testLengthOfUInt64(){ + quint64 value = -3; + checkEqu(1, ReQString::lengthOfUInt64(ReString("0"), 0, 10, &value)); + checkEqu(0LL, value); + checkEqu(3, ReQString::lengthOfUInt64("x432", 1, 10, &value)); + checkEqu(432LL, value); + checkEqu(3, ReQString::lengthOfUInt64("x432 x", 1, 10, &value)); + checkEqu(432LL, value); + checkEqu(3, ReQString::lengthOfUInt64("x432fabc x", 1, 10, &value)); + checkEqu(432LL, value); + checkEqu(16, ReQString::lengthOfUInt64("a1234567890123567", 1, 10, &value)); + checkEqu(1234567890123567LL, value); + checkEqu(10, ReQString::lengthOfUInt64("x1234abcdef", 1, 16, &value)); + checkEqu(0x1234abcdefLL, value); + checkEqu(3, ReQString::lengthOfUInt64("432", 0, 8, &value)); + checkEqu(0432LL, value); + checkEqu(6, ReQString::lengthOfUInt64(" 765432 ", 1, 8, &value)); + checkEqu(0765432LL, value); + + checkEqu(0, ReQString::lengthOfUInt64("1 ", 1, 8, &value)); + checkEqu(0, ReQString::lengthOfUInt64("", 1, 8, &value)); + } + void testLengthOfUInt(){ + uint value = 3; + checkEqu(1, ReQString::lengthOfUInt(ReString("0"), 0, 10, &value)); + checkEqu(0, value); + checkEqu(3, ReQString::lengthOfUInt("x432", 1, 10, &value)); + checkEqu(432, value); + checkEqu(3, ReQString::lengthOfUInt("x432 x", 1, 10, &value)); + checkEqu(432, value); + checkEqu(3, ReQString::lengthOfUInt("x432fabc x", 1, 10, &value)); + checkEqu(432, value); + checkEqu(3, ReQString::lengthOfUInt("432", 0, 8, &value)); + checkEqu(0432, value); + checkEqu(6, ReQString::lengthOfUInt(" 765432 ", 1, 8, &value)); + checkEqu(0765432, value); + + checkEqu(0, ReQString::lengthOfUInt("1 ", 1, 8, &value)); + checkEqu(0, ReQString::lengthOfUInt("", 1, 8, &value)); + } + void testLengthOfReal(){ + qreal value; + checkEqu(4, ReQString::lengthOfReal(ReString("0.25"), 0, &value)); + checkEqu(0.25, value); + checkEqu(3, ReQString::lengthOfReal(ReString("X.25"), 1, &value)); + checkEqu(0.25, value); + checkEqu(1, ReQString::lengthOfReal(ReString(" 0"), 1, &value)); + checkEqu(0.0, value); + checkEqu(17, ReQString::lengthOfReal(ReString("X12345678901234567"), 1, &value)); + checkEqu(12345678901234567.0, value); + checkEqu(2, ReQString::lengthOfReal(ReString(".5"), 0, &value)); + checkEqu(0.5, value); + checkEqu(5, ReQString::lengthOfReal(ReString("2.5e2x"), 0, &value)); + checkEqu(250.0, value); + checkEqu(6, ReQString::lengthOfReal(ReString("2.5e+2"), 0, &value)); + checkEqu(250.0, value); + checkEqu(7, ReQString::lengthOfReal(ReString("2.5E-33"), 0, &value)); + checkEqu(2.5e-33, value); + + checkEqu(3, ReQString::lengthOfReal(ReString("2.5E"), 0, &value)); + checkEqu(2.5, value); + checkEqu(3, ReQString::lengthOfReal(ReString("2.5E+"), 0, &value)); + checkEqu(2.5, value); + checkEqu(3, ReQString::lengthOfReal(ReString("2.5E-a"), 0, &value)); + checkEqu(2.5, value); + } + + void testValueOfHexDigit(){ + checkEqu(0, ReQString::valueOfHexDigit('0')); + checkEqu(9, ReQString::valueOfHexDigit('9')); + checkEqu(10, ReQString::valueOfHexDigit('a')); + checkEqu(15, ReQString::valueOfHexDigit('f')); + checkEqu(10, ReQString::valueOfHexDigit('A')); + checkEqu(15, ReQString::valueOfHexDigit('F')); + + checkEqu(-1, ReQString::valueOfHexDigit('0' - 1)); + checkEqu(-1, ReQString::valueOfHexDigit('9' + 1)); + checkEqu(-1, ReQString::valueOfHexDigit('A' - 1)); + checkEqu(-1, ReQString::valueOfHexDigit('F' + 1)); + checkEqu(-1, ReQString::valueOfHexDigit('a' - 1)); + checkEqu(-1, ReQString::valueOfHexDigit('f' + 1)); + } + void testUtf8(){ + ReString name = "Heinz Müller"; + char buffer[32]; + checkEqu("Heinz Müller", ReQString::utf8(name, buffer, sizeof buffer)); + memset(buffer, 'x', sizeof buffer); + checkEqu("Heinz", ReQString::utf8(name, buffer, (size_t) (5+1))); + checkEqu(buffer[6], 'x'); + } + + virtual void doIt(void) { + testUtf8(); + testLengthOfUInt64(); + testLengthOfUInt(); + testLengthOfReal(); + testValueOfHexDigit(); + } +}; +void testRplQString() { + TestRplQString test; + test.run(); +} + diff --git a/cunit/rplsource_test.cpp b/cunit/rplsource_test.cpp new file mode 100644 index 0000000..e78a9c3 --- /dev/null +++ b/cunit/rplsource_test.cpp @@ -0,0 +1,109 @@ +/* + * 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. +*/ + +/** @file + * @brief Unit test of the input media reader. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestReSource : public ReTest{ +public: + TestReSource() : ReTest("TestReSource") {} + +private: + QByteArray m_content1_1; + QByteArray m_content1_2; + QByteArray m_content2; + ReSource m_source; + +protected: + void init(){ + m_content1_1 = "# test\nimport source2\n"; + m_content1_2 = "a=1;\nveeeeeeeeery looooooooooooooong\n"; + m_content2 = "x=2"; + } + + void testReStringSourceUnit(){ + ReStringReader reader(m_source); + QByteArray content("a=1;\nveeeeeeeeery looooooooooooooong\n"); + ReStringSourceUnit unit("test", content, &reader); + unit.setLineNo(144); + checkEqu(144, unit.lineNo()); + checkEqu("test", unit.name()); + } + void checkOne(int maxSize, ReReader& reader){ + QByteArray total; + QByteArray buffer; + int lineNo = 0; + bool hasMore; + checkF(reader.openSourceUnit("unknownSource")); + checkT(reader.openSourceUnit("source1")); + while(reader.nextLine(maxSize, buffer, hasMore)){ + lineNo++; + total += buffer; + buffer.clear(); + while(hasMore && reader.fillBuffer(maxSize, buffer, hasMore)){ + total += buffer; + buffer.clear(); + } + bool isImport = total.endsWith("source2\n"); + if (isImport){ + reader.openSourceUnit("source2"); + checkEqu("source2", reader.currentSourceUnit()->name()); + while(reader.nextLine(maxSize, buffer, hasMore)){ + lineNo++; + while(hasMore && reader.fillBuffer(maxSize, buffer, hasMore)){ + total += buffer; + buffer.clear(); + } + } + checkEqu("source1", reader.currentSourceUnit()->name()); + } + } + checkEqu(5, lineNo); + checkEqu(m_content1_1 + m_content2 + m_content1_2, total); + + } + + void testReStringReader(){ + ReStringReader reader(m_source); + reader.addSource("source1", m_content1_1 + m_content1_2); + reader.addSource("source2", m_content2); + ReSourceUnit* unit = reader.openSourceUnit("source1"); + checkNN(unit); + checkEqu("source1", unit->name()); + checkEqu(0, unit->lineNo()); + checkOne(6, reader); + checkOne(100, reader); + reader.replaceSource("source2", "content2"); + + unit = reader.openSourceUnit("source2"); + QByteArray buffer; + bool hasMore; + checkT(reader.nextLine(50, buffer, hasMore)); + checkEqu("content2", buffer); + checkF(hasMore); + } + +public: + virtual void doIt(void) { + init(); + testReStringSourceUnit(); + testReStringReader(); + } +}; +void testReSource() { + TestReSource test; + test.run(); +} + + + diff --git a/cunit/rplstring_test.cpp b/cunit/rplstring_test.cpp new file mode 100644 index 0000000..313ae58 --- /dev/null +++ b/cunit/rplstring_test.cpp @@ -0,0 +1,196 @@ +/* + * 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. +*/ + +/** @file + * @brief Unit test of the QByteArray tools. + */ +#include "base/rebase.hpp" +#include "rplcore/rpltest.hpp" +/** + * @brief Unit test for ReStringUtil. + */ +class TestReString : public ReTest { +public: + TestReString() : ReTest("ReStringUtil") {} + +public: + void testCountChar(){ + checkEqu(1, ReStringUtil::countChar("x", 'x')); + checkEqu(0, ReStringUtil::countChar("X", 'x')); + checkEqu(2, ReStringUtil::countChar("xbxxbxx", 'b')); + } + + void testCount() { + checkEqu(0, ReStringUtil::count("abc", " ")); + checkEqu(1, ReStringUtil::count("abc", "b")); + checkEqu(2, ReStringUtil::count("axx", "x")); + + checkEqu(0, ReStringUtil::count("abbc", "bbb")); + checkEqu(1, ReStringUtil::count("\n\n", "\n\n")); + checkEqu(2, ReStringUtil::count(" a ", " ")); + } + + void testCutString() { + QByteArray source("123"); + QByteArray buffer; + checkEqu(QByteArray("123"), ReStringUtil::cutString(source, 4, buffer)); + checkEqu(QByteArray("123"), ReStringUtil::cutString(source, 3, buffer)); + checkEqu(QByteArray("12..."), ReStringUtil::cutString(source, 2, buffer)); + checkEqu(QByteArray("12"), ReStringUtil::cutString(source, 2, buffer, "")); + } + + void testHexDump() { + QByteArray data("abc123\nxyz"); + checkEqu(QByteArray("61 62 63 31 abc1\n" + "32 33 0a 78 23.x\n" + "79 7a yz\n"), + ReStringUtil::hexDump((uint8_t*) data.constData(), data.length(), 4)); + checkEqu(QByteArray("61 62 63 31 32 33 0a 78 79 7a abc123.xyz"), + ReStringUtil::hexDump((uint8_t*) data.constData(), data.length(), 10)); + checkEqu(QByteArray("61 62 63 31 32 33 0a 78 79 7a abc123.xyz"), + ReStringUtil::hexDump((uint8_t*) data.constData(), data.length(), 12)); + } + + void testReadWrite() { + QByteArray fn = getTempFile("test.dat"); + const char* content = "Hello world\nLine2\n"; + checkT(ReStringUtil::write(fn, content)); + checkEqu(content, ReStringUtil::read(fn, false)); + checkEqu(content, ReStringUtil::read(fn, true) + "\n"); + } + + void testToArray() { + QList array = ReStringUtil::toArray("1 abc 3", " "); + checkEqu(3, array.size()); + checkEqu("1", array.at(0)); + checkEqu("abc", array.at(1)); + checkEqu("3", array.at(2)); + } + + void testToNumber() { + checkEqu("3", ReStringUtil::toNumber(3)); + checkEqu("-33", ReStringUtil::toNumber(-33)); + checkEqu("003", ReStringUtil::toNumber(3, "%03d")); + } + + void testLengthOfNumber(){ + checkEqu(3, ReStringUtil::lengthOfNumber("0.3xxx")); + checkEqu(5, ReStringUtil::lengthOfNumber(" \t0.3xxx")); + checkEqu(3, ReStringUtil::lengthOfNumber("-.3xxx")); + checkEqu(2, ReStringUtil::lengthOfNumber(".3exxx")); + checkEqu(2, ReStringUtil::lengthOfNumber(".3e+xxx")); + checkEqu(16, ReStringUtil::lengthOfNumber("1234567.9012E+77")); + checkEqu(17, ReStringUtil::lengthOfNumber("-1234567.9012E+77 ")); + checkEqu(18, ReStringUtil::lengthOfNumber("-1234567.9012E+77 ", true)); + checkEqu(18, ReStringUtil::lengthOfNumber("-1234567.9012E+77 x", true)); + checkEqu(20, ReStringUtil::lengthOfNumber(" -1234567.9012E+77 x", true)); + } + + void checkCsv(const char* content, char expected){ + QByteArray fn = getTempFile("testrplstring.csv"); + ReStringUtil::write(fn, content); + FILE* fp = fopen(fn, "r"); + checkNN(fp); + char buffer[256]; + checkEqu(expected, ReStringUtil::findCsvSeparator(fp, buffer, sizeof buffer)); + fclose(fp); + } + + void testFindCsvSeparator(){ + const char* content = ",,,\t;;;||||"; + checkCsv(content, '\t'); + + content = "col1,col2\n1.5,3,5\n"; + checkCsv(content, ','); + + content = "col1;col2\n1,50;3.5\n" + "7;8\n10;12\n13;14"; + checkCsv(content, ';'); + + content = "0.3 7.8 8.9\n7.8 9.4 8.3"; + checkCsv(content, ' '); + + content = "0.3|7.8|8.9\n7.8| 9.4|8.3"; + checkCsv(content, '|'); + + content = "0,3;7.8;8.9"; + checkCsv(content, ';'); + } + void testLengthOfUInt64(){ + quint64 value = -3; + checkEqu(1, ReStringUtil::lengthOfUInt64("0", 10, &value)); + checkEqu(0LL, value); + checkEqu(3, ReStringUtil::lengthOfUInt64("432", 10, &value)); + checkEqu(432LL, value); + checkEqu(3, ReStringUtil::lengthOfUInt64("432 x", 10, &value)); + checkEqu(432LL, value); + checkEqu(3, ReStringUtil::lengthOfUInt64("432fabc x", 10, &value)); + checkEqu(432LL, value); + checkEqu(16, ReStringUtil::lengthOfUInt64("1234567890123567", 10, &value)); + checkEqu(1234567890123567LL, value); + checkEqu(10, ReStringUtil::lengthOfUInt64("1234abcdef", 16, &value)); + checkEqu(0x1234abcdefLL, value); + checkEqu(3, ReStringUtil::lengthOfUInt64("432", 8, &value)); + checkEqu(0432LL, value); + checkEqu(6, ReStringUtil::lengthOfUInt64("765432 ", 8, &value)); + checkEqu(0765432LL, value); + + checkEqu(0, ReStringUtil::lengthOfUInt64(" ", 8, &value)); + checkEqu(0, ReStringUtil::lengthOfUInt64("", 8, &value)); + } + void testLengthOfReal(){ + qreal value; + checkEqu(1, ReStringUtil::lengthOfReal("0", &value)); + checkEqu(0.0, value); + checkEqu(1, ReStringUtil::lengthOfReal("0%", &value)); + checkEqu(0.0, value); + checkEqu(4, ReStringUtil::lengthOfReal("0.25", &value)); + checkEqu(0.25, value); + checkEqu(3, ReStringUtil::lengthOfReal(".25", &value)); + checkEqu(0.25, value); + checkEqu(17, ReStringUtil::lengthOfReal("12345678901234567", &value)); + checkEqu(12345678901234567.0, value); + checkEqu(2, ReStringUtil::lengthOfReal(".5", &value)); + checkEqu(0.5, value); + checkEqu(5, ReStringUtil::lengthOfReal("2.5e2x", &value)); + checkEqu(250.0, value); + checkEqu(6, ReStringUtil::lengthOfReal("2.5e+2", &value)); + checkEqu(250.0, value); + checkEqu(7, ReStringUtil::lengthOfReal("2.5E-33", &value)); + checkEqu(2.5e-33, value); + + checkEqu(3, ReStringUtil::lengthOfReal("2.5E", &value)); + checkEqu(2.5, value); + checkEqu(3, ReStringUtil::lengthOfReal("2.5E+", &value)); + checkEqu(2.5, value); + checkEqu(3, ReStringUtil::lengthOfReal("2.5E-a", &value)); + checkEqu(2.5, value); + } + + virtual void doIt() { + testLengthOfReal(); + testLengthOfUInt64(); + testCountChar(); + testCount(); + testCutString(); + testToNumber(); + testToArray(); + testHexDump(); + testReadWrite(); + testLengthOfNumber(); + testFindCsvSeparator(); + } +}; + +void testReString() { + TestReString test; + test.run(); +} + + + diff --git a/cunit/rplvm_test.cpp b/cunit/rplvm_test.cpp new file mode 100644 index 0000000..bb58cd2 --- /dev/null +++ b/cunit/rplvm_test.cpp @@ -0,0 +1,72 @@ +/* + * 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 "base/rebase.hpp" +#include "expr/reexpr.hpp" +#include "rplcore/rpltest.hpp" + +class TestReVM : public ReTest{ +private: + ReSource m_source; + ReASTree m_tree; + ReStringReader m_reader; + const char* m_currentSource; +public: + TestReVM() : + ReTest("ReVM"), + m_source(), + m_tree(), + m_reader(m_source) + { + m_source.addReader(&m_reader); + } +protected: + void setSource(const char* content){ + ReASItem::reset(); + m_currentSource = content; + m_tree.clear(); + m_source.clear(); + m_reader.clear(); + m_reader.addSource("", content); + m_source.addReader(&m_reader); + m_source.addSourceUnit(m_reader.currentSourceUnit()); + } + +private: + void checkAST(const char* fileExpected, int lineNo){ + QByteArray fnExpected = "test"; + fnExpected += QDir::separator().toLatin1(); + fnExpected += "ReVM"; + fnExpected += (char) QDir::separator().toLatin1(); + fnExpected += fileExpected; + QByteArray fnCurrent = getTempFile(fileExpected, "ReVM"); + ReMFParser parser(m_source, m_tree); + parser.parse(); + ReVirtualMachine vm(m_tree, m_source); + vm.setFlag(ReVirtualMachine::VF_TRACE_STATEMENTS); + ReFileWriter writer(fnCurrent); + vm.setTraceWriter(&writer); + writer.write(m_currentSource); + vm.executeModule(""); + assertEqualFiles(fnExpected.constData(), fnCurrent.constData(), + __FILE__, lineNo); + } +public: + void baseTest(){ + setSource("Int a=2+3*4;\nfunc Void main():\na;\nendf"); + checkAST("baseTest.txt", __LINE__); + } + virtual void doIt(void) { + baseTest(); + } +}; +void testReVM() { + TestReVM test; + test.run(); +} + diff --git a/cunit/rplwriter_test.cpp b/cunit/rplwriter_test.cpp new file mode 100644 index 0000000..0922749 --- /dev/null +++ b/cunit/rplwriter_test.cpp @@ -0,0 +1,50 @@ +/* + * + * 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. +*/ + +/** @file + * @brief Unit test of the output media writers. + */ + +#include "base/rebase.hpp" +#include "rplcore/rpltest.hpp" +/** + * @brief Unit test for ReStringUtil. + */ +class TestRplWriter : public ReTest { +public: + TestRplWriter() : ReTest("ReWriter") {} + +private: + void testFileWriter(){ + QByteArray fn = getTempFile("rplwriter.txt"); + ReFileWriter writer(fn); + writer.writeLine("abc"); + writer.formatLine("%04d", 42); + writer.writeIndented(3, "123"); + writer.indent(2); + writer.write("pi"); + writer.format("%3c%.2f", ':', 3.1415); + writer.writeLine(); + writer.close(); + QByteArray current = ReStringUtil::read(fn, false); + checkEqu("abc\n0042\n\t\t\t123\n\t\tpi :3.14\n", current); + } + +public: + virtual void doIt(void) { + testFileWriter(); + } +}; +void testRplWriter() { + TestRplWriter test; + test.run(); +} + + + diff --git a/cunit/unittests.pro b/cunit/unittests.pro new file mode 100644 index 0000000..0104a97 --- /dev/null +++ b/cunit/unittests.pro @@ -0,0 +1,49 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-05-31T00:01:23 +# +#------------------------------------------------- + +QT += core network + +QT -= gui + +TARGET = unittests +CONFIG += console +CONFIG -= app_bundle + +INCLUDEPATH = .. + +TEMPLATE = app + +SOURCES += main.cpp \ + ../rplcore/rpllogger.cpp \ + ../rplcore/rpltest.cpp \ + ../rplcore/rplstring.cpp \ + ../rplcore/rplexception.cpp \ + ../rplmath/rplmatrix.cpp \ + ../rplexpr/rplsource.cpp \ + ../rplexpr/rpllexer.cpp \ + ../rplexpr/rplastree.cpp \ + ../rplexpr/rplparser.cpp \ + ../rplexpr/rplmfparser.cpp \ + ../rplcore/rplqstring.cpp \ + ../rplexpr/rplasclasses.cpp \ + ../rplcore/rplbytestorage.cpp \ + ../rplexpr/rplvm.cpp \ + ../rplcore/rplwriter.cpp \ + rplmatrix_test.cpp \ + rplexception_test.cpp \ + rplstring_test.cpp \ + rplsource_test.cpp \ + rpllexer_test.cpp \ + rplqstring_test.cpp \ + rplastree_test.cpp \ + rplmfparser_test.cpp \ + rplvm_test.cpp \ + rplbytestorage_test.cpp \ + rplwriter_test.cpp \ + rplbench.cpp \ + rplcharptrmap_test.cpp \ + ../rplcore/rplcharptrmap.cpp + diff --git a/expr/ReASTree.cpp b/expr/ReASTree.cpp new file mode 100644 index 0000000..593a67a --- /dev/null +++ b/expr/ReASTree.cpp @@ -0,0 +1,3728 @@ +/* + * 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. +*/ +/** @file + * + * @brief Implementation of an Abstract Syntax Tree. + * + */ +/** @file rplexpr/rplastree.hpp + * + * @brief Definitions for an Abstract Syntax Tree. + * + */ + +#include "../base/rebase.hpp" +#include "expr/reexpr.hpp" + +enum { + LOC_VARDEF_EXEC_1 = LOC_FIRST_OF(LOC_ASTREE), // 11001 + LOC_UNOP_CALC_1, + LOC_UNARY_CHECK_1, + LOC_UNARY_CHECK_2, + LOC_UNARY_CHECK_3, // 11005 + LOC_BINOP_1, + LOC_BINOP_CALC_1, + LOC_BINOP_CALC_2, + LOC_BINOP_CALC_3, + LOC_BINOP_CALC_4, // 11010 + LOC_BINOP_CALC_5, + LOC_BINOP_CALC_6, + LOC_BINOP_CALC_7, + LOC_BINOP_CALC_8, + LOC_BINOP_CALC_9, // 11015 + LOC_BINOP_CALC_10, + LOC_BINOP_CALC_11, + LOC_BINOP_CALC_12, + LOC_VARDEF_CHECK_1, + LOC_VARDEF_CHECK_2, // 11020 + LOC_ITEM_STATEM_LIST_1, + LOC_CONV_CHECK_1, + LOC_CONV_TRY_1, + LOC_ITEM_FORCE_ERROR_1, + LOC_UNARY_CHECK_4, // 11025 + LOC_IF_CHECK_1, + LOC_IF_CHECK_2, + LOC_FORC_CHECK_1, + LOC_FORC_CHECK_2, + LOC_FORC_CHECK_3, // 11030 + LOC_ITEM_AS_INT_1, + LOC_ITEM_AS_INT_2, + LOC_METHOD_CALL_CHECK_1, + LOC_MEHTOD_CALL_CHECK_2, + LOC_MEHTOD_CALL_CHECK_3, // 11035 + LOC_MEHTOD_CALL_CHECK_4, + LOC_COUNT +}; + +unsigned int ReASItem::m_nextId = 1; + +#define DEFINE_TABS(indent) \ + char tabs[32]; \ + memset(tabs, '\t', sizeof tabs); \ + tabs[(unsigned) indent < sizeof tabs ? indent : sizeof tabs - 1] = '\0' + +/** + * @brief Writes a map into a file. + * + * The output is sorted by key. + * + * @param writer writes to output + * @param map map to dump + * @param withEndOfLine true: '\n' will be written at the end + */ +void dumpMap(ReWriter& writer, ReASMapOfVariants& map, bool withEndOfLine) +{ + QList sorted; + sorted.reserve(map.size()); + ReASMapOfVariants::iterator it; + for (it = map.begin(); it != map.end(); it++){ + sorted.append(it.key()); + } + qSort(sorted.begin(), sorted.end(), qLess()); + QList::iterator it2; + bool first = true; + for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ + ReASVariant* value = map[*it2]; + writer.format("%c'%s':%s", first ? '{' : ',', (*it2).constData(), + value->toString().constData()); + first = false; + } + if (first) + writer.write("{"); + writer.write("}"); + if (withEndOfLine) + writer.writeLine(); +} + +/** @class ReASException rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a specific exception for the Abstract Syntax Tree. + */ + +/** + * @brief Builds the message. + * + * @param position describes the position of the error/warning + * @param format the reason of the exception + * @param varList the values for the placeholders in the format. + */ +void ReASException::build(const ReSourcePosition* position, + const char* format, va_list varList) +{ + char buffer[64000]; + if (position != NULL){ + m_message = position->toString().toUtf8(); + m_message += ": "; + } + qvsnprintf(buffer, sizeof buffer, format, varList); + m_message += buffer; +} + +/** + * @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. + */ +ReASException::ReASException(const ReSourcePosition* position, + const char* format, ...) : + ReException("") +{ + va_list ap; + va_start(ap, format); + build(position, format, ap); + va_end(ap); +} + +/** + * @brief Constructor. + */ +ReASException::ReASException() : + ReException("") +{ +} + +/** @class ReASVariant rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a class which can hold the value of any type. + * + * The VM uses some tricks (performance): Therefore this class + * must not be virtual! + */ +/** + * @brief Constructor. + */ +ReASVariant::ReASVariant() : + m_variantType(VT_UNDEF), + m_flags(VF_UNDEF), + // m_value(), + m_class(NULL) +{ +} +/** + * @brief Destructor. + */ +ReASVariant::~ReASVariant() +{ + destroyValue(); + m_variantType = VT_UNDEF; +} + +/** + * @brief Copy constructor. + * @param source the source to copy + */ +ReASVariant::ReASVariant(const ReASVariant& source): + m_variantType(source.m_variantType), + m_flags(source.m_flags), + // m_value + m_class(source.m_class) +{ + copyValue(source); +} + +/** + * @brief Assignment operator. + * + * @param source the source to copy + * @return the instance itself + */ +ReASVariant&ReASVariant::operator=(const ReASVariant& source) +{ + destroyValue(); + m_variantType = source.m_variantType; + m_flags = source.m_flags; + m_class = source.m_class; + copyValue(source); + return *this; +} + +/** + * @brief Copies the value. + * @param source the source to copy + */ +void ReASVariant::copyValue(const ReASVariant& source) +{ + destroyValue(); + m_variantType = source.m_variantType; + m_class = source.m_class; + + switch(source.m_variantType) + { + case VT_BOOL: + m_value.m_bool = source.m_value.m_bool; + break; + case VT_FLOAT: + m_value.m_float = source.m_value.m_float; + break; + case VT_INTEGER: + m_value.m_int = source.m_value.m_int; + break; + case VT_UNDEF: + break; + default: + m_value.m_object = m_class->newValueInstance(source.m_value.m_object); + break; + } + m_flags = source.m_flags; +} + +/** + * @brief Frees the resources of the instance. + */ +void ReASVariant::destroyValue() +{ + switch(m_variantType) + { + case VT_BOOL: + case VT_FLOAT: + case VT_INTEGER: + case VT_UNDEF: + break; + default: + if ((m_flags & VF_IS_COPY) == 0) + m_class->destroyValueInstance(m_value.m_object); + m_value.m_object = NULL; + break; + } + m_variantType = VT_UNDEF; +} +/** + * @brief Returns the variantType of the instance. + * + * @return the variant type + */ +ReASVariant::VariantType ReASVariant::variantType() const +{ + return m_variantType; +} + +/** + * @brief Return the name of the variant type. + * + * @return the type as string + */ +const char*ReASVariant::nameOfType() const +{ + const char* rc = "?"; + switch(m_variantType){ + case VT_UNDEF: + rc = ""; + break; + case VT_FLOAT: + rc = "Float"; + break; + case VT_INTEGER: + rc = "Int"; + break; + case VT_BOOL: + rc = "Bool"; + break; + case VT_OBJECT: + rc = "Obj"; + break; + default: + break; + } + return rc; +} + +/** + * @brief Returns the class (data type) of the instance. + * + * @return the variant type + */ +const ReASClass* ReASVariant::getClass() const +{ + return m_class; +} + + +/** + * @brief Returns the numeric value. + * + * @return the numeric value + * @throw RplException the instance is not a numberic value + * + */ +qreal ReASVariant::asFloat() const +{ + if (m_variantType != VT_FLOAT) + throw ReException("ReASVariant::asNumber: not a number: %d", + m_variantType); + return m_value.m_float; +} +/** + * @brief Returns the numeric value. + * + * @return the numeric value + * @throw RplException the instance is not a numberic value + * + */ +int ReASVariant::asInt() const +{ + if (m_variantType != VT_INTEGER) + throw ReException("ReASVariant::asInt: not an integer: %d", + m_variantType); + return m_value.m_int; +} + +/** + * @brief Returns the boolean value. + * + * @return the boolean value + * @throw RplException the instance is not a boolean value + * + */ +bool ReASVariant::asBool() const +{ + if (m_variantType != VT_BOOL) + throw ReException("ReASVariant::asBool: not a boolean: %d", + m_variantType); + 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* ReASVariant::asObject(const ReASClass** clazz) const +{ + if (m_variantType != VT_OBJECT) + throw ReException("ReASVariant::asObject: not an object: %d", + m_variantType); + 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 QByteArray* ReASVariant::asString() const +{ + const ReASClass* clazz; + const QByteArray* rc = static_cast(asObject(&clazz)); + if (clazz != ReASString::m_instance){ + const QByteArray& name = clazz->name(); + throw ReException("ReASVariant::asString: not a string: %s", + name.constData()); + } + return rc; +} + +/** + * @brief Make the instance to a numeric value. + * + * @param number the numeric value. + */ +void ReASVariant::setFloat(qreal number) +{ + destroyValue(); + m_variantType = VT_FLOAT; + m_value.m_float = number; + m_class = ReASFloat::m_instance; +} + +/** + * @brief Make the instance to an integer value. + * + * @param integer the numeric value. + */ +void ReASVariant::setInt(int integer) +{ + destroyValue(); + m_variantType = VT_INTEGER; + m_value.m_int = integer; + m_class = ReASInteger::m_instance; +} + +/** + * @brief Make the instance to a boolean value. + * + * @param value the boolean value. + */ +void ReASVariant::setBool(bool value) +{ + destroyValue(); + m_variantType = VT_BOOL; + m_value.m_bool = value; + m_class = ReASBoolean::m_instance; +} + +/** + * @brief Make the instance to a boolean value. + * + * @param string the string value. + */ +void ReASVariant::setString(const QByteArray& string) +{ + // deletion in ReASVariant::destroyValue(): + setObject(new QByteArray(string), ReASString::m_instance); +} + +/** + * @brief Builds a string. + * + * @param maxLength the maximum length of the result + * @return the value as string + */ +QByteArray ReASVariant::toString(int maxLength) const +{ + QByteArray rc; + char buffer[256]; + switch(m_variantType) + { + case VT_BOOL: + rc = m_value.m_bool ? "True" : "False"; + break; + case VT_FLOAT: + qsnprintf(buffer, sizeof buffer, "%f", m_value.m_float); + rc = buffer; + break; + case VT_INTEGER: + qsnprintf(buffer, sizeof buffer, "%lld", m_value.m_int); + rc = buffer; + break; + case VT_OBJECT: + rc = m_class->toString(m_value.m_object, maxLength); + break; + default: + case VT_UNDEF: + rc = "None"; + break; + } + return rc; +} + +/** + * @brief Make the instance to an object. + * + * @param object the class specific value object. + * @param clazz the data type of the object + */ +void ReASVariant::setObject(void* object, const ReASClass* clazz) +{ + destroyValue(); + m_variantType = VT_OBJECT; + m_value.m_object = object; + m_class = clazz; +} + + +/** @class ReASItem rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the abstract base class of all entries of an AST. + * + */ + +/** + * @brief Constructor. + * + * @param type the type of the instance + */ +ReASItem::ReASItem(ReASItemType type) : + m_id(m_nextId++), + m_nodeType(type), + m_flags(0), + m_position(NULL) +{ +} +/** + * @brief Destructor. + */ + +ReASItem::~ReASItem() +{ +} + +/** + * @brief Checks a calculable node for correctness. + * + * @param description description of the meaning, e.g. "start value" + * @param expectedClass the node must have this type + * @param parser for error processing + * @return true: instance and children are correct
+ * false: otherwise + */ +bool ReASItem::checkAsCalculable(const char* description, + ReASClass* expectedClass, ReParser& parser) +{ + bool rc = true; + if (! check(parser)) + rc = false; + if (rc){ + ReASCalculable* expr = dynamic_cast(this); + if (expr == NULL) + rc = error(LOC_ITEM_AS_INT_1, parser, "%s not calculable: %s", + description, nameOfItemType()); + else if (expr->clazz() != ReASInteger::m_instance) + rc = error(LOC_ITEM_AS_INT_2, parser, + "%s: wrong type %s instead of integer", + description, expr->clazz()->name().constData()); + } + return rc; +} + +/** + * @brief Returns the position of the item in the source code. + * + * @return the position of the item + */ +const ReSourcePosition* ReASItem::position() const +{ + return m_position; +} + +/** + * @brief Stores the position in the source code. + * + * @param position the position to store + */ +void ReASItem::setPosition(const ReSourcePosition* position) +{ + m_position = position; +} + +/** + * @brief Returns the id of the instance. + * + * @return the id + */ +unsigned int ReASItem::id() const +{ + return m_id; +} + +/** + * @brief Returns the position as a string. + * + * @param buffer OUT: the target buffer + * @param bufferSize size of the target buffer + * @return buffer + */ +char* ReASItem::positionStr(char buffer[], size_t bufferSize) const +{ + char* rc = (char*) ""; + if (m_position != NULL) + rc = m_position->utf8(buffer, bufferSize); + return rc; +} + +/** + * @brief Logs an internal error. + * + * @param logger can write to the output medium + * @param location identifies the error location + * @param format string with placeholders (optional) like sprintf() + * @param ... values for the placeholders + */ +void ReASItem::error(ReLogger* logger, int location, const char* format, ...) +{ + char buffer[1024]; + int halfBufferSize = (sizeof buffer) / 2; + qsnprintf(buffer, halfBufferSize, "id: %d [%s]:", m_id, + positionStr(buffer + halfBufferSize, halfBufferSize)); + int length = strlen(buffer); + va_list ap; + va_start(ap, format); + qvsnprintf(buffer + length, (sizeof buffer) - length, format, ap); + va_end(ap); + logger->log(LOG_ERROR, location, buffer); +} + +/** + * @brief Resets the static id counter. + */ +void ReASItem::reset() +{ + m_nextId = 1; +} +/** + * @brief Calculates an integer value. + * + * @param expr a calculable node + * @param thread the execution unit + * @return the value described by the node expr + */ +int ReASItem::calcAsInteger(ReASItem* expr, ReVMThread& thread) +{ + ReASCalculable* expr2 = dynamic_cast(expr); + expr2->calc(thread); + ReASVariant& value = thread.popValue(); + int rc = value.asInt(); + return rc; +} + +/** + * @brief Calculates an boolean value. + * + * @param expr a calculable node + * @param thread the execution unit + * @return the value described by the node expr + */ +bool ReASItem::calcAsBoolean(ReASItem* expr, ReVMThread& thread) +{ + ReASCalculable* expr2 = dynamic_cast(expr); + expr2->calc(thread); + ReASVariant& value = thread.popValue(); + bool rc = value.asBool(); + return rc; +} +/** + * @brief Checks the correctness of a statement list. + * + * @param list statement list to check + * @param parser for error processing + * @return true: all statements are correct
+ * false: otherwise + */ +bool ReASItem::checkStatementList(ReASItem* list, ReParser& parser) +{ + bool rc = true; + + while(list != NULL){ + if (! list->check(parser)) + rc = false; + if (dynamic_cast(list) == NULL) + rc = list->error(LOC_ITEM_STATEM_LIST_1, parser, "not a statement: %s", + list->nameOfItemType()); + ReASNode1* node = dynamic_cast(list); + if (node == NULL){ + list->error(LOC_ITEM_STATEM_LIST_1, parser, "not a node: %s", + list->nameOfItemType()); + list = NULL; + } else { + list = node->child(); + } + } + return rc; +} +/** + * @brief Returns the node type. + * + * @return the node type + */ +ReASItemType ReASItem::nodeType() const +{ + return m_nodeType; +} + +/** + * @brief Returns the node type as a string. + * + * @return the node type as string + */ +const char*ReASItem::nameOfItemType() const +{ + const char* rc = "?"; + switch(m_nodeType){ + case AST_CONSTANT: + rc = "constant"; + break; + case AST_LIST_CONSTANT: + rc = "list"; + break; + case AST_LIST_ENTRY: + rc = "listEntry"; + break; + case AST_MAP_CONSTANT: + rc = "map"; + break; + case AST_MAP_ENTRY: + rc = "mapEntry"; + break; + case AST_NAMED_VALUE: + rc = "namedValue"; + break; + case AST_INDEXED_VALUE: + rc = "indexedValue"; + break; + case AST_FIELD: + rc = "field"; + break; + case AST_VAR_DEFINITION: + rc = "varDef"; + break; + case AST_EXPR_STATEMENT: + rc = "exprStatement"; + break; + case AST_METHOD: + rc = "method"; + break; + case AST_ARGUMENT: + rc = "arg"; + break; + case AST_INTRINSIC_METHOD: + rc = "intrinsicMethod"; + break; + case AST_PRE_UNARY_OP: + rc = "preUnary"; + break; + case AST_POST_UNARY_OP: + rc = "postUnary"; + break; + case AST_BINARY_OP: + rc = "binOp"; + break; + case AST_METHOD_CALL: + rc = "methodCall"; + break; + case AST_WHILE: + rc = "while"; + break; + case AST_REPEAT: + rc = "repeat"; + break; + case AST_IF: + rc = "if"; + break; + case AST_CONDITION: + rc = "condition"; + break; + case AST_ITERATED_FOR: + rc = "iFor"; + break; + case AST_COUNTED_FOR: + rc = "cFor"; + break; + case AST_SWITCH: + rc = "switch"; + break; + case AST_LEAVE: + rc = "leave"; + break; + case AST_CONTINUE: + rc = "continue"; + break; + default: + break; + } + return rc; +} +/** + * @brief Returns the flags of the node. + * + * @return the bitmask with the flags + */ +int ReASItem::flags() const +{ + return m_flags; +} +/** + * @brief Sets the flags of the node. + * + * @param flags the new value of the bitmask + */ +void ReASItem::setFlags(int flags) +{ + m_flags = flags; +} + +/** + * @brief Tests the compatibility of 2 data types. + * @param class1 1st class to inspect + * @param class2 2nd class to inspect + * @return true: classes are compatible
+ * false: otherwise + */ +bool ReASItem::typeCheck(ReASClass* class1, ReASClass* class2){ + bool rc; + if (class1 == NULL || class2 == NULL) + rc = false; + else + //@ToDo: subclasses + rc = class1 == class2; + return rc; +} + +/** + * @brief Issues an error message. + * + * @param location an error specific id + * @param parser for error processing + * @param format the error message with placeholders (like printf) + * @param ... the values for the placeholders + * @return false (for chaining) + */ +bool ReASItem::error(int location, ReParser& parser, const char* format, ...) +{ + va_list varList; + va_start(varList, format); + parser.addMessage(ReParser::LT_ERROR, location, m_position, format, varList); + va_end(varList); + return false; +} + +/** + * @brief Ensures the occurrence of an error. + * + * When called a previous error should be happend. If not an internal error + * will be issued. + * + * @param parser for error processing + * @param info additional info + * @return false (for chaining) + */ +bool ReASItem::ensureError(ReParser& parser, const char* info) +{ + if (parser.errors() == 0) + error(LOC_ITEM_FORCE_ERROR_1, parser, "lost error (internal error): %s"); + return false; +} + +/** @class ReASCalculable rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief An abstract base class for items which calculates a value. + * + */ +/** + * @brief Constructor. + */ +ReASCalculable::ReASCalculable() : + m_class(NULL) +{ +} +/** + * @brief Returns the class of the node + * @return the class + */ + +ReASClass* ReASCalculable::clazz() const +{ + return m_class; +} +/** + * @brief Sets the class of the node. + * @param clazz the new class + */ +void ReASCalculable::setClass(ReASClass* clazz) +{ + m_class = clazz; +} + +/** @class ReASStorable rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the abstract base class of value containing items. + * + */ + +/** @class ReASConstant rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a constant for the Abstract Syntax Tree. + * + */ + +/** + * @brief Constructor. + * + */ +ReASConstant::ReASConstant() : + ReASItem(AST_CONSTANT), + m_value() +{ +} + +/** + * @brief Copies the const value to the top of value stack. + * + * @param thread IN/OUT: the execution unit, a VM thread + */ +void ReASConstant::calc(ReVMThread& thread) +{ + ReASVariant& value = thread.reserveValue(); + value.copyValue(m_value); +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: a constant is always correct + */ +bool ReASConstant::check(ReParser& parser) +{ + return true; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASConstant::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "const id: %d value: %s %s", m_id, + m_value.toString().constData(), + positionStr(buffer, sizeof buffer)); +} + +/** + * @brief Returns the value of the constant. + * + * This method will be used to set the value of the constant: + *
ReASConstant constant;
+ * constant.value().setString("Jonny");
+ *
+ * + * @return the internal value + */ +ReASVariant& ReASConstant::value() +{ + return m_value; +} + +/** @class ReASListConstant rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a container for constant list entries. + * + */ + +/** + * @brief Constructor. + */ +ReASListConstant::ReASListConstant() : + ReASNode1(AST_LIST_CONSTANT), + ReASCalculable() +{ + m_value.setObject(ReASList::m_instance->newValueInstance(), + ReASList::m_instance); +} +/** + * @brief Returns the list. + * + * @return the list + */ +ReASListOfVariants* ReASListConstant::list(){ + ReASListOfVariants* rc = static_cast + (m_value.asObject(NULL)); + return rc; +} + +/** + * @brief Copies the list constant to the top of value stack. + * + * @param thread IN/OUT: the execution unit, a VM thread + */ +void ReASListConstant::calc(ReVMThread& thread) +{ + ReASVariant& value = thread.reserveValue(); + value.copyValue(m_value); +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: a constant is always correct + */ +bool ReASListConstant::check(ReParser& parser) +{ + return true; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASListConstant::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "listConst id: %d %s", m_id, + positionStr(buffer, sizeof buffer)); + + QByteArray sValue = m_value.toString(8092); + writer.writeIndented(indent + 1, sValue.constData()); +} + +/** + * @brief Returns the value of the constant. + * + * This method will be used to set the value of the constant: + *
ReASConstant constant;
+ * constant.value().setString("Jonny");
+ *
+ * + * @return the internal value + */ +ReASVariant& ReASListConstant::value() +{ + return m_value; +} + +/** @class ReASMapConstant rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a hash map for constant list entries. + * + */ +/** + * @brief ReASMapConstant::ReASMapConstant + */ +ReASMapConstant::ReASMapConstant() : + ReASNode1(AST_MAP_CONSTANT), + ReASCalculable(), + m_value() +{ + m_value.setObject(new ReASMapOfVariants, ReASMap::m_instance); +} + +/** + * @brief Copies the map constant to the top of value stack. + * + * @param thread IN/OUT: the execution unit, a VM thread + */ +void ReASMapConstant::calc(ReVMThread& thread) +{ + ReASVariant& value = thread.reserveValue(); + value.copyValue(m_value); +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: a constant is always correct + */ +bool ReASMapConstant::check(ReParser& parser) +{ + return true; +} +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASMapConstant::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "mapConst id: %d %s", + m_id, positionStr(buffer, sizeof buffer)); + writer.indent(indent); + dumpMap(writer, *map(), true); +} + + +/** + * @brief Returns the value of the constant, containing a map. + * + * @return the variant value + */ +ReASVariant& ReASMapConstant::value() +{ + return m_value; +} + +/** + * @brief Returns the (low level) map of the constant. + * + * @return the map of the constant + */ +ReASMapOfVariants* ReASMapConstant::map() +{ + ReASMapOfVariants* rc = static_cast( + m_value.asObject(NULL)); + return rc; +} + +/** @class ReASNamedValue rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a named values, a constant or a variable + */ + +/** + * @brief Constructor. + * + * @param clazz the data type (class) + * @param space the current symbol space + * @param name the name of the variable + * @param attributes the attributes of the variable + */ +ReASNamedValue::ReASNamedValue(ReASClass* clazz,ReSymbolSpace* space, + const QByteArray& name, int attributes) : + ReASItem(AST_NAMED_VALUE), + m_name(name), + m_attributes(attributes), + m_symbolSpace(space), + m_variableNo(-1) +{ + m_class = clazz; +} + +/** + * @brief Returns the name. + * + * @return the name + */ +const QByteArray& ReASNamedValue::name() const +{ + return m_name; +} + +/** + * @brief Sets the symbol space. + * @param space + * @param variableNo + */ +void ReASNamedValue::setSymbolSpace(ReSymbolSpace* space, int variableNo) +{ + m_symbolSpace = space; + m_variableNo = variableNo; +} +/** + * @brief Copies the value of the variable to the top of value stack. + * + * @param thread IN/OUT: the execution unit, a VM thread + */ +void ReASNamedValue::calc(ReVMThread& thread) +{ + thread.valueToTop(m_symbolSpace, m_variableNo); + if (thread.tracing()) + thread.vm()->traceWriter()->format("nVal %s=%.80s", + m_name.constData(), + thread.topOfValues().toString().constData()); +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASNamedValue::check(ReParser& parser) +{ + return true; +} +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASNamedValue::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "namedValue %s id: %d attr: 0x%x %s", + m_name.constData(), m_id, m_attributes, + positionStr(buffer, sizeof buffer)); +} + +/** + * @brief Returns the symbol space of the variable. + * + * @return the symbol space + */ +ReSymbolSpace*ReASNamedValue::symbolSpace() const +{ + return m_symbolSpace; +} + +/** + * @brief Sets the variable no in the instance. + * + * @param variableNo the variable number + */ +void ReASNamedValue::setVariableNo(int variableNo) +{ + m_variableNo = variableNo; +} + + +/** + * @brief Returns the variable no of the variable. + * + * @return the current number of the variable in the symbol space + */ +int ReASNamedValue::variableNo() const +{ + return m_variableNo; +} + +/** @class ReASConversion rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a data type conversion. + * + * m_child: the expression which will be converted + */ +/** + * @brief Constructor. + * @param expression the expression to convert + */ +ReASConversion::ReASConversion(ReASItem* expression) : + ReASNode1(AST_CONVERSION), + m_conversion(C_UNDEF) +{ + m_child = expression; + m_position = expression->position(); +} + +/** + * @brief Convert an expression to another data type. + * + * Possible conversions: @see ReASConversion::Conversion + * + * @param thread execution value + */ +void ReASConversion::calc(ReVMThread& thread) +{ + ReASCalculable* expr = dynamic_cast(m_child); + expr->calc(thread); + ReASVariant& value = thread.topOfValues(); + + switch(m_conversion){ + case C_INT_TO_FLOAT: + value.setFloat((qreal) value.asInt()); + break; + case C_FLOAT_TO_INT: + value.setInt((int) value.asFloat()); + break; + case C_BOOL_TO_INT: + value.setInt((int) value.asBool()); + break; + case C_BOOL_TO_FLOAT: + value.setFloat((qreal) value.asBool()); + break; + default: + break; + } + if (thread.tracing()) + thread.vm()->traceWriter()->format("(%s): %s", + m_class->name().constData(), + value.toString().constData()); +} + +/** + * @brief Returns the conversion type of two classes. + * + * @param from class to convert + * @param to result class of the conversion + * + * @return C_UNDEF: not convertable
+ * otherwise: the conversion type + */ +ReASConversion::Conversion ReASConversion::findConversion(ReASClass* from, + ReASClass* to) +{ + Conversion rc = C_UNDEF; + if (from == ReASFloat::m_instance){ + if (to == ReASInteger::m_instance) + rc = C_FLOAT_TO_INT; + } else if (from == ReASInteger::m_instance){ + if (to == ReASFloat::m_instance) + rc = C_INT_TO_FLOAT; + } else if (from == ReASBoolean::m_instance){ + if (to == ReASInteger::m_instance) + rc = C_BOOL_TO_INT; + else if (to == ReASInteger::m_instance) + rc = C_BOOL_TO_INT; + else if (to == ReASFloat::m_instance) + rc = C_BOOL_TO_FLOAT; + } + return rc; +} + +/** + * @brief Checks the node. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASConversion::check(ReParser& parser) +{ + bool rc = m_child != NULL && m_child->check(parser); + ReASCalculable* expr = dynamic_cast(m_child); + if (! rc || expr == NULL) + ensureError(parser, "ReASConversion::check"); + else { + ReASClass* from = expr->clazz(); + m_conversion = findConversion(from, m_class); + if (m_conversion != C_UNDEF) + rc = true; + else + parser.error(LOC_CONV_CHECK_1, + "invalid data type conversion: %s -> %s", + from->name().constData(), + m_class->name().constData()); + } + return rc; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASConversion::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "conversion %s id: %d expr: %d %s", + m_class->name().constData(), m_id, m_child->id(), + positionStr(buffer, sizeof buffer)); +} + +/** + * @brief Tries to find a conversion to a given type. + * + * Checks if an expression has a given type. If not it will be tried to find + * a conversion. If this is not possible an error occurres. Otherwise the + * converter will be returned. + * + * @param expected the expected data type + * @param expr the expression to convert + * @param parser for error processing + * @param isCorrect OUT: false: error has been detected
+ * No change if no error + * + * @return NULL: no conversion necessary
+ * otherwise: a converter to the given type + */ +ReASConversion* ReASConversion::tryConversion(ReASClass* expected, + ReASItem* expr, ReParser& parser, bool& isCorrect) +{ + ReASConversion* rc = NULL; + if (! expr->check(parser)) + isCorrect = false; + else { + ReASCalculable* expr2 = dynamic_cast(expr); + if (expr2 != NULL){ + Conversion type = findConversion(expr2->clazz(), expected); + if (type == C_UNDEF){ + isCorrect = parser.error(LOC_CONV_TRY_1, + "invalid data type conversion: %s -> %s", + expr2->clazz()->name().constData(), + expected->name().constData()); + } else if (expr2->clazz() != expected){ + rc = new ReASConversion(expr); + rc->m_conversion = type; + rc->setClass(expected); + } + } + } + return rc; +} + +/** @class ReASIndexedValue rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an indexed values (member of a list) + * + * m_child: the parent: a list/map expression + * m_child2: the index expression + */ +ReASIndexedValue::ReASIndexedValue() : + ReASNode2(AST_INDEXED_VALUE) +{ +} + +/** + * @brief Calculates an indexed expression. + * + * Possible: list index or map index + * + * @param thread execution value + */ +void ReASIndexedValue::calc(ReVMThread& thread) +{ + ReASCalculable* expr = dynamic_cast(m_child2); + expr->calc(thread); + ReASVariant& ixValue = thread.popValue(); + int ix = ixValue.asInt(); + ReASCalculable* list = dynamic_cast(m_child); + list->calc(thread); + ReASVariant& listValue = thread.popValue(); + //@ToDo: access to the lists element: assignment or to stack + if (thread.tracing()) + thread.vm()->traceWriter()->format("[%d]: %.80s", + ix, thread.topOfValues().toString().constData()); +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASIndexedValue::check(ReParser& parser) +{ + ReASCalculable* list = dynamic_cast(m_child); + bool rc = m_child != NULL && m_child->check(parser); + if (! rc || list == NULL) + ensureError(parser, "ReASIndexedValue::check"); + else { + // index value: + // tryConversion() calls m_child2->check()! + ReASConversion* converter = ReASConversion::tryConversion( + ReASInteger::m_instance, m_child2, parser, rc); + if (rc && converter != NULL) + m_child = converter; + if (rc){ + //@ToDo: dynamic subclass of list / map + m_class = ReASString::m_instance; + rc = m_class != NULL && m_class == ReASInteger::m_instance; + } + } + return rc; +} + +/** + * @brief Writes the internals into an output media. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASIndexedValue::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "indexedValue id: %d index: %d parent: %d %s", + m_id, m_child2->id(), m_child->id(), + positionStr(buffer, sizeof buffer)); + m_child2->dump(writer, indent + 1); + m_child->dump(writer, indent + 1); +} + +/** @class RplVarDefinition rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements variable definition for the Abstract Syntax Tree. + * + * m_child: next statement
+ * m_child2: named value (name + default value expression) + * m_child3: initial value or NULL + */ + +/** + * @brief Constructor. + */ +ReASVarDefinition::ReASVarDefinition() : + ReASNode3(AST_VAR_DEFINITION), + ReASStatement(), + m_endOfScope(0) +{ + m_flags |= NF_STATEMENT; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASVarDefinition::dump(ReWriter& writer, int indent) +{ + ReASNamedValue* namedValue = dynamic_cast(m_child2); + QByteArray name = namedValue->name(); + char endOfScope[32]; + endOfScope[0] = '\0'; + if (m_endOfScope > 0) + qsnprintf(endOfScope, sizeof endOfScope, "-%d:0", m_endOfScope); + char buffer[256]; + writer.formatIndented(indent, + "varDef %s %s id: %d namedValue: %d value: %d succ: %d %s%s", + clazz() == NULL ? "?" : clazz()->name().constData(), + name.constData(), m_id, + m_child2 == NULL ? 0 : m_child2->id(), + m_child3 == NULL ? 0 : m_child3->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer), endOfScope); + if (m_child2 != NULL) + m_child2->dump(writer, indent + 1); + if (m_child3 != NULL) + m_child3->dump(writer, indent + 1); +} + +/** + * @brief Returns the name of the variable. + * + * @return the name + */ +const QByteArray& ReASVarDefinition::name() const +{ + ReASNamedValue* namedValue = dynamic_cast(m_child2); + const QByteArray& rc = namedValue->name(); + return rc; +} + +/** + * @brief Returns the data type (class) of the variable. + * + * @return the data type + */ +ReASClass* ReASVarDefinition::clazz() const +{ + ReASNamedValue* namedValue = dynamic_cast(m_child2); + ReASClass* rc = namedValue == NULL ? NULL : namedValue->clazz(); + return rc; +} +/** + * @brief Returns the column of the scope end. + * + * 0 means end of method or end of class + * + * @return 0 or the column of the scope end + */ +int ReASVarDefinition::endOfScope() const +{ + return m_endOfScope; +} + +/** + * @brief Sets the column of the scope end. + * + * @param endOfScope the column of the scope end + */ +void ReASVarDefinition::setEndOfScope(int endOfScope) +{ + m_endOfScope = endOfScope; +} + + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASVarDefinition::check(ReParser& parser) +{ + ReASNamedValue* var = dynamic_cast(m_child2); + + bool rc = var != NULL && (m_child3 == NULL || m_child3->check(parser)); + if (! rc) + ensureError(parser, "ReASVarDefinition::check"); + else { + if (m_child3 != NULL){ + // with initialization: + ReASCalculable* expr = dynamic_cast(m_child3); + if (expr == NULL) + rc = error(LOC_VARDEF_CHECK_1, parser, + "Not a calculable expression: %s", + m_child3->nameOfItemType()); + else if (! typeCheck(var->clazz(), expr->clazz())) + rc = error(LOC_VARDEF_CHECK_2, parser, + "data types are not compatible: %s/%s", + var->clazz()->name().constData(), + expr->clazz() == NULL ? "?" + : expr->clazz()->name().constData()); + } + } + return rc; +} + +/** + * @brief Executes the statement. + * + * @return 0: continue the current statement list
+ */ +int ReASVarDefinition::execute(ReVMThread& thread) +{ + if (m_child3 != NULL){ + // has an initialization: + ReASNamedValue* var = dynamic_cast(m_child2); + ReASCalculable* expr = dynamic_cast(m_child3); + expr->calc(thread); + ReASVariant& value = thread.popValue(); + ReASVariant& destination = thread.valueOfVariable( + var->m_symbolSpace, var->m_variableNo); + if (thread.tracing()) + thread.vm()->traceWriter()->format("%s = %.80s [%.80s]", + var->m_name.constData(), + value.toString().constData(), + destination.toString().constData()); + destination.copyValue(value); + } + return 0; +} + +/** @class ReASExprStatement rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an statement consisting of an expression. + * + * m_child: next statement
+ * m_child2: expression + */ + +/** + * @brief Constructor. + */ +ReASExprStatement::ReASExprStatement() : + ReASNode2(AST_EXPR_STATEMENT), + ReASStatement() +{ + m_flags |= NF_STATEMENT; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASExprStatement::check(ReParser& parser) +{ + bool rc = m_child2->check(parser); + if (rc){ + ReASCalculable* expr = dynamic_cast (m_child2); + if (expr == NULL) + rc = ensureError(parser, "ReASExprStatement::check"); + } + return rc; +} +/** + * @brief Executes the statement. + * + * @return 0: continue the current statement list
+ */ +int ReASExprStatement::execute(ReVMThread& thread) +{ + ReASCalculable* expr = dynamic_cast (m_child2); + expr->calc(thread); + ReASVariant& value = thread.popValue(); + if (thread.tracing()) + thread.vm()->traceWriter()->format("expr: %s", + value.toString().constData()); + value.destroyValue(); + return 0; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ + +void ReASExprStatement::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + if (m_id == 40) + m_id = 40; + writer.formatIndented(indent, "Expr id: %d expr: %d succ: %d %s", m_id, + m_child2 == NULL ? 0 : m_child2->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child2 != NULL) + m_child2->dump(writer, indent + 1); +} + +/** @class ReASNode1 rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an inner node of the abstract syntax tree with one child. + * + * This class is an abstract class. + */ + +/** + * @brief ReASNode1::ReASNode1 + * @param type + */ +ReASNode1::ReASNode1(ReASItemType type) : + ReASItem(type), + m_child(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASNode1::~ReASNode1() +{ + delete m_child; + m_child = NULL; +} +/** + * @brief Returns the child. + * + * @return the child of the instance + */ +ReASItem* ReASNode1::child() const +{ + return m_child; +} +/** + * @brief Sets the child. + */ +void ReASNode1::setChild(ReASItem* child) +{ + m_child = child; +} + +/** + * @brief Writes the internals of a statement list into a file. + * + * @param writer writes to output media + * @param indent the indent level of the statement list + * @param statements the chain of statements to dump + */ +void ReASNode1::dumpStatements(ReWriter& writer, int indent, + ReASItem* statements) +{ + ReASNode1* chain = dynamic_cast(statements); + while (chain != NULL){ + chain->dump(writer, indent); + chain = dynamic_cast(chain->m_child); + } +} + + +/** @class ReASNode2 rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an inner node of the abstract syntax tree with two childs. + * + * This class is an abstract class. + */ + +/** + * @brief ReASNode2::ReASNode2 + * @param type + */ +ReASNode2::ReASNode2(ReASItemType type) : + ReASNode1(type), + m_child2(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASNode2::~ReASNode2() +{ + delete m_child2; + m_child2 = NULL; +} +ReASItem* ReASNode2::child2() const +{ + return m_child2; +} + +void ReASNode2::setChild2(ReASItem* child2) +{ + m_child2 = child2; +} + + +/** @class ReASNode3 rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an inner node of the abstract syntax tree with 3 childs. + * + * This class is an abstract class. + */ + +/** + * @brief ReASNode3::ReASNode3 + * @param type + */ +ReASNode3::ReASNode3(ReASItemType type) : + ReASNode2(type), + m_child3(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASNode3::~ReASNode3() +{ + delete m_child3; + m_child3 = NULL; +} +/** + * @brief Returns the child3. + * + * @return the child 3 + */ +ReASItem* ReASNode3::child3() const +{ + return m_child3; +} + +/** + * @brief Sets the child3. + * + * @param child3 the new child3 + */ +void ReASNode3::setChild3(ReASItem* child3) +{ + m_child3 = child3; +} + + +/** @class ReASNode4 rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an inner node of the abstract syntax tree with 3 childs. + * + * This class is an abstract class. + */ + +/** + * @brief ReASNode4::ReASNode4 + * @param type + */ +ReASNode4::ReASNode4(ReASItemType type) : + ReASNode3(type), + m_child4(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASNode4::~ReASNode4() +{ + delete m_child4; + m_child4 = NULL; +} + +/** + * @brief Returns the child4. + * + * @return the child 4 + */ +ReASItem* ReASNode4::child4() const +{ + return m_child4; +} + +/** + * @brief Sets the child4. + * + * @param child4 the new child3 + */ +void ReASNode4::setChild4(ReASItem* child4) +{ + m_child4 = child4; +} + + +/** @class ReASNode5 rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an inner node of the abstract syntax tree with 4 childs. + * + * This class is an abstract class. + */ + +/** + * @brief ReASNode5::ReASNode5 + * @param type + */ +ReASNode5::ReASNode5(ReASItemType type) : + ReASNode4(type), + m_child5(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASNode5::~ReASNode5() +{ + delete m_child5; + m_child5 = NULL; +} + +/** + * @brief Returns the child5. + * + * @return the child 5 + */ +ReASItem* ReASNode5::child5() const +{ + return m_child5; +} + +/** + * @brief Sets the child5. + * + * @param child5 the new child3 + */ +void ReASNode5::setChild5(ReASItem* child5) +{ + m_child5 = child5; +} + +/** @class ReASNode6 rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an inner node of the abstract syntax tree with 4 childs. + * + * This class is an abstract class. + */ + +/** + * @brief ReASNode6::ReASNode6 + * @param type + */ +ReASNode6::ReASNode6(ReASItemType type) : + ReASNode5(type), + m_child6(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASNode6::~ReASNode6() +{ + delete m_child6; + m_child6 = NULL; +} + +/** + * @brief Returns the child5. + * + * @return the child 5 + */ +ReASItem* ReASNode6::child6() const +{ + return m_child6; +} + +/** + * @brief Sets the child6. + * + * @param child6 the new child6 + */ +void ReASNode6::setChild6(ReASItem* child6) +{ + m_child6 = child6; +} + +/** @class ReASUnaryOp rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an unary operation. + * + * This is an operation with one operand, e.g. the boolean 'not' operation. + * + * m_child: operand + */ + +/** + * @brief Constructor. + * + * @param op the operator id + * @param type the node type + */ +ReASUnaryOp::ReASUnaryOp(UnaryOp op, ReASItemType type) : + ReASNode1(type), + m_operator(op) +{ +} + +/** + * @brief Calculates the value of the unary operator. + * + * @param thread IN/OUT: the execution unit, a VM thread + */ +void ReASUnaryOp::calc(ReVMThread& thread) +{ + ReASVariant& value = thread.topOfValues(); + switch(m_operator){ + case UOP_PLUS: + break; + case UOP_MINUS_INT: + value.setInt(- value.asInt()); + break; + case UOP_MINUS_FLOAT: + value.setFloat(- value.asFloat()); + break; + case UOP_NOT_BOOL: + value.setBool(! value.asBool()); + break; + case UOP_NOT_INT: + value.setInt(~value.asInt()); + break; + case UOP_DEC: + case UOP_INC: + default: + error(thread.logger(), LOC_UNOP_CALC_1, "unknown operator: %d", m_operator); + break; + } + if (thread.tracing()) + thread.vm()->traceWriter()->format("unary %s: %s", + nameOfOp(m_operator), + value.toString().constData()); +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASUnaryOp::check(ReParser& parser) +{ + bool rc = m_child->check(parser); + if (rc){ + ReASCalculable* expr = dynamic_cast(m_child); + ReASClass* clazz = expr == NULL ? NULL : expr->clazz(); + if (clazz == NULL){ + rc = ensureError(parser, "ReASUnaryOp::check"); + } else { + switch(m_operator){ + case UOP_PLUS: + if (clazz != ReASInteger::m_instance + && clazz != ReASFloat::m_instance) + rc = error(LOC_UNARY_CHECK_1, parser, + "wrong data type for unary operator '+': %s", + clazz->name().constData()); + break; + case UOP_MINUS_INT: + if (clazz != ReASFloat::m_instance) + m_operator = UOP_MINUS_FLOAT; + else if (clazz != ReASInteger::m_instance) + rc = error(LOC_UNARY_CHECK_2, parser, + "wrong data type for unary operator '-': %s", + clazz->name().constData()); + break; + case UOP_NOT_BOOL: + if (clazz != ReASBoolean::m_instance) + rc = error(LOC_UNARY_CHECK_3, parser, + "wrong data type for unary operator '!': %s", + clazz->name().constData()); + break; + case UOP_NOT_INT: + if (clazz != ReASInteger::m_instance) + rc = error(LOC_UNARY_CHECK_4, parser, + "wrong data type for unary operator '!': %s", + clazz->name().constData()); + break; + case UOP_DEC: + break; + case UOP_INC: + break; + default: + throw ReASException(position(), "unknown operator: %d", m_operator); + break; + } + } + } + return rc; +} + +/** + * @brief Returns the operator of the unary operation. + * + * @return the operator + */ +int ReASUnaryOp::getOperator() const +{ + return m_operator; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASUnaryOp::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "Unary %d op: %s (%d) expr: %d %s", + m_id, + nameOfOp(m_operator), + m_operator, + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer) ); + if (m_child != NULL) + m_child->dump(writer, indent + 1); +} +/** + * @brief Returns the name (a string) of an unary operator. + * + * @param op the operand to convert + * @return the name of the operator + */ +const char*ReASUnaryOp::nameOfOp(ReASUnaryOp::UnaryOp op) +{ + const char* rc; + switch (op){ + case UOP_PLUS: + rc="+"; + break; + case UOP_MINUS_INT: + case UOP_MINUS_FLOAT: + rc="-"; + break; + case UOP_NOT_BOOL: + rc="!"; + break; + case UOP_NOT_INT: + rc="~"; + break; + case UOP_INC: + rc="++"; + break; + case UOP_DEC: + rc="--"; + break; + default: + throw ReException("unknown unary operator: %d", (int) op); + break; + } + return rc; +} + + +/** @class ReASStatement rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a base class for all statements. + * + * @note statements are always ReASNode1 and m_child is used + * for the successors (next statement). + */ + +/** + * @brief Constructor. + */ +ReASStatement::ReASStatement() +{ +} +/** + * @brief Executes the statements of a statement list. + * + * @param list statement list + * @param thread execution unit + * @return 0: continue the current statement list
+ * n > 0: stop the n most inner statement lists (initialized by leave) + * n < 0: stop the -n most inner statement lists (initialized by continue) + */ +int ReASStatement::executeStatementList(ReASItem* list, ReVMThread& thread) +{ + int rc = 0; + while(rc == 0 && list != NULL){ + ReASStatement* statement = dynamic_cast(list); + rc =statement->execute(thread); + } + return rc; +} + + +/** @class ReASIf rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an if statement. + * + * The if statement has a condition, a then-part and an optional else-part. + * If the condition is evaluated to true, the then-part will be executed. + * Otherwise the else-part if it exists. + * + * m_child: next statement
+ * m_child2: condition
+ * m_child3: then part
+ * m_child4: else part or NULL
+ */ + +ReASIf::ReASIf() : + ReASNode4(AST_IF) +{ + m_flags |= NF_STATEMENT; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASIf::check(ReParser& parser) +{ + bool rc = true; + if (m_child2 == NULL) + rc = ensureError(parser, "'if' misses condition"); + else if (m_child2->checkAsCalculable("condition", ReASBoolean::m_instance, + parser)) + rc = false; + if (m_child3 != NULL && ! checkStatementList(m_child3, parser)) + rc = false; + if (m_child4 != NULL && ! checkStatementList(m_child4, parser)) + rc = false; + return rc; +} + +/** + * @brief Executes the statement. + * + * @return 0: continue the current statement list
+ * n > 0: stop the n most inner statement lists (initialized by leave) + * n < 0: stop the -n most inner statement lists (initialized by continue) + */ +int ReASIf::execute(ReVMThread& thread) +{ + int rc = 0; + bool condition = calcAsBoolean(m_child2, thread); + if (thread.tracing()) + thread.vm()->traceWriter()->format("if %s", condition ? "true" : "false"); + + ReASItem* list = condition ? m_child3 : m_child4; + if (list != NULL){ + if ( (rc = executeStatementList(list, thread)) != 0){ + if (rc < 0) + rc--; + else if (rc > 0) + rc++; + } + } + return rc; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASIf::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, + "If id: %d condition: %d then: %d else: %d succ: %d%s", + m_id, + m_child2 == NULL ? 0 : m_child2->id(), + m_child3 == NULL ? 0 : m_child3->id(), + m_child4 == NULL ? 0 : m_child4->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + m_child2->dump(writer, indent + 1); + if (m_child3 != NULL) + m_child3->dump(writer, indent + 1); + if (m_child4 != NULL) + m_child4->dump(writer, indent + 1); +} + +/** @class ReASFor rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a for statement. + * + * The for statement has an initialization, a condition, a forwarding + * statement and a body. + * The initialization will be called first. + * Then the condition will be tested. If true the body will be executed + * and then the forwarding statement. + * + * m_child: next statement
+ * m_child2: body
+ * m_child3: iterator variable
+ * m_child4: container variable
+ */ + +/** + * @brief Constructor. + * + * @param variable NULL or the iterator variable + */ +ReASForIterated::ReASForIterated(ReASVarDefinition* variable) : + ReASNode4(AST_ITERATED_FOR), + ReASStatement() +{ + m_flags |= NF_STATEMENT; + m_child2 = variable; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASForIterated::check(ReParser& parser) +{ + return false; +} + +/** + * @brief Executes the statement. + * + * @return 0: continue the current statement list
+ * n > 0: stop the n most inner statement lists (initialized by leave) + * n < 0: stop the -n most inner statement lists (initialized by continue) + */ +int ReASForIterated::execute(ReVMThread& thread) +{ + return 0; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASForIterated::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "forIt id: %d var: %d set: %d body: %d succ: %d %s", + m_id, + m_child3 == NULL ? 0 : m_child3->id(), + m_child4 == NULL ? 0 : m_child4->id(), + m_child2 == NULL ? 0 : m_child2->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child3 != NULL) + m_child3->dump(writer, indent + 1); + if (m_child4 != NULL) + m_child4->dump(writer, indent + 1); + dumpStatements(writer, indent + 1, m_child2); +} + +/** @class ReASForCounted rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a for statement. + * + * The for statement has an optional variable, an optional start value, + * an end value, an optional step and a body. + * + * The start and end value will be calculated. + * The body will be executed so many times given by the start and end value. + * + * m_child: next statement
+ * m_child2: body
+ * m_child3: variable or NULL
+ * m_child4: start value or NULL
+ * m_child5: end value
+ * m_child6: step value or NULL + * + */ + +/** + * @brief Constructor. + * + * @param variable NULL or the counter variable + */ +ReASForCounted::ReASForCounted(ReASVarDefinition* variable) : + ReASNode6(AST_ITERATED_FOR), + ReASStatement() +{ + m_flags |= NF_STATEMENT; + m_child3 = variable; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASForCounted::check(ReParser& parser) +{ + bool rc = true; + ReASNamedValue* var = NULL; + if (m_child3 != NULL){ + var = dynamic_cast(m_child3); + if (! m_child3->check(parser)) + rc = false; + if (var == NULL) + rc = error(LOC_FORC_CHECK_1, parser, "not a variable: %s", + m_child3->nameOfItemType()); + } + ReASCalculable* expr; + if (m_child4 != NULL && ! m_child4->checkAsCalculable("start value", + ReASInteger::m_instance, parser)) + rc = false; + if (m_child5 != NULL && ! m_child5->checkAsCalculable("end value", + ReASInteger::m_instance, parser)) + rc = false; + if (m_child6 != NULL && ! m_child6->checkAsCalculable("step value", + ReASInteger::m_instance, parser)) + rc = false; + if (m_child2 != NULL && ! checkStatementList(m_child2, parser)) + rc = false; + return rc; +} + +/** + * @brief Executes the statement. + * + * @return 0: continue the current statement list
+ * n > 0: stop the n most inner statement lists (initialized by leave) + * n < 0: stop the -n most inner statement lists (initialized by continue) + */ +int ReASForCounted::execute(ReVMThread& thread) +{ + int rc = 0; + ReASStatement* body = dynamic_cast(m_child2); + if (body == NULL) + throw ReASException(m_child2 == NULL ? m_position : m_child2->position(), + "forc statement: body is not a statement"); + int start = m_child4 == NULL ? 1 : calcAsInteger(m_child4, thread); + int end = m_child5 == NULL ? 0 : calcAsInteger(m_child5, thread); + int step = m_child6 == NULL ? 1 : calcAsInteger(m_child6, thread); + ReASNamedValue* var = m_child3 == NULL + ? NULL : dynamic_cast(m_child3); + if (thread.tracing()) + thread.vm()->traceWriter()->format("for %s from %d to %d step %d", + var == NULL ? "?" : var->name().constData(), + start, end, step); + + for(int ii = start; ii <= end; ii += step){ + //@ToDo: assign to the variable + int rc2 = body->execute(thread); + if (rc2 != 0){ + if (rc2 > 0){ + // rc comes from "break"; + rc = rc2 - 1; + } else { + // rc comes from "continue"; + if (rc2 == -1) + continue; + else + rc = rc2 + 1; + } + break; + } + } + return rc; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASForCounted::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "forC id: %d var: %d from: %d to: %d step: %d body: %d succ: %d %s", + m_id, + m_child3 == NULL ? 0 : m_child3->id(), + m_child4 == NULL ? 0 : m_child4->id(), + m_child5 == NULL ? 0 : m_child5->id(), + m_child6 == NULL ? 0 : m_child6->id(), + m_child2 == NULL ? 0 : m_child2->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child3 != NULL) + m_child3->dump(writer, indent + 1); + if (m_child4 != NULL) + m_child4->dump(writer, indent + 1); + if (m_child5 != NULL) + m_child5->dump(writer, indent + 1); + if (m_child6 != NULL) + m_child6->dump(writer, indent + 1); + dumpStatements(writer, indent + 1, m_child2); +} + +/** @class ReASWhile rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a while statement. + * + * The while statement has an a condition and a body. + * The body will be executed while the condition returns true. + * + * m_child: next statement
+ * m_child2: condition
+ * m_child3: body
+ */ + +ReASWhile::ReASWhile() : + ReASNode3(AST_WHILE), + ReASStatement() +{ + m_flags |= NF_STATEMENT; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASWhile::check(ReParser& parser) +{ + bool rc = true; + if (m_child2 == NULL) + ensureError(parser, "missing condition for 'while''"); + else + rc = m_child2->checkAsCalculable("condition", ReASBoolean::m_instance, + parser); + if (m_child3 != NULL && ! checkStatementList(m_child3, parser)) + rc = false; + return rc; +} + +/** + * @brief Executes the statement. + * + * @return 0: continue the current statement list
+ * n > 0: stop the n most inner statement lists (initialized by leave) + * n < 0: stop the -n most inner statement lists (initialized by continue) + */ +int ReASWhile::execute(ReVMThread& thread) +{ + int rc = 0; + ReASStatement* body = dynamic_cast(m_child3); + if (thread.tracing()) + thread.vm()->traceWriter()->write("while"); + while(calcAsBoolean(m_child2, thread)){ + int rc2 = body->execute(thread); + if (rc2 != 0){ + if (rc2 > 0){ + // rc comes from "break"; + rc = rc2 - 1; + } else { + // rc comes from "continue"; + if (rc2 == -1) + continue; + else + rc = rc2 + 1; + } + break; + } + } + return rc; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASWhile::dump(ReWriter& writer, int indent) +{ + + char buffer[256]; + writer.formatIndented(indent, "while id: %d condition: %d body: %d succ: %d %s", + m_id, + m_child2 == NULL ? 0 : m_child2->id(), + m_child3 == NULL ? 0 : m_child3->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child2 != NULL) + m_child2->dump(writer, indent + 1); + dumpStatements(writer, indent + 1, m_child3); +} + +/** @class ReASRepeat rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a while statement. + * + * The while statement has an a condition and a body. + * The body will be executed while the condition returns true. + * + * m_child: next statement
+ * m_child2: condition
+ * m_child3: body
+ */ + +ReASRepeat::ReASRepeat() : + ReASNode3(AST_REPEAT), + ReASStatement() +{ + m_flags |= NF_STATEMENT; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASRepeat::check(ReParser& parser) +{ + bool rc = true; + if (m_child3 != NULL && ! checkStatementList(m_child3, parser)) + rc = false; + if (m_child2 == NULL) + ensureError(parser, "missing condition for 'repeat''"); + else if (! m_child2->checkAsCalculable("condition", ReASBoolean::m_instance, + parser)) + rc = false; + return rc; +} + +/** + * @brief Executes the statement. + * + * Meaning of the childs: + * m_child: body + * m_child2: condition + */ +int ReASRepeat::execute(ReVMThread& thread) +{ + int rc = 0; + ReASStatement* body = dynamic_cast(m_child3); + if (thread.tracing()) + thread.vm()->traceWriter()->write("repeat"); + do { + int rc2 = body->execute(thread); + if (rc2 != 0){ + if (rc2 > 0){ + // rc comes from "break"; + rc = rc2 - 1; + } else { + // rc comes from "continue"; + if (rc2 == -1) + continue; + else + rc = rc2 + 1; + } + break; + } + } while(! calcAsBoolean(m_child2, thread)); + return rc; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASRepeat::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "repeat id: %d condition: %d body: %d succ: %d %s", + m_id, + m_child2 == NULL ? 0 : m_child2->id(), + m_child3 == NULL ? 0 : m_child3->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child2 != NULL) + m_child2->dump(writer, indent + 1); + dumpStatements(writer, indent + 1, m_child3); +} + +/** @class ReASClass rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the base class of an Abstract Syntax Tree class. + * + * This class is abstract. + */ +/** + * @brief Constructor. + */ +ReASClass::ReASClass(const QByteArray& name, ReASTree& tree) : + m_name(name), + m_symbols(NULL), + m_superClass(NULL), + m_tree(tree) +{ +} + +/** + * @brief Destructor. + * + * Does nothing but forces a virtual destructor of all derived classes. + * + */ +ReASClass::~ReASClass() +{ + +} + +/** + * @brief Return the class name. + * + * @return the class name + */ +const QByteArray& ReASClass::name() const +{ + return m_name; +} + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASClass::dump(ReWriter& writer, int indent) +{ + writer.formatIndented(indent, "class %s super: %s", m_name.constData(), + m_superClass == NULL + ? "" : m_superClass->name().constData()); + m_symbols->dump(writer, indent); +} + +/** + * @brief Sets the symbol space from the current in the tree. + */ +void ReASClass::setSymbols() +{ + m_symbols = m_tree.currentSpace(); +} + +/** @class ReASTree 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. + */ +ReASTree::ReASTree() : + m_global(NULL), + m_modules(), + m_symbolSpaces(), + m_currentSpace(NULL), + m_store(128*1024) +{ + init(); +} + +/** + * @brief Destructor. + */ +ReASTree::~ReASTree() +{ + destroy(); +} + +/** + * @brief Initializes the instance. + * + * Used in the constructor and in clear. + */ +void ReASTree::init() +{ + m_global = ReSymbolSpace::createGlobal(*this); + m_symbolSpaces.append(m_global); + m_currentSpace = m_global; +} + +/** + * @brief Frees the resources of the instance. + */ +void ReASTree::destroy() +{ + SymbolSpaceMap::iterator it; + for (it = m_symbolSpaceHeap.begin(); it != m_symbolSpaceHeap.end(); it++){ + delete it.value(); + } + m_symbolSpaceHeap.clear(); +} +/** + * @brief Returns the string storage of the instance. + * + * @return the efficient allocator for C strings + */ +ReByteStorage& ReASTree::store() +{ + return m_store; +} + +/** + * @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 ReASTree::startModule(ReSourceUnitName name) +{ + bool rc = m_modules.contains(name); + if (! rc){ + // freed in ~ReASTree() + ReSymbolSpace* space = new ReSymbolSpace(ReSymbolSpace::SST_MODULE, + name, m_global); + m_symbolSpaceHeap[name] = space; + m_modules[name] = space; + m_symbolSpaces.append(space); + m_currentSpace = space; + } + return rc; +} +/** + * @brief Search for the symbol space of a given module. + * + * @param name the module's name + * @return NULL: not found
+ * otherwise: the symbol space of the module + */ +ReSymbolSpace* ReASTree::findmodule(const QByteArray& name) +{ + ReSymbolSpace* rc = m_modules.contains(name) ? m_modules[name] : NULL; + return rc; +} + +/** + * @brief Handles the end of a module. + * @param name the module's name + */ +void ReASTree::finishModule(ReSourceUnitName name) +{ + ReSymbolSpace* top = m_symbolSpaces.at(m_symbolSpaces.size() - 1); + if (top->name() != name) + throw ReException("ReASTree::finishModule(): module is not top: %s", + name); + 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 name of the class/method + * @param type the symbol space type + * @return the new symbol space + */ +ReSymbolSpace* ReASTree::startClassOrMethod(const QByteArray& name, + ReSymbolSpace::SymbolSpaceType type) +{ + // the stack m_modules is never empty because of "global" and modules. + ReSymbolSpace* parent = m_symbolSpaces[m_symbolSpaces.size() - 1]; + QByteArray fullName = parent->name() + "." + name; + // freed in ~ReASTree() + ReSymbolSpace* space = new ReSymbolSpace(type, fullName, parent); + m_symbolSpaceHeap[fullName] = space; + m_symbolSpaces.append(space); + m_currentSpace = space; + return space; +} + +/** + * @brief Handles the end of a class definition. + * + * @param name the name of the class (short form) + */ +void ReASTree::finishClassOrMethod(const QByteArray& name) +{ + ReSymbolSpace* top = m_symbolSpaces.at(m_symbolSpaces.size() - 1); + if (! top->name().endsWith("." + name)) + throw ReException("ReASTree::finishModule(): class is not top: %s", + name.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 + */ +ReASTree::SymbolSpaceStack& ReASTree::symbolSpaces() +{ + return m_symbolSpaces; +} +/** + * @brief Returns the current symbol space (top of the stack). + * + * @return the current symbol space + */ +ReSymbolSpace* ReASTree::currentSpace() const +{ + return m_currentSpace; +} + +/** + * @brief Removes all content from the abstract syntax tree. + */ +void ReASTree::clear() +{ + destroy(); + //m_global->clear(); + m_modules.clear(); + m_symbolSpaces.clear(); + init(); +} + +/** + * @brief Writes the internals into a file. + * + * @param filename filename + * @param flags what to dump: sum of DMP_... flags + * @param header NULL or a text put on the top + */ +void ReASTree::dump(const char* filename, int flags, const char* header) +{ + ReFileWriter writer(filename); + if (header != NULL) + writer.writeLine(header); + if (flags & DMP_GLOBALS){ + m_global->dump(writer, 0, "=== Globals:"); + } + if (flags & DMP_MODULES){ + QList sorted; + sorted.reserve(m_modules.size()); + SymbolSpaceMap::iterator it; + for (it = m_modules.begin(); it != m_modules.end(); it++){ + sorted.append(it.key()); + } + qSort(sorted.begin(), sorted.end(), qLess()); + QList::iterator it2; + for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ + ReSymbolSpace* space = m_modules[*it2]; + space->dump(writer, 0); + } + } + writer.close(); +} + +/** @class ReASMethodCall rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a method or function call for the Abstract Syntax Tree. + * + * m_child: next statement
+ * m_child2: argument list
+ * m_child3: parent (variable, field ...) + */ +/** + * @brief Constructor. + * + * @param name name of the method/function + * @param parent NULL: it is a function
+ * otherwise: the parent (variable, field ...) + */ + +ReASMethodCall::ReASMethodCall(const QByteArray& name, ReASItem* parent) : + ReASNode3(AST_METHOD_CALL), + ReASStatement(), + m_name(name), + m_method(NULL) +{ + m_flags |= NF_STATEMENT; + m_child3 = parent; +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASMethodCall::check(ReParser& parser) +{ + bool rc = true; + ReASExprStatement* args = dynamic_cast(m_child2); + int argCount = 0; + ReASMethod* method = m_method; + ReASVarDefinition* params = dynamic_cast(method->child2()); + while (args != NULL && params != NULL){ + argCount++; + ReASCalculable* argExpr = dynamic_cast + (args->child2()); + if (argExpr == NULL) + rc = error(LOC_METHOD_CALL_CHECK_1, parser, + "argument %d misses expr", argCount); + else { + ReASNamedValue* var; + ReASItem* param = params->child2(); + if (param == NULL + || (var = dynamic_cast(param)) == NULL) + rc = error(LOC_MEHTOD_CALL_CHECK_2, parser, + "parameter %d misses named value: %s", argCount, + param == NULL ? "" : param->nameOfItemType()); + else { + // tryConversion() calls args->args->child2()->check()! + ReASConversion* converter = ReASConversion::tryConversion( + var->clazz(), args->child2(), parser, rc); + if (rc && converter != NULL) + args->setChild2(converter); + + } + } + args = dynamic_cast(args->child()); + params = dynamic_cast(params->child()); + } + if (args != NULL && params == NULL) + rc = error(LOC_MEHTOD_CALL_CHECK_3, parser, + "too many arguments: %d are enough", argCount); + else if (args == NULL && params != NULL && params->child3() != NULL) + rc = error(LOC_MEHTOD_CALL_CHECK_4, parser, + "too few arguments: %d are not enough", argCount); + return rc; +} + + +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASMethodCall::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "call %s Id: %d args: %d parent: %d succ: %d %s", + m_name.constData(), m_id, + m_child2 == NULL ? 0 : m_child2->id(), + m_child3 == NULL ? 0 : m_child3->id(), + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child2 != NULL) + m_child2->dump(writer, indent + 1); + if (m_child3 != NULL) + m_child3->dump(writer, indent + 1); +} + +/** + * @brief Executes the method call. + * + * @return 0: continue the current statement list + */ +int ReASMethodCall::execute(ReVMThread& thread) +{ + int rc = 0; + ReASMethod* method = m_method; + ReStackFrame frame(this, m_method->symbols()); + thread.pushFrame(&frame); + ReASExprStatement* args = dynamic_cast(m_child2); + int ixArg = -1; + while (args != NULL){ + ixArg++; + ReASCalculable* argExpr = dynamic_cast + (args->child2()); + argExpr->calc(thread); + ReASVariant& value = thread.popValue(); + ReASVariant& varValue = frame.valueOfVariable(ixArg); + varValue.copyValue(value); + } + thread.popFrame(); + return rc; +} + +ReASMethod* ReASMethodCall::method() const +{ + return m_method; +} + +/** + * @brief Sets the method. + * @param method method to set + */ +void ReASMethodCall::setMethod(ReASMethod* method) +{ + m_method = method; +} + +/** + * @brief Returns the argument list. + * + * @return the first element of an argument list + */ +ReASExprStatement* ReASMethodCall::arg1() const +{ + return dynamic_cast(m_child2); +} + +/** @class ReASException rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a call of a method or function. + * + * m_child: body + * m_child2: argument list (or NULL) + */ + +/** @class ReASBinaryOp rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements binary operator for the Abstract Syntax Tree. + * + * m_child: left operand
+ * m_child2: right operand + */ +/** + * @brief Constructor. + */ +ReASBinaryOp::ReASBinaryOp() : + ReASNode2(AST_BINARY_OP), + m_operator(BOP_UNDEF) +{ +} + +/** + * @brief Calculates the binary operation. + * + * @param thread IN/OUT: the bool value of the condition + */ +void ReASBinaryOp::calc(ReVMThread& thread) +{ + if (isAssignment()) + assign(thread); + else{ + ReASCalculable* op1 = dynamic_cast(m_child); + ReASCalculable* op2 = dynamic_cast(m_child2); + if (op1 == NULL || op2 == NULL) + error(thread.logger(), LOC_BINOP_CALC_1, "operand is null: %d / %d", + m_child == NULL ? 0 : m_child->id(), + m_child2 == NULL ? 0 : m_child2->id()); + else{ + op1->calc(thread); + op2->calc(thread); + ReASVariant& val1 = thread.top2OfValues(); + ReASVariant& val2 = thread.topOfValues(); + switch(m_operator){ + case BOP_PLUS: + switch(val1.variantType()){ + case ReASVariant::VT_FLOAT: + val1.setFloat(val1.asFloat() + val2.asFloat()); + break; + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() + val2.asInt()); + break; + case ReASVariant::VT_OBJECT: + //if (val1.getClass() == ReASString::m_instance) + default: + error(thread.logger(), LOC_BINOP_CALC_2, "invalid type for '+': %s", + val1.nameOfType()); + break; + } + break; + case BOP_MINUS: + switch(val1.variantType()){ + case ReASVariant::VT_FLOAT: + val1.setFloat(val1.asFloat() - val2.asFloat()); + break; + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() - val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_3, "invalid type for '-': %s", + val1.nameOfType()); + break; + } + break; + case BOP_TIMES: + switch(val1.variantType()){ + case ReASVariant::VT_FLOAT: + val1.setFloat(val1.asFloat() * val2.asFloat()); + break; + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() * val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_4, "invalid type for '*': %s", + val1.nameOfType()); + break; + } + break; + case BOP_DIV: + switch(val1.variantType()){ + case ReASVariant::VT_FLOAT: + val1.setFloat(val1.asFloat() / val2.asFloat()); + break; + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() / val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_5, "invalid type for '/': %s", + val1.nameOfType()); + break; + } + break; + case BOP_MOD: + switch(val1.variantType()){ + case ReASVariant::VT_FLOAT: + val1.setFloat(fmod(val1.asFloat(), val2.asFloat())); + break; + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() % val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_6, "invalid type for '%': %s", + val1.nameOfType()); + break; + } + break; + case BOP_POWER: + switch(val1.variantType()){ + case ReASVariant::VT_FLOAT: + val1.setFloat(fmod(val1.asFloat(), val2.asFloat())); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_7, "invalid type for '**': %s", + val1.nameOfType()); + break; + } + break; + case BOP_LOG_OR: + switch(val1.variantType()){ + case ReASVariant::VT_BOOL: + val1.setBool(val1.asBool() || val2.asBool()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_8, "invalid type for '||': %s", + val1.nameOfType()); + break; + } + break; + case BOP_LOG_AND: + switch(val1.variantType()){ + case ReASVariant::VT_BOOL: + val1.setBool(val1.asBool() && val2.asBool()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_9, "invalid type for '&&': %s", + val1.nameOfType()); + break; + } + break; + case BOP_LOG_XOR: + switch(val1.variantType()){ + case ReASVariant::VT_BOOL: + val1.setBool(val1.asBool() != val2.asBool()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_9, "invalid type for '^^': %s", + val1.nameOfType()); + break; + } + break; + case BOP_BIT_OR: + switch(val1.variantType()){ + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() | val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_10, "invalid type for '|': %s", + val1.nameOfType()); + break; + } + break; + case BOP_BIT_AND: + switch(val1.variantType()){ + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() & val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_11, "invalid type for '&': %s", + val1.nameOfType()); + break; + } + break; + case BOP_BIT_XOR: + switch(val1.variantType()){ + case ReASVariant::VT_INTEGER: + val1.setInt(val1.asInt() ^ val2.asInt()); + break; + default: + error(thread.logger(), LOC_BINOP_CALC_12, "invalid type for '^': %s", + val1.nameOfType()); + break; + } + break; + default: + break; + } + thread.popValue(); + } + } +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASBinaryOp::check(ReParser& parser) +{ + return false; +} + +/** + * @brief Returns the operator. + * + * @return the operator + */ +ReASBinaryOp::BinOperator ReASBinaryOp::getOperator() const +{ + return m_operator; +} + +/** + * @brief Sets the operator. + * + * @param op the operator + */ +void ReASBinaryOp::setOperator(BinOperator op) +{ + m_operator = op; +} +/** + * @brief Writes the internals into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASBinaryOp::dump(ReWriter& writer, int indent) +{ + + const QByteArray& opName = nameOfOp(m_operator); + char buffer[256]; + writer.formatIndented(indent, "BinOp id: %d op: %s (%d) left: %d right: %d %s", + m_id, + opName.constData(), m_operator, + m_child == NULL ? 0 : m_child->id(), + m_child2 == NULL ? 0 : m_child2->id(), + positionStr(buffer, sizeof buffer)); + if (indent < 32 && m_child != NULL) + m_child->dump(writer, indent + 1); + if (indent < 32 && m_child2 != NULL) + m_child2->dump(writer, indent + 1); +} + +/** + * @brief Does an assignment. + * + * @param thread + */ +void ReASBinaryOp::assign(ReVMThread& thread) +{ + ReASVariant& rValue = thread.lValue(m_child); + ReASCalculable* expr = dynamic_cast(m_child2); + if (expr == NULL) + error(thread.logger(), LOC_BINOP_1, "not a calculable: id: %d", + m_child2 == NULL ? 0 : m_child2->id()); + else { + ReASVariant& value = thread.popValue(); + switch(m_operator){ + case BOP_ASSIGN: + break; + case BOP_PLUS_ASSIGN: + //switch(value.variantType()){ + + //} + break; + case BOP_MINUS_ASSIGN: + case BOP_TIMES_ASSIGN: + case BOP_DIV_ASSIGN: + case BOP_MOD_ASSIGN: + case BOP_POWER_ASSIGN: + case BOP_LOG_OR_ASSIGN: + case BOP_LOG_AND_ASSIGN: + case BOP_LOG_XOR_ASSIGN: + case BOP_BIT_OR_ASSIGN: + case BOP_BIT_AND_ASSIGN: + case BOP_BIT_XOR_ASSIGN: + break; + default: + break; + } + rValue.copyValue(value); + } +} +/** + * @brief Returns the name (a string) of a binary operator. + * + * @param op operator to convert + * + * @return the name of the operator + */ +const char* ReASBinaryOp::nameOfOp(ReASBinaryOp::BinOperator op) +{ + const char* rc; + switch (op){ + case BOP_ASSIGN: + rc = "="; + break; + case BOP_PLUS_ASSIGN: + rc = "+="; + break; + case BOP_MINUS_ASSIGN: + rc = "-="; + break; + case BOP_TIMES_ASSIGN: + rc = "*="; + break; + case BOP_DIV_ASSIGN: + rc = "/="; + break; + case BOP_MOD_ASSIGN: + rc = "%="; + break; + case BOP_POWER_ASSIGN: + rc = "**="; + break; + case BOP_LOG_OR_ASSIGN: + rc = "||="; + break; + case BOP_LOG_AND_ASSIGN: + rc = "&&="; + break; + case BOP_LOG_XOR_ASSIGN: + rc = "^^="; + break; + case BOP_BIT_OR_ASSIGN: + rc = "|="; + break; + case BOP_BIT_AND_ASSIGN: + rc = "&="; + break; + case BOP_BIT_XOR_ASSIGN: + rc = "^="; + break; + case BOP_LSHIFT_ASSIGN: + rc = "<<="; + break; + case BOP_LOG_RSHIFT_ASSIGN: + rc = ">>="; + break; + case BOP_ARTITH_RSHIFT_ASSIGN: + rc = ">>>="; + break; + case BOP_PLUS: + rc = "+"; + break; + case BOP_MINUS: + rc = "-"; + break; + case BOP_TIMES: + rc = "*"; + break; + case BOP_DIV: + rc = "/"; + break; + case BOP_MOD: + rc = "%"; + break; + case BOP_POWER: + rc = "**"; + break; + case BOP_LOG_OR: + rc = "||"; + break; + case BOP_LOG_AND: + rc = "&&"; + break; + case BOP_LOG_XOR: + rc = "^^"; + break; + case BOP_BIT_OR: + rc = "|"; + break; + case BOP_BIT_AND: + rc = "&"; + break; + case BOP_BIT_XOR: + rc = "^"; + break; + case BOP_LSHIFT: + rc = ""; break; + case BOP_LOG_RSHIFT: + rc = ">>"; + break; + case BOP_ARTITH_RSHIFT: + rc = ">>>"; + break; + case BOP_EQ: + rc = "=="; + break; + case BOP_NE: + rc = "!="; + break; + case BOP_LE: + rc = "<="; + break; + case BOP_LT: + rc = "<"; + break; + case BOP_GE: + rc = ">="; + break; + case BOP_GT: + rc = ">"; + break; + default: + throw ReException("unknown binary op %d", (int) op); + } + return rc; +} + +/** @class ReASMethod rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a method definition for the Abstract Syntax Tree. + * + * The special case "function" (a method without class) is included. + * + * m_child: body
+ * m_child2: parameter list (or NULL) + */ +/** + * @brief Constructor. + * + * @param name the method name + * @param tree the abstract syntax tree + */ +ReASMethod::ReASMethod(const QByteArray& name, ReASTree& tree) : + ReASNode2(AST_METHOD), + m_name(name), + m_resultType(NULL), + m_symbols(NULL), + m_sibling(NULL), + m_tree(tree), + firstParamWithDefault(-1) +{ +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASMethod::check(ReParser& parser) +{ + return false; +} + +/** + * @brief Executes the statement. + * + * This method will be never called. Must exit: Otherwise the class is abstract. + */ +int ReASMethod::execute(ReVMThread& thread) +{ + return 0; +} + + +/** + * @brief Writes the internals of the instance into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASMethod::dump(ReWriter& writer, int indent) +{ + + char buffer[256]; + writer.indent(indent); + writer.format("Method %s %s(", + m_resultType == NULL ? "" : m_resultType->name().constData(), + m_name.constData()); + ReSymbolSpace* parent = m_symbols->parent(); + writer.formatLine(") id: %d parent: %s args: %d body: %d %s", m_id, + parent == NULL ? "" : parent->name().constData(), + m_child2 == NULL ? 0 : m_child2->id(), + m_child->id(), + positionStr(buffer, sizeof buffer)); + if (m_child2 != NULL) + m_child2->dump(writer, indent + 1); + dumpStatements(writer, indent + 1, m_child); + m_symbols->dump(writer, indent + 1); +} +/** + * @brief Returns the symbol space of the instance. + * + * @return the symbol space + */ +ReSymbolSpace* ReASMethod::symbols() const +{ + return m_symbols; +} + +/** + * @brief Sets the symbol space from the current of the tree. + */ +void ReASMethod::setSymbols() +{ + m_symbols = m_tree.currentSpace(); +} +/** + * @brief Returns the name of the method + * @return the name + */ + +const QByteArray& ReASMethod::name() const +{ + return m_name; +} + +/** + * @brief Tests whether an other method has the same signature (parameterlist). + * @param other the method for comparison + * @return true: same signature
+ * false: otherwise + */ +bool ReASMethod::equalSignature(ReASMethod& other) const +{ + bool rc = true; + ReASExprStatement* args = dynamic_cast(m_child2); + ReASExprStatement* otherArgs = dynamic_cast(other.child2()); + + while(rc && (args != NULL || otherArgs != NULL)){ + if (args == NULL || otherArgs == NULL) + rc = false; + else { + ReASVarDefinition* def = dynamic_cast(args->child2()); + ReASVarDefinition* defOther = dynamic_cast(otherArgs->child2()); + if (def->clazz() != defOther->clazz()) + rc = false; + } + } + return rc; +} +/** + * @brief Returns the next overloaded method. + * + * @return NULL: no other method available
+ * otherwise: the next method with the same name but another signature + */ +ReASMethod* ReASMethod::sibling() const +{ + return m_sibling; +} + +/** + * @brief Sets the next overloaded method. + * + * @param sibling another method with the same name but another signature + */ +void ReASMethod::setSibling(ReASMethod* sibling) +{ + m_sibling = sibling; +} +/** + * @brief Returns the index of the first parameter with a default value. + * + * @return -1: no parameter has a default value
+ * otherwise: the index of the first parameter with a default + */ +int ReASMethod::getFirstParamWithDefault() const +{ + return firstParamWithDefault; +} +/** + * @brief Sets the index of the first parameter with default value + * @param value + */ +void ReASMethod::setFirstParamWithDefault(int value) +{ + firstParamWithDefault = value; +} + + + +/** @class ReASField rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an class field for the Abstract Syntax Tree. + * + * m_child: parent (variable, field, method) + */ + +/** + * @brief Constructor. + * + * @param name name of the field + */ +ReASField::ReASField(const QByteArray& name) : + ReASNode1(AST_FIELD), + m_name(name) +{ +} + +/** + * @brief Checks the correctness of the instance. + * + * @param parser for error processing + * @return true: node is correct
+ * false: otherwise + */ +bool ReASField::check(ReParser& parser) +{ + return false; +} + +/** + * @brief Writes the internals of the instance into a file. + * + * @param writer writes to output + * @param indent nesting level + */ +void ReASField::dump(ReWriter& writer, int indent) +{ + char buffer[256]; + writer.formatIndented(indent, "field %s id: %d parent: %d succ: %s", + m_name.constData(), m_id, + m_child == NULL ? 0 : m_child->id(), + positionStr(buffer, sizeof buffer)); + m_child->dump(writer, indent + 1); +} + + diff --git a/expr/ReASTree.hpp b/expr/ReASTree.hpp new file mode 100644 index 0000000..f7ec9f1 --- /dev/null +++ b/expr/ReASTree.hpp @@ -0,0 +1,759 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtwriterl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#ifndef RPLASTREE_HPP +#define RPLASTREE_HPP + +enum ReASItemType { + AST_UNDEF, + AST_CONSTANT, + AST_LIST_CONSTANT, + AST_LIST_ENTRY, + AST_MAP_CONSTANT, + AST_MAP_ENTRY, + AST_NAMED_VALUE, + AST_CONVERSION, + AST_INDEXED_VALUE, + AST_FIELD, + AST_VAR_DEFINITION, + AST_EXPR_STATEMENT, + AST_METHOD, + AST_ARGUMENT, + AST_INTRINSIC_METHOD, + AST_PRE_UNARY_OP, + AST_POST_UNARY_OP, + AST_BINARY_OP, + AST_METHOD_CALL, + AST_WHILE, + AST_REPEAT, + AST_IF, + AST_CONDITION, + AST_ITERATED_FOR, + AST_COUNTED_FOR, + AST_SWITCH, + AST_LEAVE, + AST_CONTINUE +}; + +class ReASException : public ReException { +public: + ReASException(); + ReASException(const ReSourcePosition* position, const char* format, ...); +protected: + void build(const ReSourcePosition* position, const char* format, + va_list varList); +}; + +class ReASClass; +class ReASNamedValue; +class ReASItem; +class ReASCondition; + +class ReASVariant { + /* The VM uses some tricks (performance): Therefore this class + * must not be virtual! + */ +public: + enum VariantType { + VT_UNDEF, + VT_FLOAT, + VT_INTEGER, + VT_BOOL, + VT_OBJECT + }; + enum VariantFlags { + VF_UNDEF, + /// if DT_OBJECT: object is a copy, don't free at method end + VF_IS_COPY = 1 << 1, + /// debugger: action if changed + VF_WATCH_POINT = 1 << 2 + }; + + friend class ReASCondition; +public: + ReASVariant(); + ~ReASVariant(); + ReASVariant(const ReASVariant& source); + ReASVariant& operator=(const ReASVariant& source); + qreal asFloat() const; + int asInt() const; + bool asBool() const; + void* asObject(const ReASClass** clazz) const; + const QByteArray* asString() const; + void setFloat(qreal number); + void setInt(int integer); + void setBool(bool value); + void setObject(void* object, const ReASClass* clazz); + void setString(const QByteArray& string); + QByteArray toString(int maxLength = 80) const; + VariantType variantType() const; + const char* nameOfType() const; + const ReASClass* getClass() const; + void copyValue(const ReASVariant& source); + void destroyValue(); +private: + VariantType m_variantType:8; + /// bitmap of VF_... flags: + int m_flags:8; + union { + qreal m_float; + int m_int; + bool m_bool; + void* m_object; + } m_value; + const ReASClass* m_class; +}; + +class ReASTree; +class ReParser; +class ReVMThread; + +class ReASItem +{ +public: + enum NodeFlags { + NF_UNDEF, + /// the node calculates a value: + NF_CALCULABLE = 1 << 1, + /// the node calculates a value: + NF_STATEMENT = 1 << 2, + /// the tree under this node is complete checked for data type correctness + NF_TYPECHECK_COMPLETE = 1 << 3, + /// debugger: this node is a breakpoint + NF_BREAKPOINT = 1 << 5 + }; + +public: + friend class ReASTree; + ReASItem(ReASItemType type); + virtual ~ReASItem(); +public: + virtual bool check(ReParser& parser) = 0; +public: + bool checkAsCalculable(const char* description, ReASClass* expectedClass, + ReParser& parser); + const ReSourcePosition* position() const; + void setPosition(const ReSourcePosition* position); + unsigned int id() const; + char* positionStr(char buffer[], size_t bufferSize) const; + void error(ReLogger* logger, int location, const char* format, ...); +public: + /** + * @brief Writes the content of the instance into an output medium. + * + * @param writer writes to output media + * @param indent nesting level: so many tabs will be used as prefix + */ + virtual void dump(ReWriter& writer,int indent) = 0; +public: + static void reset(); + static bool checkStatementList(ReASItem* list, ReParser& parser); + static int calcAsInteger(ReASItem* expr, ReVMThread& thread); + static bool calcAsBoolean(ReASItem* expr, ReVMThread& thread); +public: + ReASItemType nodeType() const; + const char* nameOfItemType() const; + + int flags() const; + void setFlags(int flags); + + bool typeCheck(ReASClass* clazz1, ReASClass* clazz2); + bool error(int location, ReParser& parser, const char* format, ...); + bool ensureError(ReParser& parser, const char* info); +protected: + unsigned int m_id:16; + ReASItemType m_nodeType:8; + int m_flags:5; + int m_dataType: 3; + const ReSourcePosition* m_position; +private: + static unsigned int m_nextId; +}; + +class ReASNode1; +class ReASCalculable +{ +public: + ReASCalculable(); +public: + virtual void calc(ReVMThread& thread) = 0; +public: + ReASClass* clazz() const; + void setClass(ReASClass* clazz); +protected: + ReASClass* m_class; +}; + +class ReStackFrame; +class ReASStorable : public ReASCalculable { +}; +class ReVMThread; +class ReASConstant : public ReASItem, public ReASCalculable +{ +public: + ReASConstant(); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); +public: + virtual void dump(ReWriter& writer,int indent); + ReASVariant& value(); +private: + ReASVariant m_value; +}; + + +class ReASNode1 : public ReASItem +{ +public: + ReASNode1(ReASItemType type); + virtual ~ReASNode1(); +public: + ReASItem* child() const; + void setChild(ReASItem* child); +public: + static void dumpStatements(ReWriter& writer, int indent, ReASItem* statements); +protected: + ReASItem* m_child; +}; + +class ReASNode2 : public ReASNode1 +{ +public: + ReASNode2(ReASItemType type); + virtual ~ReASNode2(); +public: + ReASItem* child2() const; + void setChild2(ReASItem* child2); + +protected: + ReASItem* m_child2; +}; + +class ReASNode3 : public ReASNode2 +{ +public: + ReASNode3(ReASItemType type); + virtual ~ReASNode3(); +public: + ReASItem* child3() const; + void setChild3(ReASItem* child3); + +protected: + ReASItem* m_child3; +}; + +class ReASNode4 : public ReASNode3 +{ +public: + ReASNode4(ReASItemType type); + virtual ~ReASNode4(); +public: + ReASItem* child4() const; + void setChild4(ReASItem* child4); + +protected: + ReASItem* m_child4; +}; + +class ReASNode5 : public ReASNode4 +{ +public: + ReASNode5(ReASItemType type); + virtual ~ReASNode5(); +public: + ReASItem*child5() const; + void setChild5(ReASItem* child5); +protected: + ReASItem* m_child5; +}; +class ReASNode6 : public ReASNode5 +{ +public: + ReASNode6(ReASItemType type); + virtual ~ReASNode6(); +public: + ReASItem*child6() const; + void setChild6(ReASItem* child5); +protected: + ReASItem* m_child6; +}; + +typedef QList ReASListOfVariants; +typedef QMap ReASMapOfVariants; + +class ReASListConstant : public ReASNode1, public ReASCalculable +{ +public: + ReASListConstant(); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); + +public: + virtual void dump(ReWriter& writer,int indent); + ReASVariant& value(); + ReASListOfVariants* list(); +private: + ReASVariant m_value; +}; +class ReASMapConstant : public ReASNode1, public ReASCalculable +{ +public: + ReASMapConstant(); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); +public: + virtual void dump(ReWriter& writer,int indent); + ReASVariant& value(); + ReASMapOfVariants* map(); +private: + ReASVariant m_value; +}; + +class ReSymbolSpace; +class ReASNamedValue : public ReASItem, public ReASStorable +{ + friend class ReASVarDefinition; +public: + 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, + /// the evaluation should be lazy + A_LAZY = 1<<4, + /// parameter of a method + A_PARAM = 1<<5, + /// a automatic variable in counted for loops: + A_LOOP = 1<<6 + }; + +public: + ReASNamedValue(ReASClass* clazz, ReSymbolSpace* space, + const QByteArray& name, int attributes); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); +public: + const QByteArray& name() const; + void setSymbolSpace(ReSymbolSpace* space, int variableNo); + void dump(ReWriter& writer, int indent); + ReSymbolSpace* symbolSpace() const; + int variableNo() const; + void setVariableNo(int variableNo); +protected: + QByteArray m_name; + int m_attributes; + ReSymbolSpace* m_symbolSpace; + int m_variableNo; +}; +class ReASConversion : public ReASNode1, public ReASCalculable { +public: + enum Conversion { + C_UNDEF, + C_INT_TO_FLOAT, + C_FLOAT_TO_INT, + C_BOOL_TO_INT, + C_BOOL_TO_FLOAT + }; + +public: + ReASConversion(ReASItem* expression); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); + virtual void dump(ReWriter& writer,int indent); +public: + static ReASConversion* tryConversion(ReASClass* expected, ReASItem* expr, + ReParser& parser, bool& isCorrect); + static Conversion findConversion(ReASClass* from, ReASClass* to); +private: + Conversion m_conversion; +}; + +class ReASIndexedValue : public ReASNode2, public ReASCalculable { +public: + ReASIndexedValue(); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); +public: + void dump(ReWriter& writer, int indent); +}; + +class ReASStatement +{ +public: + ReASStatement(); +public: + virtual int execute(ReVMThread& thread) = 0; +public: + static int executeStatementList(ReASItem* list, ReVMThread& thread); +}; + + +class ReASVarDefinition : public ReASNode3, public ReASStatement +{ +public: + ReASVarDefinition(); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); +public: + void dump(ReWriter& writer, int indent); + const QByteArray& name() const; + int endOfScope() const; + void setEndOfScope(int endOfScope); + ReASClass* clazz() const; +private: + /// the column of the blockend containing the definition. + /// if 0: end is end of method or end of class + /// Note: the source unit is stored in ReASItem::m_sourcePosition + int m_endOfScope; +}; + +class ReASExprStatement : public ReASNode2, public ReASStatement +{ +public: + ReASExprStatement(); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); +public: + void dump(ReWriter& writer, int indent); +}; + +class ReASUnaryOp : public ReASNode1, ReASCalculable +{ +public: + enum UnaryOp { + UOP_UNDEF, + UOP_PLUS, + UOP_MINUS_INT, + UOP_MINUS_FLOAT, + UOP_NOT_BOOL, + UOP_NOT_INT, + UOP_INC, + UOP_DEC + }; +public: + ReASUnaryOp(UnaryOp op, ReASItemType type); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); +public: + int getOperator() const; + void dump(ReWriter& writer, int indent); +public: + static const char* nameOfOp(UnaryOp op); +private: + UnaryOp m_operator; +}; +class ReASBinaryOp : public ReASNode2, public ReASCalculable +{ +public: + enum BinOperator { + BOP_UNDEF, + BOP_ASSIGN, + BOP_PLUS_ASSIGN, + BOP_MINUS_ASSIGN, + BOP_TIMES_ASSIGN, + BOP_DIV_ASSIGN, + BOP_MOD_ASSIGN, + BOP_POWER_ASSIGN, + BOP_LOG_OR_ASSIGN, + BOP_LOG_AND_ASSIGN, + BOP_LOG_XOR_ASSIGN, + BOP_BIT_OR_ASSIGN, + BOP_BIT_AND_ASSIGN, + BOP_BIT_XOR_ASSIGN, + BOP_LSHIFT_ASSIGN, + BOP_LOG_RSHIFT_ASSIGN, + BOP_ARTITH_RSHIFT_ASSIGN, + BOP_PLUS, + BOP_MINUS, + BOP_TIMES, + BOP_DIV, + BOP_MOD, + BOP_POWER, + BOP_LOG_OR, + BOP_LOG_AND, + BOP_LOG_XOR, + BOP_BIT_OR, + BOP_BIT_AND, + BOP_BIT_XOR, + BOP_LSHIFT, + BOP_LOG_RSHIFT, + BOP_ARTITH_RSHIFT, + BOP_EQ, + BOP_NE, + BOP_LE, + BOP_LT, + BOP_GE, + BOP_GT, + BOB_COUNT + }; +private: + inline bool isAssignment() const { + return m_operator >= BOP_ASSIGN + && m_operator <= BOP_ARTITH_RSHIFT_ASSIGN; + } +public: + ReASBinaryOp(); +public: + virtual void calc(ReVMThread& thread); + virtual bool check(ReParser& parser); +public: + BinOperator getOperator() const; + void setOperator(BinOperator op); + void dump(ReWriter& writer, int indent); +private: + void assign(ReVMThread& thread); +public: + static const char* nameOfOp(BinOperator op); +private: + BinOperator m_operator; +}; + +class ReASIf : public ReASNode4, public ReASStatement +{ +public: + ReASIf(); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); + virtual void dump(ReWriter& writer, int indent); +}; + +class ReASForIterated : public ReASNode4, public ReASStatement +{ +public: + ReASForIterated(ReASVarDefinition* variable); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); + virtual void dump(ReWriter& writer, int indent); +}; + +class ReASForCounted : public ReASNode6, public ReASStatement +{ +public: + ReASForCounted(ReASVarDefinition* variable); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); + virtual void dump(ReWriter& writer, int indent); +}; + +class ReASWhile : public ReASNode3, public ReASStatement +{ +public: + ReASWhile(); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); + virtual void dump(ReWriter& writer, int indent); +}; + +class ReASRepeat : public ReASNode3, public ReASStatement +{ +public: + ReASRepeat(); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); + virtual void dump(ReWriter& writer, int indent); +}; + +class ReASMethod; +class ReASMethodCall : public ReASNode3, public ReASStatement +{ +public: + ReASMethodCall(const QByteArray& name, ReASItem* parent); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); +public: + void dump(ReWriter& writer, int indent); + +public: + ReASMethod* method() const; + void setMethod(ReASMethod* method); + + ReASExprStatement* arg1() const; +private: + QByteArray m_name; + ReASMethod* m_method; +}; + +class RplParameter : ReASItem +{ +public: + RplParameter(); + virtual ~RplParameter(); +private: + QByteArray m_name; + ReASNamedValue* m_default; +}; + +class ReASField : public ReASNode1 +{ +public: + ReASField(const QByteArray& name); +public: + virtual bool check(ReParser& parser); +public: + void dump(ReWriter& writer, int indent); +private: + QByteArray m_name; +}; + + +class ReASClass; +class ReSymbolSpace; +class ReASMethod : public ReASNode2 +{ +public: + ReASMethod(const QByteArray& name, ReASTree& tree); +public: + virtual bool check(ReParser& parser); + virtual int execute(ReVMThread& thread); +public: + void dump(ReWriter& writer, int indent); + ReSymbolSpace* symbols() const; + void setSymbols(); + const QByteArray& name() const; + bool equalSignature(ReASMethod& other) const; + ReASMethod* sibling() const; + void setSibling(ReASMethod* sibling); + int getFirstParamWithDefault() const; + void setFirstParamWithDefault(int value); + +private: + QByteArray m_name; + ReASClass* m_resultType; + ReSymbolSpace* m_symbols; + // chain over all overloaded methods (same name, other signature): + ReASMethod* m_sibling; + ReASTree& m_tree; + // -1: no parameter with default value. >= 0: index of the first + int firstParamWithDefault; +}; + +class ReASClass { +public: + typedef QMap MethodMap; +public: + ReASClass(const QByteArray& name, ReASTree& m_tree); + virtual ~ReASClass(); +public: + /** + * @brief Creates a value object (used in ReASVariant). + * + * @param source NULL or a source to copy + * @return a new value object (specific for the class) + */ + virtual void* newValueInstance(void* source = NULL) const = 0; + /** + * @brief Destroys the given object. + * + * The object must be created by newValueInstance(). + * + * @param object object to destroy + */ + 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; + /** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength the maximum length of the result (string) + * @return a string describing the object + */ + virtual QByteArray toString(void *object, int maxLength = 80) const = 0; +public: + const QByteArray& name() const; + virtual void dump(ReWriter& writer, int indent); + void setSymbols(); +protected: + QByteArray m_name; + ReSymbolSpace* m_symbols; + const ReASClass* m_superClass; + ReASTree& m_tree; +}; + +#include "rplexpr/rplasclasses.hpp" + +#include "ReParser.hpp" +class ReSymbolSpace; +class ReASTree +{ +public: + enum { + DMP_NONE, + DMP_GLOBALS = 1<<1, + DMP_MODULES = 1<<2, + DMP_SPACE_STACK = 1<<3, + DMP_SPACE_HEAP = 1<<4, + DMP_ALL = DMP_GLOBALS | DMP_MODULES | DMP_SPACE_STACK | DMP_SPACE_HEAP, + DMP_NO_GLOBALS = DMP_MODULES | DMP_SPACE_STACK | DMP_SPACE_HEAP + }; + typedef QMap SymbolSpaceMap; + typedef QList SymbolSpaceStack; +public: + ReASTree(); + ~ReASTree(); +public: + bool startModule(ReSourceUnitName name); + void finishModule(ReSourceUnitName name); + ReSymbolSpace* startClassOrMethod(const QByteArray& name, + ReSymbolSpace::SymbolSpaceType type); + void finishClassOrMethod(const QByteArray& name); + SymbolSpaceStack& symbolSpaces(); + ReSymbolSpace* currentSpace() const; + ReASClass* findClass(const QByteArray& name); + void clear(); + void dump(const char* filename, int flags = DMP_ALL, + const char* header = NULL); + ReSymbolSpace*findmodule(const QByteArray& name); + ReSourcePosition* copyPosition(); + ReByteStorage& store(); + +protected: + void init(); + void destroy(); +private: + // the mother of all symbol spaces. + ReSymbolSpace* 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: + ReSymbolSpace* m_currentSpace; + // contain all ever built symbol spaces: + SymbolSpaceMap m_symbolSpaceHeap; + ReByteStorage m_store; +}; + +#endif // RPLASTREE_HPP diff --git a/expr/ReAsClasses.cpp b/expr/ReAsClasses.cpp new file mode 100644 index 0000000..2d607b5 --- /dev/null +++ b/expr/ReAsClasses.cpp @@ -0,0 +1,1186 @@ +/* + * 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. +*/ + +/** @file + * @brief Predefined classes of the virtual machine, e.g ReASInteger. + */ +/** @file rplexpr/rplasclasses.hpp + * + * @brief Definitions for predefined classes of the virtual machine. + */ + +#include "base/rebase.hpp" +#include "expr/ReExpr.hpp" + +ReASList* ReASList::m_instance = NULL; +ReASMap* ReASMap::m_instance = NULL; +ReASFloat* ReASFloat::m_instance = NULL; +ReASInteger* ReASInteger::m_instance = NULL; +ReASString* ReASString::m_instance = NULL; +ReASBoolean* ReASBoolean::m_instance = NULL; +ReASVoid* ReASVoid::m_instance = NULL; +ReASFormula* ReASFormula::m_instance = NULL; + +/** @class ReSymbolSpace rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a symbol space for the parser. + * + * 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, only for the global symbol space. + * + * @param name the symbol space's name + * @param parent the parent of the symbol space + */ +ReSymbolSpace::ReSymbolSpace(ReASTree& tree) : + m_type(SST_GLOBAL), + m_name("$global"), + m_variables(), + m_classes(), + m_parent(NULL), + m_body(NULL), + m_listOfVars(), + m_tree(tree) +{ +} +/** + * @brief Constructor. + * + * @param type the type of the symbol space: SST_MODULE, ... + * @param name the symbol space's name + * @param parent the parent of the symbol space + */ +ReSymbolSpace::ReSymbolSpace(ReSymbolSpace::SymbolSpaceType type, + const QByteArray& name, + ReSymbolSpace* parent) : + m_type(type), + m_name(name), + m_variables(), + m_classes(), + m_parent(parent), + m_body(NULL), + m_listOfVars(), + m_tree(parent->m_tree) +{ +} + + +/** + * @brief Destructor. + */ +ReSymbolSpace::~ReSymbolSpace() +{ + ClassMap::iterator it; + for (it = m_classes.begin(); it != m_classes.end(); it++){ + delete it.value(); + } + MethodMap::iterator it2; + for (it2 = m_methods.begin(); it2 != m_methods.end(); it2++){ + delete it2.value(); + } +} + +/** + * @brief Starts a scope. + * + * A scope is an "area" where variables can be defined. + * A variable "lives" in a scope. + * + * Saves the status to restore it in finishScope(). + * + * @param scope OUT: status info + */ +void ReSymbolSpace::startScope(ReASScope& scope) +{ + scope.m_varNoAtStart = m_listOfVars.size(); +} + +/** + * @brief Finishes a scope. + * + * Finishes the "live" of the variables created in the ending scope. + * + * @param endOfScope line (inside the current source unit) which finishes the + * scope + * @param scope the status of the scope at start. + */ +void ReSymbolSpace::finishScope(int endOfScope, ReASScope& scope) +{ + // in methods/classes not needed: + int ix = scope.m_varNoAtStart - scope.m_builtInVars; + int last = m_listOfVars.size(); + for (; ix < last; ix++){ + ReASVarDefinition* var = m_listOfVars[ix]; + var->setEndOfScope(endOfScope); + const QByteArray& name = var->name(); + if (m_variables.contains(name)) + m_variables.remove(name); + } +} + +/** + * @brief Search a variable in the symbol space. + * + * @param name variable to find + * + * @return NULL: not found
+ * otherwise: the variable + */ +ReASVarDefinition* ReSymbolSpace::findVariable(const QByteArray& name) const +{ + ReASVarDefinition* rc = NULL; + if (m_variables.contains(name)) + rc = m_variables[name]; + else if (m_parent != NULL) + rc = m_parent->findVariable(name); + return rc; +} + +/** + * @brief Search the class in the symbol space hierarchy. + * + * @param name Name of the class + * @return NULL: not found
+ * otherwise: the class + */ +ReASClass* ReSymbolSpace::findClass(const QByteArray& name) const +{ + ReASClass* rc = NULL; + if (m_classes.contains(name)) + rc = m_classes[name]; + else if (m_parent != NULL) + rc = m_parent->findClass(name); + return rc; +} + +/** + * @brief Find a method in the instance. + * + * @param name the method's name + * @return NULL: method not found + * otherwise: the method description + */ +ReASMethod* ReSymbolSpace::findMethod(const QByteArray& name) const +{ + ReASMethod* rc = NULL; + if (m_methods.contains(name)) + rc = m_methods[name]; + return rc; +} + +/** + * @brief Writes the content of the instance into a file. + * + * @param writer writes to output + * @param indent nesting level: so many tabs will be used as prefix + * @param header NULL or the headline + */ +void ReSymbolSpace::dump(ReWriter& writer, int indent, const char* header) +{ + if (header != NULL) + writer.writeLine(header); + writer.formatIndented(indent, "= %s (%s) parent: %s", m_name.constData(), + spaceTypeName(m_type), + m_parent == NULL ? "" : m_parent->name().constData()); + QList sorted; + if (m_classes.size() > 0){ + writer.writeIndented(indent, "== Classes:"); + sorted.reserve(m_classes.size()); + ClassMap::iterator it; + for (it = m_classes.begin(); it != m_classes.end(); it++){ + sorted.append(it.key()); + } + qSort(sorted.begin(), sorted.end(), qLess()); + QList::iterator it2; + for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ + ReASClass* clazz = m_classes[*it2]; + clazz->dump(writer, indent); + } + } + if (m_methods.size() > 0){ + writer.writeIndented(indent, "== Methods:"); + sorted.clear(); + sorted.reserve(m_variables.size()); + MethodMap::iterator it3; + for (it3 = m_methods.begin(); it3 != m_methods.end(); it3++){ + sorted.append(it3.key()); + } + qSort(sorted.begin(), sorted.end(), qLess()); + QList::iterator it4; + for (it4 = sorted.begin(); it4 != sorted.end(); it4++){ + ReASMethod* method = m_methods[*it4]; + do { + method->dump(writer, indent); + method = method->sibling(); + } while (method != NULL); + } + } + + if (m_listOfVars.size() > 0){ + writer.writeIndented(indent, "== Variables:"); + QList::iterator it6; + for (int ix = 0; ix < m_listOfVars.size(); ix++){ + ReASVarDefinition* var = m_listOfVars[ix]; + var->dump(writer, indent); + } + } + if (m_body != NULL){ + writer.writeIndented(indent, "== Body:"); + ReASNode1::dumpStatements(writer, indent, m_body); + } +} + +/** + * @brief Returns the name of a space type. + * + * @param type type to inspect + * @return the name of the type + */ +const char*ReSymbolSpace::spaceTypeName( + ReSymbolSpace::SymbolSpaceType type) +{ + const char* rc = NULL; + switch(type){ + case SST_UNDEF: + rc = "undef"; + break; + case SST_GLOBAL: + rc = "global"; + break; + case SST_MODULE: + rc = "module"; + break; + case SST_CLASS: + rc = "class"; + break; + case SST_METHOD: + rc = "method"; + break; + default: + rc = "?"; + break; + } + return rc; +} + +/** + * @brief Initilizes the global symbol space. + * + * @param tree the abstract syntax tree + * @return the global symbol space + */ +ReSymbolSpace* ReSymbolSpace::createGlobal(ReASTree& tree) +{ + ReSymbolSpace* rc = new ReSymbolSpace(tree); + rc->m_tree = tree; + ReASInteger::m_instance = new ReASInteger(tree); + rc->m_classes[ReASInteger::m_instance->name()] = ReASInteger::m_instance; + ReASBoolean::m_instance = new ReASBoolean(tree); + rc->m_classes[ReASBoolean::m_instance->name()] = ReASBoolean::m_instance; + ReASFloat::m_instance = new ReASFloat(tree); + rc->m_classes[ReASFloat::m_instance->name()] = ReASFloat::m_instance; + ReASString::m_instance = new ReASString(tree); + rc->m_classes[ReASString::m_instance->name()] = ReASString::m_instance; + ReASList::m_instance = new ReASList(tree); + rc->m_classes[ReASList::m_instance->name()] = ReASList::m_instance; + ReASMap::m_instance = new ReASMap(tree); + rc->m_classes[ReASMap::m_instance->name()] = ReASMap::m_instance; + ReASVoid::m_instance = new ReASVoid(tree); + rc->m_classes[ReASVoid::m_instance->name()] = ReASVoid::m_instance; + ReASFormula::m_instance = new ReASFormula(tree); + rc->m_classes[ReASFormula::m_instance->name()] = ReASFormula::m_instance; + return rc; +} +/** + * @brief Returns the list of the variables. + * + * @return the list of the variables + */ +ReSymbolSpace::VariableList ReSymbolSpace::listOfVars() const +{ + return m_listOfVars; +} + +/** + * @brief Returns the parent of the symbol space. + * + * @return the symbolspace of the object (module, method, class..) containing + * the object belonging to the instance + */ +ReSymbolSpace* ReSymbolSpace::parent() const +{ + return m_parent; +} + +/** + * @brief Returns the body (an abstract syntax tree) of the symbol space. + * + * @return NULL: no body available
+ * othewise: the body of the instance + */ +ReASItem* ReSymbolSpace::body() const +{ + return m_body; +} + +/** + * @brief Sets the body (an abstract syntax tree) of the symbol space. + * + * @param body the new body + */ +void ReSymbolSpace::setBody(ReASItem* body) +{ + m_body = body; +} + +/** + * @brief Adds a variable to the symbol space. + * + * @param variable the variable to add + * @param varNo OUT: variable number, current number in the symbol space + * @return NULL: success
+ * otherwise: the already defined variable/method + */ +ReASItem* ReSymbolSpace::addVariable(ReASVarDefinition* variable, int& varNo) +{ + ReASItem* rc = NULL; + const QByteArray& name = variable->name(); + if (m_variables.contains(name)) + rc = m_variables[name]; + else if (m_methods.contains(name)) + rc = m_methods[name]; + else { + m_variables[name] = variable; + varNo = m_listOfVars.size(); + m_listOfVars.append(variable); + } + return rc; +} + +/** + * @brief Adds a method to the symbol space. + * + * @param method the method to add + * @return NULL: success
+ * otherwise: the already defined variable/method + */ +ReASItem* ReSymbolSpace::addMethod(ReASMethod* method) +{ + ReASItem* rc = NULL; + const QByteArray& name = method->name(); + if (m_variables.contains(name)) + rc = m_variables[name]; + else if (! m_methods.contains(name)){ + m_methods[name] = method; + } else { + ReASMethod* first = m_methods[name]; + ReASMethod* oldMethod = first; + do { + if (oldMethod->equalSignature(*method)) + rc = oldMethod; + else + oldMethod = oldMethod->sibling(); + } while (rc == NULL && oldMethod != NULL); + if (rc == NULL){ + method->setChild(first); + m_methods[name] = method; + } + } + return rc; +} +/** + * @brief Adds a class to the instance. + * + * @param clazz the class to add + * @return NULL: success
+ * otherwise: the already defined class + */ +ReASUserClass* ReSymbolSpace::addClass(ReASUserClass* clazz) +{ + ReASUserClass* rc = NULL; + const QByteArray& name = clazz->name(); + if (m_classes.contains(name)){ + rc = dynamic_cast(m_classes[name]); + } else { + m_classes[name] = clazz; + } + return rc; +} + +/** + * @brief Returns the name of the symbol space. + * + * @return the name + */ +const QByteArray& ReSymbolSpace::name() const +{ + return m_name; +} + + +/** @class ReASBoolean rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a Boolean. + * + * A Boolean is a real value. + */ +/** + * @brief Constructor. + */ +ReASBoolean::ReASBoolean(ReASTree& tree) : + ReASClass("Bool", tree) +{ +} +/** + * @brief Creates a value object (used in ReASVariant). + * + * For Booleans nothing is to do! + * + * @param source NULL or a source to copy + * @return NULL + */ +void* ReASBoolean::newValueInstance(void*) const +{ + return NULL; +} + +/** + * @brief Destroys the given object. + * + * For Booleans nothing is to do! + * + * @param object object to destroy + */ +void ReASBoolean::destroyValueInstance(void*) const +{ +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * This method should never be called. + * + * @param object the object to test (with type QList*) + * @return false + */ +bool ReASBoolean::boolValueOf(void*) const +{ + return false; +} + +/** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength not used + * @return a string describing the object + */ +QByteArray ReASBoolean::toString(void* object, int) const +{ + return ((ReASVariant*) object)->asBool() ? "True" : "False"; +} + +/** @class ReASNumber rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a Boolean. + * + * A Boolean is one of the values true and false. + */ +/** + * @brief Constructor. + */ +ReASFloat::ReASFloat(ReASTree& tree) : + ReASClass("Float", tree) +{ +} + +ReASFloat::ReASFloat(const QByteArray& name, ReASTree& tree) : + ReASClass(name, tree) +{ + m_superClass = ReASFloat::m_instance; +} +/** + * @brief Creates a value object (used in ReASVariant). + * + * For Booleans nothing is to do! + * + * @param source NULL or a source to copy + * @return NULL + */ +void* ReASFloat::newValueInstance(void*) const +{ + return NULL; +} + +/** + * @brief Destroys the given object. + * + * For Booleans nothing is to do! + * + * @param object object to destroy + */ +void ReASFloat::destroyValueInstance(void*) 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 ReASFloat::boolValueOf(void*) const +{ + return false; +} + +/** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength not used + * @return a string describing the object + */ +QByteArray ReASFloat::toString(void* object, int) const +{ + char buffer[256]; + + qsnprintf(buffer, sizeof buffer, "%f", ((ReASVariant *) object)->asFloat()); + return buffer; +} + +/** @class ReASInteger 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. + */ +ReASInteger::ReASInteger(ReASTree& tree) : + ReASFloat("Int", tree) +{ +} + +/** + * @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 ReASInteger::boolValueOf(void*) const +{ + return false; +} + +/** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength the maximum length of the result + * @return a string describing the object + */ +QByteArray ReASInteger::toString(void* object, int maxLength) const +{ + char buffer[64]; + qsnprintf(buffer, sizeof buffer, "%.*d", maxLength, + ((ReASVariant *) object)->asInt()); + return buffer; +} + + +/** @class ReASString rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a string. + * + * A string is a mutable character sequence. + */ +/** + * @brief Constructor. + */ +ReASString::ReASString(ReASTree& tree) : + ReASClass("Str", tree) +{ +} +/** + * @brief Creates a value object (used in ReASVariant). + * + * @param source NULL or a source to copy + * @return a new value object (specific for the class) + */ +void* ReASString::newValueInstance(void* source) const +{ + QByteArray* rc = source == NULL ? new QByteArray() : new QByteArray(*(QByteArray*) source); + return (void*) rc; +} + +/** + * @brief Destroys the given object. + * + * The object must be created by newValueInstance(). + * + * @param object object to destroy + */ +void ReASString::destroyValueInstance(void* object) const +{ + delete (QByteArray*) object; +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * This method should never be called. + * + * @param object the object to test (a QByteArray* instance) + * @return false: the string is empty + * true: otherwise + */ +bool ReASString::boolValueOf(void* object) const +{ + bool rc = false; + if (object != NULL){ + QByteArray* string = static_cast(object); + if (string == NULL) + throw ReException("ReASString.boolValueOf(): not a string"); + rc = ! string->isEmpty(); + } + return rc; +} + +/** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength the maximum length of the result + * @return a string describing the object + */ +QByteArray ReASString::toString(void* object, int maxLength) const +{ + QByteArray rc; + QByteArray* string = reinterpret_cast (object); + int length = string->size(); + if (length + 2 > maxLength) + length = maxLength - 2; + rc.reserve(length); + rc += "'"; + if (string->size() < maxLength - 2) { + rc += *string; + } else { + rc += string->mid(0, maxLength - 2 - 3); + rc += "..."; + } + rc += "'"; + return rc; +} + +/** @class ReASList rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements the class of a list. + * + * A list is a container of any values. Values can be selected by the index. + */ +/** + * @brief Constructor. + */ +ReASList::ReASList(ReASTree& tree) : + ReASClass("List", tree) +{ +} + +/** + * @brief Creates a value object (used in ReASVariant). + * + * @param source NULL or a source to copy + * @return a new value object (specific for the class) + */ +void* ReASList::newValueInstance(void* source) const +{ + ReASListOfVariants* rc = new ReASListOfVariants(); + if (source != NULL){ + ReASListOfVariants* source2 = (ReASListOfVariants*) source; + rc->reserve(source2->size()); + ReASListOfVariants::iterator it; + for (it = source2->begin(); + it != source2->end(); + it++){ + // deleting in destroyValue(): + rc->append(new ReASVariant(*(*it))); + } + } + return (void*) rc; +} + +/** + * @brief Destroys the given object. + * + * The object must be created by newValueInstance(). + * + * @param object object to destroy + */ +void ReASList::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 ReASList::boolValueOf(void* object) const +{ + bool rc = false; + if (object != NULL){ + ReASListOfVariants* list = static_cast(object); + if (list == NULL) + throw ReException("ReASList.boolValueOf(): not a list"); + rc = ! list->empty(); + } + return rc; +} + +/** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength unused + * @return a string describing the object + */ +QByteArray ReASList::toString(void* object, int maxLength) const +{ + QByteArray rc; + rc.reserve(maxLength); + rc += "["; + ReASListOfVariants* list = reinterpret_cast(object); + ReASListOfVariants::iterator it; + bool first = true; + for(it = list->begin(); it != list->end(); it++){ + if (first) + first = false; + else + rc += ","; + QByteArray part = (*it)->toString(maxLength - rc.size() - 5); + if (maxLength - rc.size() - 5 - part.size() <= 0){ + rc += "..."; + break; + } else { + rc += part; + } + + } + rc += "]"; + return rc; +} + +/** @class ReASMap 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. + */ +ReASMap::ReASMap(ReASTree& tree) : + ReASClass("Map", tree) +{ +} +/** + * @brief Creates a value object (used in ReASVariant). + * + * @param source NULL or a source to copy + * @return a new value object (specific for the class) + */ +void* ReASMap::newValueInstance(void* source) const +{ + ReASMapOfVariants* rc = new ReASMapOfVariants(); + if (source != NULL){ + ReASMapOfVariants* source2 = + static_cast(source); + // rc->reserve(source2->size()); + ReASMapOfVariants::iterator it; + for (it = source2->begin(); it != source2->end(); it++){ + // deleting in destroyValue(): + const QByteArray& key = it.key(); + ReASVariant* value = new ReASVariant(*it.value()); + (*rc)[key] = value; + } + } + return (void*) rc; +} + +/** + * @brief Destroys the given object. + * + * The object must be created by newValueInstance(). + * + * @param object object to destroy + */ +void ReASMap::destroyValueInstance(void* object) const +{ + delete (ReASMapOfVariants*) object; +} + +/** + * @brief Calculates the boolean value of an class specific object. + * + * @param object the object to test (with type QMap*) + * @return + */ +bool ReASMap::boolValueOf(void* object) const +{ + bool rc = false; + if (object != NULL){ + ReASMapOfVariants* map = reinterpret_cast(object); + if (map == NULL) + throw ReException("ReASMap.boolValueOf(): not a map"); + rc = map->empty() > 0; + } + return rc; +} + +/** + * @brief Returns a string representation of an instance. + * + * @param object the object to convert + * @param maxLength maximal length of the result + * @return a string describing the object + */ +QByteArray ReASMap::toString(void* object, int maxLength) const +{ + QByteArray rc; + rc.reserve(maxLength); + rc += "["; + ReASMapOfVariants* map = reinterpret_cast(object); + ReASMapOfVariants::iterator it; + bool first = true; + for(it = map->begin(); it != map->end(); it++){ + if (first) + first = false; + else + rc += ","; + if (maxLength - rc.size() - 5 - 2 - it.key().size() <= 0){ + rc += "..."; + break; + } else { + rc += "'"; + rc += it.key(); + rc += "':"; + } + QByteArray part = it.value()->toString(maxLength - rc.size() - 5); + if (maxLength - rc.size() - 5 - part.size() <= 0){ + rc += "..."; + break; + } else { + rc += part; + } + + } + rc += "}"; + return rc; +} + + +/** @class ReVariable rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a variable of a symbol space. + */ + +/** + * @brief Constructor. + */ +ReVariable::ReVariable(const QByteArray& name) : + m_name(name), + m_namespace(NULL), + m_value(), + m_type(NULL) +{ + +} + +/** + * @brief Writes the content of the instance into an output media. + * + * @param writer writes to output + * @param indent nesting level: so many tabs will be used as prefix + */ +void ReVariable::dump(ReWriter& writer, int indent) +{ + const char* name1 = m_type == NULL ? "NoneType" : m_type->name().constData(); + QByteArray val = m_value.toString(); + writer.formatIndented(indent, "%s %s: value: %s", + name1, m_name.constData(), val.constData()); +} +/** + * @brief Returns the data type of the variable. + * + * @return the class of the variable + */ +ReASClass* ReVariable::type() const +{ + return m_type; +} + +/** + * @brief Sets the data type. + * @param type the class of the variable + */ +void ReVariable::setType(ReASClass* type) +{ + m_type = type; +} +/** + * @brief Returns the name of the variable. + * + * @return the name + */ +const QByteArray& ReVariable::name() const +{ + return m_name; +} + +/** @class ReVariable rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a data type representing a none type. + */ +ReASVoid::ReASVoid(ReASTree& tree) : + ReASClass("Void", tree) +{ +} + +/** + * @brief Instantiates a new object. + * + * In this case we do nothing. + * + * @param source ignored + * @return + */ +void*ReASVoid::newValueInstance(void*) const +{ + return NULL; +} + +/** + * @brief Destroys an object created by newValueInstance. + * + * In this case we do nothing. + * + * @param object not used + */ +void ReASVoid::destroyValueInstance(void*) const +{ +} + +/** + * @brief Returns the bool value of the given object + * @param object ignored + * @return false + */ +bool ReASVoid::boolValueOf(void*) const +{ + return false; +} + +/** + * @brief Converts the object into a string. + * + * @param object ignored + * @param maxLength ignored + * @return the empty string + */ +QByteArray ReASVoid::toString(void*, int) const +{ + return QByteArray(""); +} + +/** @class ReASFormula rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a data type representing a calculated value. + */ + +/** + * @brief Constructor. + * + * @param tree the abstract syntax tree + */ +ReASFormula::ReASFormula(ReASTree& tree) : + ReASClass("Formula", tree) +{ +} + +/** + * @brief Instantiates a new object. + * + * In this case we do nothing. + * + * @param expr the result + * @return + */ +void* ReASFormula::newValueInstance(void* expr) const +{ + return expr; +} + +/** + * @brief Destroys an object created by newValueInstance. + * + * In this case we do nothing. + * + * @param object not used + */ +void ReASFormula::destroyValueInstance(void*) const +{ +} + +/** + * @brief Returns the bool value of the given object + * @param object ignored + * @return false + */ +bool ReASFormula::boolValueOf(void*) const +{ + return false; +} + +/** + * @brief Converts the object into a string. + * + * @param object ignored + * @param maxLength ignored + * @return the empty string + */ +QByteArray ReASFormula::toString(void* object, int) const +{ + ReASExprStatement* expr = static_cast(object); + + char buffer[64]; + qsnprintf(buffer, sizeof buffer, "", expr->id()); + return buffer; +} + +/** @class ReASUserClass rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements a data type representing an user defined class. + */ + + +/** + * @brief Constructor. + * + * @param name name of the user defined class + * @param position the position of the class definition + * @param tree the abstract syntax tree + */ +ReASUserClass::ReASUserClass(const QByteArray& name, + const ReSourcePosition* position, + ReASTree& tree) : + ReASClass(name, tree), + m_position(position) +{ +} + +/** + * @brief Creates an instance of an user defined class. + * + * @param source the type (user defined class) of the result + * @return an instance of an user defined class + */ +void*ReASUserClass::newValueInstance(void* source) const +{ + ReASUserClass* clazz = static_cast(source); + ReASUserObject* rc = new ReASUserObject(clazz); + return static_cast(rc); +} + +void ReASUserClass::destroyValueInstance(void* object) const +{ + ReASUserObject* obj = static_cast(object); + delete obj; +} + +/** + * @brief Returns the bool value of the object. + * + * @param object object to test + * @return true: object != NULL
+ * false: object == NULL + */ +bool ReASUserClass::boolValueOf(void* object) const +{ + return object != NULL; +} + +/** + * @brief Returns a string representation an instance of a user defined class. + * + * @param object object to convert + * @param maxLength maximum length of the string + * @return + */ +QByteArray ReASUserClass::toString(void*, int) const +{ + return m_name; +} +/** + * @brief Returns the source position of the instance. + * + * @return the source position + */ +const ReSourcePosition* ReASUserClass::position() const +{ + return m_position; +} + + + +/** @class ReASUserObject rplastree.hpp "rplexpr/rplastree.hpp" + * + * @brief Implements an instance of an user defined class. + */ +ReASUserObject::ReASUserObject(ReASUserClass* clazz) : + m_class(clazz), + m_fields(NULL) +{ +} + +/** + * @brief Destructor. + */ +ReASUserObject::~ReASUserObject() +{ + delete[] m_fields; + m_fields = NULL; +} diff --git a/expr/ReAsClasses.hpp b/expr/ReAsClasses.hpp new file mode 100644 index 0000000..53f3f90 --- /dev/null +++ b/expr/ReAsClasses.hpp @@ -0,0 +1,214 @@ +/* + * 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 RPLASCLASSES_HPP +#define RPLASCLASSES_HPP + +class ReSymbolSpace; +class ReVariable { +public: + ReVariable(const QByteArray& name); +public: + void dump(ReWriter& writer, int indent); + + ReASClass* type() const; + void setType(ReASClass* type); + const QByteArray& name() const; + +protected: + QByteArray m_name; + // NULL for "simple" variables (int, float, bool) + ReSymbolSpace* m_namespace; + ReASVariant m_value; + ReASClass* m_type; +}; + +class ReASScope +{ +public: + int m_builtInVars; + int m_varNoAtStart; +}; + +class ReASUserClass; +class ReASTree; +class ReSymbolSpace +{ +public: + enum SymbolSpaceType { + SST_UNDEF, + SST_GLOBAL, + SST_MODULE, + SST_CLASS, + SST_METHOD + }; + +public: + typedef QMap VariableMap; + typedef QMap ClassMap; + typedef QMap MethodMap; + typedef QList VariableList; +private: + ReSymbolSpace(ReASTree& tree); +public: + ReSymbolSpace(SymbolSpaceType type, const QByteArray& name, + ReSymbolSpace* parent); + virtual ~ReSymbolSpace(); +public: + void startScope(ReASScope& scope); + void finishScope(int endOfScope, ReASScope& scope); + ReASVarDefinition* findVariable(const QByteArray& name) const; + ReASClass* findClass(const QByteArray& name) const; + ReASMethod* findMethod(const QByteArray& name) const; + void dump(ReWriter& writer, int indent, const char* header = NULL); + const QByteArray& name() const; + ReASItem* body() const; + void setBody(ReASItem* body); + ReASItem* addVariable(ReASVarDefinition* variable, int& varNo); + ReASItem* addMethod(ReASMethod* method); + ReASUserClass* addClass(ReASUserClass* clazz); + ReSymbolSpace* parent() const; + VariableList listOfVars() const; +public: + static const char* spaceTypeName(SymbolSpaceType type); + static ReSymbolSpace* createGlobal(ReASTree& tree); +private: + SymbolSpaceType m_type; + QByteArray m_name; + VariableMap m_variables; + ClassMap m_classes; + MethodMap m_methods; + ReSymbolSpace* m_parent; + ReASItem* m_body; + VariableList m_listOfVars; + ReASTree& m_tree; +}; + +class ReASBoolean : public ReASClass { +public: + ReASBoolean(ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASBoolean* m_instance; +}; + +class ReASFloat : public ReASClass { +public: + ReASFloat(ReASTree& tree); + ReASFloat(const QByteArray& name, ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASFloat* m_instance; +}; + +class ReASInteger : public ReASFloat { +public: + ReASInteger(ReASTree& tree); +public: + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASInteger* m_instance; +}; + +class ReASString : public ReASClass { +public: + ReASString(ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASString* m_instance; +}; + +class ReASList : public ReASClass { +public: + ReASList(ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASList* m_instance; +}; + +class ReASMap : public ReASClass { +public: + ReASMap(ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASMap* m_instance; +}; + +class ReASVoid : public ReASClass { +public: + ReASVoid(ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASVoid* m_instance; +}; + +class ReASFormula : public ReASClass { +public: + ReASFormula(ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; +public: + static ReASFormula* m_instance; +}; + +class ReASUserClass : public ReASClass { +public: + ReASUserClass(const QByteArray& name, const ReSourcePosition* position, + ReASTree& tree); +public: + void* newValueInstance(void* source = NULL) const; + void destroyValueInstance(void* object) const; + virtual bool boolValueOf(void* object) const; + virtual QByteArray toString(void *object, int maxLength = 80) const; + const ReSourcePosition* position() const; + +private: + const ReSourcePosition* m_position; +}; + +class ReASUserObject { +public: + ReASUserObject(ReASUserClass* clazz); + ~ReASUserObject(); +public: + void callMember(); +private: + ReASUserClass* m_class; + ReASVariant* m_fields; +}; + +#endif // RPLASCLASSES_HPP diff --git a/expr/ReLexer.cpp b/expr/ReLexer.cpp new file mode 100644 index 0000000..986ebc6 --- /dev/null +++ b/expr/ReLexer.cpp @@ -0,0 +1,1234 @@ +/* + * 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. +*/ +/** @file + * @brief Configurable scanner, which separates syntactic symbols from an input media. + */ +/** @file expr/ReLexer.hpp + * @brief Definitions for a configurable lexical analyser. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" + +#define CHAR_INFO_SIZE (int(sizeof m_charInfo / sizeof m_charInfo[0])) + + +/** @class ReToken ReLexer.hpp "expr/ReLexer.hpp" + * + * @brief Implements specific exception for the lexer. + * + */ + +/** + * @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. + */ +ReLexException::ReLexException(const ReSourcePosition& position, + const char* format, ...) : + ReException("") +{ + char buffer[64000]; + m_message = position.toString().toUtf8(); + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + m_message += buffer; +} + +/** @class ReToken ReLexer.hpp "expr/ReLexer.hpp" + * + * @brief Implements a token which is the smallest unit for a parser. + * + */ +/** + * @brief Constructor. + * @param type token type + */ +ReToken::ReToken(RplTokenType type) : + m_tokenType(type), + m_string(), + m_printableString() + // m_value +{ + memset(&m_value, 0, sizeof m_value); +} + +/** + * @brief Destructor. + */ +ReToken::~ReToken() +{ +} +/** + * @brief Copy constructor. + * + * @param source source to copy + */ +ReToken::ReToken(const ReToken& source) : + m_tokenType(source.m_tokenType), + m_string(source.m_string), + m_printableString(source.m_printableString), + m_value(source.m_value) +{ +} +/** + * @brief Assignment operator. + * + * @param source source to copy + * @return + */ +ReToken& ReToken::operator =(const ReToken& source) +{ + m_tokenType = source.m_tokenType; + m_string = source.m_string; + m_value = source.m_value; + return *this; +} + +/** + * @brief Returns the string representation of the instance + * @return a string representing the instance + */ +const QByteArray& ReToken::toString() +{ + return m_string; +} + +/** + * @brief Returns the integer value of the token + * + * Only relevant if a TOKEN_NUMBER. + * + * @return the value of the token as integer + */ +int ReToken::asInteger() const +{ + return (int) m_value.m_integer; +} + +/** + * @brief Returns the integer value of the token + * + * Only relevant if a TOKEN_NUMBER. + * + * @return the value of the token as unsigned integer (64 bit) + */ +quint64 ReToken::asUInt64() const +{ + return m_value.m_integer; +} + +/** + * @brief Returns the floating point value of the token + * + * Only relevant if a TOKEN_REAL. + * + * @return the value of the token as floating point value + */ +qreal ReToken::asReal() const +{ + return m_value.m_real; +} + +/** + * @brief Returns the floating point value of the token + * + * Only relevant if a TOKEN_NUMBER. + * + * @return the value of the token as floating point value + */ +const QByteArray& ReToken::rawString() const +{ + return m_printableString; +} +/** + * @brief Returns the id of the token. + * + * Ids are more handy than string, e.g. allowing switch statements. + * + * Only relevant for TOKEN_KEYWORD and TOKEN_OPERATOR. + * + * @return the id of the token + */ +int ReToken::id() const +{ + return m_value.m_id; +} +/** + * @brief Returns the token type. + * @return the token type + */ +RplTokenType ReToken::tokenType() const +{ + return m_tokenType; +} + +/** + * @brief Checks whether the instance has a given token type. + * + * @param expected the token type to compare + * + * @return true: the expected type is the current
+ * false: otherwise + */ +bool ReToken::isTokenType(RplTokenType expected) const +{ + return m_tokenType == expected; +} + +/** + * @brief Checks whether the instance is a given operator. + * + * @param expected the expected operator + * @param alternative 0 or a second possibility + * + * @return true: the instance is an operator and the expected or the alternative
+ * false: otherwise + */ +bool ReToken::isOperator(int expected, int alternative) const +{ + return m_tokenType == TOKEN_OPERATOR && (m_value.m_id == expected + || m_value.m_id == alternative); +} + +/** + * @brief Checks whether the instance is a given keyword. + * + * @param expected the expected keyword + * @param alternative 0 or a second possibility + * + * @return true: the instance is a keyword and the expected or the alternative
+ * false: otherwise + */ + +bool ReToken::isKeyword(int expected, int alternative) const +{ + return m_tokenType == TOKEN_KEYWORD && (m_value.m_id == expected + || m_value.m_id == alternative); +} + +/** + * @brief Makes all members undefined. + */ +void ReToken::clear() +{ + m_string.clear(); + m_printableString.clear(); + m_tokenType = TOKEN_UNDEF; + m_value.m_integer = 0; +} + +/** + * @brief Returns whether the token is a capitalized id + * + * @return true: the token is an id and the first char is an upper case char
+ * false: otherwise + */ +bool ReToken::isCapitalizedId() const +{ + bool rc = m_tokenType == TOKEN_ID && isupper(m_string.at(0)) + && (m_string.length() == 1 || islower(m_string.at(1))); + return rc; +} + +/** + * @brief Returns the description of the current token. + * + * @return a description of the instance + */ +QByteArray ReToken::dump() const +{ + QByteArray rc; + rc = nameOfType(m_tokenType); + rc.append(": ").append(this->asUtf8()); + return rc; +} +QByteArray ReToken::asUtf8() const +{ + char buffer[4096]; + buffer[0] = '\0'; + + switch(m_tokenType){ + case TOKEN_UNDEF: + break; + case TOKEN_STRING: + qsnprintf(buffer, sizeof buffer, "'%.*s'", int(sizeof buffer) - 1, + m_printableString.constData()); + break; + case TOKEN_NUMBER: + qsnprintf(buffer, sizeof buffer, "%lld", m_value.m_integer); + break; + case TOKEN_REAL: + qsnprintf(buffer, sizeof buffer, "%f", m_value.m_real); + break; + case TOKEN_KEYWORD: + case TOKEN_OPERATOR: + qsnprintf(buffer, sizeof buffer, "%lld", (int) m_value.m_id); + break; + case TOKEN_ID: + qsnprintf(buffer, sizeof buffer, "'%.*s'", int(sizeof buffer) - 1, + m_string.constData()); + break; + case TOKEN_COMMENT_REST_OF_LINE: + case TOKEN_COMMENT_START: + case TOKEN_COMMENT_END: + case TOKEN_SPACE: + case TOKEN_END_OF_SOURCE: + default: + break; + } + return buffer; +} +/** + * @brief Returns then name of a token type. + * @param type the type to convert + * @return the token type name + */ +const char* ReToken::nameOfType(RplTokenType type) +{ + const char* rc = "?"; + + switch(type){ + case TOKEN_UNDEF: + rc = "undef"; + break; + case TOKEN_STRING: + rc = "String"; + break; + case TOKEN_NUMBER: + rc = "Number"; + break; + case TOKEN_REAL: + rc = "Real"; + break; + case TOKEN_KEYWORD: + rc = "Keyword"; + break; + case TOKEN_OPERATOR: + rc = "Operator"; + break; + case TOKEN_ID: + rc = "Id"; + break; + case TOKEN_COMMENT_REST_OF_LINE: + rc = "Comment-1-line"; + break; + case TOKEN_COMMENT_START: + rc = "Comment-m-line"; + break; + case TOKEN_COMMENT_END: + rc = "end of comment"; + break; + case TOKEN_SPACE: + rc = "space"; + break; + case TOKEN_END_OF_SOURCE: + rc = "end of source"; + break; + default: + break; + } + return rc; +} + + +/** @class ReLexer ReLexer.hpp "expr/ReLexer.hpp" + * + * @brief Implements a lexical analyser. + * + * A lexical analyser reads a text source and separates the tokens for a parser. + * Tokens are the smallest elements of a parsing process. + * + */ + +static void itemsToVector(const char* items, ReLexer::StringList& vector, + int firstCharFlag, int secondCharFlag, + int thirdCharFlag, int restCharFlag, + int charInfo[]) +{ + QByteArray array2(items); + QList list = array2.split(' '); + QList::iterator it; + int id = 0; + for (it = list.begin(); it < list.end(); it++){ + QByteArray& item2 = *it; + QByteArray item(item2); + id++; + item.append(' ').append(id % 256).append(id / 256); + vector.append(item); + unsigned char cc = item2.at(0); + if (cc < 128) + charInfo[cc] |= firstCharFlag; + if(item2.size() > 1){ + cc = item2.at(1); + if (cc < 128) + charInfo[cc] |= secondCharFlag; + } + if(item2.size() > 2){ + cc = item2.at(2); + if (cc < 128) + charInfo[cc] |= thirdCharFlag; + } + if(item2.size() > 3){ + const char* ptr = item2.constData() + 3; + while( (cc = *ptr++) != '\0'){ + if (cc < 128) + charInfo[cc] |= restCharFlag; + } + } + } + qSort(vector.begin(), vector.end(), qLess()); +} + +static void charClassToCharInfo(const char* charClass, int flag, + int charInfo[]) +{ + for (int ix = 0; charClass[ix] != '\0'; ix++){ + unsigned char cc = (unsigned char) charClass[ix]; + if (cc < 128) + charInfo[cc] |= flag; + if (charClass[ix+1] == '-'){ + unsigned char ubound = charClass[ix+2]; + if (ubound == '\0') + charInfo['-'] |= flag; + else if (cc >= ubound) + throw new ReException("wrong character class range: %c-%c (%s)", + cc, ubound, charClass); + else { + for (int ii = cc + 1; ii <= ubound; ii++){ + charInfo[ii] |= flag; + } + } + ix += 2; + } + } +} + +/** + * @brief Constructor. + * + * @param source the input source handler + * @param keywords a string with all keywords delimited by " ". + * Example: "if then else fi while do done" + * @param operators a string with the operators separated by blank or "\n". + * "\n" separates the operators with the same priority. + * Lower position means lower priority + * @param rightAssociatives + * the operators which are right associative + * @param comments a string with pairs of comment begin and end delimited + * by " ". The comment end can be "\n" for line end. + * Example: "/ * * / // \n" (ignore the blank in "* /") + * @param firstCharsId string with the characters which are allowed as first + * characters of an id + * @param restCharsId string with the characters which are allowed as non + * first characters of an id + * @param numericTypes bit mask of allowed numeric, + * e.g. NUMTYPE_DECIMAL | NUMTYPE_HEXADECIMAL + * @param stringFeatures bit mask of the string features, + * e.g. SF_QUOTE | SF_TICK + * @param storageFlags describes the things which should be stored, e.g. + * S_ORG_STRINGS | S_COMMENTS | S_BLANKS + */ +ReLexer::ReLexer(ReSource* source, + const char* keywords, + const char* operators, const char* rightAssociatives, + const char* comments, + const char* firstCharsId, const char* restCharsId, int numericTypes, + int stringFeatures, int storageFlags) : + m_source(source), + m_keywords(), + m_operators(), + m_commentStarts(), + m_commentEnds(), + //m_charInfo() + m_idFirstRare(), + m_idRestRare(), + m_numericTypes(numericTypes), + m_idRest2(), + m_currentToken(&m_token1), + m_waitingToken(NULL), + m_waitingToken2(NULL), + m_token1(TOKEN_UNDEF), + m_token2(TOKEN_UNDEF), + m_currentPosition(NULL), + m_waitingPosition1(NULL), + m_waitingPosition2(NULL), + m_maxTokenLength(64), + m_input(), + m_currentCol(0), + m_hasMoreInput(false), + m_stringFeatures(stringFeatures), + m_storageFlags(storageFlags), + // m_prioOfOp + // m_assocOfOp + #if defined (RPL_LEXER_TRACE) + m_trace(true), + #endif + m_opNames() +{ + memset(m_prioOfOp, 0, sizeof m_prioOfOp); + memset(m_assocOfOp, 0, sizeof m_assocOfOp); + + memset(m_charInfo, 0, sizeof m_charInfo); + itemsToVector(keywords, m_keywords, CC_FIRST_KEYWORD, CC_2nd_KEYWORD, + CC_3rd_KEYWORD, CC_REST_KEYWORD, m_charInfo); + prepareOperators(operators, rightAssociatives); + charClassToCharInfo(firstCharsId, CC_FIRST_ID, m_charInfo); + charClassToCharInfo(restCharsId, CC_REST_ID, m_charInfo); + initializeComments(comments); + m_input.reserve(m_maxTokenLength*2); +} +/** + * @brief Destructor. + */ +ReLexer::~ReLexer() +{ +} + +/** + * @brief Returns the count of blanks in a given range of a string. + * + * @param start pointer to the first char to check + * @param end pointer to the last char to check + * @return the count of blanks + */ +int countBlanks(const char* start, const char* end){ + int rc = 0; + while(start != end){ + if (*start++ == ' '){ + rc++; + } + } + return rc; +} + +/** + * @brief Stores the operators in the internal members + * + * @param operators a string with the operators separated by blank or '\n'. + * '\n' separates the operators with the same priority. + * Lower position means lower priority + */ +void ReLexer::prepareOperators(const char* operators, + const char* rightAssociatives){ + QByteArray op2(operators); + QByteArray rightAssociatives2(" "); + rightAssociatives2 += rightAssociatives; + op2.replace("\n", " "); + itemsToVector(op2.constData(), m_operators, CC_FIRST_OP, CC_2nd_OP, + CC_3rd_OP, CC_REST_OP, m_charInfo); + // m_operators is now sorted: + // test whether the successor of 1 char operators is starting with this char: + // if not this operator will be marked with CC_OP_1_ONLY: + for (int ix = 0; ix < m_operators.size() - 1; ix++){ + // the entries of m_operators end with ' ' and id: + if (m_operators.at(ix).size() == 1 + 2 + && m_operators.at(ix).at(0) != m_operators.at(ix+1).at(0)){ + int cc = (char) m_operators[ix].at(0); + m_charInfo[cc] |= CC_OP_1_ONLY; + } + + } + m_opNames.reserve(m_operators.size() + 1); + op2 = " " + op2; + m_opNames = op2.split(' '); + QByteArray rAssoc = QByteArray(" ") + rightAssociatives + " "; + for (int opId = m_opNames.size() - 1; opId >= 1; opId--){ + QByteArray item = " " + m_opNames[opId] + " "; + if (rAssoc.indexOf(item) >= 0) + m_assocOfOp[opId] = true; + } + const char* start = operators; + const char* end; + int prio = 0; + int endId = 0; + int startId = 1; + bool again = true; + while (again){ + if ( (end = strchr(start, '\n')) == NULL){ + end = strchr(start, '\0'); + again = false; + } + prio++; + endId = startId + countBlanks(start, end) + 1 - 1; + while(startId <= endId){ + m_prioOfOp[startId++] = prio; + } + start = end + 1; + } +} + +void ReLexer::initializeComments(const char* comments) +{ + if (comments != NULL) + { + QByteArray starters; + QByteArray comments2(comments); + int ix = comments2.indexOf(" "); + if (ix >= 0) + throw ReException("more than one blank between comment pair(s): col %d %s", + ix + 1, comments + ix); + // the index of m_commentEnds is the position number: we need a dummy entry: + m_commentEnds.append(""); + + QList items = comments2.split(' '); + QList::iterator it; + ix = 0; + for (it = items.begin(); it != items.end(); it++, ix++){ + if (ix % 2 == 0){ + if (ix > 0) + starters += " "; + starters += *it; + }else{ + m_commentEnds.append(*it); + } + } + if (ix % 2 != 0) + throw ReException("not only pairs in the comment list"); + itemsToVector(starters, m_commentStarts, CC_FIRST_COMMENT_START, + CC_2nd_COMMENT_START, CC_3rd_COMMENT_START, CC_REST_COMMENT_START, + m_charInfo); + } +} +/** + * @brief Searches the prefix of m_input in the vector. + * + * @param tokenLength the length of the prefix in m_input + * @param vector the vector to search. Each element contains the id + * as last entry + * @param id the id of the entry in the vector. Only set if found + * @return + */ +int ReLexer::findInVector(int tokenLength, const StringList& vector) +{ + int id = 0; + int lbound = 0; + int ubound = vector.size() - 1; + // binary search over the sorted vector: + while(lbound <= ubound){ + int half = (ubound + lbound) / 2; + int compareRc = 0; + int ix = 0; + const QByteArray& current = vector[half]; + // vector items end with ' ' and id (2 byte): + int currentLength = current.size() - 3; + while(ix < tokenLength && compareRc == 0){ + if (ix >= currentLength) + // current is shorter: + compareRc = 1; + else + compareRc = m_input.at(ix) - (int) current.at(ix); + ix++; + } + if (compareRc == 0 && current.at(ix) != ' ') + // token.size() < current.size(): + compareRc = -1; + if (compareRc < 0) + ubound = half - 1; + else if (compareRc > 0) + lbound = half + 1; + else { + id = current[currentLength + 1] + current[currentLength + 2] * 256; + break; + } + } + return id; +} +/** + * @brief Reads data until enough data are available for one token. + * + * Data will be read from the current source unit. + * If this unit does not contain more data the next source unit from the stack + * will be used until the stack is empty. + * + * @return false: no more input is available
+ * true: data are available + */ +bool ReLexer::fillInput() +{ + if (m_hasMoreInput){ + if (m_input.size() < m_maxTokenLength){ + m_source->currentReader()->fillBuffer(m_maxTokenLength, m_input, + m_hasMoreInput); + } + } + + while (m_input.size() == 0 && m_source->currentReader() != NULL){ + if (m_source->currentReader()->nextLine(m_maxTokenLength, + m_input, m_hasMoreInput)){ + m_currentCol = 0; + } + } + return m_input.size() > 0; +} + +/** + * @brief Finds a token with an id: TOKEN_OP, TOKEN_KEYWORD, ... + * + * @post the token is removed from the input + * + * @param tokenType the token type + * @param flag2 the flag of the 2nd char + * @param names the vector with the names, sorted + * @return NULL: not found
+ * otherwise: the token + */ +ReToken* ReLexer::findTokenWithId(RplTokenType tokenType, int flag2, + StringList& names) +{ + int length = 1; + int inputLength = m_input.size(); + int cc; + if (inputLength > 1){ + cc = m_input[1]; + if (cc < CHAR_INFO_SIZE && (m_charInfo[cc] & flag2)){ + length++; + if (inputLength > 2){ + cc = m_input[2]; + // the 3rd char flag is the "successor" of the 2nd char flag: + int flag = (flag2 << 1); + if (cc < CHAR_INFO_SIZE && (m_charInfo[cc] & flag)){ + length++; + // the rest char flag is the "successor" of the 3nd char flag: + flag <<= 1; + while (length < inputLength){ + cc = m_input[length]; + if (cc < CHAR_INFO_SIZE && (m_charInfo[cc] & flag)) + length++; + else + break; + } + } + } + } + } + ReToken* rc = NULL; + if (! (tokenType == TOKEN_KEYWORD && length < inputLength + && (cc = m_input[length]) < CHAR_INFO_SIZE + && (m_charInfo[cc] & CC_REST_ID))) { + int id; + // the length could be too long: the CC_2nd_.. flag could be ambigous + while( (id = findInVector(length, names)) <= 0){ + if (length == 1 || tokenType == TOKEN_KEYWORD){ + break; + } + length--; + } + if (id > 0){ + rc = m_currentToken; + rc->m_tokenType = tokenType; + rc->m_value.m_id = id; + if (tokenType == TOKEN_COMMENT_START + && (m_storageFlags & STORE_COMMENT) != 0) + rc->m_string.append(m_input.mid(0, length)); + m_input.remove(0, length); + m_currentCol += length; + } + } + return rc; +} + +/** + * @brief Converts the number into a token. + * + * @return the token with the number + */ +ReToken* ReLexer::scanNumber() +{ + int inputLength = m_input.size(); + int cc; + int length; + quint64 value = 0; + if ( (cc = m_input[0]) == '0' && inputLength > 1 + && (m_numericTypes & NUMTYPE_HEXADECIMAL) + && (m_input[1] == 'x' || m_input[1] == 'X')){ + length = ReStringUtil::lengthOfUInt64(m_input.constData() + 2, 16, &value); + if (length > 0) + length += 2; + else + throw ReException("invalid hexadecimal number: no digit behind 'x"); + } else if (cc == '0' && (m_numericTypes & NUMTYPE_OCTAL) + && inputLength > 1){ + length = 1; + while (length < inputLength){ + if ( (cc = m_input[length]) >= '0' && cc <= '7') + value = value * 8 + cc - '0'; + else if (cc >= '8' && cc <= '9') + throw ReLexException(*m_currentPosition, + "invalid octal digit: %c", cc); + else + break; + length++; + } + } else { + length = 1; + value = cc - '0'; + while (length < inputLength){ + if ( (cc = m_input[length]) >= '0' && cc <= '9') + value = value * 10 + cc - '0'; + else + break; + length++; + } + } + m_currentToken->m_value.m_integer = value; + m_currentToken->m_tokenType = TOKEN_NUMBER; + if (length + 1 < inputLength + && ((cc = m_input[length]) == '.' || toupper(cc) == 'E')){ + qreal realValue; + int realLength = ReStringUtil::lengthOfReal(m_input.constData(), &realValue); + if (realLength > length){ + m_currentToken->m_tokenType = TOKEN_REAL; + m_currentToken->m_value.m_real = realValue; + length = realLength; + } + } + m_input.remove(0, length); + m_currentCol += length; + return m_currentToken; +} + +/** + * @brief Reads a string into the internal data. + * @return the token with the string + */ +ReToken*ReLexer::scanString() +{ + int delim = m_input[0]; + int inputLength = m_input.size(); + int cc; + int length = 1; + m_currentToken->m_tokenType = TOKEN_STRING; + m_currentToken->m_value.m_id = delim; + bool again = false; + do { + while(length < inputLength && (cc = m_input[length]) != delim){ + length++; + if (cc != '\\' + || (m_stringFeatures + & (SF_C_ESCAPING | SF_C_HEX_CHARS | SF_C_SPECIAL)) == 0){ + m_currentToken->m_string.append(QChar(cc)); + } else { + if (length >= inputLength) + throw ReLexException(*m_currentPosition, + "backslash without following character"); + cc = m_input[length++]; + if ( (m_stringFeatures & SF_C_HEX_CHARS) && toupper(cc) == 'X'){ + if (length >= inputLength) + throw ReLexException(*m_currentPosition, + "missing hexadecimal digit behind \\x"); + cc = m_input[length++]; + int hexVal = ReQString::valueOfHexDigit(cc); + if (hexVal < 0) + throw ReLexException(*m_currentPosition, + "not a hexadecimal digit behind \\x: %lc", + QChar(cc)); + if (length < inputLength){ + cc = m_input[length]; + int nibble = ReQString::valueOfHexDigit(cc); + if (nibble >= 0){ + length++; + hexVal = hexVal * 16 + nibble; + } + } + m_currentToken->m_string.append(QChar(hexVal)); + } else if ( (m_stringFeatures & SF_C_SPECIAL)){ + switch(cc){ + case 'r': + cc = '\r'; + break; + case 'n': + cc = '\n'; + break; + case 't': + cc = '\t'; + break; + case 'a': + cc = '\a'; + break; + case 'v': + cc = '\v'; + break; + case 'f': + cc = '\f'; + break; + default: + break; + } + m_currentToken->m_string.append(QChar(cc)); + } else { + m_currentToken->m_string.append(QChar(cc)); + } + } + } + if (cc == delim){ + length++; + } + if ( (m_stringFeatures & SF_DOUBLE_DELIM) && length < inputLength + && m_input[length] == (char) delim){ + m_currentToken->m_printableString.append(delim); + length++; + again = true; + } + } + while(again); + if (m_storageFlags & STORE_ORG_STRING) + m_currentToken->m_printableString.append(m_input.mid(0, length)); + m_input.remove(0, length); + m_currentCol += length; + return m_currentToken; +} + +/** + * @brief Reads a comment into the internal data. + * + * precondition: the current token is prepared yet + */ +void ReLexer::scanComment() +{ + int inputLength = m_input.size(); + int length = 1; + QByteArray& commentEnd = m_commentEnds[m_currentToken->id()]; + int ix; + if (commentEnd[0] == '\n'){ + // single line comment: + if (m_storageFlags & STORE_COMMENT) + m_currentToken->m_string.append(m_input); + length = inputLength; + } else { + // multiline comment: + while( (ix = m_input.indexOf(commentEnd)) < 0){ + if (m_storageFlags & STORE_COMMENT) + m_currentToken->m_string.append(m_input); + m_input.clear(); + if (! fillInput()) + throw ReLexException(*m_currentPosition, + "comment end not found"); + } + length = ix + commentEnd.size(); + if (m_storageFlags & STORE_COMMENT) + m_currentToken->m_string + .append(m_input.mid(0, length)); + } + m_input.remove(0, length); + m_currentCol += length; +} +#if defined (RPL_LEXER_TRACE) +bool ReLexer::trace() const +{ + return m_trace; +} + +void ReLexer::setTrace(bool trace) +{ + m_trace = trace; +} +#endif +/** + * @brief Returns the last read token. + * + * @return the current token + */ +ReToken* ReLexer::currentToken() const +{ + return m_currentToken; +} + +/** + * @brief Returns the current position. + * + * @return the current source code position + */ +const ReSourcePosition* ReLexer::currentPosition() const +{ + return m_currentPosition; +} + +/** + * @brief Returns the next token. + * + * @return the next token + */ +ReToken* ReLexer::nextToken() +{ + ReToken* rc = NULL; + int ix; + if (m_waitingToken != NULL){ + rc = m_currentToken = m_waitingToken; + m_waitingToken = m_waitingToken2; + m_waitingToken2 = NULL; + m_currentPosition = m_waitingPosition1; + m_waitingPosition1 = m_waitingPosition2; + m_waitingPosition2 = NULL; + } else { + m_currentToken->clear(); + ReReader* reader = m_source->currentReader(); + if (reader == NULL) + m_currentToken->m_tokenType = TOKEN_END_OF_SOURCE; + else { + m_waitingPosition2 = m_waitingPosition1; + m_waitingPosition1 = m_currentPosition; + m_currentPosition = m_source->newPosition(m_currentCol); + if (! fillInput()){ + m_currentToken->m_tokenType = TOKEN_END_OF_SOURCE; + } else { + int cc = m_input.at(0); + if (isspace(cc)){ + //waitingPosition = m_currentPosition; + m_currentToken->m_tokenType = TOKEN_SPACE; + ix = 1; + while(ix < m_input.size() && isspace(m_input.at(ix))) + ix++; + if (m_storageFlags & STORE_BLANK){ + m_currentToken->m_string.append(m_input.mid(0, ix)); + } + m_currentCol += ix; + m_input.remove(0, ix); + rc = m_currentToken; + } else if (isdigit(cc)){ + rc = scanNumber(); + } else if ( (cc == '"' && (m_stringFeatures & SF_QUOTE) != 0) + || (cc == '\'' && (m_stringFeatures & SF_TICK) != 0)){ + rc = scanString(); + } else { + if (cc >= CHAR_INFO_SIZE) + throw ReLexException(*m_currentPosition, + "no lexical symbol can start with this char: %lc", + cc); + else + { + if (rc == NULL && (m_charInfo[cc] & CC_FIRST_COMMENT_START)){ + rc = findTokenWithId(TOKEN_COMMENT_START, + CC_2nd_COMMENT_START, m_commentStarts); + if (rc != NULL) + scanComment(); + //waitingPosition = m_currentPosition; + } + + if (rc == NULL && (m_charInfo[cc] & CC_FIRST_OP)){ + if ( (m_charInfo[cc] & CC_OP_1_ONLY) == 0){ + rc = findTokenWithId(TOKEN_OPERATOR, + CC_2nd_OP, m_operators); + } else { + rc = m_currentToken; + rc->m_tokenType = TOKEN_OPERATOR; + rc->m_value.m_id = findInVector(1, m_operators); + m_input.remove(0, 1); + m_currentCol += 1; + } + } + if (rc == NULL && (m_charInfo[cc] & CC_FIRST_KEYWORD)){ + rc = findTokenWithId(TOKEN_KEYWORD, + CC_2nd_KEYWORD, m_keywords); + } + if (rc == NULL && (m_charInfo[cc] & CC_FIRST_ID)){ + int length = 1; + while(length < m_input.size() + && (cc = m_input[length]) < CHAR_INFO_SIZE + && (m_charInfo[cc] & CC_REST_ID) != 0) + length++; + rc = m_currentToken; + rc->m_tokenType = TOKEN_ID; + rc->m_string.append(m_input.mid(0, length)); + m_input.remove(0, length); + m_currentCol += length; + } + + } + } + } + } + } + if (rc == NULL || rc->tokenType() == TOKEN_UNDEF){ + if (m_input.size() == 0){ + rc = m_currentToken; + rc->m_tokenType = TOKEN_END_OF_SOURCE; + } else { + QByteArray symbol = m_input.mid(0, qMin(20, m_input.size()-1)); + throw ReLexException(*m_currentPosition, + "unknown lexical symbol: %s", symbol.constData()); + } + } +#if defined (RPL_LEXER_TRACE) + if (m_trace){ + char buffer[256]; + printf("token: %s pos: %s\n", m_currentToken->dump().constData(), + m_currentPosition->utf8(buffer, sizeof buffer)); + if (strstr(buffer, "0:28") != NULL) + buffer[0] = 0; + } +#endif + return rc; +} +/** + * @brief Reverses the last nextToken(). + * + * Makes that nextToken() returns the current token again. + */ +void ReLexer::undoLastToken() +{ + m_waitingToken = m_currentToken; + m_currentToken = m_currentToken == &m_token1 ? &m_token2 : &m_token1; + m_waitingPosition1 = m_currentPosition; +#if defined (RPL_LEXER_TRACE) + if (m_trace){ + char buffer[256]; + printf("undo last token: waiting-token: %s pos: %s\n", + m_waitingToken->dump().constData(), + m_waitingPosition1->utf8(buffer, sizeof buffer)); + if (strcmp(buffer, ":2:6") == 0) + buffer[0] = 0; + } +#endif +} + +/** + * @brief Reverses the last nextToken(). + * + * Makes that nextToken() returns the current token again. + */ +void ReLexer::undoLastToken2() +{ + m_waitingToken2 = m_currentToken; + m_waitingToken = m_currentToken == &m_token1 ? &m_token2 : &m_token1; + m_waitingPosition2 = m_waitingPosition1; + m_waitingPosition1 = m_currentPosition; +} + +/** + * @brief Prevents the current token from data loss. + * + * Usage: + *

+ * token1 = nextToken();
+ * saveLastToken();
+ * token2 = nextToken();
+ * 
+ * Then token1 and token2 contains the wanted content. + */ +void ReLexer::saveLastToken() +{ + if (m_waitingToken == NULL) + m_currentToken = m_currentToken == &m_token1 ? &m_token2 : &m_token1; +} + +/** + * @brief Returns the next relevant token, but the token remains "unread". + * + * @return the next token which is not a space/comment + */ +ReToken*ReLexer::peekNonSpaceToken() +{ + ReToken* token = nextNonSpaceToken(); + undoLastToken(); + return token; +} +/** + * @brief Returns the maximal length of a token + * @return the maximal length of a token + */ +size_t ReLexer::maxTokenLength() const +{ + return m_maxTokenLength; +} + +/** + * @brief Sets the maximal length of a token + * + * @param maxTokenLength the new maximal length of a token + */ +void ReLexer::setMaxTokenLength(size_t maxTokenLength) +{ + m_maxTokenLength = maxTokenLength; +} + +/** + * @brief ReLexer::nextNonSpaceToken + * @return + */ +ReToken* ReLexer::nextNonSpaceToken() +{ + ReToken* rc = NULL; + RplTokenType type; + do{ + rc = nextToken(); + } while( (type = m_currentToken->tokenType()) == TOKEN_SPACE + || type == TOKEN_COMMENT_START || type == TOKEN_COMMENT_END + || type == TOKEN_COMMENT_REST_OF_LINE); + return rc; +} + +/** + * @brief Prepares a given source unit for reading. + * + * Saves the current source position onto the top of stack. + * Pushes the source unit onto the top of stack. + * + * Precondition: the unit must be known by exactly one reader + * + * @param unit the new source unit + */ +void ReLexer::startUnit(ReSourceUnitName unit) +{ + m_source->startUnit(unit, *m_currentPosition); +} +/** + * @brief Returns the source of the instance. + * + * @return the source of the instance + */ +ReSource* ReLexer::source() +{ + return m_source; +} + +/** + * @brief Returns the priority of a given operator. + * + * @param op the operator + * @return the priority of the op + */ +int ReLexer::prioOfOp(int op) const +{ + int rc = op > 0 && (unsigned) op < sizeof m_prioOfOp / sizeof m_prioOfOp[0] + ? m_prioOfOp[op] : 0; + return rc; +} + +/** + * @brief Returns the name of an operator. + * + * @param op the operator id + * @return the name of the operator + */ +const QByteArray&ReLexer::nameOfOp(int op) const +{ + const QByteArray& rc = m_opNames.at(op); + return rc; +} + +/** + * @brief Returns whether an operator is right associative + * @param op op to test + * @return true: the operator is right associative
+ * false: otherwise + */ +bool ReLexer::isRightAssociative(int op) const +{ + bool rc = false; + if (op >= 0 && (unsigned) op < sizeof m_assocOfOp / sizeof m_assocOfOp[0]){ + rc = m_assocOfOp[op]; + } + return rc; +} + + diff --git a/expr/ReLexer.hpp b/expr/ReLexer.hpp new file mode 100644 index 0000000..fea3b0d --- /dev/null +++ b/expr/ReLexer.hpp @@ -0,0 +1,247 @@ +/* + * 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 RPLLEXER_HPP +#define RPLLEXER_HPP + +//#define RPL_LEXER_TRACE + +enum RplTokenType { + TOKEN_UNDEF, + TOKEN_STRING, + TOKEN_NUMBER, + TOKEN_REAL, + TOKEN_KEYWORD, + TOKEN_OPERATOR, + TOKEN_ID, + TOKEN_COMMENT_REST_OF_LINE, + TOKEN_COMMENT_START, + TOKEN_COMMENT_END, + TOKEN_SPACE, + TOKEN_END_OF_SOURCE, + TOKEN_COUNT +}; + +class ReLexException : public ReException { +public: + ReLexException(const ReSourcePosition& position, const char* message, ...); +}; + +class ReLexer; + +class ReToken { +public: + ReToken(RplTokenType type); + ~ReToken(); + ReToken(const ReToken& source); + ReToken& operator =(const ReToken& source); +public: + friend class ReLexer; + const QByteArray& toString(); + bool isInteger(); + int asInteger() const; + quint64 asUInt64() const; + qreal asReal() const; + const QByteArray& rawString() const; + int id() const; + RplTokenType tokenType() const; + bool isTokenType(RplTokenType expected) const; + bool isOperator(int expected, int alternative = 0) const; + bool isKeyword(int expected, int alternative = 0) const; + void clear(); + bool isCapitalizedId() const; + QByteArray dump() const; + static const char* nameOfType(RplTokenType type); + QByteArray asUtf8() const; +protected: + RplTokenType m_tokenType; + QByteArray m_string; + // only for TOKEN_STRING: copy from source but with escaped chars like "\\n" + QByteArray m_printableString; + union { + // only for TOKEN_KEYWORD and TOKEN_OPERATOR + int m_id; + quint64 m_integer; + qreal m_real; + } m_value; +}; + + +class ReSource; +class ReLexer +{ +public: + typedef QList StringList; + enum NumericType { + NUMTYPE_UNDEF, + NUMTYPE_DECIMAL = 1 << 0, + NUMTYPE_OCTAL = 1 << 1, + NUMTYPE_HEXADECIMAL = 1 << 2, + NUMTYPE_FLOAT = 1 << 3, + /// + NUMTYPE_ALL_INTEGER = NUMTYPE_DECIMAL | NUMTYPE_OCTAL |NUMTYPE_HEXADECIMAL, + NUMTYPE_ALL = NUMTYPE_ALL_INTEGER | NUMTYPE_FLOAT + }; + enum CharClassTag { + CC_UNDEF = 0, + /// this char is possible as first char of an id + CC_FIRST_ID = 1 << 0, + /// this char is possible as 2nd char of an id + CC_2nd_ID = 1 << 1, + /// this char is possible as 3rd char of an id + CC_3rd_ID = 1 << 2, + /// this char is possible as 4th... char of an id + CC_REST_ID = 1 << 3, + /// this char can start a comment + CC_FIRST_COMMENT_START = 1 << 4, + /// this char can be the 2nd char of a comment start + CC_2nd_COMMENT_START = 1 << 5, + /// this char can be the 3rd char of a comment start + CC_3rd_COMMENT_START = 1 << 6, + /// this char can be the 4th ... of a comment start + CC_REST_COMMENT_START = 1 << 7, + /// this char can start a keyword + CC_FIRST_KEYWORD = 1 << 8, + /// this char can be the 2nd char of a keyword + CC_2nd_KEYWORD = 1 << 9, + /// this char can be the 3rd char of a keyword + CC_3rd_KEYWORD = 1 << 10, + /// this char can be the 4th... char of a keyword + CC_REST_KEYWORD = 1 << 11, + /// this char can be the 1st char of an operator + CC_FIRST_OP = 1 << 12, + /// this char can be the 2nd char of an operator + CC_2nd_OP = 1 << 13, + /// this char can be the 3rd char of an operator + CC_3rd_OP = 1 << 14, + /// this char can be the 4th... char of an operator + CC_REST_OP = 1 << 15, + /// there is an operator with exactly this char + /// and there is no other operator starting with this char + CC_OP_1_ONLY = 1 << 16 + }; + enum StringFeatures { + SF_UNDEF, + /// ' can be a string delimiter + SF_TICK = 1 << 1, + /// " can be a string delimiter + SF_QUOTE = 1 << 2, + /// character escaping like in C: "\x" is "x" + SF_C_ESCAPING = 1 << 3, + /// 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, + /// A delimiter inside a string must be doubled (like in Pascal) + SF_DOUBLE_DELIM = 1 << 6, + // Redefinitions for better reading: + SF_LIKE_C = SF_TICK | SF_QUOTE | SF_C_ESCAPING | SF_C_SPECIAL + | SF_C_HEX_CHARS + }; + enum StorageFlags { + S_UNDEF, + /// the original string will be stored in m_string too + /// (not only m_rawString) + STORE_ORG_STRING = 1 << 1, + /// comments will be stored in m_string + STORE_COMMENT = 1 << 2, + /// blanks will be stored in m_string + STORE_BLANK = 1 << 3, + /// redefinitions for better reading: + STORE_NOTHING = 0, + STORE_ALL = STORE_ORG_STRING | STORE_COMMENT | STORE_BLANK + }; + +public: + ReLexer(ReSource* source, + const char* keywords, + const char* operators, + const char* rightAssociatives, + const char* comments, + const char* firstCharsId = "a-zA-Z_", + const char* restCharsId = "a-zA-Z0-9_", + int numericTypes = NUMTYPE_DECIMAL | NUMTYPE_HEXADECIMAL | NUMTYPE_FLOAT, + int stringFeatures = SF_TICK | SF_QUOTE | SF_C_ESCAPING | SF_C_SPECIAL + | SF_C_HEX_CHARS, + int storageFlags = STORE_NOTHING); + virtual ~ReLexer(); +public: + ReToken* nextToken(); + void undoLastToken(); + void undoLastToken2(); + void saveLastToken(); + ReToken* peekNonSpaceToken(); + ReToken* nextNonSpaceToken(); + size_t maxTokenLength() const; + void setMaxTokenLength(size_t maxTokenLength); + void startUnit(ReSourceUnitName unit); + ReSource* source(); + int prioOfOp(int op) const; + const QByteArray& nameOfOp(int op) const; + bool isRightAssociative(int op) const; + const ReSourcePosition* currentPosition() const; + ReToken* currentToken() const; +#if defined RPL_LEXER_TRACE + bool trace() const; + void setTrace(bool trace); +#endif +private: + void prepareOperators(const char* operators, const char* rightAssociatives); + void initializeComments(const char* comments); + bool fillInput(); + int findInVector(int tokenLength, const StringList& vector); + ReToken* findTokenWithId(RplTokenType tokenType, int flag2, + StringList& names); + ReToken* scanNumber(); + ReToken* scanString(); + void scanComment(); +protected: + ReSource* m_source; + /// sorted, string ends with the id of the keyword + StringList m_keywords; + // sorted, string ends with the id of the operator + StringList m_operators; + // sorted, each entry ends with the id of the comment start + StringList m_commentStarts; + // index: id content: comment_end + 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 + QByteArray m_idFirstRare; + // index: ord(char) content: chr(ix) can be the non first char of an id + QByteArray m_idRestRare; + // a list of QChars with ord(cc) > 127 and which can be the first char + int m_numericTypes; + QByteArray m_idRest2; + ReToken* m_currentToken; + ReToken* m_waitingToken; + ReToken* m_waitingToken2; + ReToken m_token1; + ReToken m_token2; + const ReSourcePosition* m_currentPosition; + const ReSourcePosition* m_waitingPosition1; + const ReSourcePosition* m_waitingPosition2; + int m_maxTokenLength; + QByteArray m_input; + int m_currentCol; + bool m_hasMoreInput; + int m_stringFeatures; + int m_storageFlags; + /// priority of the operators: index: id of the operator. content: prio + char m_prioOfOp[128]; + char m_assocOfOp[128]; + QList m_opNames; +#if defined (RPL_LEXER_TRACE) + bool m_trace; +#endif +}; + + +#endif // RPLLEXER_HPP diff --git a/expr/ReMFParser.cpp b/expr/ReMFParser.cpp new file mode 100644 index 0000000..38475da --- /dev/null +++ b/expr/ReMFParser.cpp @@ -0,0 +1,1286 @@ +/* + * 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. +*/ + +/** @file + * @brief A parser for the language ML. + */ +/** @file + * @brief Definition for a parser for the language ML. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" + +enum MFLocations{ + L_PARSE_OPERAND_RPARENTH = 2001, + L_PARSE_OPERAND_RPARENTH_INFO, + L_TERM_WRONG_STRING, + L_TERM_WRONG_NUMBER, + L_PARSE_OPERAND_WRONG = 2005, + L_DEFINITION_NO_ID, + L_DEFINITION_WRONG_ID, + L_DEFINITION_UNKNOWN_CLASS, + L_DEFINITION_MISSING_ID, + L_DEFINITION_NO_OP = 2010, + L_DEFINITION_NO_SEMICOLON, + L_PARSE_IF_NO_THEN, + L_PARSE_IF_NO_ELSE, + L_PARSE_IF_NO_FI, + L_PARSE_WHILE_NO_DO = 2015, + L_PARSE_WHILE_NO_OD, + L_PARSE_REPEAT_NO_UNTIL, + L_PARSE_REPEAT_NO_SEMI, + L_PARSE_BODY_WRONG_ITEM, + L_PARSE_FOR_NO_TO = 2020, + L_PARSE_LIST_NO_COMMA, + L_PARSE_MAP_BOOL, + L_PARSE_MAP_NONE, + L_PARSE_MAP_NUMERIC, + L_PARSE_MAP_EXPR = 2025, + L_PARSE_MAP_EXPR2, + L_PARSE_MAP_NO_COLON, + L_PARSE_MAP_NO_COMMA, + L_PARSE_OPERAND_NOT_OPERAND = 2030, + L_PARSE_BODY_NO_START, + L_PARSE_OPERAND_NO_BRACKET, + L_PARSE_ARGS_NO_COMMA_OR_PARENT, + L_PARSE_OPERAND_NO_FIELD2, + L_PARSE_OPERAND_NO_BRACKET2 = 2035, + L_PARSE_OPERAND_NO_FIELD, + L_PARSE_METH_NO_CLASS, + L_PARSE_METH_NO_CLASS2, + L_PARSE_METH_NO_NAME, + L_PARSE_METH_NO_NAME2 = 2040, + L_PARSE_METH_NO_LPARENTH, + L_PARSE_METH_NO_COLON, + L_PARSE_PARAMLIST_NO_PARENTH, + L_PARSE_PARAMLIST_NO_PARENTH2, + L_PARSE_METH_NO_END = 2045, + L_PARSE_METH_NO_END2, + L_PARSE_VAR_DEF_ALREADY_DEFINED, + L_PARSE_VAR_DEF_ALREADY_DEFINED2, + L_PARSE_CLASS_NO_NAME, + L_PARSE_CLASS_LOWERCASE = 2050, + L_PARSE_CLASS_ALREADY_DEFINED, + L_PARSE_CLASS_ALREADY_DEFINED2 + +}; + +/** @class ReMFParser 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. + */ + +ReMFParser::ReMFParser(ReSource& source, ReASTree& abstractSyntaxTree) : + ReParser(m_lexer, abstractSyntaxTree), + m_lexer(&source, + MF_KEYWORDS, MF_OPERATORS, MF_RIGHT_ASSOCIATIVES, + "/* */ // \n", + "a-zA-Z_", "a-zA-Z0-9_", + ReLexer::NUMTYPE_ALL, ReLexer::SF_LIKE_C) +{ +} + +/** + * @brief Parses an if statement. + */ +ReASItem* ReMFParser::parseIf() +{ + ReASIf* rc = new ReASIf(); + rc->setPosition(m_lexer.currentPosition()); + + ReASItem* condition = parseExpr(0); + if (! m_lexer.currentToken()->isKeyword(K_THEN)) + syntaxError(L_PARSE_IF_NO_THEN, "'then' expected"); + rc->setChild2(condition); + ReASItem* body = parseBody(K_ELSE, K_FI); + rc->setChild3(body); + if (! m_lexer.currentToken()->isKeyword(K_ELSE, K_FI)) + syntaxError(L_PARSE_IF_NO_ELSE, "'else' or 'fi' expected"); + if ( m_lexer.currentToken()->isKeyword(K_ELSE)){ + ReASItem* body = parseBody(K_FI); + rc->setChild4(body); + } + if (! m_lexer.currentToken()->isKeyword(K_FI)) + syntaxError(L_PARSE_IF_NO_FI, "'fi' expected"); + m_lexer.nextNonSpaceToken(); + return rc; +} +/** + * @brief Parses a while statement. + */ + +ReASItem* ReMFParser::parseWhile() +{ + ReASWhile* rc = new ReASWhile(); + rc->setPosition(m_lexer.currentPosition()); + + ReASItem* condition = parseExpr(0); + if (! m_lexer.currentToken()->isKeyword(K_DO)) + syntaxError(L_PARSE_WHILE_NO_DO, "'do' expected"); + rc->setChild2(condition); + ReASItem* body = parseBody(K_OD); + rc->setChild3(body); + if (! m_lexer.currentToken()->isKeyword(K_OD)) + syntaxError(L_PARSE_WHILE_NO_OD, "'od' expected"); + m_lexer.nextNonSpaceToken(); + return rc; +} + +/** + * @brief Parses a repeat statement. + */ +ReASItem* ReMFParser::parseRepeat() +{ + ReASRepeat* rc = new ReASRepeat(); + rc->setPosition(m_lexer.currentPosition()); + + ReASItem* body = parseBody(K_UNTIL); + rc->setChild3(body); + if (! m_lexer.currentToken()->isKeyword(K_UNTIL)) + syntaxError(L_PARSE_REPEAT_NO_UNTIL, "'until' expected"); + + ReASItem* condition = parseExpr(0); + if (! m_lexer.currentToken()->isOperator(O_SEMICOLON)) + syntaxError(L_PARSE_REPEAT_NO_SEMI, "';' expected"); + rc->setChild2(condition); + m_lexer.nextNonSpaceToken(); + return rc; +} + +/** + * @brief Creates a variable definition for a builtin variable. + * @param var the basic variable data + * @return + */ +ReASVarDefinition* ReMFParser::buildVarDef(ReASNamedValue* var) +{ + ReASVarDefinition* rc = new ReASVarDefinition(); + rc->setPosition(var->position()); + rc->setChild2(var); + ReSymbolSpace* symbols = m_tree.currentSpace(); + int varNo; + symbols->addVariable(rc, varNo); + var->setVariableNo(varNo); + return rc; +} + +/** + * @brief Parses a for statement. + * + * Syntax: + * for [ VAR ] [ from START_EXPR ] to END_EXPR [ step STEP_EXPR ] do + * BODY od + * + * for VAR in ITERABLE_EXPR do BODY od + * + * @post the token behind the do is read + * @return the abstract syntax tree of the for statement + */ +ReASItem* ReMFParser::parseFor() +{ + int builtinVars = 1; + ReASNode2* rc = NULL; + const ReSourcePosition* startPosition = m_lexer.currentPosition(); + ReToken* token = m_lexer.nextNonSpaceToken(); + ReASNamedValue* var = NULL; + if (token->isTokenType(TOKEN_ID)){ + var = new ReASNamedValue(ReASInteger::m_instance, + m_tree.currentSpace(), token->toString(), + ReASNamedValue::A_LOOP); + var->setPosition(m_lexer.currentPosition()); + token = m_lexer.nextNonSpaceToken(); + } + if (token->isKeyword(K_IN)){ + ReASVarDefinition* varDef = buildVarDef(var); + ReASForIterated* node = new ReASForIterated(varDef); + rc = node; + node->setPosition(startPosition); + node->setChild3(var); + ReASItem* iterable = parseExpr(0); + node->setChild4(iterable); + } else { + if (var == NULL){ + char name[32]; + // Build a unique name inside the scope: + qsnprintf(name, sizeof name, "$%d_%d", startPosition->lineNo(), + startPosition->column()); + var = new ReASNamedValue(ReASInteger::m_instance, + m_tree.currentSpace(), name, + ReASNamedValue::A_LOOP); + var->setPosition(startPosition); + } + ReASVarDefinition* varDef = buildVarDef(var); + ReASForCounted* node = new ReASForCounted(varDef); + rc = node; + node->setPosition(startPosition); + node->setChild3(var); + if (token->isKeyword(K_FROM)){ + node->setChild4(parseExpr(0)); + } + if (! m_lexer.currentToken()->isKeyword(K_TO)){ + syntaxError(L_PARSE_FOR_NO_TO, "'to' expected"); + } + node->setChild5(parseExpr(0)); + if (m_lexer.currentToken()->isKeyword(K_STEP)){ + node->setChild6(parseExpr(0)); + } + } + if (! m_lexer.currentToken()->isKeyword(K_DO)) + syntaxError(L_PARSE_FOR_NO_TO, "'to' expected"); + rc->setChild2(parseBody(K_OD, K_UNDEF, builtinVars)); + m_lexer.nextNonSpaceToken(); + return rc; +} + +/** + * @brief Parses a variable definition. + * + * Syntax: + * Variable: { "const" | "lazy" }* TYPE id [ = EXPR ] [ ";" ] + * Parameter: { "const" | "lazy" }* TYPE id [ = EXPR ] { "," | ")" } + * + * @pre first token of the definition is read + * @post token behind the definition is read: ';', ',', ')' + * @param attribute attribute of the variable: A_PARAM... + * @return a variable/parameter definition + */ +ReASVarDefinition* ReMFParser::parseVarDefinition( + ReASNamedValue::Attributes attribute) +{ + int attributes = attribute; + ReToken* token = m_lexer.currentToken(); + while(token->isKeyword(K_CONST, K_LAZY)){ + switch(token->id()){ + case K_CONST: + attributes += ReASNamedValue::A_CONST; + break; + case K_LAZY: + attributes += ReASNamedValue::A_LAZY; + break; + default: + break; + } + token = m_lexer.nextNonSpaceToken(); + } + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_DEFINITION_NO_ID, "class name expected, but no id found"); + if (! token->isCapitalizedId()) + syntaxError(L_DEFINITION_WRONG_ID, + "a class name must start with an upper case character"); + ReASClass* clazz = m_tree.currentSpace()->findClass(token->toString()); + if (clazz == NULL) + syntaxError(L_DEFINITION_UNKNOWN_CLASS, "unknown class"); + token = m_lexer.nextNonSpaceToken(); + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_DEFINITION_MISSING_ID, "variable name expected"); + ReSymbolSpace* symbols = m_tree.currentSpace(); + // freed in the destructor of the nodes: + ReASNamedValue* namedValue = new ReASNamedValue(clazz, symbols, + token->toString(), + attributes); + namedValue->setPosition(m_lexer.currentPosition()); + ReASVarDefinition* rc = new ReASVarDefinition(); + rc->setPosition(m_lexer.currentPosition()); + rc->setChild2(namedValue); + token = m_lexer.nextNonSpaceToken(); + if (token->id() == O_ASSIGN){ + ReASItem* value = parseExpr(0); + rc->setChild3(value); + token = m_lexer.currentToken(); + } + int varNo = 0; + ReASItem* oldSymbol = symbols->addVariable(rc, varNo); + if (oldSymbol != NULL) + error(L_PARSE_VAR_DEF_ALREADY_DEFINED, oldSymbol->position(), + "symbol already defined", "previous definition"); + namedValue->setVariableNo(varNo); + return rc; +} + +/** + * @brief Reads the current tokens as an formula and returns the variant. + * + * @post the token behind the formula is read + * @param parent the parent for the formula: because of the destroying + * the new expression is chained into the parent + * @return the variant containing the formula + */ +ReASVariant* ReMFParser::createFormula(ReASNode1* parent) +{ + ReASVariant* variant = NULL; + m_lexer.undoLastToken2(); + ReASExprStatement* expr = dynamic_cast + (parseExprStatement(false)); + if (expr != NULL){ + // chaining per m_child (= next statement) is for freeing while destruction: + expr->setChild(parent->child()); + parent->setChild(expr); + // freed in the destructor of varList (~ReASVariant()): + variant = new ReASVariant(); + variant->setObject(expr, ReASFormula::m_instance); + } + return variant; +} + +/** + * @brief Converts the current expression into a ReASVariant. + * + * If the expression is a constant, the constant value will be the content + * of the variant. Otherwise a formula will be stored. + * + * @pre the first token of the variant expression is read + * @post the token behind the variant expression is read + * @param token the token to convert + * @param endsWithComma true: the 2nd token is a ',' + * @param parent the parent node of the expression + * @return the variant with the token's content + */ +ReASVariant* ReMFParser::tokenToVariant(ReToken* token, + bool endsWithComma, ReASNode1* parent) +{ + ReASVariant* variant = NULL; + if (endsWithComma){ + switch(token->tokenType()){ + case TOKEN_NUMBER: + // freed in the destructor of varList (~ReASVariant()): + variant = new ReASVariant(); + variant->setInt(token->asInteger()); + break; + case TOKEN_STRING: + // freed in the destructor of varList (~ReASVariant()): + variant = new ReASVariant(); + variant->setString(token->toString()); + break; + case TOKEN_REAL: + // freed in the destructor of varList (~ReASVariant()): + variant = new ReASVariant(); + variant->setFloat(token->asReal()); + break; + case TOKEN_KEYWORD: + switch(token->id()){ + case K_TRUE: + case K_FALSE: + // freed in the destructor of varList (~ReASVariant()): + variant = new ReASVariant(); + variant->setBool(token->id() == K_TRUE); + break; + case K_NONE: + // freed in the destructor of varList (~ReASVariant()): + variant = new ReASVariant(); + break; + default: + break; + } + break; + default: + break; + } + } + if (variant == NULL) + variant = createFormula(parent); + return variant; +} + +/** + * @brief Parses a list. + * + * Syntax:
+ * '[' [ EXPR_1 [ ',' EXPR_2 ...]] ']' + * + * @pre '[' is the current token + * @post the token behind the ']' is read + * @return a node of the abstract syntax tree + */ +ReASItem* ReMFParser::parseList() +{ + ReASListConstant* rc = new ReASListConstant(); + ReASVariant& varList = rc->value(); + ReASListOfVariants* list = static_cast + (varList.asObject(NULL)); + rc->setPosition(m_lexer.currentPosition()); + ReASVariant* variant; + bool again = true; + ReToken* token; + ReToken* token2; + // read the token behind '[': + token = m_lexer.nextNonSpaceToken(); + if (token->isOperator(O_RBRACKET)){ + // read token behind ']': + m_lexer.nextNonSpaceToken(); + } else{ + while(again){ + m_lexer.saveLastToken(); + token2 = m_lexer.nextNonSpaceToken(); + variant = tokenToVariant(token, token2->isOperator(O_COMMA), rc); + token = m_lexer.currentToken(); + if (token->isOperator(O_RBRACKET)) + again = false; + else if (! token->isOperator(O_COMMA)) + syntaxError(L_PARSE_LIST_NO_COMMA, "',' or ']' expected"); + // read token behind ',' or ']' + token = m_lexer.nextNonSpaceToken(); + if (variant != NULL) + list->append(variant); + } + } + return rc; +} + + +/** + * @brief Parses a map. + * + * Syntax:
+ * '{' [ STRING_EXPR_1 ':' EXPR_1 [ ',' STRING_EXPR_2 ': EXPR_2 ...]] '}' + * + * @pre '{' is the current token + * @post the token behind the '}' is read + * @return a node of the abstract syntax tree + */ +ReASItem* ReMFParser::parseMap() +{ + ReASMapConstant* rc = new ReASMapConstant(); + ReASVariant& varMap = rc->value(); + ReASMapOfVariants* map = static_cast + (varMap.asObject(NULL)); + rc->setPosition(m_lexer.currentPosition()); + ReASVariant* variant; + bool again = true; + ReToken* token; + ReToken* token2; + QByteArray key; + while(again){ + token = m_lexer.nextNonSpaceToken(); + if (token->isOperator(O_RBRACE)) + again = false; + else{ + key.clear(); + switch(token->tokenType()){ + case TOKEN_STRING: + // freed in the destructor of varList (~ReASVariant()): + key = token->toString(); + break; + case TOKEN_KEYWORD: + switch(token->id()){ + case K_TRUE: + case K_FALSE: + syntaxError(L_PARSE_MAP_BOOL, + "boolean value not allowed as key type. Use a string"); + break; + case K_NONE: + syntaxError(L_PARSE_MAP_NONE, + "'none' is not allowed as key type. Use a string"); + break; + default: + syntaxError(L_PARSE_MAP_EXPR, + "a non constant expression is not allowed as key type. Use a string"); + break; + } + break; + case TOKEN_NUMBER: + case TOKEN_REAL: + syntaxError(L_PARSE_MAP_NUMERIC, + "numeric values not allowed as key type. Use a string"); + break; + default: + syntaxError(L_PARSE_MAP_EXPR2, + "a non constant expression is not allowed as key type. Use a string"); + break; + } + token = m_lexer.nextNonSpaceToken(); + if (! token->isOperator(O_COLON)){ + syntaxError(L_PARSE_MAP_NO_COLON, "':' expected"); + } else { + token = m_lexer.nextNonSpaceToken(); + m_lexer.saveLastToken(); + token2 = m_lexer.nextNonSpaceToken(); + variant = tokenToVariant(token, token2->isOperator(O_COMMA), rc); + (*map)[key] = variant; + variant = NULL; + token = m_lexer.currentToken(); + if (token->isOperator(O_RBRACE)) + again = false; + else if (! token->isOperator(O_COMMA)) + syntaxError(L_PARSE_MAP_NO_COMMA, "',' or '}' expected"); + } + } + } + m_lexer.nextNonSpaceToken(); + return rc; +} + +/** + * @brief Builds a node of a variable or a field (as an operand). + * + * @param name name of the variable/field + * @param position source position + * @param parent NULL: result is a variable + * + * @return a ReASNamedValue or a + * ReASNamedValue instance + */ +ReASItem* ReMFParser::buildVarOrField(const QByteArray& name, + const ReSourcePosition* position, ReASItem* parent) +{ + ReASItem* rc = NULL; + if (parent == NULL){ + ReSymbolSpace* space = m_tree.currentSpace(); + ReASVarDefinition* var = space->findVariable(name); + ReASClass* clazz = NULL; + if (var != NULL) + clazz = var->clazz(); + ReASNamedValue* var2 = new ReASNamedValue(clazz, space, + name, ReASNamedValue::A_NONE); + var2->setPosition(position); + rc = var2; + } else { + ReASField* field = new ReASField(name); + field->setPosition(position); + rc = field; + field->setChild(parent); + } + return rc; +} +/** + * @brief Converts a MF specific unary operator into one of AST. + * + * @param op operator known by MF parser + * + * @return operator known by the abstract syntax tree + */ +ReASUnaryOp::UnaryOp ReMFParser::convertUnaryOp(int op) +{ + ReASUnaryOp::UnaryOp rc; + switch(op){ + case O_PLUS: + rc = ReASUnaryOp::UOP_PLUS; + break; + case O_MINUS: + rc = ReASUnaryOp::UOP_MINUS_INT; + break; + case O_NOT: + rc = ReASUnaryOp::UOP_NOT_BOOL; + break; + case O_BIT_NOT: + rc = ReASUnaryOp::UOP_NOT_INT; + break; + case O_INC: + rc = ReASUnaryOp::UOP_INC; + break; + case O_DEC: + rc = ReASUnaryOp::UOP_DEC; + break; + default: + throw ReException("unknown unary operator %d", op); + break; + } + return rc; +} + +/** + * @brief Parses an operand. + * + * An operand is the first and the third part of a binary operation. + * Examples: constant, variable, method call, an expression in parentheses + * + * @post the token behind the operand is read + * @return the node with the operand + */ +ReASItem* ReMFParser::parseOperand(int level, ReASItem* parent) +{ + ReToken* token = m_lexer.nextNonSpaceToken(); + const ReSourcePosition* startPosition = m_lexer.currentPosition(); + ReASItem* rc = NULL; + bool readNext = true; + switch(token->tokenType()){ + case TOKEN_OPERATOR: + { + Operator opId = (Operator) token->id(); + if (parent != NULL && opId != O_LBRACKET) + syntaxError(L_PARSE_OPERAND_NO_FIELD, + "field expected (behind a '.')"); + if (opId == O_LBRACKET){ + if (parent == NULL){ + rc = parseList(); + readNext = false; + } else { + ReASIndexedValue* value = new ReASIndexedValue(); + value->setPosition(startPosition); + value->setChild(parent); + rc = value; + value->setChild2(parseExpr(level + 1)); + if (! m_lexer.currentToken()->isOperator(O_RBRACKET)) + syntaxError(L_PARSE_OPERAND_NO_BRACKET2, + "']' expected"); + } + } else if (opId == O_LBRACE){ + rc = parseMap(); + readNext = false; + } else if (opId == O_LPARENTH){ + rc = parseExpr(level + 1); + token = m_lexer.currentToken(); + if(! token->isOperator(O_RPARENTH)){ + // this call never comes back (exception!) + syntaxError(L_PARSE_OPERAND_RPARENTH, + "')' expected", "(", startPosition); + } + } else if (IS_UNARY_OP(opId)){ + ReASUnaryOp* op = new ReASUnaryOp(convertUnaryOp(token->id()), + AST_PRE_UNARY_OP); + op->setPosition(m_lexer.currentPosition()); + op->setChild(parseOperand(level)); + readNext = false; + rc = op; + } else + syntaxError(L_PARSE_OPERAND_NOT_OPERAND, + "operand expected, not an operator"); + break; + } + case TOKEN_STRING: + case TOKEN_NUMBER: + case TOKEN_REAL: + { + if (parent != NULL) + syntaxError(L_PARSE_OPERAND_NO_FIELD2, + "field expected (behind a '.')"); + ReASConstant* constant = new ReASConstant(); + constant->setPosition(m_lexer.currentPosition()); + rc = constant; + switch(token->tokenType()){ + case TOKEN_STRING: + constant->value().setString(token->toString()); + break; + case TOKEN_NUMBER: + constant->value().setInt(token->asInteger()); + break; + case TOKEN_REAL: + constant->value().setFloat(token->asReal()); + break; + default: + break; + } + break; + } + case TOKEN_ID: + { + QByteArray name = token->toString(); + if (name == "a") + name = "a"; + token = m_lexer.nextNonSpaceToken(); + startPosition = m_lexer.currentPosition(); + if (token->tokenType() != TOKEN_OPERATOR){ + rc = buildVarOrField(name, startPosition, parent); + readNext = false; + } else { + if (token->id() == O_LPARENTH){ + ReASMethodCall* call = new ReASMethodCall(name, parent); + call->setPosition(startPosition); + + rc = call; + token = m_lexer.nextNonSpaceToken(); + if (! token->isOperator(O_RPARENTH)){ + m_lexer.undoLastToken(); + ReASExprStatement* args = parseArguments(); + call->setChild2(args); + readNext = false; + } + } else { + rc = buildVarOrField(name, startPosition, parent); + if (token->id() == O_LBRACKET){ + ReASItem* indexExpr = parseExpr(0); + if (! m_lexer.currentToken()->isOperator(O_RBRACKET)) + syntaxError(L_PARSE_OPERAND_NO_BRACKET, "']' expected"); + dynamic_cast(rc)->setChild(indexExpr); + } else { + if (token->id() == O_INC || token->id() == O_DEC){ + ReASUnaryOp* op = new ReASUnaryOp(convertUnaryOp( + token->id()), + AST_POST_UNARY_OP); + op->setChild(rc); + rc = op; + } else { + readNext = false; + } + } + } + } + break; + } + case TOKEN_END_OF_SOURCE: + readNext = false; + break; + default: + // this call never comes back (exception!) + syntaxError(L_PARSE_OPERAND_WRONG, + "unexpected symbol detected. Operand expected"); + break; + } + if (readNext) + m_lexer.nextNonSpaceToken(); + if (m_lexer.currentToken()->isOperator(O_DOT, O_LBRACKET)){ + if (m_lexer.currentToken()->asInteger() == O_LBRACKET) + m_lexer.undoLastToken(); + rc = parseOperand(level, rc); + } + return rc; +} +/** + * @brief Converts a MF specific binary operator into one of AST. + * + * @param op operator known by MF parser + * + * @return operator known by the abstract syntax tree + */ +ReASBinaryOp::BinOperator ReMFParser::convertBinaryOp(int op){ + ReASBinaryOp::BinOperator rc; + switch(op){ + case ReMFParser::O_ASSIGN: + rc = ReASBinaryOp::BOP_ASSIGN; + break; + case ReMFParser::O_PLUS_ASSIGN: + rc = ReASBinaryOp::BOP_PLUS_ASSIGN; + break; + case ReMFParser::O_MINUS_ASSIGN: + rc = ReASBinaryOp::BOP_MINUS_ASSIGN; + break; + case ReMFParser::O_DIV_ASSIGN: + rc = ReASBinaryOp::BOP_DIV_ASSIGN; + break; + case ReMFParser::O_TIMES_ASSIGN: + rc = ReASBinaryOp::BOP_TIMES_ASSIGN; + break; + case ReMFParser::O_MOD_ASSIGN: + rc = ReASBinaryOp::BOP_MOD_ASSIGN; + break; + case ReMFParser::O_POWER_ASSIGN: + rc = ReASBinaryOp::BOP_POWER_ASSIGN; + break; + case ReMFParser::O_OR_ASSIGN: + rc = ReASBinaryOp::BOP_LOG_OR_ASSIGN; + break; + case ReMFParser::O_AND_ASSIGN: + rc = ReASBinaryOp::BOP_LOG_AND_ASSIGN; + break; + case ReMFParser::O_LSHIFT_ASSIGN: + rc = ReASBinaryOp::BOP_LSHIFT_ASSIGN; + break; + case ReMFParser::O_RSHIFT_ASSIGN: + rc = ReASBinaryOp::BOP_LOG_RSHIFT_ASSIGN; + break; + case ReMFParser::O_RSHIFT2_ASSIGN: + rc = ReASBinaryOp::BOP_ARTITH_RSHIFT_ASSIGN; + break; + case ReMFParser::O_OR: + rc = ReASBinaryOp::BOP_LOG_OR; + break; + case ReMFParser::O_AND: + rc = ReASBinaryOp::BOP_LOG_AND; + break; + case ReMFParser::O_EQ: + rc = ReASBinaryOp::BOP_EQ; + break; + case ReMFParser::O_NE: + rc = ReASBinaryOp::BOP_NE; + break; + case ReMFParser::O_LT: + rc = ReASBinaryOp::BOP_LT; + break; + case ReMFParser::O_GT: + rc = ReASBinaryOp::BOP_GT; + break; + case ReMFParser::O_LE: + rc = ReASBinaryOp::BOP_LE; + break; + case ReMFParser::O_GE: + rc = ReASBinaryOp::BOP_GE; + break; + case ReMFParser::O_PLUS: + rc = ReASBinaryOp::BOP_PLUS; + break; + case ReMFParser::O_MINUS: + rc = ReASBinaryOp::BOP_MINUS; + break; + case ReMFParser::O_DIV: + rc = ReASBinaryOp::BOP_DIV; + break; + case ReMFParser::O_MOD: + rc = ReASBinaryOp::BOP_MOD; + break; + case ReMFParser::O_TIMES: + rc = ReASBinaryOp::BOP_TIMES; + break; + case ReMFParser::O_POWER: + rc = ReASBinaryOp::BOP_POWER; + break; + case ReMFParser::O_XOR: + rc = ReASBinaryOp::BOP_LOG_XOR; + break; + case ReMFParser::O_BIT_OR: + rc = ReASBinaryOp::BOP_BIT_OR; + break; + case ReMFParser::O_BIT_AND: + rc = ReASBinaryOp::BOP_BIT_AND; + break; + case ReMFParser::O_LSHIFT: + rc = ReASBinaryOp::BOP_LSHIFT; + break; + case ReMFParser::O_RSHIFT: + rc = ReASBinaryOp::BOP_LOG_RSHIFT; + break; + case ReMFParser::O_RSHIFT2: + rc = ReASBinaryOp::BOP_ARTITH_RSHIFT; + break; + default: + throw ReException("unknown binary operator %d", op); + break; + } + return rc; +} + +/** + * @brief Parses an expression. + * + * This method parses the part of an expression with the same parenthesis level. + * The nested parts will be processed recursivly by calling parseOperand which + * calls parseExpr in the case of inner parentheses. + * + * Example: a + (3 * 7 - 2)
+ * expr with level 1: 3*7-2
+ * expr with level 0: a + expr1
+ * + * @pre the nextNonSpaceToken() will return the first token of the expr. + * @post the token behind the expression is read + * + * @param depth the level of the parenthesis + * @return the abstract syntax tree representing the parsed expression + */ +ReASItem* ReMFParser::parseExpr(int depth){ + ReToken* token; + ReASItem* top = parseOperand(depth); + if (top != NULL){ + int lastPrio = INT_MAX; + bool again = true; + do { + token = m_lexer.currentToken(); + switch(token->tokenType()){ + case TOKEN_OPERATOR: + { + Operator opId = (Operator) token->id(); + if (IS_BINARY_OP(opId)){ + ReASBinaryOp* op = new ReASBinaryOp(); + op->setPosition(m_lexer.currentPosition()); + op->setOperator(convertBinaryOp(opId)); + + int prio = m_lexer.prioOfOp(token->id()); + if (prio < lastPrio + || (prio == lastPrio + && ! m_lexer.isRightAssociative(opId))){ + op->setChild(top); + top = op; + } else { + // right assoc or higher priority: + ReASBinaryOp* top2 = dynamic_cast(top); + op->setChild(top2->child2()); + top2->setChild2(op); + } + lastPrio = prio; + op->setChild2(parseOperand(depth)); + } else + again = false; + break; + } + case TOKEN_STRING: + syntaxError(L_TERM_WRONG_STRING, "Operator expected, not a string"); + break; + case TOKEN_NUMBER: + case TOKEN_REAL: + syntaxError(L_TERM_WRONG_NUMBER, "Operator expected, not a number"); + break; + case TOKEN_KEYWORD: + case TOKEN_ID: + case TOKEN_END_OF_SOURCE: + default: + again = false; + break; + } + } while(again); + } + return top; +} + + +/** + * @brief Parses an expression as a statement. + * + * @pre the nextNonSpaceToken() will return the first token of the expr.
+ * Note: a ';' is part of the expression statement + * @post the token behind the expression is read + * @param eatSemicolon true: a trailing ';' will be read + * @return the abstract syntax tree of the expression statement + */ +ReASItem* ReMFParser::parseExprStatement(bool eatSemicolon) +{ + ReASItem* item = parseExpr(0); + ReASExprStatement* statement = NULL; + if (item != NULL){ + statement = new ReASExprStatement(); + statement->setPosition(item->position()); + statement->setChild2(item); + } + if (eatSemicolon && m_lexer.currentToken()->isOperator(O_SEMICOLON)) + m_lexer.nextNonSpaceToken(); + return statement; +} + +/** + * @brief Parses a local variable. + * + * @return the variable definition + */ +ReASItem* ReMFParser::parseLocalVar(){ + ReASItem* rc = parseVarDefinition(ReASNamedValue::A_NONE); + return rc; +} + +/** + * @brief Parses the body. + * + * A body is a module, a class region or a method body. + * + * @param keywordStop the first possible keyword which finishes the stm. list + * @param keywordStop2 the 2nd possible keyword which finishes the statements + * @param builtinVars number of variables valid only in this body + * @return the first element of the statement list + */ +ReASItem* ReMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, + int builtinVars) +{ + ReToken* token = m_lexer.nextNonSpaceToken(); + ReASItem* item = NULL; + ReASItem* body = NULL; + ReASNode1* lastStatement = NULL; + ReASScope scope; + m_tree.currentSpace()->startScope(scope); + scope.m_builtInVars = builtinVars; + bool again = true; + const ReSourcePosition* lastPos = NULL; + do { + token = m_lexer.currentToken(); + if (lastPos == m_lexer.currentPosition()) + syntaxError(L_PARSE_BODY_NO_START, "no statement starts with this symbol"); + lastPos = m_lexer.currentPosition(); + // eat a superflous ';' + if (token->isOperator(O_SEMICOLON)) + token = m_lexer.nextNonSpaceToken(); + try { + switch(token->tokenType()) + { + case TOKEN_OPERATOR: + case TOKEN_STRING: + case TOKEN_NUMBER: + case TOKEN_REAL: + m_lexer.undoLastToken(); + item = parseExprStatement(); + break; + case TOKEN_KEYWORD: + switch (token->id()){ + case K_IF: + item = parseIf(); + break; + case K_WHILE: + item = parseWhile(); + break; + case K_REPEAT: + item = parseRepeat(); + break; + case K_FOR: + item = parseFor(); + break; + case K_CLASS: + parseClass(); + item = NULL; + break; + case K_FUNCTION: + case K_GENERATOR: + parseMethod(); + item = NULL; + break; + case K_IMPORT: + parseImport(); + item = NULL; + break; + case K_CONST: + case K_LAZY: + item = parseLocalVar(); + break; + default: + if (token->isKeyword(keywordStop, keywordStop2)) + again = false; + break; + } + break; + case TOKEN_ID: + { + if (token->isCapitalizedId()){ + item = parseLocalVar(); + } else { + m_lexer.undoLastToken(); + item = parseExprStatement(); + } + break; + } + case TOKEN_END_OF_SOURCE: + again = false; + break; + default: + break; + } + if (again && item != NULL){ + if (body == NULL){ + body = item; + } else { + lastStatement->setChild(item); + } + lastStatement = dynamic_cast(item); + if (lastStatement == NULL) + error(L_PARSE_BODY_WRONG_ITEM, "wrong item type: %d", + item == NULL ? 0 : item->nodeType()); + token = m_lexer.currentToken(); + if (keywordStop != K_UNDEF + && token->isKeyword(keywordStop, keywordStop2)) + again = false; + } + } catch(RSyntaxError exc){ + // we look for the end of the statement: + token = m_lexer.currentToken(); + RplTokenType type; + Operator op; + Keyword key; + while( (type = token->tokenType()) != TOKEN_END_OF_SOURCE) + if (type == TOKEN_OPERATOR + && ((op = Operator(token->id())) == O_SEMICOLON + || op == O_SEMI_SEMICOLON)) + break; + else if (type == TOKEN_KEYWORD){ + key = Keyword(token->id()); + if (key == K_ENDC || key == K_ENDF){ + // we need the token again! + m_lexer.undoLastToken(); + break; + } else if (key == K_FI || key == K_OD){ + break; + } else { + token = m_lexer.nextNonSpaceToken(); + } + } else { + token = m_lexer.nextNonSpaceToken(); + } + } + } while(again); + + if (keywordStop != K_ENDF && keywordStop != K_ENDC + && keywordStop != K_UNDEF) + m_tree.currentSpace()->finishScope(m_lexer.currentPosition()->lineNo(), + scope); + return body; +} + +/** + * @brief Parses a parameter list of a method/function. + * + * @pre token behind '(' is read + * @post token behind ')' is read + * @return + */ +ReASVarDefinition* ReMFParser::parseParameterList(){ + ReASVarDefinition* rc = NULL; + ReASVarDefinition* last = NULL; + const ReSourcePosition* startPos = m_lexer.currentPosition(); + ReASItem* definition = NULL; + do { + if (definition != NULL) + m_lexer.nextNonSpaceToken(); + ReASVarDefinition* current = parseVarDefinition(ReASNamedValue::A_PARAM); + if (rc == NULL){ + rc = current; + } else { + last->setChild(current); + } + last = current; + } while(m_lexer.currentToken()->isOperator(O_COMMA)); + if (! m_lexer.currentToken()->isOperator(O_RPARENTH)) + syntaxError(L_PARSE_PARAMLIST_NO_PARENTH, ") expected", ")", startPos); + m_lexer.nextNonSpaceToken(); + return rc; +} + +/** + * @brief Parses a class definition. + * + * The method definition will be stored in the symbol space of the parent + * + * @pre token "func" is read + * @post token behind "endf" is read + * @return NULL + */ +void ReMFParser::parseMethod() +{ + ReASMethod* method = NULL; + const ReSourcePosition* startPos = m_lexer.currentPosition(); + ReToken* token = m_lexer.nextNonSpaceToken(); + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_PARSE_METH_NO_CLASS, "type name expected"); + QByteArray type = token->toString(); + if (! isupper(type.at(0))) + syntaxError(L_PARSE_METH_NO_CLASS2, + "type name expected (must start with an upper case character)"); + token = m_lexer.nextNonSpaceToken(); + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_PARSE_METH_NO_NAME, "method name expected"); + QByteArray name = token->toString(); + if (! isupper(type.at(0))) + syntaxError(L_PARSE_METH_NO_CLASS2, + "method name expected (must start with an lower case character)"); + token = m_lexer.nextNonSpaceToken(); + if (! token->isOperator(O_LPARENTH, O_COLON)) + syntaxError(L_PARSE_METH_NO_LPARENTH, "'(' or ':' expected"); + + ReASVarDefinition* parameterList = NULL; + method = new ReASMethod(name, m_tree); + method->setPosition(startPos); + ReSymbolSpace* symbols = m_tree.currentSpace(); + symbols->addMethod(method); + m_tree.startClassOrMethod(name, ReSymbolSpace::SST_METHOD); + method->setSymbols(); + if (token->isOperator(O_LPARENTH)){ + token = m_lexer.nextNonSpaceToken(); + if (token->isOperator(O_RPARENTH)){ + token = m_lexer.nextNonSpaceToken(); + } else { + parameterList = parseParameterList(); + method->setChild2(parameterList); + } + } + if (! token->isOperator(O_COLON)) + syntaxError(L_PARSE_METH_NO_COLON, "':' expected"); + + method->setChild(parseBody(K_ENDF)); + if (! m_lexer.currentToken()->isKeyword(K_ENDF)) + syntaxError(L_PARSE_METH_NO_END, "end of function not found", "endf", + startPos); + m_lexer.nextNonSpaceToken(); + m_tree.finishClassOrMethod(name); +} + +/** + * @brief Parses a class definition. + * + * @pre "class" is read + * @post token behind "endc" is read + */ +void ReMFParser::parseClass() +{ + const ReSourcePosition* startPos = m_lexer.currentPosition(); + ReToken* token = m_lexer.nextNonSpaceToken(); + if (! token->isTokenType(TOKEN_ID)) + syntaxError(L_PARSE_CLASS_NO_NAME, "class name expected"); + if (! token->isCapitalizedId()) + syntaxError(L_PARSE_CLASS_LOWERCASE, "class name must start with an uppercase character"); + QByteArray name = token->toString(); + ReASUserClass* clazz = new ReASUserClass(name, startPos, m_tree); + ReSymbolSpace* parent = m_tree.currentSpace(); + ReASUserClass* alreadyDefined = parent->addClass(clazz); + if (alreadyDefined != NULL){ + error(L_PARSE_CLASS_ALREADY_DEFINED, alreadyDefined->position(), + "class already defined", "previous defined class"); + } + m_tree.startClassOrMethod(name, ReSymbolSpace::SST_CLASS); + clazz->setSymbols(); + + m_tree.finishClassOrMethod(name); +} + +/** + * @brief Parses a the import statement + */ +void ReMFParser::parseImport() +{ + +} + +/** + * @brief Parses a module. + * + * @pre the first char of the module is the next char to read. + * @post the total module is read + * + * @param name the name of the module (without path) + */ +ReASItem* ReMFParser::parseModule(ReSourceUnitName name) +{ + m_tree.startModule(name); + // parse until EOF: + ReASItem* body = parseBody(K_UNDEF); + m_tree.finishModule(name); + return body; +} +/** + * @brief Parse the input given by the source. + */ +void ReMFParser::parse() +{ + ReSource* source = m_lexer.source(); + ReSourceUnit* mainModule = source->currentReader()->currentSourceUnit(); + ReSourceUnitName mainModuleName = mainModule->name(); + try { + ReASItem* body = parseModule(mainModuleName); + ReSymbolSpace* module = m_tree.findmodule(mainModuleName); + if (module != NULL) + module->setBody(body); + } catch(RplParserStop exc){ + printf("compiling aborted: %s\n", exc.reason()); + } +} + +/** + * @brief Parses an argument list in a method call. + * + * @pre the token '(' is read + * @post the token behind the ')' is read + * @return the first element of the argument list + */ +ReASExprStatement* ReMFParser::parseArguments() +{ + ReASExprStatement* first = NULL; + ReASExprStatement* last = NULL; + bool again = false; + do { + ReASItem* expr = parseExpr(0); + if (! m_lexer.currentToken()->isOperator(O_COMMA, O_RPARENTH)) + syntaxError(L_PARSE_ARGS_NO_COMMA_OR_PARENT, "',' or ')' expected"); + again = m_lexer.currentToken()->isOperator(O_COMMA); + ReASExprStatement* current = new ReASExprStatement(); + current->setPosition(expr->position()); + current->setChild2(expr); + if (first == NULL) + first = last = current; + else{ + last->setChild(current); + last = current; + } + } while (again); + // skip ')': + m_lexer.nextNonSpaceToken(); + return first; +} + diff --git a/expr/ReMFParser.hpp b/expr/ReMFParser.hpp new file mode 100644 index 0000000..fb5aeca --- /dev/null +++ b/expr/ReMFParser.hpp @@ -0,0 +1,107 @@ +/* + * 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 REMFPARSER_HPP +#define REMFPARSER_HPP + +class ReMFParser : public ReParser +{ +public: + enum Keyword { + K_UNDEF, K_IF, K_THEN, K_ELSE, K_FI, K_WHILE, // 5 + K_DO, K_OD, K_REPEAT, K_UNTIL, K_FOR, // 10 + K_FROM, K_TO, K_STEP, K_IN, K_CASE, // 15 + K_OF, K_ESAC, K_LEAVE, K_CONTINUE, K_PASS, // 20 + K_CLASS, K_ENDC, K_ENDF, K_FUNCTION, K_GENERATOR, // 25 + K_IMPORT, K_CONST, K_LAZY, K_NONE, K_TRUE, // 30 + K_FALSE + }; +#define MF_KEYWORDS "if then else fi while do od repeat until" \ + " for from to step in case of esac leave continue pass" \ + " class endc endf func generator import" \ + " const lazy none true false" + enum Operator { + O_UNDEF, O_SEMI_SEMICOLON, O_SEMICOLON, O_COMMA, O_COLON, // 4 + O_ASSIGN, O_PLUS_ASSIGN, O_MINUS_ASSIGN, O_DIV_ASSIGN, O_TIMES_ASSIGN, // 8 + O_MOD_ASSIGN, O_POWER_ASSIGN, O_OR_ASSIGN, O_AND_ASSIGN, // 13 + O_LSHIFT_ASSIGN, O_RSHIFT_ASSIGN, O_RSHIFT2_ASSIGN, // 16 + O_OR, O_AND, // 18 + O_EQ, O_NE, // 20 + O_LT, O_GT, O_LE, O_GE, // 24 + O_QUESTION, // 25 + O_PLUS, O_MINUS, // 27 + O_DIV, O_MOD, O_TIMES, // 30 + O_POWER, // 31 + O_XOR, O_BIT_OR, O_BIT_AND, // 34 + O_LSHIFT, O_RSHIFT, O_RSHIFT2, // 37 + O_NOT, O_BIT_NOT, // 39 + O_INC, O_DEC, // 41 + O_DOT, O_LPARENTH, O_RPARENTH, O_LBRACKET, O_RBRACKET, O_LBRACE, O_RBRACE // 48 + }; +#define IS_BINARY_OP(op) (Operator(op) >= O_ASSIGN && Operator(op) <= O_DOT) +#define IS_UNARY_OP(op) (op==O_PLUS || op==O_MINUS || (op>=O_NOT && op<=O_DEC)) + +/// \n separates the priority classes +#define MF_OPERATORS ";; ; , :\n" \ + "= += -= /= *= %= **= |= &= <<= >>= >>>=\n" \ + "||\n" \ + "&&\n" \ + "== !=\n" \ + "< > <= >=\n" \ + "?\n" \ + "+ -\n" \ + "/ % *\n" \ + "**\n" \ + "^ | &\n" \ + "<< >> >>>\n" \ + "! ~\n" \ + "++ --\n" \ + ". ( ) [ ] { }" +#define MF_RIGHT_ASSOCIATIVES "= += -= /= *= %= **= |= &= <<= >>= >>>= ** ." +public: + ReMFParser(ReSource& source, ReASTree& ast); +public: + ReASItem* parseIf(); + ReASItem* parseWhile(); + ReASItem* parseRepeat(); + ReASItem* parseFor(); + ReASVarDefinition* parseVarDefinition(ReASNamedValue::Attributes attribute); + ReASItem* parseExpr(int depth); + ReASItem* parseBody(Keyword keywordStop, Keyword keywordStop2 = K_UNDEF, + int builtinVars = 0); + void parseMethod(); + void parseClass(); + void parseImport(); + ReASItem* parseModule(ReSourceUnitName name); + void parse(); + ReASItem*parseExprStatement(bool eatSemicolon = true); + ReASItem*parseList(); + ReASItem*parseMap(); +protected: + ReASExprStatement* parseArguments(); + ReASItem* parseOperand(int level, ReASItem* parent = NULL); + ReASVariant* tokenToVariant(ReToken* token, bool endsWithComma, + ReASNode1* parent); + ReASVariant*createFormula(ReASNode1* parent); + ReASItem* buildVarOrField(const QByteArray& name, + const ReSourcePosition* position, + ReASItem* parent); + ReASVarDefinition* parseParameterList(); + ReASItem* parseLocalVar(); + ReASVarDefinition* buildVarDef(ReASNamedValue* var); +protected: + static ReASBinaryOp::BinOperator convertBinaryOp(int op); + static ReASUnaryOp::UnaryOp convertUnaryOp(int op); +private: + ///syntax token builder. + /// Note: the super class contains a reference with the same name + ReLexer m_lexer; +}; + +#endif // REMFPARSER_HPP diff --git a/expr/ReParser.cpp b/expr/ReParser.cpp new file mode 100644 index 0000000..a0be1bb --- /dev/null +++ b/expr/ReParser.cpp @@ -0,0 +1,263 @@ +/* + * 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. +*/ + +/** @file + * + * @brief Generally usable parts of an parser, e.g. error handling. + */ +/** @file expr/ReParser.hpp + * + * @brief Definition of a generally usable parts of an parser. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" + + +/** @class RSyntaxError ReParser.hpp "expr/ReParser.hpp" + * + * @brief Implements an exception used for jumping out from many nested calls. + * + * We don't want to cancel the parse process if an syntax error has been + * occurred. Therefore we want to recreate after it. + * A relative simple solution: + * Ignoring the rest of the statement and start again with the next statement. + * Often the detection is done deep in an expression and we must jump out. + * This allows this exception. + */ + +/** + * @brief Constructor. + * @param reason the reason of the exception + * @return + */ +ReSyntaxError::ReSyntaxError(const char* reason) : + m_reason(reason) +{ +} +/** + * @brief Returns the description of the exception. + * + * @return the reason + */ +const char* ReSyntaxError::reason() const +{ + return m_reason; +} + +/** @class RplParserStop ReParser.hpp "expr/ReParser.hpp" + * + * @brief Implements an exception used for jumping out from many nested calls. + * + * In some situation we want to abort the parsing process. + * This exception allows this without high costs even the abort position + * is in a deep nested call. + */ + +/** + * @brief Constructor. + * @param reason the reason of the exception + */ +RplParserStop::RplParserStop(const char* reason) : + ReSyntaxError(reason) +{ +} + +/** @class ReParser ReParser.hpp "expr/ReParser.hpp" + * + * @brief Implements a base class for parsers. + * + * This class offers common things for all parsers, e.g. error handling. + */ +/** + * @brief Constructor. + * + * @param lexer the tokenizer + * @param tree the abstract syntax tree + */ +ReParser::ReParser(ReLexer& lexer, ReASTree& tree) : + m_lexer(lexer), + m_tree(tree), + m_messages(), + m_errors(0), + m_warnings(0), + m_maxErrors(20), + m_maxWarnings(20) +{ +} + +/** + * @brief Common actions for the error/warning functions. + * + * @param prefix first char in the message: 'E' (error) or 'W' (warning) + * @param location unique id of the error/warning message + * @param position position of the error/warning + * @param message message with placeholdes like sprintf() + * @return false (for chaining) + */ +bool ReParser::addSimpleMessage(LevelTag prefix, int location, + const ReSourcePosition* position, + const char* message){ + char buffer[2048]; + QByteArray msg; + qsnprintf(buffer, sizeof buffer, "%c%04d %s:%d-%d: ", prefix, location, + position->sourceUnit()->name(), + position->lineNo(), position->column()); + int used = strlen(buffer); + int length = strlen(message); + if (length >= (int) sizeof buffer - used) + length = sizeof buffer - used - 1; + memcpy(buffer + used, message, length); + buffer[used + length] = '\0'; + m_messages.append(buffer); + return false; +} + +/** + * @brief Common actions for the error/warning functions. + * + * @param prefix first char in the message: 'E' (error) or 'W' (warning) + * @param location unique id of the error/warning message + * @param position position of the error/warning + * @param format message with placeholdes like sprintf() + * @param varList the variable argument list + * @return false (for chaining) + */ +bool ReParser::addMessage(LevelTag prefix, int location, + const ReSourcePosition* position, + const char* format, va_list varList){ + char buffer[2048]; + qvsnprintf(buffer, sizeof buffer, format, varList); + return addSimpleMessage(prefix, location, position, buffer); +} + +/** + * @brief Adds an error message and throws an exception. + * + * The exception will be catched at a position where error recovery can take place. + * + * @param location unique id of the error/warning message + * @param message error message + */ + +void ReParser::syntaxError(int location, const char* message) +{ + addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message); + throw RSyntaxError(message); +} + +/** + * @brief Adds an error message and throws an exception. + * + * This method is used if a closing symbol (e.g. a ')' or 'end') is missed. + * The message contains a %s as an placeholder for the position of the + * starting symbol. + * The exception will be catched at a position where error recovery + * can take place. + * + * @param location unique id of the error + * @param message message describing the error + * @param symbol starting symbol corresponding to the missed closing symbol + * @param position position of the starting symbol + */ + +void ReParser::syntaxError(int location, const char* message, + const char* symbol, + const ReSourcePosition* position) +{ + char buffer[256]; + char buffer2[512]; + qsnprintf(buffer2, sizeof buffer2, + "The starting symbol %s is located here. Missing point: %s", + symbol, m_lexer.currentPosition()->utf8(buffer, sizeof buffer)); + + addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message); + addSimpleMessage(LT_INFO, location + 1, position, buffer2); + throw RSyntaxError(message); +} + +/** + * @brief Adds an error message. + * + * If too much errors an exception will be thrown to stop parsing. + * + * @param location unique id of the error/warning message + * @param format message with placeholdes like sprintf() + * @param ... optional: the variable argument list + * @return false (for chaining) + */ +bool ReParser::error(int location, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + addMessage(LT_ERROR, location, m_lexer.currentPosition(), format, ap); + va_end(ap); + if (++m_errors >= m_maxErrors) + throw RplParserStop("too many errors"); + return false; +} +/** + * @brief Adds an error message with an additional info message. + * + * If too much errors an exception will be thrown to stop parsing. + * + * @param location unique id of the error/warning message + * @param position source position of the additional message + * @param message describes the error + * @param message2 the additional message + * @param ... optional: the variable argument list + * @return false (for chaining) + */ +bool ReParser::error(int location, const ReSourcePosition* position, + const char* message, const char* message2) +{ + addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message); + addSimpleMessage(LT_INFO, location + 1, position, message2); + if (++m_errors >= m_maxErrors) + throw RplParserStop("too many errors"); + return false; +} + +/** + * @brief Adds a warning message. + * + * If too much warnings an exception will be thrown to stop parsing. + * + * @param location unique id of the error/warning message + * @param format message with placeholdes like sprintf() + * @param ... optional: the variable argument list + */ +void ReParser::warning(int location, const char* format, ...) +{ + va_list ap; + va_start(ap, format); + addMessage(LT_WARNING, location, m_lexer.currentPosition(), format, ap); + va_end(ap); + if (++m_warnings >= m_maxWarnings) + throw RplParserStop("too many warnings"); +} +/** + * @brief Return the number of errors. + * + * @return the count of errors occurred until now + */ +int ReParser::errors() const +{ + return m_errors; +} +/** + * @brief Return the number of warnings. + * + * @return the count of errors occurred until now + */ +int ReParser::warnings() const +{ + return m_warnings; +} + + diff --git a/expr/ReParser.hpp b/expr/ReParser.hpp new file mode 100644 index 0000000..80b9f15 --- /dev/null +++ b/expr/ReParser.hpp @@ -0,0 +1,66 @@ +/* + * 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 REPARSER_HPP +#define REPARSER_HPP + +class ReSyntaxError +{ +public: + ReSyntaxError(const char* reason); +public: + const char* reason() const; +private: + const char* m_reason; +}; + +class RplParserStop : public ReSyntaxError { +public: + RplParserStop(const char* reason); +}; + +class ReParser { +public: + enum LevelTag { + LT_ERROR = 'E', + LT_WARNING = 'W', + LT_INFO = 'I' + }; + +public: + typedef QList MessageList; +public: + ReParser(ReLexer& lexer, ReASTree& ast); +public: + bool addSimpleMessage(LevelTag prefix, int location, + const ReSourcePosition* pos, + const char* message); + bool addMessage(LevelTag prefix, int location, + const ReSourcePosition* pos, + const char* format, va_list varList); + void syntaxError(int location, const char* message); + void syntaxError(int location, const char* message, const char* symbol, + const ReSourcePosition* position); + bool error(int location, const char* format, ...); + bool error(int location, const ReSourcePosition* position, + const char* message, const char* message2); + void warning(int location, const char* format, ...); + int errors() const; + int warnings() const; +protected: + ReLexer& m_lexer; + ReASTree& m_tree; + MessageList m_messages; + int m_errors; + int m_warnings; + int m_maxErrors; + int m_maxWarnings; +}; + +#endif // REPARSER_HPP diff --git a/expr/ReSource.cpp b/expr/ReSource.cpp new file mode 100644 index 0000000..b9e21e9 --- /dev/null +++ b/expr/ReSource.cpp @@ -0,0 +1,867 @@ +/* + * 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. +*/ + +/** @file + * @brief Reading from several input media. + * + * The abstract base class ReReader and its concrete derivations + * ReStringReader, ReFileReader are used to read + * from one medium. + * The ReSource combines several readers and build an uniquely + * usable input stream. + */ +/** @file expr/ReSource.hpp + * + * @brief Definitions for reading from several input media. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" + + +/** @class ReSource ReSource.hpp "expr/ReSource.hpp" + * + * @brief Stores all source positions. + * + */ + +/** @class ReSourceUnit ReSource.hpp "expr/ReSource.hpp" + * + * @brief Implements the base class of input source units. + * + * A source unit is set of input lines with a name, e.g. a file. + */ + +/** + * @brief Constructor + * @param name name of the unit + * @param reader the reader which can read the unit + */ +ReSourceUnit::ReSourceUnit(ReSourceUnitName name, ReReader* reader) : + m_name(name), + m_lineNo(0), + m_reader(reader) { +} + +/** + * @brief Destructor. + */ +ReSourceUnit::~ReSourceUnit() { +} + +/** + * @brief Returns the name. + * @return the name + */ +ReSourceUnitName ReSourceUnit::name() const { + return m_name.constData(); +} + +/** + * @brief Returns the line number. + * + * @return the line number + */ +int ReSourceUnit::lineNo() const { + return m_lineNo; +} + +/** + * @brief Sets the line number. + * + * @param lineNo the new line number + */ +void ReSourceUnit::setLineNo(int lineNo) { + m_lineNo = lineNo; +} +/** + * @brief Returns the reader of the instance. + * + * @return the reader belonging to the instance + */ +ReReader* ReSourceUnit::reader() const { + return m_reader; +} + +/** @class ReSourcePosition ReSource.hpp "expr/ReSource.hpp" + * + * @brief Stores a precise position in the input source. + * + * The input source contains normally a lot of nested source units. + * + * Each source unit owns a name and a sequence of lines. + * + * The mostly used source unit is a text file. + * + * A precice position is the stack of source unit positions. + */ + +/** + * @brief Constructor. + */ +ReSourcePosition::ReSourcePosition() : + m_sourceUnit(NULL), + m_lineNo(0), + m_column(0), + m_caller(NULL) +{ +} + +/** + * @brief Constructor. + * + * @param unit name of the input source (normally a file) + * @param lineNo line number inside the input source + * @param colNo distance to the line start + */ +ReSourcePosition::ReSourcePosition(ReSourceUnit* unit, int lineNo, + int colNo) : + m_sourceUnit(unit), + m_lineNo(lineNo), + m_column(colNo), + m_caller(NULL) +{ + ReReader* reader = dynamic_cast(unit->reader()); + m_caller = reader->source().caller(); +} +/** + * @brief Destructor + */ +ReSourcePosition::~ ReSourcePosition(){ + // That should never occure! + assert(false); +} + +/** + * @brief Placement new operator (for our own memory management). + * + * @param cbSize size of the instance + * @param buffer buffer for the instance + * @return buffer + */ +void*ReSourcePosition::operator new(size_t, void* buffer) +{ + return buffer; +} + +/** + * @brief Returns a description of the source position: "- ():". + * + * @return a description of the instance + */ +ReString ReSourcePosition::toString() const +{ + char buffer[512]; + utf8(buffer, sizeof buffer); + + return ReString(buffer); +} + +/** + * @brief Returns the position as a C string. + * + * @param buffer OUT: the target buffer + * @param bufferSize the size of the buffer + * @return buffer + */ +char* ReSourcePosition::utf8(char buffer[], size_t bufferSize) const +{ + qsnprintf(buffer, bufferSize, "%s:%d:%d", + m_sourceUnit == NULL ? "" : m_sourceUnit->name(), + m_lineNo, m_column); + return buffer; +} + +/** + * @brief Returns the line number. + * @return the line number + */ +int ReSourcePosition::lineNo() const +{ + return m_lineNo; +} + +/** + * @brief Sets the line number. + * + * @param lineNo the new lineNo + */ +void ReSourcePosition::setLineNo(int lineNo) +{ + m_lineNo = lineNo; +} +/** + * @brief Returns the column. + * + * @return the column of instance. + */ +int ReSourcePosition::column() const +{ + return m_column; +} + +/** + * @brief Sets the column. + * + * @param column the new column + */ +void ReSourcePosition::setColumn(int column) +{ + m_column = column; +} + +/** + * @brief Returns the source unit belonging to the instance. + * + * @return the source unit of the instance + */ +ReSourceUnit* ReSourcePosition::sourceUnit() const +{ + return m_sourceUnit; +} + +/** + * @brief Sets the source unit. + * + * @param sourceUnit the new source unit of the instance + */ +void ReSourcePosition::setSourceUnit(ReSourceUnit* sourceUnit) +{ + m_sourceUnit = sourceUnit; + m_lineNo = sourceUnit->lineNo(); +} + + +/** @class ReReader ReSource.hpp "expr/ReSource.hpp" + * + * @brief Implements a base class for readers of different media. + */ + + +/** + * @brief Constructor. + * + * @param source the parent + */ +ReReader::ReReader(ReSource& source) : + m_currentSourceUnit(NULL), + m_units(), + m_source(source) { +} + +/** + * @brief Destructor. + */ +ReReader::~ReReader() +{ + clear(); +} + +/** + * @brief Frees the resources. + */ +void ReReader::clear() +{ + UnitMap::iterator it; + for(it = m_units.begin(); it != m_units.end(); it++) { + ReStringSourceUnit* unit = (ReStringSourceUnit*)(*it); + delete unit; + } + m_units.clear(); + m_currentSourceUnit = NULL; +} + +/** + * @brief Returns the source of the reader. + * + * @return the parent, a source instance + */ +ReSource&ReReader::source() +{ + return m_source; +} +/** + * @brief Returns the current source unit. + * + * @return the source unit + */ +ReSourceUnit* ReReader::currentSourceUnit() const { + return m_currentSourceUnit; +} + +/** + * @brief Sets the current source unit. + * + * @param sourceUnit the name of the new source unit + * @return true: source unit exists
+ * false: source unit not found + */ +bool ReReader::setCurrentSourceUnit(ReSourceUnitName& sourceUnit) { + bool rc = m_units.contains(sourceUnit); + if(rc) { + m_currentSourceUnit = m_units.value(sourceUnit); + m_source.pushSourceUnit(m_currentSourceUnit); + } + return rc; +} + +/** + * @brief Removes the "latest" sourceUnit. + */ +void ReReader::removeSourceUnit() { + + m_currentSourceUnit = m_source.popSourceUnit(this);; +} + +/** @class ReSourcePositionBlock ReSource.hpp "expr/ReSource.hpp" + * + * @brief Efficient heap of ReSourcePosition instances. + * + * The ReSourcePosition heap is only growing. The deletion is + * done for all entries together. + * Therefore a simple allocation is possible with blocks. + */ +ReSourcePositionBlock::ReSourcePositionBlock() : + m_successor(NULL) + // m_positions +{ + memset(m_positions, 0, sizeof m_positions); +} + +/** @class ReSource ReSource.hpp "expr/ReSource.hpp" + * + * @brief Administrates a set of input sources with different readers. + * + * An input stream can be built by some different resources, e.g. files + * and memory buffers. The ReSource can administrate these resources. + */ +/** + * @brief Constructor. + */ +ReSource::ReSource() : + m_sourcePositionStack(), + m_sourcePositionBlock(NULL), + m_countPositionBlock(RPL_POSITIONS_PER_BLOCK + 1), + m_readers(), + m_sourceUnits(), + m_unitStack(), + m_currentReader(NULL) +{ + // the stack should never be empty: + m_sourcePositionStack.push(NULL); +} + +/** + * @brief Destructor. + */ +ReSource::~ReSource() { + destroy(); +} + +/** + * @brief Frees the resources of the instance. + */ +void ReSource::destroy() +{ + m_sourcePositionStack.clear(); + m_readers.clear(); + m_sourceUnits.clear(); + m_currentReader = NULL; + ReSourcePositionBlock* block = m_sourcePositionBlock; + m_sourcePositionBlock = NULL; + m_countPositionBlock = RPL_POSITIONS_PER_BLOCK + 1; + while(block != NULL){ + ReSourcePositionBlock* last = block; + block = block->m_successor; + delete last; + } +} + +/** + * @brief Returns the permanently valid source unit name. + * + * If unit names can be local objects (not string constants + * like __FILE__) then this method must be overridden by + * a method which builds a permanently valid string. + * + * @param unit unit to find + * @return a permanently valid unit name + */ +ReSourceUnitName ReSource::permanentUnitName(ReSourceUnitName unit) { + return unit; +} + +/** + * @brief Returns the stack with the positions of the open input resources. + * + * @return the stack + */ +QStack ReSource::sourcePositionStack() const { + return m_sourcePositionStack; +} + +/** + * @brief Returns the source unit stack. + * @return the stack of the source units + */ +QStack& ReSource::sourceUnitStack() +{ + return m_unitStack; +} + +/** + * @brief Adds a source reader. + * + * @param reader the new reader. Will be freed in the destructor + */ +void ReSource::addReader(ReReader* reader) { + m_readers.push_back(reader); + if (m_currentReader == NULL) + m_currentReader = reader; +} + +/** + * @brief Adds a source unit. + * + * @param unit the new unit. Will be freed in the destructor + */ +void ReSource::addSourceUnit(ReSourceUnit* unit) { + m_sourceUnits.push_back(unit); +} + +/** + * @brief Starts a new source unit. + * + * Saves the current source position onto the top of stack. + * Pushes the source unit onto the top of stack. + * + * @param unit the source unit + * @param caller the position of the include + * + */ +bool ReSource::startUnit(ReSourceUnitName unit, + const ReSourcePosition& caller) { + m_sourcePositionStack.push_back(&caller); + ReReader* reader = NULL; + QList::iterator it; + for(it = m_readers.begin(); + reader == NULL && it != m_readers.end(); + it++) { + ReReader* current = *it; + if(current->openSourceUnit(unit)) { + reader = current; + m_currentReader = current; + break; + } + } + return reader != NULL; +} + +/** + * @brief Pushes a source unit onto the stack. + * + * @param unit the source unit + */ +void ReSource::pushSourceUnit(ReSourceUnit* unit) { + m_unitStack.push(unit); +} + +/** + * @brief Removes the latest source unit from the stack. + * + * @param reader the current reader + * @return NULL: the current reader does not have an open source unit
+ * otherwise: the last entry from the source unit stack + */ +ReSourceUnit* ReSource::popSourceUnit(ReReader* reader) { + ReSourceUnit* rc = NULL; + if(m_unitStack.size() > 0) + m_unitStack.pop(); + m_currentReader = m_unitStack.size() <= 0 + ? NULL : m_unitStack.top()->reader(); + if(m_currentReader == reader) + rc = m_unitStack.top(); + else { + for(int ix = m_unitStack.size() - 2; ix >= 0; ix--){ + if(m_unitStack[ix]->reader() == reader){ + rc = m_unitStack[ix]; + break; + } + } + } + return rc; +} + +/** + * @brief Returns the reader of the current source unit. + * + * @return NULL: no reader active
+ * otherwise: the current reader + */ +ReReader* ReSource::currentReader() { + return m_currentReader; +} + +/** + * @brief Returns a new instance of the current source position. + * + * The storage is done in a block (efficency). + * + * @param colNo the column in the line + * @return a new instance of a source position + */ +const ReSourcePosition* ReSource::newPosition(int colNo) +{ + if (m_countPositionBlock >= RPL_POSITIONS_PER_BLOCK){ + ReSourcePositionBlock* newBlock = new ReSourcePositionBlock; + newBlock->m_successor = m_sourcePositionBlock; + m_sourcePositionBlock = newBlock; + m_countPositionBlock = 0; + } + unsigned offset = m_countPositionBlock * sizeof(ReSourcePosition); + m_countPositionBlock++; + char* posInBlock = &m_sourcePositionBlock->m_positions[offset]; + ReSourceUnit* unit = dynamic_cast( + m_currentReader->currentSourceUnit()); + ReSourcePosition* rc = new (posInBlock) ReSourcePosition( + unit, unit->lineNo(), colNo); + return rc; +} + +/** + * @brief Resets all states in the source. + */ +void ReSource::clear() +{ + destroy(); +} + +/** + * @brief Returns the top position of the source unit stack. + * + * @return NULL: stack is empty
+ * the top of the source unit stack + */ +const ReSourcePosition* ReSource::caller() const +{ + return m_sourcePositionStack.size() == 0 + ? NULL : m_sourcePositionStack.top(); +} + +/** @class ReStringSourceUnit ReSource.hpp "expr/ReSource.hpp" + * + * @brief Stores the state of a string based source unit. + * + */ + +/** + * @brief Constructor. + * + * @param name name of the unit + * @param content content of the unit + * @param reader the parent + */ +ReStringSourceUnit::ReStringSourceUnit(ReSourceUnitName name, + const ReSourceUnitContent& content, ReStringReader* reader) : + ReSourceUnit(name, reader), + m_currentPosition(0), + m_content(content) { +} + +/** + * @brief Destructor. + */ +ReStringSourceUnit::~ReStringSourceUnit() { +} +/** + * @brief Returns the current read position. + * + * @return the offset (count of QChars) of the end of the last read block + * inside m_content + */ +int ReStringSourceUnit::currentPosition() const { + return m_currentPosition; +} + +/** + * @brief Sets the current read position. + * + * @param currentPosition the offset (count of QChars) of the end of + * the last read block inside m_content + */ +void ReStringSourceUnit::setCurrentPosition(int currentPosition) { + m_currentPosition = currentPosition; +} +/** + * @brief Returns the content of the source unit. + * + * @return the content + */ +ReSourceUnitContent ReStringSourceUnit::content() const { + return m_content.constData(); +} + +/** @class ReStringReader ReSource.hpp "expr/ReSource.hpp" + * + * @brief Implements a source which provides reading from memory based buffers. + * + * Examples for usage: interactive input + */ + +/** + * @brief Constructor. + * + * @param source the parent + */ +ReStringReader::ReStringReader(ReSource& source) : + ReReader(source) { +} + +/** + * @brief Destructor. + */ +ReStringReader::~ReStringReader() { + clear(); +} + +/** + * @brief Opens a new source unit. + * + * @param unit name of the source + * @return NULL: unknown source
+ * otherwise: an instance of a sub class of + * ReSourceUnit + */ +ReSourceUnit* ReStringReader::openSourceUnit(ReSourceUnitName unit) { + ReSourceUnit* rc = NULL; + if(setCurrentSourceUnit(unit)) { + rc = m_currentSourceUnit; + ((ReStringSourceUnit*) rc)->setCurrentPosition(0); + } + return rc; +} +/** + * @brief Delivers the next line from the input medium or the first part of it. + * + * @param maxSize if the line length is longer than this value, only the + * first part of the line will be returned + * @param buffer OUT: the line will be put here + * @param hasMore true: the line was longer than maxSize, only + * the first part of the line is put into the buffer + * @return false: no more input available
+ * true: success + */ +bool ReStringReader::nextLine(int maxSize, QByteArray& buffer, bool& hasMore) { + bool rc = m_currentSourceUnit != NULL; + if (rc){ + m_currentSourceUnit->setLineNo(m_currentSourceUnit->lineNo() + 1); + rc = fillBuffer(maxSize, buffer, hasMore); + } + return rc; +} + +/** + * @brief Delivers the next part of a long line. + * + * @param maxSize if the remaining line length is longer than this value, + * only the a part of the line will be returned + * @param buffer OUT: the part of line will be put here + * @param hasMore true: the line was longer than maxSize, only + * the first part of the line is put into the buffer + * @return false: no more input available
+ * true: success + */ +bool ReStringReader::fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore) { + ReStringSourceUnit* unit = (ReStringSourceUnit*) m_currentSourceUnit; + ReSourceUnitContent content = unit->content(); + int startPos = unit->currentPosition(); + const char* start = content + startPos; + const char* end = strchr(start, '\n'); + hasMore = false; + int size = end == NULL ? strlen(start) : end - start + 1; + hasMore = false; + if(size > maxSize) { + size = maxSize; + hasMore = true; + } + if(size > 0) { + buffer.append(start, size); + unit->setCurrentPosition(startPos + size); + } else { + removeSourceUnit(); + } + return size > 0; +} + +/** + * @brief Adds a source buffer to the reader + * + * @param name name of the medium + * @param content + */ +void ReStringReader::addSource(ReSourceUnitName name, + ReSourceUnitContent content) { + // Deletion in the destructor of the base class ReReader + ReStringSourceUnit* unit = new ReStringSourceUnit(name, content, this); + m_units.insert(m_units.begin(), unit->name(), unit); + m_currentSourceUnit = unit; +} + +/** + * @brief Replaces the content of a source unit. + * + * @param name name of the source unit + * @param content new content + */ +void ReStringReader::replaceSource(ReSourceUnitName name, + ReSourceUnitContent content) +{ + if (m_units.contains(name)){ + ReStringSourceUnit* unit = dynamic_cast(m_units[name]); + unit->m_content = content; + } +} + +/** @class ReFileSourceUnit ReSource.hpp "expr/ReSource.hpp" + * + * @brief Stores the state of a file based source unit. + * + * This is the mostly used implementation of the ReSourceUnit/ReReader. + */ + + +ReFileSourceUnit::ReFileSourceUnit(ReSourceUnitName filename, + ReFileReader* reader) : + ReSourceUnit(filename, reader), + m_currentPosition(0), + m_fp(fopen(filename, "r")), + m_textStream(m_fp, QIODevice::ReadOnly), + m_line() +{ +} + + +/** + * @brief Destructor. + */ +ReFileSourceUnit::~ReFileSourceUnit() +{ + fclose(m_fp); +} + +bool ReFileSourceUnit::isOpen() const +{ + return m_fp != NULL; +} +/** @class ReFileReader ReSource.hpp "expr/ReSource.hpp" + * + * @brief Implements a source which provides reading from memory based buffers. + * + * Examples for usage: interactive input + */ + +/** + * @brief Constructor. + */ +ReFileReader::ReFileReader(ReSource& source) : + ReReader(source) +{ +} + +/** + * @brief Destructor. + */ +ReFileReader::~ReFileReader() { +} + +/** + * @brief Opens a new source unit. + * + * @param unit name of the source + * @return NULL: unknown source
+ * otherwise: an instance of a sub class of + * ReSourceUnit + */ +ReSourceUnit* ReFileReader::openSourceUnit(ReSourceUnitName unit) { + ReSourceUnit* rc = NULL; + if(m_units.contains(unit)) { + rc = *m_units.find(unit); + m_currentSourceUnit = static_cast(rc); + } + return rc; +} +/** + * @brief Delivers the next line from the input medium or the first part of it. + * + * @param maxSize if the line length is longer than this value, only the + * first part of the line will be returned + * @param buffer OUT: the line will be put here + * @param hasMore true: the line was longer than maxSize, only + * the first part of the line is put into the buffer + * @return false: no more input available
+ * true: success + */ +bool ReFileReader::nextLine(int maxSize, QByteArray& buffer, bool& hasMore) { + ReFileSourceUnit* unit = static_cast + (m_currentSourceUnit); + bool rc = ! feof(unit->m_fp); + if(! rc) { + m_source.popSourceUnit(this); + } else { + m_currentSourceUnit->setLineNo(m_currentSourceUnit->lineNo() + 1); + unit->m_currentPosition = 0; + QByteArray& line = unit->m_line; + line.reserve(maxSize+1); + if (fgets(line.data(), maxSize, unit->m_fp) == NULL) + rc = false; + else{ + line[maxSize] = '\0'; + line.resize(strlen(line.constData())); + rc = fillBuffer(maxSize, buffer, hasMore); + } + } + return rc; +} + +/** + * @brief Delivers the next part of a long line. + * + * @param maxSize if the remaining line length is longer than this value, + * only the a part of the line will be returned + * @param buffer OUT: the part of line will be put here + * @param hasMore true: the line was longer than maxSize, only + * the first part of the line is put into the buffer + * @return false: no more input available
+ * true: success + */ +bool ReFileReader::fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore) { + ReFileSourceUnit* unit = static_cast(m_currentSourceUnit); + int start = unit->m_currentPosition; + QByteArray& content = unit->m_line; + int size = content.size() - start; + if(size > maxSize) + size = maxSize; + buffer += content.mid(start, size); + unit->m_currentPosition = (start += size); + hasMore = start < content.size(); + return size > 0; +} + +/** + * @brief Adds a source file to the reader + * + * @param filename the file' name (relative or absolute) + */ +void ReFileReader::addSource(ReSourceUnitName filename) { + // Deleting in ~ReSourceUnit(): + ReFileSourceUnit* unit = new ReFileSourceUnit(filename, this); + m_units.insert(m_units.begin(), unit->name(), unit); + m_currentSourceUnit = unit; +} + + diff --git a/expr/ReSource.hpp b/expr/ReSource.hpp new file mode 100644 index 0000000..cba5ea0 --- /dev/null +++ b/expr/ReSource.hpp @@ -0,0 +1,227 @@ +/* + * 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 RPLSOURCE_HPP +#define RPLSOURCE_HPP + +// type of buffer names and filenames. Codec: UTF-8 +typedef const char* ReSourceUnitName; + +typedef const char* ReSourceUnitContent; + +class ReSource; +class ReReader; + +class ReSourceUnit { +public: + ReSourceUnit(const char* name, ReReader* reader); + virtual ~ReSourceUnit(); +public: + const char* name() const; + int lineNo() const; + void setLineNo(int lineNo); + ReReader* reader() const; + +protected: + QByteArray m_name; + int m_lineNo; + ReReader* m_reader; +}; + +class ReSourcePosition { +public: + ReSourcePosition(); + ReSourcePosition(ReSourceUnit* unit, int lineNo, int colNo); + ~ ReSourcePosition(); + void* operator new(size_t cbSize, void* buffer); +private: + /// forbid usage of the copy constructor! + ReSourcePosition(const ReSourcePosition& source); + /// forbid usage of the the assignment! + ReSourcePosition& operator=(const ReSourcePosition& source); +public: + ReString toString() const; + int lineNo() const; + void setLineNo(int lineNo); + + int column() const; + void setColumn(int column); + + ReSourceUnit* sourceUnit() const; + void setSourceUnit(ReSourceUnit* sourceUnit); + char*utf8(char buffer[], size_t bufferSize) const; +private: + ReSourceUnit* m_sourceUnit; + int m_lineNo; + int m_column; + const ReSourcePosition* m_caller; +}; + +class ReReader { +public: + typedef ReCharPtrMap UnitMap; +public: + ReReader(ReSource& source); + ~ReReader(); +public: + /** + * @brief Prepares the reading from a given source unit. + * + * @param unit name of the unit + * @return NULL: unit not known
+ * otherwise: an instance with the state of the reader + * for the source. This is normally a sub class of + * ReSourceUnit + */ + virtual ReSourceUnit* openSourceUnit(const char* unit) = 0; + /** + * @brief Reads the first part of the next line into a given buffer. + * + * @param maxSize the maximum length of the read input. + * If a line is longer the next part must be read + * by fillBuffer() + * @param buffer IN/OUT: the read input will be appended here + * @param hasMore OUT: true: the line is longer than maxSize + * @return true: the read was successful
+ * false: no more input is available + */ + virtual bool nextLine(int maxSize, QByteArray& buffer, bool& hasMore) = 0; + /** + * @brief Reads the next part of the current line into a given buffer. + * + * @param maxSize the maximum length of the read input. + * @param buffer IN/OUT: the read input will be appended here + * @param hasMore OUT: true: the rest of line is longer than maxSize + * @return true: the read was successful
+ * false: no more input is available + */ + virtual bool fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore) = 0; +public: + virtual void clear(); + ReSource& source(); + ReSourceUnit* currentSourceUnit() const; + bool setCurrentSourceUnit(ReSourceUnitName& currentSourceUnit); +protected: + void removeSourceUnit(); + +protected: + ReSourceUnit* m_currentSourceUnit; + /// name -> source + UnitMap m_units; + ReSource& m_source; +}; + +#define RPL_POSITIONS_PER_BLOCK 512 +class ReSourcePositionBlock{ + friend class ReSource; +public: + ReSourcePositionBlock(); +private: + ReSourcePositionBlock* m_successor; + char m_positions[RPL_POSITIONS_PER_BLOCK * sizeof(ReSourcePosition)]; +}; + +class ReSource { +public: + ReSource(); + virtual ~ReSource(); +public: + virtual const char* permanentUnitName(const char* unit); + void finishSourceUnit(); + void addReader(ReReader* reader); + void addSourceUnit(ReSourceUnit* unit); + QStack sourcePositionStack() const; + QStack& sourceUnitStack(); + + bool startUnit(const char* unit, const ReSourcePosition& caller); + void pushSourceUnit(ReSourceUnit* unit); + ReSourceUnit* popSourceUnit(ReReader* reader); + ReReader* currentReader(); + const ReSourcePosition* newPosition(int colNo); + void clear(); + const ReSourcePosition* caller() const; +protected: + void destroy(); +protected: + // stack of the info about the stacked (open) source units: + QStack m_sourcePositionStack; + ReSourcePositionBlock* m_sourcePositionBlock; + int m_countPositionBlock; + QList m_readers; + QList m_sourceUnits; + // setCurrentSourceUnit() pushes one entry, removeSourceUnit() pops it + // (when end of input has been reached). + QStack m_unitStack; + ReReader* m_currentReader; +}; + +class ReStringReader; + +class ReStringSourceUnit : public ReSourceUnit { + friend class ReStringReader; +public: + ReStringSourceUnit(ReSourceUnitName name, + const ReSourceUnitContent& content, + ReStringReader* reader); + virtual ~ReStringSourceUnit(); +public: + int currentPosition() const; + void setCurrentPosition(int currentPosition); + ReSourceUnitContent content() const; + +private: + int m_currentPosition; + QByteArray m_content; +}; + +class ReStringReader : public ReReader{ +public: + ReStringReader(ReSource& source); + virtual ~ReStringReader(); + // ReReader interface +public: + virtual ReSourceUnit* openSourceUnit(ReSourceUnitName unit); + virtual bool nextLine(int maxSize, QByteArray& buffer, bool& hasMore); + virtual bool fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore); +public: + void addSource(ReSourceUnitName name, ReSourceUnitContent content); + void replaceSource(ReSourceUnitName name, ReSourceUnitContent content); +}; + +class ReFileReader; + +class ReFileSourceUnit : public ReSourceUnit { + friend class ReFileReader; +public: + ReFileSourceUnit(ReSourceUnitName filename, ReFileReader* reader); + virtual ~ReFileSourceUnit(); +public: + bool isOpen() const; +private: + int m_currentPosition; + FILE* m_fp; + QTextStream m_textStream; + QByteArray m_line; +}; + +class ReFileReader : public ReReader{ +public: + ReFileReader(ReSource& source); + virtual ~ReFileReader(); + // ReReader interface +public: + virtual ReSourceUnit* openSourceUnit(ReSourceUnitName unit); + virtual bool nextLine(int maxSize, QByteArray& buffer, bool& hasMore); + virtual bool fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore); +public: + void addSource(ReSourceUnitName filename); +}; + + +#endif // RPLSOURCE_HPP diff --git a/expr/ReVM.cpp b/expr/ReVM.cpp new file mode 100644 index 0000000..1a4e177 --- /dev/null +++ b/expr/ReVM.cpp @@ -0,0 +1,486 @@ +/* + * 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. +*/ + +/** @file + * + * @brief Implements an interpreter of an abstract syntax tree. + */ + +/** @file expr/ReVM.hpp + * + * @brief Definitions for an interpreter of an abstract syntax tree. + */ + +#include "base/rebase.hpp" +#include "expr/reexpr.hpp" + +enum { + LOC_VAL_OF_VAR_1 = LOC_FIRST_OF(LOC_VM), // 11401 + LOC_UNOP_1, + LOC_UNOP_2, + LOC_UNOP_3, + LOC_UNOP_4, // 10005 + LOC_BINOP_1, + LOC_COUNT +}; + +int ReVMThread::m_nextId = 1; + +/** @class ReVMException ReVM.hpp "expr/ReVM.hpp" + * + * @brief Implements an exception for the virtual machine. + * + */ +/** + * @brief Constructor + * @param format the message with placeholders + * @param ... the values for the placeholders + */ +ReVMException::ReVMException(const char* format, ...) : + ReException("") +{ + char buffer[16000]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + m_message = buffer; +} + +/** @class ReStackFrame ReVM.hpp "expr/ReVM.hpp" + * + * @brief Implements the storage for a symbol space. + * + * The owner of a symbol space can be "global", a module, a class, or a method. + * Some symbol spaces have more than one stack frame, e.g. a recursive called + * method. + */ + + +/** + * @brief Constructor. + * + * @param symbols the symbol space belonging to the stack frame + */ + +ReStackFrame::ReStackFrame(ReASItem* caller, ReSymbolSpace* symbols) : + m_countVariables(symbols->listOfVars().size()), + m_variables(NULL), + m_symbols(symbols), + m_caller(caller) +{ + if (m_countVariables > 0) + m_variables = new ReASVariant[m_countVariables]; +} + +/** + * @brief Destructor. + */ +ReStackFrame::~ReStackFrame() +{ + delete[] m_variables; + m_variables = NULL; +} + +/** + * @brief Returns the storage of a variable given by the index. + * + * @param index the index of the variable + * + * @return the storage of the variable + */ +ReASVariant& ReStackFrame::valueOfVariable(int index) +{ + if (index < 0 || index >= m_countVariables) + throw ReVMException("valueOfVariable(): invalid index: %d", index); + return m_variables[index]; +} +/** + * @brief Returns the symbol space of the frame. + * + * @return the symbol space + */ +ReSymbolSpace* ReStackFrame::symbols() const +{ + return m_symbols; +} + +/** @class ReVMThread ReVM.hpp "expr/ReVM.hpp" + * + * @brief Implements a thread of the virtual machine. + * + * The virtual machine can execute many threads at the same time. + * Each thread has its own stack. + */ + +/** + * @brief Constructor. + * + * @param maxStack the maximal number of nested stack frames + * @param vm the parent, the virtual machine + */ +ReVMThread::ReVMThread(int maxStack, ReVirtualMachine* vm) : + m_id(m_nextId++), + m_debugMode(false), + m_singleStep(false), + m_tracing(false), + m_maxStack(maxStack), + m_frameStack(), + // the stack is never empty! + m_topOfFrames(0), + m_valueStack(), + // the stack is never empty! + m_topOfValues(0), + m_vm(vm), + m_logger(new ReLogger()) +{ + QByteArray prefix = "vm_thread_" + QByteArray::number(m_id); + m_logger->buildStandardAppender(prefix); + m_frameStack.reserve(maxStack); + // the stack is never empty! + m_frameStack.append(new ReStackFrame(vm->tree().symbolSpaces()[0])); + // the stack is never empty! + m_valueStack.append(new ReASVariant); +} + +/** + * @brief Executes a statement list. + * + * @param statements the first statement of a statement list chained by + * m_child + * @param space the current symbol space + */ +void ReVMThread::execute(ReASNode1* statements, ReSymbolSpace* space) +{ + bool debugMode = m_debugMode; + ReASNode1 *next; + while(statements != NULL){ + if (debugMode + && (m_singleStep + || (statements->flags() & ReASItem::NF_BREAKPOINT) != 0)) + debug(statements); + ReASStatement* statement = dynamic_cast(statements); + if (statement != NULL) + statement->execute(*this); + statements = dynamic_cast(statements->child()); + } +} + +/** + * @brief Handles a debugger break. + * + * @param statement the current statement (not yet executed) + */ +void ReVMThread::debug(ReASNode1* statement) +{ + +} + +/** + * @brief Returns the logger of the instance. + * + * @return the logger + */ +ReLogger* ReVMThread::logger() const +{ + return m_logger; +} +/** + * @brief Reserves a value in the thread's value stack. + * + * @return the reserved value + */ +ReASVariant&ReVMThread::reserveValue() +{ + ReASVariant* rc; + if (++m_topOfValues < m_valueStack.size()){ + rc = m_valueStack[m_topOfValues]; + rc->destroyValue(); + } else { + rc = new ReASVariant(); + m_valueStack.append(rc); + } + return *rc; +} + +/** + * @brief Returns the top of the value stack. + * + * @return the top of the value stack + */ +ReASVariant& ReVMThread::topOfValues() +{ + ReASVariant& rc = *m_valueStack[m_topOfValues]; + return rc; +} + + +/** + * @brief Returns the entry under the top of the value stack. + * + * @return the 2nd value the value stack + */ +ReASVariant& ReVMThread::top2OfValues() +{ + ReASVariant& rc = *m_valueStack[m_topOfValues-1]; + return rc; +} + +/** + * @brief Returns the top of stack and removes it. + * + * @return the old top of stack + */ +ReASVariant& ReVMThread::popValue() +{ + ReASVariant& rc = *m_valueStack[m_topOfValues]; + if (m_topOfValues > 0) + m_topOfValues--; + return rc; +} + +/** + * @brief Copies a variable value to the top of the stack. + * + * @param symbolSpace the symbol space of the variable + * @param variableNo the current no of the variable in the symbol space + */ +void ReVMThread::valueToTop(ReSymbolSpace* symbolSpace, int variableNo) +{ + //@ToDo +} + +/** + * @brief Returns the "left value" (of an assignment). + * @param item + * @return + */ +ReASVariant& ReVMThread::lValue(ReASItem* item) +{ + ReASVariant* rc; + switch(item->nodeType()){ + case AST_NAMED_VALUE: + { + ReASNamedValue* var = dynamic_cast(item); + rc = &valueOfVariable(var->symbolSpace(), var->variableNo()); + break; + } + default: + break; + } + return *rc; +} + +/** + * @brief Returns the reference of the value of a variable. + * + * @param symbolSpace the symbol space + * @param variableNo the current number in the symbol space + * @return + */ +ReASVariant& ReVMThread::valueOfVariable(ReSymbolSpace* symbolSpace, + int variableNo) +{ + ReASVariant& rc = *m_valueStack[0]; + int ix = m_topOfFrames; + ReStackFrame* frame = NULL; + for(int ix = m_topOfFrames; ix >= 0; ix--){ + frame = m_frameStack[ix]; + if (frame->symbols() == symbolSpace){ + rc = frame->valueOfVariable(variableNo); + break; + } + } + if (frame == NULL) + m_logger->logv(LOG_ERROR, LOC_VAL_OF_VAR_1, "no frame has symbolspace %s", + symbolSpace->name().constData()); + + return rc; +} +/** + * @brief Returns whether each execution step should be dumped. + * @return true: tracing is on
+ * false: otherwise + */ +bool ReVMThread::tracing() const +{ + return m_tracing; +} + +/** + * @brief Sets the tracing flag: if set each execution step is dumped. + * @param tracing true: tracing will be done
+ * false: tracing will not be done + */ +void ReVMThread::setTracing(bool tracing) +{ + m_tracing = tracing; +} + +/** + * @brief Returns the parent, a virtual machine. + * + * @return the virtual machine + */ +ReVirtualMachine* ReVMThread::vm() const +{ + return m_vm; +} + +/** + * @brief Adds a frame to the frame stack. + * + * @param frame frame to add + */ +void ReVMThread::pushFrame(ReStackFrame* frame) +{ + if (m_frameStack.size() >= m_maxStack) + throw ReASException(NULL, "too deep recursion: %d", m_maxStack); + m_frameStack.push_back(frame); +} + +/** + * @brief Removes the top of the frames from the stack. + */ +void ReVMThread::popFrame() +{ + if (m_frameStack.size() <= 0) + throw ReASException(NULL, "frame stack is empty"); + m_frameStack.pop_back(); +} + +/** @class ReVirtualMachine ReVM.hpp "expr/ReVM.hpp" + * + * @brief Implements a virtual machine. + * + * This is an execution unit which interprets an abstract syntax tree. + */ +ReVirtualMachine::ReVirtualMachine(ReASTree& tree, ReSource& source, + int maxStack) : + m_maxStack(maxStack), + m_threads(), + m_flags(VF_UNDEF), + m_source(source), + m_tree(tree), + m_trace() +{ + m_threads.reserve(8); + m_trace.reserve(1024); +} + +/** + * @brief Executes the program in a module. + * + * @param module the module's name + */ +void ReVirtualMachine::executeModule(const char* module) +{ + ReSymbolSpace* space = m_tree.findmodule(module); + if (space == NULL) + throw ReVMException("module not found: %s", module); + ReStackFrame frame(space); + ReSymbolSpace* mainSpace = NULL; + ReASItem* mainStatements = NULL; + ReASMethod* method = space->findMethod("main"); + if (method != NULL){ + mainStatements = method->child(); + mainSpace = method->symbols(); + } + addThread(space->body(), space, mainStatements, mainSpace); +} + +/** + * @brief Adds a thread to the instance. + * + * @param initialization the statements for initialization + * @param spaceInitialization the symbol space of the initialization + * @param statements the statement list to execute. This is normally + * the body of the main program + * @param space the symbol space of the statements, normally + * the symbol space of the main program + * @param maxStack the maximal number of nested stack frames. + * <= 0: use the default + */ +void ReVirtualMachine::addThread(ReASItem* initialization, + ReSymbolSpace* spaceInitialization, + ReASItem* statements, + ReSymbolSpace* space, + int maxStack) +{ + ReVMThread* thread = new ReVMThread( + maxStack <= 0 ? m_maxStack : maxStack, this); + m_threads.append(thread); + if (initialization != NULL){ + thread->execute(dynamic_cast(initialization), + spaceInitialization); + } + if (statements != NULL) + thread->execute(dynamic_cast(statements), space); +} +/** + * @brief Tests whether a given flag is set. + * + * @param flag flag to test + * @return true: the flag is set
+ * false: otherwise + */ +bool ReVirtualMachine::hasFlag(ReVirtualMachine::VMFlag flag) const +{ + bool rc = (m_flags & flag) != 0; + return rc; +} +/** + * @brief Adds a flag to the current flags. + * + * @param flag + * @return + */ +void ReVirtualMachine::setFlag(ReVirtualMachine::VMFlag flag) +{ + m_flags |= flag; +} + +/** + * @brief Adds a flag to the current flags. + * + * @param flag + * @return + */ +void ReVirtualMachine::clearFlag(ReVirtualMachine::VMFlag flag) +{ + m_flags &= ~flag; +} + +/** + * @brief Returns the trace writer. + * @return the trace writer + */ +ReWriter* ReVirtualMachine::traceWriter() const +{ + return m_traceWriter; +} + +/** + * @brief Sets the trace writer. + * + * @param traceWriter the new writer + */ +void ReVirtualMachine::setTraceWriter(ReWriter* traceWriter) +{ + m_traceWriter = traceWriter; +} +/** + * @brief Returns the abstract symbol tree. + * + * @return the abstract symbol tree + */ +ReASTree& ReVirtualMachine::tree() const +{ + return m_tree; +} + diff --git a/expr/ReVM.hpp b/expr/ReVM.hpp new file mode 100644 index 0000000..938a1a4 --- /dev/null +++ b/expr/ReVM.hpp @@ -0,0 +1,114 @@ +/* + * 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 ReVM_HPP +#define ReVM_HPP + +class ReVMException : public ReException { +public: + ReVMException(const char* message, ...); +}; +class ReStackFrame { +public: + ReStackFrame(ReASItem* caller, ReSymbolSpace* symbols); + ~ReStackFrame(); +public: + ReASVariant& valueOfVariable(int index); + ReSymbolSpace* symbols() const; + +private: + int m_countVariables; + ReASVariant* m_variables; + ReSymbolSpace* m_symbols; + ReASItem* m_caller; +}; + +class ReVirtualMachine; +class ReVMThread +{ + friend class ReASItem; + friend class ReASStatement; + friend class ReASCalculable; + friend class ReASCondition; +public: + typedef QList StackFrameList; +public: + ReVMThread(int maxStack, ReVirtualMachine* vm); +public: + void execute(ReASNode1* statements, ReSymbolSpace* space); + virtual void debug(ReASNode1* statement); + ReWriter* errorWriter() const; + void setErrorWriter(ReWriter* errorWriter); + ReLogger* logger() const; + ReASVariant& reserveValue(); + ReASVariant& topOfValues(); + ReASVariant& top2OfValues(); + ReASVariant& popValue(); + void valueToTop(ReSymbolSpace* symbolSpace, int variableNo); + ReASVariant& lValue(ReASItem* item); + ReASVariant& valueOfVariable(ReSymbolSpace* symbolSpace, int variableNo); + bool tracing() const; + void setTracing(bool tracing); + ReVirtualMachine* vm() const; + void pushFrame(ReStackFrame* frame); + void popFrame(); + +protected: + int m_id; + bool m_debugMode; + bool m_singleStep; + bool m_tracing; + int m_maxStack; + StackFrameList m_frameStack; + int m_topOfFrames; + QList m_valueStack; + int m_topOfValues; + ReVirtualMachine* m_vm; + ReLogger* m_logger; +private: + static int m_nextId; +}; + +class ReVirtualMachine +{ +public: + enum VMFlag { + VF_UNDEF, + VF_TRACE_STATEMENTS = 1<<1, + VF_TRACE_LOCALS = 1<<2, + VF_TRACE_AUTO_VARIABLES = 1<<3 + + }; + typedef QList LineList; +public: + ReVirtualMachine(ReASTree& tree, ReSource& source, int maxStack = 1024); +public: + void executeModule(const char* module); + void addThread(ReASItem* initialization, + ReSymbolSpace* spaceInitialization, + ReASItem* statements, ReSymbolSpace* space, + int maxStack = 0); + bool hasFlag(VMFlag flag) const; + void setFlag(VMFlag flag); + void clearFlag(VMFlag flag); + ReWriter* traceWriter() const; + void setTraceWriter(ReWriter* traceWriter); + ReASTree& tree() const; + +private: + int m_maxStack; + QList m_threads; + int m_flags; + ReSource& m_source; + ReASTree& m_tree; + LineList m_trace; + ReWriter* m_traceWriter; +}; + +#endif // ReVM_HPP diff --git a/expr/reexpr.hpp b/expr/reexpr.hpp new file mode 100644 index 0000000..3cb43d5 --- /dev/null +++ b/expr/reexpr.hpp @@ -0,0 +1,28 @@ +/* + * 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 RPLEXPR_HPP +#define RPLEXPR_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "rplexpr/rplsource.hpp" +#include "rplexpr/rpllexer.hpp" +#include "rplexpr/rplastree.hpp" +#include "rplexpr/ReVM.hpp" +#include "rplexpr/rplparser.hpp" +#include "rplexpr/rplmfparser.hpp" + +#endif // RPLEXPR_HPP diff --git a/math/ReEnigma.cpp b/math/ReEnigma.cpp new file mode 100644 index 0000000..f575f43 --- /dev/null +++ b/math/ReEnigma.cpp @@ -0,0 +1,284 @@ +/* + * + * 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. + */ +/** @file + * + * @brief Implements encryption and decryption engines. + */ +/** @file math/ReEnigma.hpp + * + * @brief Definitions for encryption and decryption engines. + */ + +#include "base/rebase.hpp" +#include "math/remath.hpp" + +/** @class ReEnigma::secret_t ReEnigma.hpp "math/ReEnigma.hpp" + * + * @brief Stores the internal structure of a secret. + * + * A secret can be a password, a salt, a certificate or simlar + * which makes an encryption individually. + */ +/** @class ReEnigma ReEnigma.hpp "math/ReEnigma.hpp" + * + * @brief Implements a portable en/decryption engine. + * + * It is used a symetric encryption. + * Therefore the encrypt methods can be used for decryption too. + * + * The encryption can be unique by using certificates. + * A certificate is a series of bytes affecting the pseudo random sequence. + * + * More than one certificate can be used. + * + * The encryption is done with an pseudo random generator. + * + * The portability (over byte order) is guaranteed if the + * pseudo random generator is portable (nomalizing big/little endian) + * and a 64 bit integer arithmetic is available. + * + */ +const char* ReEnigma::SET_DECIMALS = "0123456789"; +const char* ReEnigma::SET_HEXDIGITS = "0123456789abcdef"; +const char* ReEnigma::SET_ALPHANUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz_"; +const char* ReEnigma::SET_FILENAME = " !^°$%&=+~#-.0123456789ABCDEFGHIJKLM" + "NOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; +const char* ReEnigma::SET_32_127 = " !\"#$%&'()*+,-./0123456789:;<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f"; +const char* ReEnigma::SET_32_255 = " !\"#$%&'()*+,-./0123456789:;<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; +const char* ReEnigma::SET_PRINTABLE_127 = + "\t\r\n !\"#$%&'()*+,-./0123456789:;<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f"; +const char* ReEnigma::SET_PRINTABLE_255 = + "\t\r\n !\"#$%&'()*+,-./0123456789:;<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; + +/** + * @brief Constructor. + * + * @param random pseudo random generator + */ +ReEnigma::ReEnigma(ReRandom* random) : + m_random(random), + m_ownsRandom(false), + m_secrets(), + m_randomCalls(0), + m_randomSource("4711") { + m_randomSource.reserve(8096); + if(random == NULL) { + m_random = new ReRandom(); + m_ownsRandom = true; + } +} +/** + * @brief Destructor. + */ +ReEnigma::~ReEnigma() { + if(m_ownsRandom) { + delete m_random; + m_random = NULL; + } +} + +/** + * @brief Reads a certificate. + * + * @param filename the name of the certificate + * + * @return empty string: error while reading
+ * otherwise: the certificate as byte array + */ +QByteArray ReEnigma::readCertificate(const char* filename) { + QByteArray rc = "not implemented: readCertificate(): "; + rc += filename; + assert(rc.isEmpty()); + return rc; +} + +inline void buildBooster(QByteArray& booster, const char* charSet) { + int size = 257; + booster.fill(0, size); + int ix = 0; + unsigned char cc; + while((cc = (unsigned char) *charSet++) != '\0') { + booster[cc] = ++ix; + } + booster[0] = ix; +} + +/** + * @brief Encodes a string in place with a given character set. + * + * If a character of data is not included in the character set + * it remains unchanged + * + * @param data IN: data to encode
+ * OUT: data encoded + * @param size length of data. If 0 + * @param charSet a string containing all valid characters + * @param booster a performance booster. Once built it can be shared + * between all calls of encodeChar() and decodeChar() + * with the same character set
+ * IN: "": not initialized otherwise: ready for work + * OUT: ready for work + */ +void ReEnigma::encode(char* data, int size, const char* charSet, + QByteArray& booster) { + if(booster.length() == 0) { + buildBooster(booster, charSet); + } + int lengthOfCharSet = (int)(unsigned char) booster.at(0); + for(int ii = 0; ii < size; ii++) { + unsigned char cc = (unsigned char) data[ii]; + int ix = booster.at(cc); + if(ix != 0) { + int next = nextInt(lengthOfCharSet); + int ix2 = (ix - 1 + next) % lengthOfCharSet; + data[ii] = charSet[ix2]; + } + } +} + +/** + * @brief Decodes a string in place with a given character set. + * + * If a character of data is not included in the character set + * it remains unchanged + * + * @param data IN: data to decode
+ * OUT: data decoded + * @param size length of data. If 0 + * @param charSet a string containing all valid characters + * @param booster a performance booster. Once built it can be shared + * between all calls of encodeChar() and decodeChar() + * with the same character set
+ * IN: "": not initialized otherwise: ready for work + * OUT: ready for work + */ +void ReEnigma::decode(char* data, int size, const char* charSet, + QByteArray& booster) { + if(booster.length() == 0) { + buildBooster(booster, charSet); + } + int lengthOfCharSet = (int)(unsigned char) booster.at(0); + for(int ii = 0; ii < size; ii++) { + unsigned char cc = (unsigned char) data[ii]; + int ix = booster.at(cc); + if(ix != 0) { + int next = nextInt(lengthOfCharSet); + int ix2 = (lengthOfCharSet + ix -1 - next) % lengthOfCharSet; + data[ii] = charSet[ix2]; + } + } +} +/** + * @brief Encodes or decode a byte array. + * + * The encoding method is symetric. Therefore it can encode and decode. + * + * @param data IN: data to encode/decoded
+ * OUT: data encoded/decoded + */ +void ReEnigma::change(QByteArray& data) { + int randomLength = m_randomSource.length(); + for(int ix = data.length() - 1; ix >= 0; ix--) { + char item = data.at(ix); + item = (item ^ nextInt(0xff) ^ m_randomSource.at(ix % randomLength)); + data[ix] = item; + } +} + +/** + * @brief Adds a random source given by an byte array. + * + * A random source can be the binary form of an certificate, + * a salt, a password or similar. + * + * @param byteSecret a byte sequence which influences the random generation + */ +void ReEnigma::addByteSecret(QByteArray byteSecret) { + // we expand it to a multiple of 64 bit: + int oldSize = byteSecret.length(); + int newSize = (oldSize + 7) / 8 * 8; + int ix; + if(newSize > oldSize) { + byteSecret.resize(newSize); + int sum = 0; + int start = oldSize > 8 ? oldSize - 8 : 0; + for(ix = start; ix < oldSize; ix++) { + sum += ix + byteSecret.at(ix); + } + for(ix = oldSize; ix < newSize; ix++) { + sum += ix + 7; + byteSecret[ix] = (char)(sum + byteSecret.at(ix-1)); + } + } + int count = newSize / 8; + secret_t* secret = new secret_t(); + secret->m_count = count; + secret->m_list = new uint64_t[count]; + m_secrets.append(secret); + for(ix = 0; ix < count; ix++) { + uint64_t value = 0; + for(int ix2 = 0; ix2 < 8; ix2++) + value = (value << 8) + byteSecret.at(ix * 8 + ix2); + secret->m_list[ix] = value; + } + QByteArray value; + QCryptographicHash hash(QCryptographicHash::Md5); + ReRandom rand; + hash.addData(m_randomSource.constData(), 4); + for(ix = 0; ix < byteSecret.length(); ix++) { + hash.addData(byteSecret.constData() + ix, 1); + QByteArray current = hash.result(); + int ix2 = rand.nextInt(0, m_randomSource.length() - 1); + m_randomSource[ix2] = (m_randomSource.at(ix2) ^ current.at(0)); + m_randomSource.insert(0, current); + } +} + +/** + * @brief Returns the next random integer + * @param maxValue + * @return + */ +int ReEnigma::nextInt(int maxValue) { + uint64_t seed = 0; + QList::const_iterator it; + int ixSecret = m_randomCalls++; + int ix = ixSecret; + for(it = m_secrets.constBegin(); it != m_secrets.constEnd(); ++it) { + secret_t* secret = *it; + seed |= ((secret->m_list[ixSecret % secret->m_count]) >> (ix % 8)); + } + m_random->xorSeed(seed); + int rc = m_random->nextInt(0, maxValue); + return rc; +} + +/** + * @brief Sets the pseudo random to a define state. + * + * @param seed the initial state + */ +void ReEnigma::setSeed(uint64_t seed) { + m_random->setSeed(seed); + m_randomCalls = 0; +} + diff --git a/math/ReEnigma.hpp b/math/ReEnigma.hpp new file mode 100644 index 0000000..1f252c4 --- /dev/null +++ b/math/ReEnigma.hpp @@ -0,0 +1,59 @@ +#ifndef RPLENIGMA_HPP +#define RPLENIGMA_HPP + +class ReEnigma { +public: + ///> '0'..'9' + static const char* SET_DECIMALS; + ///> '0'..'9' 'a'..'f' + static const char* SET_HEXDIGITS; + ///> '0'..'9' 'A'..'Z' a'..'z' '_' + static const char* SET_ALPHANUM; + ///> '0'..'9' 'A'..'Z' z'..'z' '_' + static const char* SET_FILENAME; + ///> ' ' .. chr(127) + static const char* SET_32_127; + ///> ' ' .. chr(255) + static const char* SET_32_255; + ///> TAB, CR, NL, ' '..chr(127) + static const char* SET_PRINTABLE_127; + ///> TAB, CR, NL, ' '..chr(255) + static const char* SET_PRINTABLE_255; + +protected: + typedef struct { + int m_count; + uint64_t* m_list; + } secret_t; +public: + ReEnigma(ReRandom* random = NULL); + virtual ~ReEnigma(); +private: + // No copy constructor: no implementation! + ReEnigma(const ReEnigma& source); + // Prohibits assignment operator: no implementation! + ReEnigma& operator =(const ReEnigma& source); +public: + void encode(char* data, int size, const char* charSet, QByteArray& cache); + void decode(char* data, int size, const char* charSet, QByteArray& cache); + void change(QByteArray& data); + void addByteSecret(QByteArray byteSeed); + void setSeed(uint64_t seed); + QByteArray readCertificate(const char* filename); +protected: + int nextInt(int maxValue); + +protected: + ///> a pseudo random generator + ReRandom* m_random; + ///> true: m_random must be destroyed (in the destructor). + bool m_ownsRandom; + ///> This values will be mixed with m_random' seed + QList m_secrets; + ///> each call of setSeed sets this value to 0. + int m_randomCalls; + ///> a byte sequence derived from the secrets + QByteArray m_randomSource; +}; + +#endif // RPLENIGMA_HPP diff --git a/math/ReMatrix.cpp b/math/ReMatrix.cpp new file mode 100644 index 0000000..626fe26 --- /dev/null +++ b/math/ReMatrix.cpp @@ -0,0 +1,518 @@ +/* + * RplMatrix.cpp + * + * Created on: 29.05.2014 + * Author: hm + */ + +/** @file + * @brief Implements 2 dimensional matrices. + */ +/** @file rplmath/rplmatrix.hpp + * + * @brief Definitions for 2 dimensional matrices. + */ + +#include "base/rebase.hpp" +#include "math/remath.hpp" + +RplMatrixException::RplMatrixException(const RplMatrix& RplMatrix, + const char* format, ...) : + ReException() +{ + if (! RplMatrix.getName().isEmpty()) + m_message = RplMatrix.getName() + ": "; + char buffer[16*1024]; + + va_list args; + va_start(args, format); + qvsnprintf(buffer, sizeof buffer, format, args); + va_end(args); + m_message += buffer; +} + +/** + * Constructor. + */ +RplMatrix::RplMatrix(const char* name) : + m_rows(0), + m_cols(0), + m_values(NULL), + m_name(name) +{ +} + +/** + * Constructor. + * + * @param rows number of rows + * @param cols number of columns + * @param name the name of the matrix + */ +RplMatrix::RplMatrix(int rows, int cols, const char* name): + m_rows(rows), + m_cols(cols), + m_values(new MatVal[rows*cols]), + m_name(name) +{ +} +/** + * Destructor. + */ +RplMatrix::~RplMatrix() { + delete m_values; + m_values = NULL; +} + +/** + * Copy constructor. + * + * @param source source to copy + */ +RplMatrix::RplMatrix(const RplMatrix& source) : + m_rows(0), + m_cols(0), + m_values(NULL), + m_name(source.m_name + QByteArray("-copy")) +{ + resize(source.m_rows, source.m_cols, source.m_values); +} + +/** + * Checks the validity of the definition parameters. + * + * @param rows the row number + * @param cols the column number + * @throws RplMatrixException + */ +void RplMatrix::checkDefinition(int rows, int cols) const +{ + if (rows < 0) + throw RplMatrixException(*this, "row number negative: %d", rows); + if (cols < 0) + throw RplMatrixException(*this, "column number negative: %d", cols); + if (double(rows) * cols > 1.0*1000*1000) + throw RplMatrixException(*this, "too many elements: %d*%d", rows, cols); +} + +/** + * Checks the validity of the indexes. + * + * @param row the RplMatrix row number: 0..N-1 + * @param col the RplMatrix column number: 0..M-1 + * @throws RplMatrixException + */ +void RplMatrix::check(int row, int col) const +{ + if (row < 0 || row >= m_rows) + throw RplMatrixException(*this, "invalid row: %d not in [0,%d[", row, + m_rows); + if (col < 0 || col >= m_cols) + throw RplMatrixException(*this, "invalid column: %d not in [0,%d[", col, + m_cols); +} +/** + * Checks whether a given Matrix has the same dimensions. + * + * @param operand Matrix to compare + * @throws RplMatrixException + */ +void RplMatrix::checkSameDimension(const RplMatrix& operand) const +{ + if (m_rows != operand.getRows()) + throw RplMatrixException(*this, + "%s has a different row count: %d / %d", + operand.getName().constData(), m_rows, operand.getRows()); + if (m_cols != operand.getCols()) + throw RplMatrixException(*this, + "%s has a different column count: %d / %d", + operand.getName().constData(), m_cols, operand.getCols()); +} + +/** + * Assignment operator. + * + * @param source the source to copy + */ +RplMatrix& RplMatrix::operator =(const RplMatrix& source) +{ + resize(source.m_rows, source.m_cols, source.m_values); + return *this; +} +/** + * Adds a Matrix to the instance. + * + * @param operand Matrix to add + * @return the instance itself + */ +RplMatrix& RplMatrix::operator +=(const RplMatrix& operand) +{ + checkSameDimension(operand); + for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ + m_values[ix] += operand.m_values[ix]; + } + return *this; +} +/** + * Subtracts a matrix from the instance. + * + * @param operand matrix to subtract + * @return the instance itself + */ +RplMatrix& RplMatrix::operator -=(const RplMatrix& operand) +{ + checkSameDimension(operand); + for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ + m_values[ix] -= operand.m_values[ix]; + } + return *this; +} +/** + * Builds the sum of the instance and a given matrix. + * + * @param operand RplMatrix to add + * @return a new RplMatrix with the sum + */ +RplMatrix RplMatrix::operator +(const RplMatrix& operand) +{ + RplMatrix rc(*this); + rc += operand; + return rc; +} +/** + * Builds the difference of the instance and a given matrix. + * + * @param operand matrix to subtract + * @return a new matrix with the difference + */ +RplMatrix RplMatrix::operator -(const RplMatrix& operand) +{ + RplMatrix rc(*this); + rc -= operand; + return rc; +} +/** + * Adds a scalar to the instance. + * + * @param scalar scalar to add + * @return the instance itself + */ +RplMatrix& RplMatrix::operator +=(MatVal scalar) +{ + for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ + m_values[ix] += scalar; + } + return *this; +} +/** + * Adds a scalar to the instance. + * + * @param scalar scalar to add + * @return the instance itself + */ +RplMatrix& RplMatrix::operator -=(MatVal scalar) +{ + for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ + m_values[ix] -= scalar; + } + return *this; +} +/** + * Builds the sum of the instance and a given scalar. + * + * @param scalar scalar to add + * @return a new matrix with the sum + */ +RplMatrix RplMatrix::operator +(MatVal scalar) +{ + RplMatrix rc(*this); + rc += scalar; + return rc; +} +/** + * Builds the difference of the instance and a given scalar. + * + * @param scalar scalar to subtract + * @return a new matrix with the sum + */ +RplMatrix RplMatrix::operator -(MatVal scalar) +{ + RplMatrix rc(*this); + rc -= scalar; + return rc; +} +/** + * Tests the equiness of the instance with a given matrix. + * + * @param operand the matrix to compare + * @return true: the matrices are equal
+ * false: otherwise + */ +bool RplMatrix::operator ==(const RplMatrix& operand) const +{ + checkSameDimension(operand); + bool rc = true; + for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ + if (m_values[ix] != operand.m_values[ix]){ + rc = false; + break; + } + } + return rc; +} +/** + * Compares the instance with a given scalar. + * + * @param scalar the scalar to compare + * @return true: all elements are equal to the scalar
+ * false: otherwise + */ +bool RplMatrix::operator ==(MatVal scalar) const +{ + bool rc = true; + for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ + if (m_values[ix] != scalar){ + rc = false; + break; + } + } + return rc; +} +/** + * Sets a new row-column pair. + */ +RplMatrix& RplMatrix::resize(int rows, int cols, const MatVal values[], + MatVal defaultValue) +{ + checkDefinition(rows, cols); + if (rows != m_rows || cols != m_cols){ + delete m_values; + m_values = new MatVal[rows * cols]; + m_rows = rows; + m_cols = cols; + } + if (values == NULL) + { + for (int ix = rows*cols - 1; ix >= 0; ix--){ + m_values[ix] = defaultValue; + } + } else { + for (int ix = rows*cols - 1; ix >= 0; ix--){ + m_values[ix] = values[ix]; + } + } + return *this; +} +/** + * Returns the minimum and the maximum of the instance. + * + * @return a tuple with the minimum and the maximum + */ +Tuple2 RplMatrix::minMax() const +{ +#ifndef DBL_MAX +#define DBL_MAX std::numeric_limits::max() +#define DBL_MIN std::numeric_limits::min() +#endif + Tuple2 rc(DBL_MAX, DBL_MIN); + + for (int ix = m_rows*m_cols - 1; ix >= 0; ix--){ + MatVal x; + if ( (x = m_values[ix]) < rc.m_value1) + rc.m_value1 = x; + if (x > rc.m_value2) + rc.m_value2 = x; + } + return rc; +} + +/** + * Builds a matrix with exchanged rows and cols. + * + * @return the transposed matrix + */ +RplMatrix RplMatrix::transpose() const +{ + RplMatrix rc(m_cols, m_rows); + + for (int row = 0; row < m_rows; row++){ + for (int col = 0; col < m_cols; col++){ + rc.m_values[m_rows*col + row] = m_values[row * m_cols + col]; + } + } + return rc; +} +QByteArray RplMatrix::toString(const char* prefix, const char* format, + const char* rowSeparator, const char* colSeparator) const +{ + char buffer[128]; + Tuple2 minMaxi(minMax()); + QByteArray rc; + qsnprintf(buffer, sizeof buffer, format, minMaxi.m_value1); + int length = strlen(buffer); + qsnprintf(buffer, sizeof buffer, format, minMaxi.m_value2); + int length2 = strlen(buffer); + if (length < length2) + length = length2; + qsnprintf(buffer, sizeof buffer, format, + (minMaxi.m_value1 + minMaxi.m_value2) / 2); + length2 = strlen(buffer); + if (length < length2) + length = length2; + if (prefix == NULL) + prefix = ""; + length = m_rows * m_cols * (length + strlen(colSeparator)) + + m_rows * strlen(rowSeparator) + strlen(prefix) + 20; + rc.reserve(length); + rc += prefix; + rc += "["; + for (int row = 0; row < m_rows; row++){ + for (int col = 0; col < m_cols; col++){ + qsnprintf(buffer, sizeof buffer, format, m_values[m_cols*row + col]); + rc += buffer; + rc += colSeparator; + } + rc += rowSeparator; + } + rc += "]"; + return rc; +} +/** + * Finds the length of a column. + * + * @param text the text to inspect + * @param separator the column separator + * @return the count of chars between start and the next separator + */ +static int lengthOfColumn(const char* text, char separator){ + const char* ptr = text; + while(*ptr == ' ') + ptr++; + char delimiter = 0; + if (*ptr == '"' || *ptr == '\''){ + delimiter = *ptr++; + } + while(*ptr){ + if (*ptr == '\\'){ + ptr++; + if (*ptr != '\0') + ptr++; + } else if (*ptr == separator) + break; + else if (*ptr != delimiter){ + ptr++; + while(*ptr && *ptr != separator) + ptr++; + } + } + int rc = ptr - text; + return rc; +} +/** + * Skips all columns with a content other than a numeric value. + * + * @param line the text line + * @param separator the column separator + * @return the start of a number or "" + */ +static const char* skipNonNumbers(const char* line, char separator) +{ + int len1, len2 = 0; + + while ( (len1 = ReStringUtil::lengthOfNumber(line)) == 0 + && (len2 = lengthOfColumn(line, separator)) > 0) + line += len2; + if (*line == separator) + line++; + return line; +} +/** + * Returns the count of numeric numbers in a CSV line. + * + * @param line the line from a CSV file + * @return 0: not only numbers are in the line
+ * otherwise: the count of numeric columns in the line + */ +static int countNumbers(const char* line, char separator){ + line = skipNonNumbers(line, separator); + bool again = true; + int rc = 0; + char cc; + while(again && (cc = *line) != '\0' && cc != '\n' && cc != '\r'){ + int length = ReStringUtil::lengthOfNumber(line, true); + if (length == 0){ + rc = 0; + again = false; + } else { + line += length; + rc++; + if (*line == separator) + line++; + } + } + return rc; +} +/** + * Reads a file with the CSV (comma separated values) format + * into the instance. + */ +void RplMatrix::readFromCvs(const char* filename, int maxLineLength) +{ + FILE* fp = fopen(filename, "r"); + if (fp == NULL) + throw RplMatrixException(*this, "Cannot open %s (%d)", filename, errno); + char* buffer = new char[maxLineLength + 1]; + const char* line; + char separator = ReStringUtil::findCsvSeparator(fp, buffer, maxLineLength); + int rows = 0; + int cols = 0; + int nCols; + // find the count of rows and columns: + while( (line = fgets(buffer, maxLineLength, fp)) != NULL) + { + if ( (nCols = countNumbers(line, separator)) > 0){ + rows++; + if (nCols > cols) + cols = nCols; + } + } + resize(rows, cols); + // find the values + fseek(fp, 0, SEEK_SET); + int row = -1; + while( (line = fgets(buffer, maxLineLength, fp)) != NULL) + { + int nCols; + if ( (nCols = countNumbers(line, separator)) > 0){ + row++; + line = skipNonNumbers(line, separator); + int col = -1; + int length; + const char* ptr; + while( (length = ReStringUtil::lengthOfNumber(line, true)) > 0){ + col++; + ptr = line; + line += length; + while(*ptr == ' ') + ptr++; + MatVal value = atof(ptr); + m_values[m_cols*row + col] = value; + if (*line == separator) + line++; + else + break; + } + } + } + + fclose(fp); + delete buffer; +} +void RplMatrix::readFromXml(const char* filename, const char* tagCol, + const char* tagRow, const char* tagTable, + int maxLineLength) +{ + throw RplMatrixException(*this, "readFromXml not implementes: %s %s %s %s %d", + filename, tagCol, tagRow, tagTable, maxLineLength); +} + diff --git a/math/ReMatrix.hpp b/math/ReMatrix.hpp new file mode 100644 index 0000000..d0f168d --- /dev/null +++ b/math/ReMatrix.hpp @@ -0,0 +1,96 @@ +/* + * RplMatrix.hpp + * + * Created on: 29.05.2014 + * Author: hm + */ + +#ifndef RplMatrix_HPP_ +#define RplMatrix_HPP_ + + +class RplMatrix; +/** + * Implements a RplMatrix specific exception. + */ +class RplMatrixException : public ReException +{ +public: + RplMatrixException(const RplMatrix& RplMatrix, const char* format, ...); +}; + +/** + * The type of a RplMatrix element. + */ +typedef qreal MatVal; + +class Tuple2 { +public: + Tuple2(MatVal value1, MatVal value2) : + m_value1(value1), + m_value2(value2) + {} +public: + MatVal m_value1; + MatVal m_value2; +}; +/** + * Implements a RplMatrix with 2 dimensions. + */ +class RplMatrix { +public: + RplMatrix(const char* name = NULL); + RplMatrix(int rows, int cols, const char* name = NULL); + virtual ~RplMatrix(); + RplMatrix(const RplMatrix& source); + RplMatrix& operator =(const RplMatrix& source); +public: + RplMatrix& operator +=(const RplMatrix& operand); + RplMatrix& operator -=(const RplMatrix& operand); + RplMatrix operator +(const RplMatrix& operand); + RplMatrix operator -(const RplMatrix& operand); + RplMatrix& operator +=(MatVal scalar); + RplMatrix& operator -=(MatVal scalar); + RplMatrix operator +(MatVal scalar); + RplMatrix operator -(MatVal scalar); + bool operator ==(const RplMatrix& operand) const; + bool operator ==(MatVal scalar) const; + inline bool operator !=(const RplMatrix& operand) const + { return ! (*this == operand); } + inline bool operator !=(MatVal operand) + { return ! (*this == operand); } +public: + inline const QByteArray& getName() const + { return m_name; } + inline MatVal get(int row, int col) const + { check(row, col); return m_values[row*m_cols + col]; } + inline RplMatrix& set(int row, int col, MatVal value) + { check(row, col); m_values[row*m_cols + col] = value; return *this; } + inline int getRows() const + { return m_rows; } + inline int getCols() const + { return m_cols; } +public: + void checkDefinition(int rows, int cols) const; + void check(int row, int col) const; + void checkSameDimension(const RplMatrix& operand) const; + RplMatrix& resize(int rows, int cols, const MatVal values[] = NULL, + MatVal defaultValue = 0.0); + Tuple2 minMax() const; + RplMatrix transpose() const; + QByteArray toString(const char* prefix = NULL, + const char* format = "%f", + const char* rowSeparator = "\n", + const char* colSeparator = ",") const; + void readFromCvs(const char* filename, int maxLineLength = 1024*1024); + void readFromXml(const char* filename, const char* tagCol, + const char* tagRow, const char* tagTable, + int maxLineLength = 1024*1024); +protected: + int m_rows; + int m_cols; + MatVal* m_values; + QByteArray m_name; +}; + +#endif /* RplMatrix_HPP_ */ diff --git a/math/ReRandom.cpp b/math/ReRandom.cpp new file mode 100644 index 0000000..4d38a4f --- /dev/null +++ b/math/ReRandom.cpp @@ -0,0 +1,129 @@ +/* + * 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. + */ + +/** @file + * + * @brief Implements pseudo random generators. + */ +/** @file math/ReRandom.hpp + * + * @brief Definitions for pseudo random generators. + */ +#include "base/rebase.hpp" +#include "math/remath.hpp" + +/** @class ReRandom ReRandom.hpp "math/ReRandom.hpp" + * + * @brief Implements a portable pseudo random generator. + * + */ + +/** + * @brief Constructor. + */ +ReRandom::ReRandom() : + m_seed(0) { +} + +/** + * @brief Destructor. + */ +ReRandom::~ReRandom() { +} + +/** + * @brief Returns the next random number as 64 bit unsigned integer. + * + * @return the next random number. + */ +uint64_t ReRandom::nextInt64() { + // Donald Knuth recommands (for 64-Bit): + m_seed = m_seed * 6364136223846793005L + 1442695040888963407L; + return m_seed; +} +/** + * @brief Sets the random seed. + * + * @param seed the new seed. + */ +void ReRandom::setSeed(uint64_t seed) { + m_seed = seed; +} +/** + * @brief Modifies the seed. + * + * @param seed the XOR operand. + */ +void ReRandom::xorSeed(uint64_t seed) { + m_seed ^= seed; +} + +/** + * @brief nextByte returns the next random byte. + * + * @return a pseudo random value 0..255 + */ +uint8_t ReRandom::nextByte() { + uint64_t value = nextInt64(); + // forget the last 3 bits: + uint8_t rc = (uint8_t)(value >> 3) % 256; + return rc; +} + +/** + * @brief Returns the next pseudo random integer. + * + * @param minValue the minimal result (including) + * @param maxValue the maximal result (includeing) + * @return the next integer + */ +int ReRandom::nextInt(int minValue, int maxValue) { + uint64_t value = nextInt64(); + uint64_t diff = maxValue - minValue; + int rc; + if(diff <= 0) + rc = minValue; + else + rc = (int)(minValue + value % diff); + return rc; +} + +/** + * @brief Returns a random string. + * + * @param length the length of the result + * @param minChar all characters of the result are greater or equal than this value + * @param maxChar all characters of the result are lower or equal than this value + * @return a random string + */ +QByteArray ReRandom::nextString(int length, char minChar, char maxChar) { + QByteArray rc; + rc.resize(length); + for(int ii = 0; ii < length; ii++) { + rc[ii] = nextInt(minChar, maxChar); + } + return rc; +} + +/** + * @brief Returns a random string. + * + * @param length the length of the result + * @param charSet a string with all allowed characters + * @return a random string with characters from the given set + */ +QByteArray ReRandom::nextString(int length, char* charSet) { + QByteArray rc; + rc.resize(length); + int ubound = strlen(charSet) - 1; + for(int ii = 0; ii < length; ii++) { + rc[ii] = charSet[nextInt(0, ubound)]; + } + return rc; +} + diff --git a/math/ReRandom.hpp b/math/ReRandom.hpp new file mode 100644 index 0000000..14e04f7 --- /dev/null +++ b/math/ReRandom.hpp @@ -0,0 +1,26 @@ +#ifndef RERANDOM_HPP +#define RERANDOM_HPP + +class ReRandom { +public: + ReRandom(); + virtual ~ReRandom(); +private: + // No copy constructor: no implementation! + ReRandom(const ReRandom& source); + // Prohibits assignment operator: no implementation! + ReRandom& operator =(const ReRandom& source); +public: + virtual uint64_t nextInt64(); + virtual void setSeed(uint64_t seed); + void xorSeed(uint64_t seed); + uint8_t nextByte(); + int nextInt(int minValue, int maxValue); + QByteArray nextString(int length = 8, char minChar = ' ', char maxChar = 127); + QByteArray nextString(int length, char* charSet); +protected: + uint64_t m_seed; +}; + + +#endif // RERANDOM_HPP diff --git a/math/remath.hpp b/math/remath.hpp new file mode 100644 index 0000000..236f3fd --- /dev/null +++ b/math/remath.hpp @@ -0,0 +1,17 @@ +/* + * 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 RPLMATH_HPP +#define RPLMATH_HPP + +#include +#include +#include "math/ReRandom.hpp" +#include "math/ReEnigma.hpp" +#include "math/ReMatrix.hpp" + +#endif // RPLMATH_HPP diff --git a/net/ReNetConfig.cpp b/net/ReNetConfig.cpp new file mode 100644 index 0000000..da7bcc1 --- /dev/null +++ b/net/ReNetConfig.cpp @@ -0,0 +1,15 @@ +/* + * 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 "base/rebase.hpp" +#include "math/remath.hpp" +#include "net/renet.hpp" + +const char* ReNetConfig::IP = "connection.ip"; +const char* ReNetConfig::PORT = "connection.port"; +const char* ReNetConfig::SLEEP_MILLISEC = "connection.sleepmillisec"; diff --git a/net/ReNetConfig.hpp b/net/ReNetConfig.hpp new file mode 100644 index 0000000..9cef175 --- /dev/null +++ b/net/ReNetConfig.hpp @@ -0,0 +1,21 @@ +/* + * 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 RPLNETCONFIG_HPP +#define RPLNETCONFIG_HPP + +class ReNetConfig +{ +public: + static const char* IP; + static const char* PORT; + static const char* SLEEP_MILLISEC; +}; + +#endif // RPLNETCONFIG_HPP diff --git a/net/ReTCPClient.cpp b/net/ReTCPClient.cpp new file mode 100644 index 0000000..8912fe9 --- /dev/null +++ b/net/ReTCPClient.cpp @@ -0,0 +1,151 @@ +/* + * 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 "base/rebase.hpp" +#include "math/remath.hpp" +#include "net/renet.hpp" + +enum { + LOC_1 = LOC_FIRST_OF(LOC_TCPCLIENT), // 10701 + LOC_HANDLE_ERROR_1, + LOC_SET_REMOTE_ADDRESS_1, +}; + +/** @class RplTcpClient rpltcpclient.hpp "rplnet/rpltcpclient.hpp" + * + * @brief Implements a TCP client. + * + * Use the protocol defined at ReTCPServer. + */ +/** + * @brief Constructor. + * + * @param configurator some parameters will be get from this configurator + * @param thread current thread. Used for sleep() + * @param terminator NULL or for controlled termination + * @param logger a logger + */ +RplTcpClient::RplTcpClient(ReConfigurator& configurator, QThread* thread, + ReTerminator* terminator, + ReLogger* logger) : + m_peer(new ReTCPPeer(configurator, thread, terminator, false, logger)), + m_logger(logger), + m_configurator(configurator){ + QByteArray ip = configurator.asString(ReNetConfig::IP, "localhost"); + int port = configurator.asInt(ReNetConfig::PORT, 12345); + if(! ip.isEmpty() && port != 0) + setRemoteAddress(ip.constData(), port); +} + +/** + * @brief Destructor. + */ +RplTcpClient::~RplTcpClient() { + delete m_peer; + m_peer = NULL; +} + +/** + * @brief Defines the remote address for a client. + * @param ip NULL or the ip to connect + * @param port 0 or the port to connect + */ +void RplTcpClient::setRemoteAddress(const char* ip, int port) { + QTcpSocket* socket = (QTcpSocket*) m_peer->getSocket(); + delete socket; + if(ip == NULL || port == 0) + m_peer->setSocket(NULL); + else { + socket = new QTcpSocket(); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(handleError(QAbstractSocket::SocketError))); + m_peer->setSocket(socket); + m_peer->setAddress(ip, port); + m_logger->logv(LOG_INFO, LOC_SET_REMOTE_ADDRESS_1, + "connect with %s:%d", ip, port); + socket->connectToHost(ReString(ip), port); + socket->waitForConnected(); + } +} + +/** + * @brief Returns the peer info. + * @return the peer info + */ +ReTCPPeer* RplTcpClient::getPeer() const { + return m_peer; +} + +/** + * @brief Handles a network error. + * + * @param socketError the error code + */ +void RplTcpClient::handleError(QAbstractSocket::SocketError socketError) { + if (m_logger != NULL) + m_logger->logv(LOG_ERROR, LOC_HANDLE_ERROR_1, "Network error %d", + socketError); +} + +/** @class RplClientThread rpltcpclient.hpp "rplnet/rpltcpclient.hpp" + * + * @brief Implements a thread usable for a tcp client. + * + * Each ReTCPPeer needs a thread. Therefore this class provides all things + * needed for a RplTcpClient which uses a ReTCPPeer. + */ + +/** + * @brief Constructor. + * + * @param configurator delivers some connection parameters + * @param logger the logger. If NULL a default logger will be used + */ +RplClientThread::RplClientThread(ReConfigurator& configurator, + ReLogger* logger) : + m_client(NULL), + m_logger(logger), + m_configurator(configurator), + m_ownsLogger(false) { + m_client = new RplTcpClient(configurator, this, NULL, logger); +} +/** + * @brief Destructor. + */ +RplClientThread::~RplClientThread() { + delete m_client; + m_client = NULL; + if(m_ownsLogger) { + delete m_logger; + m_logger = NULL; + } +} +/** + * @brief Returns the peer which can be used for sending and receiving messages. + * + * @return the peer + */ +ReTCPPeer* RplClientThread::getPeer() const { + return m_client->getPeer(); +} + +/** + * @brief Returns the logger of the thread. + * @return the logger + */ +ReLogger* RplClientThread::getLogger() const { + return m_logger; +} + +/** + * @brief Contains the main method of the thread. + * + * Calls doIt() for the real things. + */ +void RplClientThread::run() { + doIt(); +} diff --git a/net/ReTCPClient.hpp b/net/ReTCPClient.hpp new file mode 100644 index 0000000..227df7c --- /dev/null +++ b/net/ReTCPClient.hpp @@ -0,0 +1,72 @@ +/* + * 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 RPLTCPCLIENT_HPP +#define RPLTCPCLIENT_HPP + +#ifndef RPLNET_HPP +#include "renet.hpp" +#endif + +class ReTCPPeer; + +class RplTcpClient : public QObject { + Q_OBJECT +public: + RplTcpClient(ReConfigurator& configurator, QThread* thread, + ReTerminator* terminator, + ReLogger* logger = NULL); + virtual ~RplTcpClient(); +private: + // No copy constructor: no implementation! + RplTcpClient(const RplTcpClient& source); + // Prohibits assignment operator: no implementation! + RplTcpClient& operator =(const RplTcpClient& source); +public: + ReTCPPeer* getPeer() const; +private: + void setRemoteAddress(const char* ip, int port); +public slots: + void handleError(QAbstractSocket::SocketError socketError); +private: + ReTCPPeer* m_peer; + ReLogger* m_logger; + ReConfigurator& m_configurator; +}; + +class RplClientThread : public QThread { +public: + RplClientThread(ReConfigurator& configurator, + ReLogger* logger = NULL); + virtual ~RplClientThread(); +private: + // No copy constructor: no implementation! + RplClientThread(const RplClientThread& source); + // Prohibits the assignment operator. Not implemented! + RplClientThread& operator=(const RplClientThread& source); +public: + /** + * @brief Does the main task of the thread. + * + * Will be called from QThread::run(). + * The implementations of this abstract method should be call getPeer() + * to send and receive messages. + */ + virtual void doIt() = 0; + ReTCPPeer* getPeer() const; + ReLogger* getLogger() const; +private: + virtual void run(); +protected: + RplTcpClient* m_client; + ReLogger* m_logger; + ReConfigurator& m_configurator; +private: + bool m_ownsLogger; +}; + +#endif // RPLTCPCLIENT_HPP diff --git a/net/ReTCPPeer.cpp b/net/ReTCPPeer.cpp new file mode 100644 index 0000000..cd8dcf6 --- /dev/null +++ b/net/ReTCPPeer.cpp @@ -0,0 +1,362 @@ +/* + * 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 "base/rebase.hpp" +#include "math/remath.hpp" +#include "net/renet.hpp" + +enum { + LOC_SEND_1 = LOC_FIRST_OF(LOC_TCPPEER), // 10801 + LOC_READ_BYTES_1, + LOC_READ_BYTES_2, + LOC_READ_BYTES_3, + LOC_READ_BYTES_4, + LOC_HANDLE_ERROR_1, + LOC_SEND_2, +}; + +static int s_dummy = 0; + +/** @class ReTCPPeer rpltcppeer.hpp "rplnet/rpltcppeer.hpp" + * + * @brief Implements the common things for TCP server and client. + * + * The communication is done with the following protocol: + *
    + *
  • The data is transmitted via TCP.
  • + *
  • The data exchange is done with info units. + *
  • Each info unit contains a header and the data.
  • + *
+ * The format of the header: + *
FLAGS [SALT] COMMAND SIZE
+ * 
+ *
    + *
  • FLAGS (1 byte): a XOR sum of the flags defined in rpltcppeer::flag_t.
  • + *
  • SALT (4 byte): a random value. Controls the encryption. Only available if FLAG_ENCRYPT is set.
  • + *
  • COMMAND (5 byte): define the task to do (client to server) or the answer (server to client). + *
  • SIZE (2 or 4 byte): the size of the data behind the header. 4 bytes if FLAG_4_BYTE_SIZE is set.
  • + *
+ * + */ + +/** + * @brief Creates an instance of a ReTCPPeer. + * + * @param configurator delivers some connection parameters + * @param thread the current thread. Used for sleep() + * @param terminator NULL or for controlled thread termination + * @param logger logger. If Null the global logger will be taken (not thread safe!) + * @return an instance of ReTCPPeer + */ +ReTCPPeer* ReTCPPeer::createPeer(ReConfigurator& configurator, + QThread* thread, + ReTerminator* terminator, + ReLogger* logger) { + return new ReTCPPeer(configurator, thread, terminator, logger); +} + +/** + * @brief Constructor. + * + * @param configurator delivers some connection parameters + * @param thread the current thread. Used for sleep() + * @param terminator NULL or for controlled thread termination + * @param isServer true: the receiving does have a timeout + * @param logger logger. If Null the global logger will be taken (not thread safe!) + */ +ReTCPPeer::ReTCPPeer(ReConfigurator& configurator, QThread* thread, + ReTerminator* terminator, + bool isServer, + ReLogger* logger) : + m_socket(NULL), + m_logger(logger == NULL ? ReLogger::globalLogger() : logger), + m_thread(thread), + m_random(), + m_timeout(isServer ? 0 : configurator.asInt("connection.timeout", 60)), + m_terminator(terminator), + m_configurator(configurator), + m_isServer(isServer), + m_dataLocker(QMutex::NonRecursive), + m_waitForData() { + // Simulate a true random with time, and addresses from stack and code segment: + m_random.setSeed(time(NULL) + ((int64_t) this << 8) + ((int64_t) &s_dummy << 16) + + ((int64_t) &createPeer << 24)); +} + +/** + * @brief Destructor. + */ +ReTCPPeer::~ReTCPPeer() { + +} + +/** + * @brief Sends a message via TCP. + * + * @param flags a sum of FLAGS_... constants + * @param command defines the content of the message + * @param data NULL or additional data + * @return true: success
+ * false: error occurred + */ +bool ReTCPPeer::send(qint8 flags, const char* command, + const QByteArray& data) { + bool rc = false; + QByteArray header; + QByteArray data2 = ReStringUtil::toCString(data.constData(), 20); + m_logger->logv(LOG_INFO, LOC_SEND_1, "send: flags: %x %s %s (%d)", + flags, command, data2.constData(), data.length()); + header.reserve(16); + header.append((char) flags); + if(flags & FLAG_ENCRYPT) { + header.append((char) m_random.nextByte()); + header.append((char) m_random.nextByte()); + header.append((char) m_random.nextByte()); + header.append((char) m_random.nextByte()); + } + unsigned int length = data.length(); + header.append(char(length % 256)); + header.append(char((length >> 8) % 256)); + if(flags & FLAG_4_BYTE_SIZE) { + header.append(char((length >> 16) % 256)); + header.append(char((length >> 24) % 256)); + } + length = strlen(command); + header.append(command, length < 5 ? length : 5); + while(length++ < 5) { + header.append(' '); + } + int64_t written = m_socket->write(header.constData(), header.length()); + int64_t written2 = m_socket->write(data); + m_socket->flush(); + int count = 0; + if(written != header.length() || written2 != data.length()) { + int endTime = time(NULL) + m_timeout; + // wait until the data are sent or timeout or external termination: + while(m_socket->bytesToWrite() > 0) { + m_thread->msleep(1); + if(++count % 20 == 0) { + if(m_terminator == NULL || m_terminator->isStopped() + || time(NULL) > endTime) + break; + } + } + } + if(m_logger->isActive(LOG_DEBUG)) + m_logger->logv(LOG_DEBUG, LOC_SEND_1, "send %s: %s len=%d loops=%d %s", + m_address.constData(), command, data.length(), count, + ReStringUtil::hexDump((const void*) data.constData(), 16, 16).constData()); + return rc; +} + +/** + * @brief Reads an amount of bytes with a timeout. + * + * @param bytes count of bytes to read + * @param maxTime IN/OUT: last time the read must be ready + * @param loops IN/OUT: number of sleeps + * + * @return "": read not successful: timeout or termination or error
+ * otherwise: the read bytes + */ +QByteArray ReTCPPeer::readBytes(int bytes, time_t maxTime, int& loops) { + QAbstractSocket* socket = getSocket(); + bool success = true; + int64_t available; + long msec = m_configurator.asInt(ReNetConfig::SLEEP_MILLISEC, 1); + int divider = 1000L / (msec == 0 ? 1 : msec); + if (divider < 1) + divider = 1; + QMutexLocker locker(&m_dataLocker); + m_dataLocker.lock(); + + while(! m_waitForData.wait(&m_dataLocker, 1000L)){ + if(loops == 0 && ! m_isServer) + maxTime = time(NULL) + m_timeout; + if(++loops % divider == 0 && ! m_isServer) { + if(time(NULL) > maxTime) { + m_logger->logv(LOG_ERROR, LOC_READ_BYTES_1, + "receive: timeout (%d)", m_timeout); + success = false; + break; + } + } + if(m_terminator != NULL && m_terminator->isStopped()) { + m_logger->log(LOG_ERROR, LOC_READ_BYTES_2, "receive: stopped"); + success = false; + break; + } + } + available = socket->bytesAvailable(); + m_logger->logv(LOG_DEBUG, LOC_READ_BYTES_4, + "readBytes(): available: %ld/%ld", available, bytes); + QByteArray rc; + if(success) { + rc = socket->read(bytes); + if(rc.length() != bytes) { + m_logger->logv(LOG_ERROR, LOC_READ_BYTES_3, + "receive: too few bytes: %d of %d", + rc.length(), bytes); + } + } + return rc; +} + +int getInt(const QByteArray& data, int offset, int size) { + int rc = ((int)(unsigned char) data.at(offset++)); + while(--size > 0) { + rc = rc * 256 + (unsigned char) data.at(offset++); + } + return rc; +} + +/** + * @brief Receives a message via TCP. + * + * @param command OUT: defines the content of the read message + * @param data OUT: "" or additional data + * @return true: success
+ * false: error occurred + */ +bool ReTCPPeer::receive(QByteArray& command, QByteArray& data) { + bool rc = true; + command.clear(); + data.clear(); + QByteArray header; + header.reserve(16); + int minHeaderSize = 8; + int loops = 0; + time_t maxTime = 0; + uint8_t flags = 0; + header = readBytes(minHeaderSize, maxTime, loops); + if(header.length() > 0) { + flags = header.at(0); + int headerSize = minHeaderSize; + if((flags & FLAG_4_BYTE_SIZE) != 0) + headerSize += 2; + if((flags & FLAG_ENCRYPT) != 0) + headerSize += 4; + if(headerSize != minHeaderSize) { + QByteArray restHeader = readBytes(headerSize - minHeaderSize, + maxTime, loops); + if(restHeader.length() == 0) + header.clear(); + else + header.append(restHeader); + } + } + rc = header.length() > 0; + if(rc) { + int offset = (flags & FLAG_ENCRYPT) == 0 ? 6 : 8; + int size = (flags & FLAG_4_BYTE_SIZE) == 0 ? 4 : 2; + int dataLength = getInt(header, offset, size); + command = header.mid(offset - 5, 5); + data = readBytes(dataLength, maxTime, loops); + rc = data.length() == dataLength; + } + return rc; + +} + +/** + * @brief Sends a message and receives an answer message via TCP. + * + * @param flags a sum of FLAGS_... constants + * @param command defines the content of the message to send + * @param data NULL or additional data + * @param answer OUT: the command of the answer + * @param answerData OUT: "" or additional data of the answer + * @return true: success
+ * false: error occurred + */ +bool ReTCPPeer::sendAndReceive(uint8_t flags, char command [4], + QByteArray* data, QByteArray& answer, + QByteArray& answerData) { + answer.clear(); + answerData.clear(); + bool rc = send(flags, command, data == NULL ? QByteArray("") : *data); + if(rc) + rc = receive(answer, answerData); + return rc; +} + +/** + * @brief Sets the socket. + * + * @param socket the socket to set + */ +void ReTCPPeer::setSocket(QAbstractSocket* socket) { + m_socket = socket; + if (socket != NULL) + connect( m_socket, SIGNAL(readyRead()), SLOT(readTcpData()) ); +} + +/** + * @brief Reads the (ready) data from the socket. + */ +void ReTCPPeer::readTcpData() { + m_waitForData.wakeOne(); +} + +/** + * @brief Handles a network error. + * + * @param socketError the error code + */ +void ReTCPPeer::handleError(QTcpSocket::SocketError socketError) { + m_logger->logv(LOG_ERROR, LOC_HANDLE_ERROR_1, "Network error %d", + socketError); +} + +/** + * @brief Returns a human readable peer address. + * @return a string with the peer address: e.g. "192.16.2.3:44335" + */ +QByteArray ReTCPPeer::getPeerAddress() { + QByteArray rc; + if(m_socket == NULL) + rc = ""; + else + rc = m_socket->peerAddress().toString().toUtf8(); + return rc; +} + +/** + * @brief Returns the socket. + * + * @return the socket + */ +QAbstractSocket* ReTCPPeer::getSocket() const { + return m_socket; +} + +/** + * @brief Returns the port. + * @return the port of the peer. + */ +int ReTCPPeer::getPort(){ + int port = m_configurator.asInt(ReNetConfig::PORT, 12345); + return port; +} +/** + * @brief Returns the ip address. + * @return "": all addresses (for listening)
+ * otherwise: the address (e.g. 127.0.0.1) + */ +QByteArray ReTCPPeer::getIp(){ + QByteArray ip = m_configurator.asString(ReNetConfig::IP, ""); + return ip; +} +/** + * @brief Sets the address (ip:port). + * @param ip the ip address + * @param port the port + */ +void ReTCPPeer::setAddress(const char* ip, int port) +{ + m_address = QByteArray(ip) + ":" + QByteArray::number(port); +} diff --git a/net/ReTCPPeer.hpp b/net/ReTCPPeer.hpp new file mode 100644 index 0000000..51f7dea --- /dev/null +++ b/net/ReTCPPeer.hpp @@ -0,0 +1,84 @@ +/* + * 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 RPLTCPPEER_HPP +#define RPLTCPPEER_HPP + +#ifndef RPLNET_HPP +#include "renet.hpp" +#endif + +class ReTCPPeer : public QObject { + Q_OBJECT +public: + enum { + ///> standard behaviour + FLAG_UNDEF, + ///> if set: the size field is 4 byte (max. 4 GByte) instead of 2 byte (64kByte) + FLAG_4_BYTE_SIZE = 1, + ///> if set the data are compressed by the gzip algorithm + FLAG_GZIP = 2, + ///> if set the data are encrypted. In front of the size field a 4 byte salt field exists. + ///> In this case all data behind the salt field are encrypted. + FLAG_ENCRYPT = 4, + ///> connection initialization of + } flag_t; +public: + static ReTCPPeer* createPeer(ReConfigurator& configurator, + QThread* thread, + ReTerminator* terminator, + ReLogger* logger = NULL); +public: + ReTCPPeer(ReConfigurator& configurator, QThread* thread, + ReTerminator* terminator, + bool isServer, + ReLogger* logger = NULL); + virtual ~ReTCPPeer(); +private: + // No copy constructor: no implementation! + ReTCPPeer(const ReTCPPeer& source); + // No assignment operator: no implementation! + ReTCPPeer& operator =(const ReTCPPeer& source); +public: + virtual bool send(qint8 flags, const char* command, const QByteArray& data); + virtual bool receive(QByteArray& command, QByteArray& data); + virtual bool sendAndReceive(uint8_t flags, char command [4], + QByteArray* data, QByteArray& answer, QByteArray& answerData); + void setSocket(QAbstractSocket* socket); + QAbstractSocket* getSocket() const; + QByteArray getPeerAddress(); + void handleError(QTcpSocket::SocketError socketError); + int getPort(); + QByteArray getIp(); + void setAddress(const char* ip, int port); +private: + QByteArray readBytes(int bytes, time_t maxTime, int& loops); + +public slots: + void readTcpData(); + +private: + QAbstractSocket* m_socket; + // : + QByteArray m_address; + ReLogger* m_logger; + QByteArray m_received; + int m_expected; + QThread* m_thread; + // Only used for salt generation: + ReRandom m_random; + ///> maximum allowed time (in seconds) for sending/receiving one info unit + int m_timeout; + ///> for controlled termination + ReTerminator* m_terminator; + ReConfigurator& m_configurator; + bool m_isServer; + QMutex m_dataLocker; + QWaitCondition m_waitForData; +}; + +#endif // RPLTCPPEER_HPP diff --git a/net/ReTCPServer.cpp b/net/ReTCPServer.cpp new file mode 100644 index 0000000..83a1326 --- /dev/null +++ b/net/ReTCPServer.cpp @@ -0,0 +1,230 @@ +/* + * 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 "base/rebase.hpp" +#include "math/remath.hpp" +#include "net/renet.hpp" + +enum { + LOC_RUN_1 = LOC_FIRST_OF(LOC_TCPSERVER), // 10601 + LOC_TCP_TREAD_RUN_1, + LOC_TCP_TREAD_RUN_2, + LOC_TCP_INCOMING_CONNECTION_1, +}; + +/** @class ReTCPThread rpltcpserver.hpp "rplcore/rpltcpserver.hpp" + * + * @brief Serves one connection of a multihreaded TCP server. + * + * Note: The real thing is done by the ReTaskHandler instance. + */ + +/** + * @brief Constructor. + * + * @param configurator delivers some connection parameters + * @param socketDescriptor socket of the connection to handle + * @param threadId an unique id for the thread + * @param handler does the work + */ +ReTCPThread::ReTCPThread(ReConfigurator& configurator, + qintptr socketDescriptor, int threadId, + ReTaskHandler* handler) : + m_threadId(threadId), + m_taskHandler(handler), + m_socketDescriptor(socketDescriptor), + m_configurator(configurator){ +} +/** + * @brief Destructor. + */ +ReTCPThread::~ReTCPThread() { + +} + +/** + * @brief Does the proper thread task. + * + * Initializes the socket and loops for incoming commands. + */ +void ReTCPThread::run() { + QTcpSocket socket; + if(!socket.setSocketDescriptor(getSocketDescriptor())) { + emit error(socket.error()); + } else { + ReTCPPeer peer(m_configurator, this, m_taskHandler->getTerminator(), + true, m_taskHandler->getLogger()); + peer.setSocket(&socket); + QByteArray addr = peer.getPeerAddress(); + m_taskHandler->getLogger()->logv(LOG_DEBUG, LOC_TCP_TREAD_RUN_1, + "ReTCPThread::run(): start Peer: %s", addr.constData()); + while(m_taskHandler->handle(&peer)) { + // do nothing + } + socket.disconnectFromHost(); + socket.waitForDisconnected(); + m_taskHandler->getLogger()->logv(LOG_DEBUG, LOC_TCP_TREAD_RUN_1, + "ReTCPThread::run(): end Peer: %s", addr.constData()); + } +} + +/** + * @brief Returns the thread id. + * + * @return the thread id + */ +int ReTCPThread::getThreadId() const { + return m_threadId; +} +/** + * @brief Returns the task handler. + * + * @return the task handler + */ +ReTaskHandler* ReTCPThread::getTaskHandler() const { + return m_taskHandler; +} +/** + * @brief Returns the tcp socket of the served connection. + * + * @return the socket + */ +qintptr ReTCPThread::getSocketDescriptor() const { + return m_socketDescriptor; +} + +/** @class ReTCPServer rpltcpserver.hpp "rplcore/rpltcpserver" + * + * @brief Implements a multithreaded TCP server. + */ + +/** + * @brief Constructor. + * + * @param configurator some parameters will be get from this configurator + * @param taskHandler this handler reads from the tcp and interprets the content + * @param threadFactory creates a thread for a new connection + * @param logger NULL or logger + * @param parent NULL or the parent which deletes the childen + */ +ReTCPServer::ReTCPServer(ReConfigurator& configurator, + ReTaskHandler* taskHandler, + ReVMThreadFactory& threadFactory, + ReLogger* logger, + QObject* parent) : + QTcpServer(parent), + m_taskHandler(taskHandler), + m_threadId(0), + m_threadFactory(threadFactory), + m_configurator(configurator){ +} + +/** + * @brief The slot handling a new tcp connection. + * + * @param socketDescriptor the tcp socket + */ +void ReTCPServer::incomingConnection(qintptr socketDescriptor) { + ReTCPThread* thread = m_threadFactory.create(m_configurator, + socketDescriptor, ++m_threadId, m_taskHandler); + m_taskHandler->getLogger()->log(LOG_DEBUG, LOC_TCP_INCOMING_CONNECTION_1, + "Connection detected"); + QTcpServer::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + thread->start(); +} + +/** @class ReTCPThread rpltcpserver.hpp "rplcore/rpltcpserver.hpp" + * + * @brief Defines a function pointer type to create a ReTCPThread instance. + * + */ + +/** @class ReTaskHandler rpltcpserver.hpp "rplcore/rpltcpserver.hpp" + * + * @brief An abstract base class for an handler processing an data unit. + * + * The handler knows the stucture of the data unit and can interpret this. + */ +/** + * @brief Constructor + * + * @param configurator delivers some connection parameters + * @param terminator external controller for thread termination + * @param logger the logger + */ +ReTaskHandler::ReTaskHandler(ReConfigurator& configurator, + ReTerminator* terminator, ReLogger* logger) : + m_answerFlags(0), + m_logger(logger), + m_terminator(terminator), + m_configurator(configurator){ +} + +/** + * @brief Destructor. + */ +ReTaskHandler::~ReTaskHandler() { +} + +/** + * @brief Reads one data unit, processes it and sends the answer. + * + * @param peer the communication partner + * @return false: the application should stop
+ * true: processing remains + */ +bool ReTaskHandler::handle(ReTCPPeer* peer) { + QByteArray command; + QByteArray data; + QByteArray answer; + QByteArray answerData; + bool rc = true; + if(peer->receive(command, data)) { + rc = process(command, data, answer, answerData); + if(answer.length() > 0) { + peer->send(m_answerFlags, answer, answerData); + } + } + return rc; +} + +/** + * @brief Sets the thead id. + * + * @param id the thread id + */ +void ReTaskHandler::setThreadId(int id) { + m_threadId = id; +} + +/** + * @brief Gets the thread id. + * + * @return the thread id + */ +int ReTaskHandler::getThreadId() const { + return m_threadId; +} + +/** + * @brief Returns the logger. + * + * @return the logger + */ +ReLogger* ReTaskHandler::getLogger() const { + return m_logger; +} + +/** + * @brief Returns the termination controller. + * + * @return the termination controller + */ +ReTerminator* ReTaskHandler::getTerminator() const { + return m_terminator; +} + diff --git a/net/ReTCPServer.hpp b/net/ReTCPServer.hpp new file mode 100644 index 0000000..bcbe5d0 --- /dev/null +++ b/net/ReTCPServer.hpp @@ -0,0 +1,117 @@ +/* + * 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 RPLTCPSERVER_HPP +#define RPLTCPSERVER_HPP + +// the sources generated from QT include this file directly: +#ifndef RPLNET_HPP +#include "renet.hpp" +#endif + +class ReTCPPeer; +class ReTaskHandler { +public: + ReTaskHandler(ReConfigurator& configurator, + ReTerminator* terminator, + ReLogger* logger); + virtual ~ReTaskHandler(); +public: + + virtual bool handle(ReTCPPeer* peer); + /** + * @brief Processes one data unit from the socket. + * + * @param command defines the meaning of the information unit + * @param data "" or the data of the information unit + * @param answer OUT: "" or the answer to send back + * @param answerData OUT: "" or the answer data to send back + * @return true: the receiving loop should be continued
+ * false: the process should be stopped + */ + virtual bool process(const QByteArray& command, const QByteArray& data, + QByteArray& answer, QByteArray& answerData) = 0; + void setThreadId(int id); + int getThreadId() const; + ReLogger* getLogger() const; + ReTerminator* getTerminator() const; +protected: + uint8_t m_answerFlags; +private: + int m_threadId; + ReLogger* m_logger; + ReTerminator* m_terminator; + ReConfigurator& m_configurator; +}; + +class ReTCPThread : public QThread { + Q_OBJECT +public: + ReTCPThread(ReConfigurator& m_configurator, + qintptr socketDescriptor, int threadId, + ReTaskHandler* handler); + virtual ~ReTCPThread(); +private: + // No copy constructor: no implementation! + ReTCPThread(const ReTCPThread& source); + // No assignment operator: no implementation! + ReTCPThread& operator=(const ReTCPThread& source); +public: + void run(); + int getThreadId() const; + ReTaskHandler* getTaskHandler() const; + qintptr getSocketDescriptor() const; + +signals: + void error(QTcpSocket::SocketError socketError); + +private: + // a unique id for the thread + int m_threadId; + // this handler interprets the info from the TCP connection + ReTaskHandler* m_taskHandler; + // the assigned socket + qintptr m_socketDescriptor; + ReConfigurator& m_configurator; +}; +class ReThreadFactory { +public: + virtual ReTCPThread* create(ReConfigurator& configurator, + qintptr socketDescriptor, + int threadId, + ReTaskHandler* handler) = 0; +}; + +class ReTCPPeer; +class ReTCPServer : public QTcpServer, public ReTerminator { + Q_OBJECT +public: + explicit ReTCPServer(ReConfigurator& configurator, + ReTaskHandler* taskHandler, + ReThreadFactory& threadFactory, + ReLogger* logger = NULL, QObject* parent = 0); +private: + // No copy constructor: no implementation! + ReTCPServer(const ReTCPServer& source); + // No assignment operator: no implementation! + ReTCPServer& operator=(const ReTCPServer& source); +public: + ReTCPPeer* getPeer() const; + bool handleTask(); + +protected slots: + void incomingConnection(qintptr socketDescriptor); + +private: + ReTaskHandler* m_taskHandler; + int m_threadId; + ReTCPPeer* m_peer; + ReThreadFactory& m_threadFactory; + ReConfigurator& m_configurator; +}; + +#endif // RPLTCPSERVER_HPP diff --git a/net/renet.hpp b/net/renet.hpp new file mode 100644 index 0000000..f151faa --- /dev/null +++ b/net/renet.hpp @@ -0,0 +1,24 @@ +/* + * 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 RPLNET_HPP +#define RPLNET_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "net/ReTCPPeer.hpp" +#include "net/ReTCPServer.hpp" +#include "net/ReTCPClient.hpp" +#include "net/ReNetConfig.hpp" + +#endif // RPLNET_HPP diff --git a/remodules.hpp b/remodules.hpp new file mode 100644 index 0000000..4cd4dc3 --- /dev/null +++ b/remodules.hpp @@ -0,0 +1,29 @@ +#ifndef RPLMODULES_HPP +#define RPLMODULES_HPP + +enum { + LOC_LOGGER = 101, + LOC_CONFIG, + LOC_CONTAINER, + LOC_EXCEPTION, + LOC_TEST, // 105 + LOC_TCPSERVER, + LOC_TCPCLIENT, + LOC_TCPPEER, + LOC_TERMINATOR, + LOC_ASTREE, // 110 + LOC_ASCLASSES, + LOC_LEXER, + LOC_SOURCE, + LOC_VM, + LOC_MFPARSER, // 115 +}; +#define LOC_FIRST_OF(moduleNo) (moduleNo*100+1) +class RplModules{ +public: + static int fileToNumber(const char* file); + static const char* numberToFile(int location); +}; + + +#endif // RPLMODULES_HPP diff --git a/rplcore/rplbytestorage.cpp b/rplcore/rplbytestorage.cpp deleted file mode 100644 index df1600b..0000000 --- a/rplcore/rplbytestorage.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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. -*/ - -/** @file - * - * @brief A very efficient storage for bytes and C strings. - */ -/** @file rplcore/rplbytestorage.hpp - * - * @brief Definitions for a very efficient storage for bytes and C strings. - */ - -#include "rplcore/rplcore.hpp" - -/** @class RplByteStorage rplbytestorage.hpp "rplcore/rplbytestorage.hpp" - * - * @brief Implements a very efficient byte storage. - * - * Efficiency: Allocation of one block needs mostly only 1 comparison - * and 2 assignments. - * - * Restriction: The blocks can be returned (freed) only once, not block by block. - * This can be an advantage! - * - * Process: - * The storage administrates large buffers. Allocation can be done only in the - * last buffer. If the buffer has too little space for the new block a new - * buffer will be allocated and linked into the buffer list. - * One buffer can store dozens or hundreds of blocks. Therefore allocation and - * freeing is much cheeper than allocation by new(). - */ - -/** - * @brief Constructor. - * - * @param bufferSize the size of one buffer - */ -RplByteStorage::RplByteStorage(int bufferSize) : - m_bufferSize(bufferSize), - m_buffer(NULL), - m_rest(0), - m_freePosition(NULL), - m_summarySize(0), - m_buffers(0) -{ -} - -/** - * @brief Destructor. - */ -RplByteStorage::~RplByteStorage() -{ - const uint8_t* ptr = m_buffer; - while(ptr != NULL){ - const uint8_t* old = ptr; - ptr = *(const uint8_t**)(ptr); - delete[] old; - m_buffers--; - } - assert(m_buffers == 0); -} - -/** - * @brief Allocates a block in a new allocated buffer. - * - * This method will be called if the buffer has too little space. - * A new buffer will be allocated and the block will be allocated - * in this new block. - * - * @note The block address is returned, but the allocation must be done outside! - * - * @param size of the new block (inclusive the trailing '\0') - * @return a new block with the size bytes - */ -char* RplByteStorage::allocBuffer(int size) -{ - m_rest = size + sizeof(uint8_t*) <= (size_t) m_bufferSize - ? m_bufferSize : size + sizeof(uint8_t*); - m_summarySize += m_rest; - m_buffers++; - uint8_t* rc = new uint8_t[m_rest]; - * (uint8_t**)rc = m_buffer; - m_buffer = rc; - rc += sizeof(uint8_t*); - // the block allocation will be done outside! - m_freePosition = rc; - m_rest -= sizeof(uint8_t*); - return reinterpret_cast(rc); -} - -/** - * @brief Duplicates a string into a new allocated block. - * - * @param source the source string - * @param size the length of the string. - * If < 0 strlen(source) will be used - * @return a copy of the source string. The copy ends always with '\0' - */ -const char* RplByteStorage::allocateChars(const char* source, int size) -{ - if (size < 0) - size = strlen(source); - const char* rc = allocateChars(size + 1); - memcpy((void*) rc, source, size); - ((char*)rc)[size] = '\0'; - return rc; -} - -/** - * @brief Duplicates a string into a new allocated block. - * - * The unicode string will be converted into an UTF-8 string. - * - * @param source the source string - * @return a copy of the source string. The copy ends always with '\0' - */ -const char* RplByteStorage::allocUtf8(const QString& source) -{ - const char* rc = allocateChars(source.toUtf8().constData()); - return rc; -} - -/** - * @brief Allocates a byte block without initialization. - * - * @param size the size of the block to allocate - * - * @return a byte block (without a trailing '\0') - */ -uint8_t* RplByteStorage::allocateBytes(int size) -{ - uint8_t* rc = size <= m_rest ? m_freePosition - : reinterpret_cast(allocBuffer(size)); - m_freePosition += size; - m_rest -= size; - return rc; -} - -/** - * @brief Allocates a byte block initialized by '\0'. - * - * @param size the size of the block to allocate - * - * @return a byte block (without a trailing '\0') - */ -uint8_t* RplByteStorage::allocateZeros(int size) -{ - uint8_t* rc = allocateBytes(size); - memset(rc, 0, size); - return rc; -} - -/** - * @brief Copy a byte block to a new allocated byte block. - * - * @param source the source to copy - * @param size the size of the block to allocate - * @return a byte block (without a trailing '\0') - */ -uint8_t* RplByteStorage::allocateBytes(void* source, int size) -{ - uint8_t* rc = allocateBytes(size); - memcpy(rc, source, size); - return rc; -} diff --git a/rplcore/rplbytestorage.hpp b/rplcore/rplbytestorage.hpp deleted file mode 100644 index 41e0a9c..0000000 --- a/rplcore/rplbytestorage.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 RPLCHARSTORAGE_HPP -#define RPLCHARSTORAGE_HPP - -class RplByteStorage -{ -public: - RplByteStorage(int blockSize); - ~RplByteStorage(); -public: - char* allocBuffer(int size); - /** - * @brief Allocates a char block. - * - * @return a new block - */ - inline char* allocateChars(int size){ - char* rc = size <= m_rest ? (char*) m_freePosition - : allocBuffer(size); - m_freePosition += size; - m_rest -= size; - return rc; - } - const char* allocateChars(const char* source, int size = -1); - const char* allocUtf8(const QString& source); - uint8_t* allocateBytes(int size); - uint8_t* allocateZeros(int size); - uint8_t*allocateBytes(void* source, int size); -private: - int m_bufferSize; - uint8_t* m_buffer; - int m_rest; - uint8_t* m_freePosition; - qint64 m_summarySize; - int m_buffers; -}; - -#endif // RPLCHARSTORAGE_HPP diff --git a/rplcore/rplcharptrmap.cpp b/rplcore/rplcharptrmap.cpp deleted file mode 100644 index a995536..0000000 --- a/rplcore/rplcharptrmap.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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" - -/** @class RplKeyCharPtr rplcharptrmap.hpp "rplcore/rplcharptrmap.hpp" - * - * @brief Allows using char* pointers as key in QMap. - * - * The template QMap uses the operator < to search in the map. - * But the char* pointers have no such an operator. - * The solution is the class RplMapCharPtr which implements - * this operator. - * - * strcmp() is used to implement the '<' operator. - */ - -/** - * @brief Constructor. - * - * @param ptr the key used in the map. - * @note the pointer will be stored in the map as a key, - * not the content - */ -RplKeyCharPtr::RplKeyCharPtr(const char* ptr) : - m_ptr(ptr) -{ -} - - -/** @class RplCharPtrMap rplcharptrmap.hpp "rplcore/rplcharptrmap.hpp" - * - * @brief A template for a map using const char* as keys. - * - * The value type is dynamic (a parameter type of the template). - * - * Usage: - *

- * RplCharPtrMap ids;
- * if (! id.contains("jonny"))
- *    ids["jonny"] = 1;
- * 
- * - * Important:
- * Keys used with this class must be unchangable and must live during the - * whole life of the map. - * - * Wrong example: - *

- * RplCharPtrMap ids;
- * void init(int keyNo, int value){
- *    char key[10];
- *    qsnprintf(buffer, sizeof buffer, "key%d", keyNo);
- *    ids[key] = value;
- * }
- * init(1, 100);
- * 
- * The lifetime of the key is the body of the function init(). - * The key becomes invalid after the call. - */ diff --git a/rplcore/rplcharptrmap.hpp b/rplcore/rplcharptrmap.hpp deleted file mode 100644 index 93df1c7..0000000 --- a/rplcore/rplcharptrmap.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 RPLCHARPTRMAP_HPP -#define RPLCHARPTRMAP_HPP - -class RplKeyCharPtr { - friend bool operator <(RplKeyCharPtr const& op1, RplKeyCharPtr const& op2); -public: - RplKeyCharPtr(const char* ptr); -private: - const char* m_ptr; -}; -/** - * @brief Compares two instances of the class RplKeyCharPtr. - * @param op1 1st operand - * @param op2 2nd operand - * @return true: op1 < op2
- * false: op1 >= op2 - */ -inline bool operator <(RplKeyCharPtr const& op1, RplKeyCharPtr const& op2){ - bool rc = strcmp(op1.m_ptr, op2.m_ptr) < 0; - return rc; -} - -template -class RplCharPtrMap : public QMap -{ -}; - -#endif // RPLCHARPTRMAP_HPP diff --git a/rplcore/rplconfig.cpp b/rplcore/rplconfig.cpp deleted file mode 100644 index 642a647..0000000 --- a/rplcore/rplconfig.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * 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" - -/** @file - * - * @brief Reading/writing configuration files. - */ -/** @file rplcore/rplconfig.hpp - * - * @brief Definitions for reading/writing configuration files. - */ - -/** @class RplConfig rplconfig.hpp "rplcore/rplconfig.hpp" - * - * @brief Imports and exports a configuration file into a QHash instance. - * - * The format of the file:
- * DEFS or COMMENTS - * - * DEFS ::= KEY=VALUE - * - * KEY is a string starting with an alphanumeric character and does not contain a '=' - * - * VALUE is a string - */ - -enum Locations { - LOC_WRITE_1 = RPL_FIRST_OF(RPLMODULE_CONFIG), // 10201 - LOC_WRITE_2, - LOC_READ_1, - LOC_READ_2, -}; - -/** - * Constructor. - * - * Initializes the logger and reads the configuration file - * - * @param file name of the configuration file. May be NULL - * @param readOnly true: the configuration must not be written - * @param logger NULL or a logger - */ -RplConfig::RplConfig(const char* file, bool readOnly, RplLogger* logger) : - m_file(file), - m_lineList(), - m_readOnly(readOnly), - m_logger(logger), - m_ownLogger(logger == NULL) { - if(logger == NULL) { - initLogger(); - } - if(file != NULL) - read(file); -} - -/** - * Destructor. - * - * Frees the resources. - */ -RplConfig::~RplConfig() { - if(m_ownLogger) - delete m_logger; - m_logger = NULL; -} - -/** - * Inititializes a logger. - */ -void RplConfig::initLogger() { - m_logger = new RplLogger(); - RplMemoryAppender* appender = new RplMemoryAppender(); - appender->setAutoDelete(true); - m_logger->addAppender(appender); - - RplStreamAppender* appender2 = new RplStreamAppender(stdout); - appender2->setAutoDelete(true); - m_logger->addAppender(appender2); -} - -/** - * Returns configuration value as an integer. - * - * @param key key of the wanted value - * @param defaultValue if the key does not exist this is the result - * @return defaultValue: key does not exist - * otherwise: the value assigned to key - */ -int RplConfig::asInt(const char* key, int defaultValue) const { - int rc = defaultValue; - if(contains(key)) { - rc = atoi((*this)[key]); - } - return rc; -} - -/** - * Returns the configuration value as a boolean. - * - * @param key key of the wanted value - * @param defaultValue if the key does not exist this is the result - * @return defaultValue: key does not exist - * otherwise: the value assigned to key - */ -bool RplConfig::asBool(const char* key, bool defaultValue) const { - bool rc = defaultValue; - if(contains(key)) { - QByteArray value = (*this)[key].toLower(); - rc = value == "1" || value == "y" || value == "yes" || value == "t" - || value == "true"; - } - - return rc; -} - -/** - * Returns a configuration value. - * - * @param key key of the wanted value - * @param defaultValue if the key does not exist this is the result - * @return defaultValue: key does not exist - */ -QByteArray RplConfig::asString(const char* key, const char* defaultValue) { - QByteArray rc = defaultValue; - if(contains(key)) { - rc = (*this)[key]; - } - return rc; -} - -/** - * Reads a configuration file. - * - * @param file file to read. - * @return true: OK
- * false: error occurred - */ -bool RplConfig::read(const char* file) { - bool rc = true; - m_lineList.reserve(1024); - FILE* fp = fopen(file, "r"); - if(fp == NULL) { - m_logger->logv(LOG_ERROR, LOC_READ_1, "cannot read: %s", file); - rc = false; - } else { - char line[64000]; - char* separator; - int lineNo = 0; - while(fgets(line, sizeof line, fp) != NULL) { - lineNo++; - m_lineList.append(line); - if(isalnum(line[0]) && (separator = strchr(line, '=')) != NULL) { - QByteArray key(line, separator - line); - QByteArray value(separator + 1); - key = key.trimmed(); - value = value.trimmed(); - if(contains(key)) - m_logger->logv(LOG_WARNING, LOC_READ_2, - "defined more than once: %s-%d: %s", file, lineNo, line); - else - insert(key, value); - } - } - } - return rc; -} - -/** - * Writes a configuration file. - * - * @param file file to write. - * @return true: OK
- * false: error occurred - */ -bool RplConfig::write(const char* file) { - bool rc = false; - if(m_readOnly) - m_logger->log(LOG_ERROR, LOC_WRITE_1, "cannot write: (readonly"); - else { - m_logger->logv(LOG_ERROR, LOC_WRITE_2, "not implemented: write(%s)", - file); - } - return rc; -} - -// ------------------ -#if ! defined RPL_TEST -#define RPL_TEST -#endif -#ifdef RPL_TEST -#include "rplcore/rpltest.hpp" -/** - * @brief Unit test of RplConfig. - */ -class TestRplConfig: public RplTest { -public: - TestRplConfig() : - RplTest("RplConfig") { - } - -public: - void testBasic() { - QByteArray fn = getTempFile("test.data", "config"); - RplString::write(fn, "#comment\na=1\nb.1==x\n#=\nB=zzz"); - RplConfig config(fn.constData()); - checkE(3, config.size()); - checkE("1", config["a"]); - checkE("=x", config["b.1"]); - checkE("zzz", config["B"]); - } - void testAsX() { - QByteArray fn = getTempFile("test.data", "config"); - RplString::write(fn, "i=123\nb=1\nb2=true\nb3=yes\ns=abc"); - RplConfig config(fn.constData()); - checkE(5, config.size()); - checkE(123, config.asInt("i", -1)); - checkE(-1, config.asInt("I", -1)); - checkT(config.asBool("b", false)); - checkT(config.asBool("b2", false)); - checkT(config.asBool("b3", false)); - checkT(config.asBool("-", true)); - checkF(config.asBool("-", false)); - checkE("abc", config.asString("s", "x")); - checkE("x", config.asString("S", "x")); - } - - virtual void doIt() { - testAsX(); - testBasic(); - - } -}; - -#endif -void testRplConfig() { -#ifdef RPL_TEST - TestRplConfig test; - test.run(); -#endif -} diff --git a/rplcore/rplconfig.hpp b/rplcore/rplconfig.hpp deleted file mode 100644 index abf3a71..0000000 --- a/rplcore/rplconfig.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 RPLCONFIG_HPP -#define RPLCONFIG_HPP - -class RplConfig : public RplConfigurator, public QHash { -public: - RplConfig(const char* file = NULL, bool readOnly = true, - RplLogger* logger = NULL); - virtual ~RplConfig(); - -public: - bool read(const char* file); - bool write(const char* file); - void clear(); - const QList& getLines() const; - - virtual bool asBool(const char* key, bool defaultValue) const; - virtual int asInt(const char* key, int defaultValue) const; - virtual QByteArray asString(const char* key, const char* defaultValue); -private: - void initLogger(); -private: - const char* m_file; - QList m_lineList; - bool m_readOnly; - RplLogger* m_logger; - // true: the logger must be destroyed in the destructor - bool m_ownLogger; -}; - -#endif // RPLCONFIG_HPP diff --git a/rplcore/rplconfigurator.hpp b/rplcore/rplconfigurator.hpp deleted file mode 100644 index c651800..0000000 --- a/rplcore/rplconfigurator.hpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 RPLCONFIGURATOR_HPP -#define RPLCONFIGURATOR_HPP - -class RplConfigurator { -public: - virtual int asInt(const char* key, int defaultValue) const = 0; - virtual bool asBool(const char* key, bool defaultValue) const = 0; - virtual QByteArray asString(const char* key, const char* defaultValue) = 0; -}; - -#endif // RPLCONFIGURATOR_HPP diff --git a/rplcore/rplcontainer.cpp b/rplcore/rplcontainer.cpp deleted file mode 100644 index c05b7bf..0000000 --- a/rplcore/rplcontainer.cpp +++ /dev/null @@ -1,507 +0,0 @@ -/* - * 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" - -/** @file - * @brief Implements a portable data container. - */ - -/** @file rplcore/rplcontainer.hpp - * - * @brief Definition for a portable data container. - */ - -/** @class RplContainer rplcontainer.hpp "rplcore/rplcontainer.hpp" - * - * @brief Implements a portable data container. - * - * The container contains a list of "bags". - * Each bag contains a sequence of items (with a simple data type). - * The items are portable: transported to another architecture - * the item is restored correct (independent of big/little endian). - * - * Format: - * container ::= magic header_size_hex2 container_size_hex '[' list_count ']' bag_descr list_of_bags
- * list_of_bag ::= bag1 bag2 ...
- * bag_descr ::= bag_type1 bag_type2 ... ':'
- * bag_types := i(nteger) ' ' | s(tring) | b(indata255) | B(indata64k) X(blob4G)
- * item_list ::= item1 item2...
- * The binary items (bBX) are byte sequences with a starting size element. - * The size element can be a byte (255) or a word (64k) or a double word(4G). - * The order of the size element is big endian. - * - * Example (additional blanks for readibility): - *
- * Rpl&1 09 36[2]cis:!7b Nirwana <0> Y -ab34 A long string with an trailing '0' <0>
- * 
- * magic header: Rpl&1 length: 09h data length: 46h bag count: 2 item types: char int string - * - */ - -enum { - // 11000 - LOC_FILL_1 = RPLMODULE_CONTAINER * 1000, - LOC_FILL_2, - LOC_FILL_3, - LOC_NEXT_BAG_1, - LOC_NEXT_ITEM_1, - LOC_NEXT_ITEM_2 = 11005, - LOC_NEXT_INT_1, - LOC_NEXT_ITEM_3, - LOC_NEXT_BAG_2, -}; - -const char* RplContainer::MAGIC_1 = "Rpl&1"; - -/** - * @brief Constructor. - * - * @param sizeHint Probable length of the container - */ -RplContainer::RplContainer(size_t sizeHint) : - m_data(""), - m_countBags(0), - m_typeList(""), - m_ixItem(0), - m_ixBag(0), - m_readPosition(NULL) { - if (sizeHint > 0) - m_data.reserve(sizeHint); -} - -/** - * @brief Destructor. - */ -RplContainer::~RplContainer() { -} - -/** - * @brief Adds an type to the type list. - * - * @param tag the type tag - */ -void RplContainer::addType(type_tag_t tag) { - if(m_countBags == 0) - startBag(); - if(m_countBags == 1) - m_typeList.append((char) tag); -} - -/** - * @brief Starts a new bag. - */ -void RplContainer::startBag() { - m_countBags++; - m_ixBag = 0; -} -/** - * @brief Adds a character to the current bag. - * - * @param value value to insert - */ -void RplContainer::addChar(char value) { - addType(TAG_CHAR); - //if (m_typeList.at(m_ixBag) != TAG_INT) - // RplLogger::logAndThrow(LOG_ERROR, __FILE__, __LINE__, 1, "unexpected type: %c instead of c", m_typeList.at(m_ixBag)); - m_data.append(value); -} -/** - * @brief Adds an integer to the current bag. - * - * @param value value to add - */ -void RplContainer::addInt(int value) { - addType(TAG_INT); - char buffer[64]; - char* ptr = buffer; - if(value < 0) { - *ptr++ = '-'; - value = - value; - } - qsnprintf(ptr, sizeof buffer - 1, "%x ", value); - m_data.append(buffer); -} -/** - * @brief Adds an integer to the current bag. - * - * @param value value to add - */ -void RplContainer::addInt(qint64 value) { - addType(TAG_INT); - char buffer[128]; - qsnprintf(buffer, sizeof buffer, "%llx ", value); - m_data.append(buffer); -} - -/** - * @brief Adds a string to the current bag.n - * - * @param value value to add - */ -void RplContainer::addString(const char* value) { - addType(TAG_STRING); - // store with trailing '\0' - m_data.append(value, strlen(value) + 1); -} -/** - * @brief Adds binary data to the current bag. - * - * @param value binary data - * @param size size of the binary data in bytes - */ -void RplContainer::addData(uint8_t* value, size_t size) { - if(size <= 255) { - addType(TAG_DATA255); - m_data.append((char) size); - } else if(size <= 0xffff) { - addType(TAG_DATA64K); - m_data.append((char)(size / 256)); - m_data.append((char)(size % 256)); - m_data.append((const char*) value, size); - } else { - addType(TAG_DATA4G); - m_data.append((char)(size / 256 / 256 / 256)); - m_data.append((char)(size / 256 / 256 % 256)); - m_data.append((char)(size / 256 % 256)); - m_data.append((char)(size % 256)); - m_data.append((const char*) value, size); - } - addType(TAG_DATA255); - -} - -/** - * @brief Returns the container byte buffer. - * - * @return the container as a byte array - */ -const QByteArray& RplContainer::getData() { - if(m_typeList.length() != 0) { - char buffer[128]; - // RPL&1 0a b5[2]cis: !12 - qsnprintf(buffer, sizeof buffer, "%x[%d]%s:", (unsigned int) m_data.length(), - m_countBags, m_typeList.data()); - char header[128+8]; - qsnprintf(header, sizeof header, "%s%02x%s", MAGIC_1, - (unsigned int) strlen(buffer), buffer); - m_data.insert(0, header); - } - return m_data; -} - -/** - * @brief Fills the container with an byte array. - * - * @param data the container as a byte array - */ -void RplContainer::fill(const QByteArray& data) { - m_data = data; - const char* ptr = m_data.data(); - if(strncmp(ptr, MAGIC_1, strlen(MAGIC_1)) != 0) - throw RplInvalidDataException(LOG_ERROR, LOC_FILL_1, "container has no magic", - data.data(), data.length()); - ptr += strlen(MAGIC_1); - unsigned int headerSize = 0; - if(sscanf(ptr, "%02x", &headerSize) != 1) - throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2, - "container has no header size", ptr, 2); - ptr += 2; - - unsigned int dataSize = 0; - unsigned int countBags = 0; - if(sscanf(ptr, "%x[%x]", &dataSize, &countBags) != 2) - throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2, - "container has no data_size[bag_count]", ptr, 16); - m_countBags = countBags; - ptr = strchr(ptr, ']') + 1; - const char* end = ptr + strspn(ptr, "cisdDX!"); - if(end == ptr || *end != ':') { - throw RplInvalidDataException(LOG_ERROR, LOC_FILL_2, - "container has no valid typelist", ptr, 16); - } - m_typeList.clear(); - m_typeList.append(ptr, end - ptr); - m_ixBag = -1; - m_readPosition = (uint8_t*) end + 1; - - -} -/** - * @brief Returns the number of bags in the container. - * - * @return the number of bags - */ -int RplContainer::getCountBags() const { - return m_countBags; -} -/** - * @brief Sets the begin of the new bag. - */ -void RplContainer::nextBag() { - if(m_ixItem < m_typeList.length() && m_ixItem != -1) - throw RplException(LOG_ERROR, LOC_NEXT_BAG_1, NULL, - "end of bag not reached: remaining items: %s", - m_typeList.data() + m_ixItem); - m_ixItem = 0; - m_ixBag++; - if(m_ixBag >= m_countBags) - throw RplException(LOG_ERROR, LOC_NEXT_BAG_2, NULL, - "no more bags: %d", m_ixBag); -} -/** - * @brief Sets the next item. - * - * @param expected the expected data type - */ -void RplContainer::nextItem(type_tag_t expected) { - if(m_ixBag < 0) { - m_ixBag = 0; - m_ixItem = 0; - } - if(m_ixItem >= m_typeList.length()) - throw RplException(LOG_ERROR, LOC_NEXT_ITEM_1, "no more items in the bag"); - type_tag_t current = (type_tag_t) m_typeList.at(m_ixItem); - // Unify all data types: - if(current == TAG_DATA4G || current == TAG_DATA64K) - current = TAG_DATA255; - if(current != expected) - throw RplException(LOG_ERROR, LOC_NEXT_ITEM_2, NULL, - "current item is a %c, not a %c", - (char) m_typeList.at(m_ixItem), (char) expected); - m_ixItem++; - if(m_readPosition > (uint8_t*)(m_data.data() + m_data.length())) - throw RplException(LOG_ERROR, LOC_NEXT_ITEM_3, NULL, - "container size too small. Bag: %d of %d Item: %d of %d", - 1 + m_ixBag, m_countBags, 1 + m_ixItem, m_typeList.length()); -} - -/** - * @brief Reads the next character from the current item in the current bag. - * - * @return the next char from the container - */ -char RplContainer::nextChar() { - nextItem(TAG_CHAR); - char rc = *m_readPosition++; - return rc; -} - -/** - * @brief Reads the next integer from the current item in the current bag. - * - * @return the next integer from the container - */ -int RplContainer::nextInt() { - nextItem(TAG_INT); - bool isNegativ = *m_readPosition == '-'; - if(isNegativ) - m_readPosition++; - unsigned int value = 0; - if(sscanf((const char*) m_readPosition, "%x ", &value) != 1) - throw RplInvalidDataException(LOG_ERROR, LOC_NEXT_INT_1, - "not a hex_number", m_readPosition, 16); - m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1; - if(isNegativ) - value = - value; - return value; -} -/** - * @brief Reads the next integer from the current item in the current bag. - * - * @return the next integer from the container - */ -qint64 RplContainer::nextInt64() { - nextItem(TAG_INT); - bool isNegativ = *m_readPosition == '-'; - if(isNegativ) - m_readPosition++; - qint64 value = 0; - if(sscanf((const char*) m_readPosition, "%llx ", &value) != 1) - throw RplInvalidDataException(LOG_ERROR, LOC_NEXT_INT_1, - "not a hex_number", m_readPosition, 16); - m_readPosition = (uint8_t*) strchr((const char*) m_readPosition, ' ') + 1; - if(isNegativ) - value = - value; - return value; -} - -/** - * @brief Reads the next string from the current item in the current bag. - * - * @return the next '\0' delimited string from the container - */ -const char* RplContainer::nextString() { - nextItem(TAG_STRING); - const char* rc = (const char*) m_readPosition; - m_readPosition += strlen(rc) + 1; - return rc; -} - -/** - * @brief Reads the next string from the current item in the current bag. - * - * @param data OUT: the next data item from the container - * @param append true: the item data will be appended to data
- * false: data contains the item data only - * @return the size of the read data - */ -size_t RplContainer::nextData(QByteArray& data, bool append) { - nextItem(TAG_DATA255); - type_tag_t tag = (type_tag_t) m_typeList.at(m_ixItem - 1); - size_t length = 0; - switch(tag) { - case TAG_DATA4G: - for(int ix = 3; ix >= 0; ix--) { - length = 256 * length + m_readPosition[ix]; - } - m_readPosition += 4; - break; - case TAG_DATA64K: - length = *m_readPosition++ * 256; - length += *m_readPosition++; - break; - case TAG_DATA255: - length = *m_readPosition++; - break; - default: - break; - } - if(! append) - data.clear(); - data.append((const char*) m_readPosition, length); - m_readPosition += length; - return length; -} - - - -/** - * @brief Dumps a container as a human readable string. - * - * @param title will be used in the first line - * @param maxBags if there are more bags they will be ignored - * @param maxStringLength if strings are longer the will be cut - * @param maxBlobLength maximum count of bytes which will be dumped - * @param separatorItems separator between two items, e.g. '\\n' or '|' - * @return a human readable string describing the container - */ -QByteArray RplContainer::dump(const char* title, - int maxBags, int maxStringLength, int maxBlobLength, - char separatorItems) { - QByteArray rc; - rc.reserve(64000); - rc.append("=== ").append(title).append('\n'); - rc.append("Bags: ").append(RplString::toNumber(m_countBags)); - rc.append(" Types: ").append(m_typeList).append('\n'); - // save the current state: - int safeIxBag = m_ixBag; - int safeIxItem = m_ixItem; - m_ixBag = -1; - m_ixItem = 0; - int iValue; - QByteArray sValue; - if(maxBags > m_countBags) - maxBags = m_countBags; - for(int ixBag = 0; ixBag < maxBags; ixBag++) { - rc.append("--- bag ").append(RplString::toNumber(ixBag)).append(":\n"); - nextBag(); - QByteArray item; - int maxLength; - for(int ixItem = 0; ixItem < m_typeList.length(); ixItem++) { - type_tag_t currentType = (type_tag_t) m_typeList.at(ixItem); - switch(currentType) { - case TAG_CHAR: - rc.append(" c: ").append(nextChar()).append(separatorItems); - break; - case TAG_INT: - iValue = nextInt(); - rc.append(" i: ").append(RplString::toNumber(iValue)).append(" / "); - rc.append(RplString::toNumber(iValue, "%x")).append(separatorItems); - break; - case TAG_STRING: - sValue = nextString(); - if(sValue.length() > maxStringLength) - sValue = sValue.left(maxStringLength); - rc.append(" s: ").append(sValue).append(separatorItems); - break; - case TAG_DATA255: - case TAG_DATA64K: - case TAG_DATA4G: - nextData(item, false); - rc.append(' ').append((char) currentType).append(": ["); - rc.append(RplString::toNumber(item.length())).append("] "); - maxLength = item.length() < maxBlobLength ? item.length() : maxBlobLength; - rc.append(RplString::hexDump(item.data(), maxLength, - 16)).append(separatorItems); - break; - default: - break; - } - } - } - - // restore the current state: - m_ixBag = safeIxBag; - m_ixItem = safeIxItem; - return rc; -} - -// ------------------ -#if ! defined RPL_TEST -#define RPL_TEST -#endif -#ifdef RPL_TEST -#include "rplcore/rpltest.hpp" -/** - * @brief Unit test for RplContainer - */ -class TestRplContainer : public RplTest { -public: - TestRplContainer() : RplTest("RplContainer") {} - -public: - void testBasic() { - RplContainer container(256); - // Rpl&1 09 36[2]cis:!7b Nirwana <0> Y -ab34 A long string with an trailing '0' <0>
- container.startBag(); - container.addChar('!'); - container.addInt(123); - container.addString("Nirwana"); - container.startBag(); - container.addChar('Y'); - container.addInt(-0xab34); - container.addString("A long string with an trailing '0'"); - - QByteArray data = container.getData(); - - RplContainer container2(256); - container2.fill(data); - checkE(2, container2.getCountBags()); - checkE('!', container2.nextChar()); - checkE(123, container2.nextInt()); - checkE("Nirwana", container2.nextString()); - container2.nextBag(); - checkE('Y', container2.nextChar()); - checkE(-0xab34, container2.nextInt()); - checkE("A long string with an trailing '0'", container2.nextString()); - - log(("Example: " + data).constData()); - } - - virtual void doIt() { - testBasic(); - } -}; - -#endif -void testRplContainer() { -#ifdef RPL_TEST - TestRplContainer test; - test.run(); -#endif -} diff --git a/rplcore/rplcontainer.hpp b/rplcore/rplcontainer.hpp deleted file mode 100644 index 621a616..0000000 --- a/rplcore/rplcontainer.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 RPLCONTAINER_HPP -#define RPLCONTAINER_HPP - -// the sources generated from QT include this file directly: -#ifndef RPLCORE_HPP -#include -#include -#endif -class RplContainer { -public: - typedef enum { - - TAG_CHAR = 'c', ///< one character - TAG_INT = 'i', ///< an integer number, up to 64 bit - TAG_STRING = 's', ///< a string ending with a '\\0' - TAG_DATA255 = 'd', ///< binary data, up to 255 bytes long - TAG_DATA64K = 'D', ///< binary data, up to 64 KiBytes long - TAG_DATA4G = 'X', ///< binary data, up to 4 GiBytes long - TAG_CONTAINER = '!' ///< a container (recursion) - } type_tag_t; - static const char* MAGIC_1; -public: - RplContainer(size_t sizeHint); - virtual ~RplContainer(); -private: - // No copy constructor: no implementation! - RplContainer(const RplContainer& source); - // Prohibits assignment operator: no implementation! - RplContainer& operator =(const RplContainer& source); -public: - // Building the container: - void addType(type_tag_t tag); - void startBag(); - void addChar(char cc); - void addInt(int value); - void addInt(qint64 value); - void addString(const char* value); - void addData(uint8_t* value, size_t size); - const QByteArray& getData(); - - // Getting data from the container: - void fill(const QByteArray& data); - int getCountBags() const; - const char* getTypeList() const; - void nextBag(); - char nextChar(); - int nextInt(); - qint64 nextInt64(); - const char* nextString(); - size_t nextData(QByteArray& data, bool append = false); - - QByteArray dump(const char* title, - int maxBags, int maxStringLength = 80, int maxBlobLength = 16, - char separatorItems = '\n'); -private: - void nextItem(type_tag_t expected); -private: - // the complete data of the container - QByteArray m_data; - // the number of elements in the container - int m_countBags; - // a string with the data types of a bag - QByteArray m_typeList; - - // Getting data from the container: - - // current read position in m_typeList - int m_ixItem; - // the index of the current current bag: - int m_ixBag; - // read position in m_data: - const uint8_t* m_readPosition; -}; - -#endif // RPLCONTAINER_HPP diff --git a/rplcore/rplcore.hpp b/rplcore/rplcore.hpp deleted file mode 100644 index 8df4f75..0000000 --- a/rplcore/rplcore.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 RPLCORE_HPP -#define RPLCORE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef unsigned char uint8_t; -#define RPL_UNUSED(x) (void)(x) - -#include "rplmodules.hpp" -#include "rplcore/rplbytestorage.hpp" -#include "rplcore/rplcharptrmap.hpp" -#include "rplcore/rplwriter.hpp" -#include "rplcore/rpllogger.hpp" -#include "rplcore/rplexception.hpp" -#include "rplcore/rplcontainer.hpp" -#include "rplcore/rplstring.hpp" -#include "rplcore/rplqstring.hpp" -#include "rplcore/rplconfigurator.hpp" -#include "rplcore/rplconfig.hpp" -#include "rplcore/rplterminator.hpp" - -#endif // RPLCORE_HPP diff --git a/rplcore/rplexception.cpp b/rplcore/rplexception.cpp deleted file mode 100644 index fac935f..0000000 --- a/rplcore/rplexception.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 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. - */ -/** @mainpage - * - * @note A real public library for QT. - * - * This library contains the module groups - *
    - *
  • rplcore: basic definitions, used in all other module groups
  • - *
  • rplmath: mathematic definitions and tools
  • - *
  • rplexpr: definition for parsing and interpretation of languages
  • - *
  • rplnet: definitions and tools for tcp/udp communication
  • - *
- * - * Each module group has a central include file, which organizes the necessary - * include files. You had to include only the central include file. - * - * Example: - *

- * #include "rplcore/rplcore.hpp"
- * #include "rplexpr/rplexpr.hpp"
- * 
- * In this case all definitions of rplcore and rplexpr are available. - */ -/** @file - * @brief Generally usable exceptions. - */ -/** @file rplcore/rplexception.hpp - * - * @brief Definitions for a generally usable exceptions. - */ -#include "rplcore/rplcore.hpp" - -/** @class RplException rplexception.hpp "rplcore/rplexception.hpp" - * - * @brief A generally usable exception with or without logging. - * - * Note: If the logger is not given by parameter - * the usage of the global logger is not threadsafe. - */ -class RplException; - - -/** - * @brief Constructor. - * - * For derived classes only! - */ -RplException::RplException() : - m_message("") -{ -} - -/** - * @brief Constructor. - * - * @param format the reason of the exception - * @param ... the values for the placeholders in the format. - */ -RplException::RplException(const char* format, ...) : - m_message("") { - char buffer[64000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - m_message = buffer; -} - -/** - * @brief Constructor. - * - * This constructor automatically logs the given data. - * - * @param level the logging level, e.g. LOG_ERROR - * @param location an unique identifier for the location - * where the exception was thrown - * @param format the reason of the exception. - * Can contain placeholders (@see - * std::printf()) - * @param ... the values of the placeholders - * in format - * @param logger if NULL the global logger will be used - */ -RplException::RplException(RplLoggerLevel level, int location, - RplLogger* logger, const char* format, ...) : - m_message("") { - char buffer[64000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - m_message = buffer; - if(logger == NULL) - logger = RplLogger::globalLogger(); - logger->log(level, location, buffer); -} - - -/** @class RplRangeException rplexception.hpp "rplcore/rplexception.hpp" - * - * @brief An exception for integer range errors. - * - * The error will be logged. - * - * Note: If the logger is not given by parameter - * the usage of the global logger is not threadsafe. - */ - -/** - * @brief Constructor. - * - * This exception can be used if a value does not be - * inside a given range. - * - * This constructor automatically logs the given data. - * - * @param level the logging level, e.g. LOG_ERROR - * @param location an unique identifier for the location - * where the exception was thrown - * @param current the current value - * @param lbound the minimum of the allowed values - * @param ubound the maximum of the allowed values - * @param message the reason. If NULL a generic - * message will be used - * @param logger if NULL the global logger will be used - */ - -RplRangeException::RplRangeException(RplLoggerLevel level, int location, - size_t current, - size_t lbound, size_t ubound, const char* message, RplLogger* logger) : - RplException("") { - char buffer[64000]; - if(message == NULL) - message = "value outside limits"; - qsnprintf(buffer, sizeof buffer, "%s: %lu [%lu, %lu]", - message == NULL ? "" : message, - current, lbound, ubound); - if(logger == NULL) - logger = RplLogger::globalLogger(); - logger->log(level, location, buffer); -} - -/** @class RplInvalidDataException rplexception.hpp "rplcore/rplexception.hpp" - * - * @brief An exception usable if binary data have the wrong structure. - * - * The data will be dumped as hex and ASCII dump. - * - * Note: If the logger is not given by parameter - * the usage of the global logger is not threadsafe. - */ - -/** - * @brief Constructor. - * - * This exception can be used if data does not have a given fomat. - * - * This constructor automatically logs the given data. This data - * will be dumped (hexadecimal dump and ASCII interpretation). - * - * @param level the logging level, e.g. LOG_ERROR - * @param location an unique identifier for the location - * where the exception was thrown - * @param message the reason - * @param data pointer to binary data - * @param dataSize the size of the data which should be dumped - * @param logger if NULL the global logger will be used - */ -RplInvalidDataException::RplInvalidDataException(RplLoggerLevel level, - int location, - const char* message, const void* data, - size_t dataSize, RplLogger* logger) : - RplException("") { - char buffer[64000]; - if(message == NULL) - message = "invalid data: "; - if(data == NULL) - data = ""; - if(dataSize > 16) - dataSize = 16; - size_t ix; - char* ptr = buffer + strlen(buffer); - for(ix = 0; ix < dataSize; ix++) { - qsnprintf(ptr, sizeof(buffer) - (ptr - buffer) - 1, "%02x ", - ((unsigned char*) data)[ix]); - ptr += strlen(ptr); - } - for(ix = 0; ix < dataSize; ix++) { - char cc = ((char*) data)[ix]; - if(cc > ' ' && cc <= '~') - *ptr++ = cc; - else - *ptr++ = '.'; - } - if(logger == NULL) - logger = RplLogger::globalLogger(); - logger->log(level, location, buffer); -} diff --git a/rplcore/rplexception.hpp b/rplcore/rplexception.hpp deleted file mode 100644 index 64fd530..0000000 --- a/rplcore/rplexception.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 RPLEXCEPTION_HPP -#define RPLEXCEPTION_HPP - -// the sources generated from QT include this file directly: -#ifndef RPLCORE_HPP -#include -#endif - -class RplException { -protected: - RplException(); -public: - RplException(const char* message, ...); - RplException(RplLoggerLevel level, int location, const char* message, - RplLogger* logger = NULL); - RplException(RplLoggerLevel level, int location, RplLogger* logger, - const char* message, ...); - const QByteArray& getMessage() const { - return m_message; - } -protected: - QByteArray m_message; -}; - -class RplRangeException : public RplException { -public: - RplRangeException(RplLoggerLevel level, int location, size_t current, - size_t lbound, size_t ubound, - const char* message = NULL, RplLogger* logger = NULL); -}; - -class RplInvalidDataException : public RplException { -public: - RplInvalidDataException(RplLoggerLevel level, int location, const char* message, - const void* data = NULL, size_t dataSize = 0, RplLogger* logger = NULL); -}; - -#endif // RPLEXCEPTION_HPP diff --git a/rplcore/rpllogger.cpp b/rplcore/rpllogger.cpp deleted file mode 100644 index 84f20f7..0000000 --- a/rplcore/rpllogger.cpp +++ /dev/null @@ -1,649 +0,0 @@ -/* - * 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. - */ - -/** @file - * A configurable logger for different output media. - */ -/** @file rplcore/rpllogger.hpp - * - * Definitions for a configurable logger for different output media. - */ -#include "rplcore/rplcore.hpp" -#include -#include - -enum { - LOC_ADD_APPENDER_1 = RPL_FIRST_OF(RPLMODULE_LOGGER), // 10101 -}; - -RplLogger* RplLogger::m_globalLogger = NULL; - -/** - * @brief Returns the global logger. - * - * If it does not exist it will be created (singleton). - * - * @return the global logger - */ -RplLogger* RplLogger::globalLogger() { - if(m_globalLogger == NULL) { - m_globalLogger = new RplLogger(); - m_globalLogger->buildStandardAppender("globallogger"); - } - return m_globalLogger; -} -/** - * @brief Frees the resources of the global logger. - */ -void RplLogger::destroyGlobalLogger() { - delete m_globalLogger; - m_globalLogger = NULL; -} - -/** @class RplAppender rpllogger.hpp "rplcore/rpllogger.hpp" - * - * @brief Puts the logging info to a medium (e.g. a file). - * - * This is an abstract base class. - */ - -/** - * @brief Constructor. - * - * @param name identifies the logger. Useful for RplLogger::findLogger() - */ -RplAppender::RplAppender(const QByteArray& name) : - m_name(name), - m_level(LOG_INFO) { - -} -/** - * @brief Destructor. - */ -RplAppender::~RplAppender() { -} - -/** - * Returns the name. - * - * @return the name of the instance - */ -const char* RplAppender::getName() const { - return m_name.data(); -} - -/** - * @brief Sets the level. - * - * @param level - */ -void RplAppender::setLevel(RplLoggerLevel level) { - m_level = level; -} -/** - * @brief Returns the level. - * - * @return the level - */ -RplLoggerLevel RplAppender::getLevel() const { - return m_level; -} -/** - * @brief Checks whether the current location should be logged. - * - * @param level the level of the location. - * @return true: the location level is greater or equals to the appender's level - */ -bool RplAppender::isActive(RplLoggerLevel level) { - return level <= m_level; -} - -/** - * @brief Sets or clears the automatic deletions. - * - * @param onNotOff the state of the auto deletion - */ -void RplAppender::setAutoDelete(bool onNotOff) { - m_autoDelete = onNotOff; -} - -/** - * @brief Returns the state of the auto deletion. - * - * @return true: the logger destroys the instance - */ -bool RplAppender::isAutoDelete() const { - return m_autoDelete; -} - -/** @class RplLogger rpllogger.hpp "rplcore/rpllogger.hpp" - * - * @brief Implements a logger. - * - * The logger takes the call from the calling location. - * But the output assumes the class RplAppender, - * more exactly: a subclass from the abstract class - * RplAppender, - * - * For single threaded applications there is a possability of - * a global logger. In this case the logger can be got with the static - * method RplLogger::globalLogger(). - * - * Note: using the global logger is not threadsafe! - * - * Each call of the logger should be provided by a unique identifier - * named the location. This allows to find the error quickly. - */ - -/** - * @brief Constructor. - */ -RplLogger::RplLogger() : - // m_appenders(), - m_countAppenders(0), - m_stdPrefix(), - m_mutex(), - m_withLocking(false) { - memset(m_appenders, 0, sizeof m_appenders); -} - -/** - * @brief Destructor. - */ -RplLogger::~RplLogger() { - for(size_t ix = 0; ix < m_countAppenders; ix++) { - RplAppender* appender = m_appenders[ix]; - if(appender->isAutoDelete()) { - delete appender; - } - m_appenders[ix] = NULL; - } -} -/** - * @brief Returns the first char of a logging line displaying the logging level. - * - * @param level the level to "convert" - * @return the assigned prefix char - */ -char RplLogger::getPrefixOfLevel(RplLoggerLevel level) const { - char rc = ' '; - switch(level) { - case LOG_ERROR: - rc = '!'; - break; - case LOG_WARNING: - rc = '+'; - break; - case LOG_INFO: - rc = ' '; - break; - case LOG_DEBUG: - rc = '='; - break; - default: - rc = '?'; - break; - } - return rc; -} - -/** - * @brief Tests whether at least one appender is active for a given level. - * - * @param level level to test - * @return false: all appenders are not activated by this level
- * true: otherwise - */ -bool RplLogger::isActive(RplLoggerLevel level) const { - bool rc = false; - for(size_t ix = 0; ix < m_countAppenders; ix++) { - RplAppender* appender = m_appenders[ix]; - if(appender->isActive(level)) { - rc = true; - break; - } - } - return rc; -} - -/** - * @brief Sets the log level for all appenders. - * - * @param level level to set - */ -void RplLogger::setLevel(RplLoggerLevel level) { - for(size_t ix = 0; ix < m_countAppenders; ix++) { - RplAppender* appender = m_appenders[ix]; - appender->setLevel(level); - } -} - -/** - * @brief Sets or clears the state "with locking". - * - * @param onNotOff true: the logger is thread save.
- * false: not thread save - */ -void RplLogger::setWithLocking(bool onNotOff) { - m_withLocking = onNotOff; -} - -/** - * @brief Returns the standard prefix of a logging line. - * - * If it does not exist it will be created. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @return the standard logging line prefix - */ -const QByteArray& RplLogger::getStdPrefix(RplLoggerLevel level, int location) { - if(m_stdPrefix.isEmpty()) - m_stdPrefix = buildStdPrefix(level, location); - return m_stdPrefix; -} - -/** - * @brief Logs (or not) the calling location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param message the logging message - * @return true: for chaining - */ -bool RplLogger::log(RplLoggerLevel level, int location, const char* message) { - m_stdPrefix = ""; - bool first = true; - for(size_t ix = 0; ix < m_countAppenders; ix++) { - RplAppender* appender = m_appenders[ix]; - if(appender->isActive(level)) { - if(first && m_withLocking) - m_mutex.lock(); - appender->log(level, location, message, this); - } - } - if(! first && m_withLocking) - m_mutex.unlock(); - return true; -} -/** - * @brief Logs (or not) the calling location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param message the logging message - * @return true: for chaining - */ -bool RplLogger::log(RplLoggerLevel level, int location, - const QByteArray& message) { - return log(level, location, message.data()); -} - -/** - * @brief Logs (or not) the calling location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param message the logging message - * @return true: for chaining - */ -bool RplLogger::log(RplLoggerLevel level, int location, - const QString& message) { - return log(level, location, message.toUtf8().data()); -} - -/** - * @brief Logs (or not) the calling location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param format the logging message with placeholders (like printf). - * @param ... the values of the placeholders (varargs) - * @return true: for chaining - */ -bool RplLogger::logv(RplLoggerLevel level, int location, const char* format, - ...) { - char buffer[64000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - return log(level, location, buffer); -} - -/** - * @brief Logs (or not) the calling location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param format the logging message with placeholders (like printf). - * @param ... the values of the placeholders (varargs) - * @return true: for chaining - */ -bool RplLogger::logv(RplLoggerLevel level, int location, - const QByteArray& format, ...) { - char buffer[64000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - return log(level, location, buffer); -} - -/** - * @brief Logs (or not) the calling location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param format the logging message with placeholders (like printf). - * @param varlist variable arguments - * @return true: for chaining - */ -bool RplLogger::log(RplLoggerLevel level, int location, const char* format, - va_list& varlist) { - char buffer[64000]; - qvsnprintf(buffer, sizeof buffer, format, varlist); - return log(level, location, buffer); -} - -/** - * @brief Builds the standard prefix of a logging line. - * - * @param level the level of the location - * @param location an unique identifier of the location - */ -QByteArray RplLogger::buildStdPrefix(RplLoggerLevel level, int location) { - time_t now = time(NULL); - struct tm* now2 = localtime(&now); - char buffer[64]; - qsnprintf(buffer, sizeof buffer, "%c%d.%02d.%02d %02d:%02d:%02d (%d): ", - getPrefixOfLevel(level), - now2->tm_year + 1900, - now2->tm_mon + 1, - now2->tm_mday, - now2->tm_hour, - now2->tm_min, - now2->tm_sec, - location); - return QByteArray(buffer); -} - -/** - * @brief Adds an appender. - * - * @param appender appender to add - */ -void RplLogger::addAppender(RplAppender* appender) { - if(m_countAppenders < sizeof m_appenders / sizeof m_appenders[0]) { - m_appenders[m_countAppenders++] = appender; - } else { - log(LOG_ERROR, LOC_ADD_APPENDER_1, "too many appenders"); - } -} - -/** - * @brief Returns the appender with a given name. - * - * @param name the appender's name - * - * @return NULL: no appender with this name is registered
- * otherwise: the wanted appender - */ -RplAppender* RplLogger::findAppender(const char* name) const { - RplAppender* rc = NULL; - for(size_t ix = 0; ix < m_countAppenders; ix++) { - RplAppender* current = m_appenders[ix]; - if(strcmp(name, current->getName()) == 0) { - rc = current; - break; - } - } - return rc; -} - -/** - * @brief Builds the standard appender configured by a configuration file. - * - * @param config configuration file - * @param prefix the prefix of the key in the config file - * (in front of "name") - * @param defaultLogfilePrefix - * the prefix of the log file if no entry in the - * configuration file - */ -void RplLogger::buildStandardAppender(RplConfig* config, - const char* prefix, - const char* defaultLogfilePrefix) { - QByteArray sPrefix(prefix); - QByteArray logFilePrefix = config->asString(sPrefix + "name", - defaultLogfilePrefix); - - int maxSize = config->asInt( + "maxsize", 10100100); - int maxCount = config->asInt(sPrefix + "maxfiles", 5); - buildStandardAppender(logFilePrefix, maxSize, maxCount); - QByteArray sLevel = config->asString(sPrefix + "level", "info"); - RplLoggerLevel level = LOG_INFO; - if (strcasecmp(sLevel.constData(), "error") == 0) - level = LOG_ERROR; - else if (strcasecmp(sLevel, "warning") == 0) - level = LOG_WARNING; - else if (strcasecmp(sLevel, "debug") == 0) - level = LOG_DEBUG; - setLevel(level); -} - -/** - * @brief Builds the standard appender for the instance: a console logger and a file logger. - * - * @param prefix the prefix of the log file name, e.g. /var/log/server - * @param maxSize the maximum of the file size - * @param maxCount the maximal count of files. If neccessary the oldest file will be deleted - */ -void RplLogger::buildStandardAppender(const QByteArray& prefix, int maxSize, - int maxCount) { - RplStreamAppender* streamAppender = new RplStreamAppender(stderr); - streamAppender->setAutoDelete(true); - addAppender((RplAppender*) streamAppender); - RplFileAppender* fileAppender = new RplFileAppender(prefix, maxSize, maxCount); - fileAppender->setAutoDelete(true); - addAppender((RplAppender*) fileAppender); -} - -/** @class RplStreamAppender rpllogger.hpp "rplcore/rpllogger.hpp" - * - * @brief Puts the logging info to a standard output stream. - * - * The possible streams are std::stdout or std::stderr - */ - - -/** - * @brief Constructor. - */ -RplStreamAppender::RplStreamAppender(FILE* file, const char* appenderName) : - RplAppender(QByteArray(appenderName)), - m_fp(file) { -} - -/** - * @brief Destructor. - */ -RplStreamAppender::~RplStreamAppender() { - fflush(m_fp); -} - - -/** - * @brief Logs (or not) the current location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param message the logging message - * @param logger the calling logger - */ -void RplStreamAppender::log(RplLoggerLevel level, int location, - const char* message, RplLogger* logger) { - const QByteArray& prefix = logger->getStdPrefix(level, location); - fputs(prefix, m_fp); - fputs(message, m_fp); - fputc('\n', m_fp); - fflush(m_fp); -} -#pragma GCC diagnostic warning "-Wunused-parameter" - - -/** @class RplFileAppender rpllogger.hpp "rplcore/rpllogger.hpp" - * - * @brief Puts the logging info to a file. - * - * The appender creates a collection of files to limit the used disk space. - * Each logfile is limited to a given size. And the number of files is limited. - * If the count exceeds the oldest file will be deleted. - * - * Each logfile's name has a given name prefix, a running number - * and the suffix ".log", e.g. "globallogger.003.log". - */ - -/** - * @brief Constructor. - * - * @param prefix the prefix of the log file name, e.g. /var/log/server - * @param maxSize the maximum of the file size - * @param maxCount the maximal count of files. If neccessary the oldest file will be deleted - * @param appenderName the name of the appender. @see RplLogger::findAppender() - */ -RplFileAppender::RplFileAppender(const QByteArray& prefix, int maxSize, - int maxCount, const char* appenderName) : - RplAppender(QByteArray(appenderName)), - m_prefix(prefix), - m_maxSize(maxSize), - m_maxCount(maxCount), - m_currentSize(0), - m_currentNo(0), - m_fp(NULL) { - open(); -} - -/** - * @brief Destructor. - */ -RplFileAppender::~RplFileAppender() { - if(m_fp != NULL) { - fclose(m_fp); - m_fp = NULL; - } -} - -/** - * @brief Opens the next log file. - */ -void RplFileAppender::open() { - if(m_fp != NULL) - fclose(m_fp); - char fullName[512]; - qsnprintf(fullName, sizeof fullName, "%s.%03d.log", m_prefix.data(), - ++m_currentNo); - m_fp = fopen(fullName, "a"); - if(m_fp == NULL) - fprintf(stderr, "cannot open: %s\n", fullName); - else { - //@ToDo - m_currentSize = 0; - } -} - -/** - * @brief Logs (or not) the current location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param message the logging message - * @param logger the calling logger - */ -#pragma GCC diagnostic ignored "-Wunused-parameter" -void RplFileAppender::log(RplLoggerLevel level, int location, - const char* message, - RplLogger* logger) { - if(m_fp != NULL) { - const QByteArray& prefix = logger->getStdPrefix(level, location); - fputs(prefix, m_fp); - fputs(message, m_fp); - fputc('\n', m_fp); - fflush(m_fp); - } -} -#pragma GCC diagnostic warning "-Wunused-parameter" - -/** @class RplMemoryAppender rpllogger.hpp "rplcore/rpllogger.hpp" - * - * @brief Puts the logging info to an internal buffer. - * - * This line list can be required: getLines(). - */ - -/** - * @brief Constructor. - * - * @param maxLines the maximum of lines. - * If the buffer is full the oldest lines will be deleted - * @param appenderName NULL or the name of the appender - */ -RplMemoryAppender::RplMemoryAppender(int maxLines, const char* appenderName) : - RplAppender(appenderName), - m_lines(), - m_maxLines(maxLines), - m_addPrefix(true) -{ - m_lines.reserve(maxLines); -} - -/** - * @brief Destructor. - */ -RplMemoryAppender::~RplMemoryAppender() { -} - -/** - * Logs (or not) the current location. - * - * @param level the level of the location - * @param location an unique identifier of the location - * @param message the logging message - * @param logger the calling logger - */ -#pragma GCC diagnostic ignored "-Wunused-parameter" -void RplMemoryAppender::log(RplLoggerLevel level, int location, - const char* message, - RplLogger* logger) { - if(m_lines.size() >= m_maxLines) - m_lines.removeFirst(); - if (! m_addPrefix) - m_lines.append(message); - else { - QByteArray msg(logger->getStdPrefix(level, location)); - msg += message; - m_lines.append(msg); - } -} -#pragma GCC diagnostic warning "-Wunused-parameter" - -/** - * @brief Returns the list of lines. - * - * @return the line list - */ -const QList& RplMemoryAppender::getLines() const { - return m_lines; -} - -/** - * @brief Deletes all log lines. - */ -void RplMemoryAppender::clear() { - m_lines.clear(); -} diff --git a/rplcore/rpllogger.hpp b/rplcore/rpllogger.hpp deleted file mode 100644 index 791d14c..0000000 --- a/rplcore/rpllogger.hpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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 RPLLOGGER_HPP -#define RPLLOGGER_HPP - -/** - * - */ -class RplLogger; -class RplConfig; -/** - * @brief Logging level: for controlling of the logging. - * - * Each logging location defines one of the following level. - * If the level of an appender is lower or equals to this level - * the logging is done. - */ -enum RplLoggerLevel { - LOG_ERROR = 10, ///< marks an error. - LOG_WARNING = 15, ///< marks a warning - LOG_INFO = 20, ///< marks an information - LOG_DEBUG = 25 ///< for debug purpose only -}; - -class RplAppender { -public: - RplAppender(const QByteArray& name); - virtual ~RplAppender(); -private: - // No copy constructor: no implementation! - RplAppender(const RplAppender& source); - // Prohibits assignment operator: no implementation! - RplAppender& operator =(const RplAppender& source); -public: - virtual void log(RplLoggerLevel level, int location, const char* message, - RplLogger* logger) = 0; - bool isActive(RplLoggerLevel level); - void setLevel(RplLoggerLevel level); - void setAutoDelete(bool onNotOff); - bool isAutoDelete() const; - RplLoggerLevel getLevel() const; - const char* getName() const; - -private: - // Name of the appender. Used to find the appender in a list of appenders - QByteArray m_name; - // only locations with a lower or equal level will be logged - RplLoggerLevel m_level; - // true: the logger destroys the instance. false: the deletion must be done outside of the logger - bool m_autoDelete; -}; - -class RplLogger { -public: - static RplLogger* globalLogger(); - static void destroyGlobalLogger(); -private: - // the standard logger, can be called (with globalLogger()) from each location - static RplLogger* m_globalLogger; -public: - RplLogger(); - virtual ~RplLogger(); -private: - // No copy constructor: no implementation! - RplLogger(const RplLogger& source); - // Prohibits assignment operator: no implementation! - RplLogger& operator =(const RplLogger& source); -public: - bool log(RplLoggerLevel level, int location, const char* message); - bool log(RplLoggerLevel level, int location, const QByteArray& message); - bool log(RplLoggerLevel level, int location, const QString& message); - bool logv(RplLoggerLevel level, int location, const char* format, ...); - bool logv(RplLoggerLevel level, int location, const QByteArray& format, ...); - bool log(RplLoggerLevel level, int location, const char* format, - va_list& varlist); - void addAppender(RplAppender* appender); - RplAppender* findAppender(const char* name) const; - void buildStandardAppender(RplConfig* config, const char* prefix = "logfile.", - const char* defaultLoggerName = "logger"); - void buildStandardAppender(const QByteArray& prefix, int maxSize = 10*1024*1024, - int maxCount = 5); - QByteArray buildStdPrefix(RplLoggerLevel level, int location); - const QByteArray& getStdPrefix(RplLoggerLevel level, int location); - char getPrefixOfLevel(RplLoggerLevel level) const; - bool isActive(RplLoggerLevel level) const; - void setLevel(RplLoggerLevel level); - void setWithLocking(bool onNotOff); -private: - // the assigned appenders: - RplAppender* m_appenders[16]; - // the number of appenders in m_appenders: - size_t m_countAppenders; - // "" or the cache of the prefix of the current logging line: This can be reused by any appender. - QByteArray m_stdPrefix; - QMutex m_mutex; - bool m_withLocking; -}; - -/** - * Implements an appender which puts the messages to a standard stream: stdout or stderr - */ -class RplStreamAppender : public RplAppender { -public: - RplStreamAppender(FILE* stream, const char* appenderName = "FileAppender"); - virtual ~RplStreamAppender(); -public: - virtual void log(RplLoggerLevel level, int location, const char* message, - RplLogger* logger); -private: - // stdout or stderr: - FILE* m_fp; -}; - -/** - * Implements an appender which puts the messages to a file - */ -class RplFileAppender : public RplAppender { -public: - RplFileAppender(const QByteArray& name, int maxSize, int maxCount, - const char* appenderName = "FileAppender"); - virtual ~RplFileAppender(); -public: - void open(); - virtual void log(RplLoggerLevel level, int location, const char* message, - RplLogger* logger); - -private: - // prefix of the log file name. Will be appended by "..log" - QByteArray m_prefix; - // maximal size of a logging file: - int m_maxSize; - // maximal count of logging files. If neccessary the oldest file will be deleted. - int m_maxCount; - // the size of the current log file: - int m_currentSize; - // the number of the current log file: - int m_currentNo; - // the current log file: - FILE* m_fp; -}; - -/** - * Stores the log messages in a list. - */ -class RplMemoryAppender : public RplAppender { -public: - RplMemoryAppender(int maxLines = 1024, - const char* appenderName = "MemoryAppender"); - ~RplMemoryAppender(); -public: - virtual void log(RplLoggerLevel level, int location, const char* message, - RplLogger* logger); - const QList& getLines() const; - void clear(); -private: - 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. - bool m_addPrefix; -}; - -#endif // RPLLOGGER_HPP diff --git a/rplcore/rplqstring.cpp b/rplcore/rplqstring.cpp deleted file mode 100644 index 2cd8995..0000000 --- a/rplcore/rplqstring.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Missed operation for QStrings. - */ -/** @file rplcore/rplqstring.hpp - * - * @brief Definitions for missed operation for QStrings. - */ -#include "rplcore/rplcore.hpp" - - -/** - * @brief Determines the length and vlaue of an integer. - * - * @param text the number as text - * @param start the first index to inspect - * @param radix the base of the number sytem: 8 (octal), 10 or 16 - * @param pValue OUT: the value of the integer. May be NULL - * - * @return <=0: no integer found - * otherwise: the length of the integer - */ -int RplQString::lengthOfUInt64(const QString& text, int start, - int radix, quint64* pValue) -{ - int inputLength = text.size(); - qint64 value = 0; - int ix = start; - int cc; - if (radix == 10){ - while (ix < inputLength){ - if ( (cc = text[ix].unicode()) >= '0' && cc <= '9') - value = value * 10 + cc - '0'; - else - break; - ix++; - } - } else if (radix == 16){ - while (ix < inputLength){ - if ( (cc = text[ix].unicode()) >= '0' && cc <= '9') - value = value * 16 + cc - '0'; - else if (cc >= 'A' && cc <= 'F') - value = value * 16 + cc - 'A' + 10; - else if (cc >= 'a' && cc <= 'f') - value = value * 16 + cc - 'a' + 10; - else - break; - ix++; - } - } else if (radix == 8){ - while (ix < inputLength){ - if ( (cc = text[ix].unicode()) >= '0' && cc <= '7') - value = value * 8 + cc - '0'; - else - break; - ix++; - } - } else { - throw RplException("RplQString::lengthOfInt(): wrong radix: %d", radix); - } - if (pValue != NULL) - *pValue = value; - return ix - start; -} -/** - * @brief Determines the length and value of an unsigned integer. - * - * @param text the number as text - * @param start the first index to inspect - * @param radix the base of the number sytem: 8 (octal), 10 or 16 - * @param pValue OUT: the value of the integer. May be NULL - * - * @return 0: no integer found - * otherwise: the length of the integer - */ -int RplQString::lengthOfUInt(const QString& text, int start, - int radix, uint* pValue) -{ - quint64 value; - int rc = lengthOfUInt64(text, start, radix, &value); - if (pValue != NULL) - *pValue = (uint) value; - return rc; -} - -/** - * @brief Determines the length and value of a floting point number. - * - * @param text the number as text - * @param start the first index to inspect - * @param pValue OUT: the value of the integer. May be NULL - * - * @return <=0: no real number found - * otherwise: the length of the floating point number - */ -int RplQString::lengthOfReal(const QString& text, int start, qreal* pValue) -{ - int inputLength = text.size(); - qreal value = 0.0; - int cc; - int ix = start; - while (ix < inputLength){ - if ( (cc = text[ix].unicode()) >= '0' && cc <= '9') - value = value * 10 + (cc - '0'); - else - break; - ix++; - } - // found: a digit has been found (in front of or behind the '.' - bool found = ix > start; - if (ix < inputLength && text[ix].unicode() == '.'){ - ix++; - } - if (ix < inputLength && text[ix].isDigit()){ - found = true; - qreal divisor = 1; - qreal precision = 0; - while ( ix < inputLength && (cc = text[ix].unicode()) >= '0' && cc <= '9'){ - divisor *= 10; - precision = precision*10 + cc - '0'; - ix++; - } - value += precision / divisor; - } else if (! found){ - ix = start; - } - if (found && ix + 1 < inputLength && toupper(text[ix].unicode()) == 'E'){ - int savePoint = ix; - ix++; - bool negative = false; - if ( (cc = text[ix].unicode()) == '+') - ix++; - else if (cc == '-'){ - ix++; - negative = true; - } - if (ix >= inputLength || ! text[ix].isDigit()) - ix = savePoint; - else{ - int exponent = 0; - while (ix < inputLength && text[ix].isDigit()){ - exponent = exponent * 10 + text[ix].unicode() - '0'; - ix++; - } - if (negative) - value /= qPow(10, exponent); - else - value *= qPow(10, exponent); - } - } - if (pValue) - *pValue = value; - return found ? ix - start : 0; -} - -/** - * @brief Converts a QString into an utf-8 string - * - * The expression qstring.toUtf8().constData() is not allowed - * in a variable argument list like sprintf. This is a thread save workaround. - * - * @param source string to convert - * @param buffer OUT: target buffer - * @param bufferSize size of the target buffer - * @return buffer - */ -char*RplQString::utf8(const QString& source, char buffer[], size_t bufferSize) -{ - QByteArray val = source.toUtf8(); - if (val.length() < (int) bufferSize) - bufferSize = val.length() + 1; - memcpy(buffer, val.constData(), bufferSize - 1); - buffer[bufferSize - 1] = '\0'; - return buffer; -} diff --git a/rplcore/rplqstring.hpp b/rplcore/rplqstring.hpp deleted file mode 100644 index 780ef24..0000000 --- a/rplcore/rplqstring.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 RPLQSTRING_HPP -#define RPLQSTRING_HPP - -class RplQString -{ -public: - static int lengthOfUInt64(const QString& text, int start = 0, - int radix = 10, quint64* value = NULL); - static int lengthOfUInt(const QString& text, int start, int radix, - uint* pValue); - static int lengthOfReal(const QString& text, int start = 0, - qreal* value = NULL); - /** - * @brief Returns the value of a hexadecimal digit. - * - * @param digit a (unicode) character - * @return -1: not a hexadecimal digit
- * otherwise: the value, e.g. 10 for 'a' - */ - inline static int valueOfHexDigit(int digit){ - return digit >= '0' && digit <= '9' ? digit - '0' - : digit >= 'A' && digit <= 'F' ? digit - 'A' + 10 - : digit >= 'a' && digit <= 'f' ? digit - 'a' + 10 : -1; - } - static char* utf8(const QString& source, char buffer[], size_t bufferSize); -}; - -#endif // RPLQSTRING_HPP diff --git a/rplcore/rplstring.cpp b/rplcore/rplstring.cpp deleted file mode 100644 index a8bd039..0000000 --- a/rplcore/rplstring.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/* - * 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. - */ -/** @file - * @brief Missed operations for QByteArrays. - */ -/** @file rplcore/rplstring.cpp - * - * @brief Definitions for missed operations for QByteArrays. - */ -#include "rplcore.hpp" - -/** @class RplString rplstring.hpp "rplcore/rplstring.hpp" - * - * @brief Implements some services around strings. - * - * This is a class with static members only. - */ - -/** - * @brief Counts the occurrences of a given char in a string. - * - * @param line the text to inspect - * @param cc the char to count - * @return the number of cc in the text - */ -int RplString::countChar(const char* line, char cc) -{ - const char* ptr = line; - int rc = 0; - while( (ptr = strchr(ptr, cc)) != NULL){ - rc++; - ptr++; - } - return rc; -} -/** - * Counts the occurrences of a string in a string. - * - * @param source in this string will be searched - * @param item this item will be searched - * @return the count of occurrences - */ -int RplString::count(const char* source, const char* item) { - const char* end = source; - int rc = 0; - int lengthItem = strlen(item); - while(true) { - const char* start = end; - end = strstr(start, item); - if(end == NULL) - break; - else { - rc++; - end += lengthItem; - } - } - return rc; -} - -/** - * Returns a string with a given maximum length. - * - * @param source the source - * @param maxLength the maximum length of the result - * @param buffer Out: used if length of the result is shorter - * @param appendix if the result is cut this string will be appended.
- * May be NULL. - * @return source: the source is enough short
- * the prefix of source with the given length - */ -const QByteArray& RplString::cutString(const QByteArray& source, int maxLength, - QByteArray& buffer, const char* appendix) { - QByteArray& rc = source.length() <= maxLength ? (QByteArray&) source : buffer; - if(source.length() > maxLength) { - buffer = source.left(maxLength); - if(appendix != NULL && appendix[0] != '\0') - buffer.append(appendix); - } - return rc; -} -static char s_fileSeparator = 0; - -/** - * @brief Returns the os specific file path separator. - * @return the file path separator, e.g. "/" for linux - */ -const char* RplString::fileSeparator(){ - return fileSeparatorChar() == '/' ? "/" : "\\"; -} - -/** - * @brief Returns the os specific file path separator. - * @return the file path separator, e.g. '/' for linux - */ -char RplString::fileSeparatorChar(){ - if (s_fileSeparator == 0){ - const char* path = getenv("PATH"); - if (path != NULL){ - s_fileSeparator = strchr(path, ';') != NULL - || strchr(path, '\\') != NULL ? '\\' : '/'; - } else { - if (getenv("windows") != NULL) - s_fileSeparator = '\\'; - else - s_fileSeparator = '/'; - } - } - return s_fileSeparator; -} - -/** - * Builds a hexadecimal dump. - * - * Format: a sequence of hex digits followed by the ascii interpretation. - * - * Example: "42 30 61 B0a" - * - * @param data data to convert - * @param length length of data - * @param bytesPerLine one line containes so many bytes of data - * @return the hex dump - */ -QByteArray RplString::hexDump(uint8_t* data, int length, int bytesPerLine) { - QByteArray rc; - int fullLines = length / bytesPerLine; - int expectedLength = (bytesPerLine * 4 + 2) * (fullLines + 1); - rc.reserve(expectedLength + 100); - int ixData = 0; - int col; - char buffer[16]; - for(int lineNo = 0; lineNo < fullLines; lineNo++) { - for(col = 0; col < bytesPerLine; col++) { - qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]); - rc.append(buffer); - } - rc.append(' '); - for(col = 0; col < bytesPerLine; col++) { - uint8_t cc = data[ixData + col]; - rc.append(cc > ' ' && cc < 128 ? (char) cc : '.'); - } - ixData += bytesPerLine; - rc.append('\n'); - } - // incomplete last line: - int restBytes = length - ixData; - if(restBytes > 0) { - for(col = 0; col < restBytes; col++) { - qsnprintf(buffer, sizeof buffer, "%02x ", data[ixData + col]); - rc.append(buffer); - } - for(col = restBytes; col < bytesPerLine; col++) { - rc.append(" "); - } - rc.append(' '); - for(col = 0; col < restBytes; col++) { - uint8_t cc = data[ixData + col]; - rc.append(cc > ' ' && cc < 128 ? (char) cc : '.'); - } - rc.append('\n'); - } - return rc; -} - - -/** - * Reads a file into a string. - * - * @param file file to read - * @param removeLastNewline true: if the last character is a newline - * the result will not contain this - * @return the file's content - */ -QByteArray RplString::read(const char* file, bool removeLastNewline) { - QByteArray rc; - struct stat info; - size_t size; - if(stat(file, &info) == 0 && (size = info.st_size) > 0) { - FILE* fp = fopen(file, "r"); - if(fp != NULL) { - rc.resize(info.st_size); - fread(rc.data(), 1, size, fp); - fclose(fp); - if(removeLastNewline && rc.at(size - 1) == '\n') { - rc.resize(size - 1); - } - } - } - return rc; -} - -QByteArray RplString::replaceNode(const char* source, const char* newNode){ - char sep = fileSeparatorChar(); - const char* ptr = strrchr(source, sep); - QByteArray rc; - rc.reserve(strlen(source) + strlen(newNode) + 1); - if (ptr == NULL){ - rc.append(source).append(sep).append(newNode); - } else if (ptr[0] == '\0'){ - rc.append(source).append(newNode); - } else { - rc.append(source, ptr - source + 1).append(newNode); - } - return rc; -} - -/** - * Converts a string into an array of strings. - * - * @param source string to convert - * @param separator the separator between the items to split - * @return an array with the splitted source - */ -QList RplString::toArray(const char* source, - const char* separator) { - const char* end = source; - QList rc; - rc.reserve(count(source, separator) + 1); - int lengthItem = strlen(separator); - while(*end != '\0') { - const char* start = end; - end = strstr(start, separator); - if(end == NULL) { - end = start + strlen(start); - } - rc.append(QByteArray(start, end - start)); - if(end[0] != '\0') - end += lengthItem; - } - return rc; -} - -QByteArray RplString::toCString(const char* source, int maxLength){ - if (maxLength <= 0) - maxLength = strlen(source); - int binaries = 0; - int ix; - for (ix = 0; ix < maxLength; ix++) - if (source[ix] < ' '){ - binaries++; - } - QByteArray rc; - rc.reserve(maxLength + 3 * binaries + 1); - char cc; - for (ix = 0; ix < maxLength; ix++) - if ( (cc = source[ix]) >= ' '){ - rc += source[ix]; - } else { - switch(cc){ - case '\0': - // stop looping: - ix = maxLength; - break; - case '\n': - rc += "\\n"; - break; - case '\r': - rc += "\\r"; - break; - case '\t': - rc += "\\t"; - break; - default: - { - char buffer[5]; - qsnprintf(buffer, sizeof buffer, "\\x%02x", - ((unsigned int) cc) % 0xff); - rc += buffer; - break; - } - } - } - return rc; -} - -/** - * Return an integer as an QByteArray. - * - * @param value value to convert - * @param format format like in sprintf() - * @return the ascii form of the value - */ -QByteArray RplString::toNumber(int value, const char* format) { - char buffer[128]; - qsnprintf(buffer, sizeof buffer, format, value); - return QByteArray(buffer); -} - -/** - * Writes a string to a file. - * - * @param file the file's name - * @param content NULL or the file's content - * @param mode the file open mode: "w" for write, "a" for append - * @return true: successful
- * false: error occurred - */ -bool RplString::write(const char* file, const char* content, const char* mode) { - FILE* fp = fopen(file, mode); - if(fp != NULL) { - fputs(content, fp); - fclose(fp); - } - return fp != NULL; -} -/** - * @brief Returns the length of the number string. - * - * @param text a text to inspect - * @param skipTrailingSpaces true: if spaces are behind the number - * the result contains the length of these - * @return 0: not a number
- * otherwise: the length of the number string - */ -int RplString::lengthOfNumber(const char* text, bool skipTrailingSpaces){ - int rc = 0; - bool found = false; - const char* ptr = text; - while(isspace(*ptr)) - ptr++; - if ( (*ptr == '+' || *ptr == '-')) - ptr++; - found = isdigit(*ptr); - while(isdigit(*ptr)){ - ptr++; - } - if (*ptr == '.'){ - ptr++; - if (isdigit(*ptr)){ - found = true; - while(isdigit(*ptr)) - ptr++; - } - } - if (found && toupper(*ptr) == 'E'){ - const char* ptrToE = ptr; - ptr++; - if (*ptr == '+' || *ptr == '-') - ptr++; - if (! isdigit(*ptr)) - ptr = ptrToE; - else { - while(isdigit(*ptr)) - ptr++; - } - } - if (found && skipTrailingSpaces){ - while(isspace(*ptr)){ - ptr++; - } - } - rc = ! found ? 0 : ptr - text; - return rc; -} -/** - * @brief Adds the count of the possible separators. - * - * @param countCommas IN/OUT: number of ',' - * @param countSemicolons IN/OUT: number of ';' - * @param countPipes IN/OUT: number of '|' - * @param countBlanks IN/OUT: number of ' ' - */ -static void addSeparators(const char* line, int& commas, int& semicolons, - int& pipes, int& blanks) -{ - commas += RplString::countChar(line, ','); - semicolons += RplString::countChar(line, ';'); - pipes += RplString::countChar(line, '|'); - blanks += RplString::countChar(line, ' '); -} - -/** - * @brief Finds the separator of the CSV file. - * - * If the file contain TABs the result is TAB. - * If not: - * Inspects the first 5 lines and counts the possible separators. - * The most found separator will be returned. - * - * @param fp CSV file - * @param buffer a line buffer - * @param bufferSize the size of buffer[] - */ -char RplString::findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize){ - char rc = '\0'; - int lineNo = 0; - int maxLines = 5; - const char* line; - int commas = 0; - int semicolons = 0; - int pipes = 0; - int blanks = 0; - while(++lineNo < maxLines && (line = fgets(buffer, bufferSize, fp)) != NULL){ - if (strchr(line, '\t') != NULL){ - rc = '\t'; - break; - } - addSeparators(line, commas, semicolons, pipes, blanks); - } - fseek(fp, 0, SEEK_SET); - if (rc != '\t'){ - if (semicolons > 0 && commas > 0){ - // if ',' is decimal separator and ';' is the column separator: - // Add one semicolon per line because of number of values is - // 1 greater than the number of separators - semicolons += lineNo; - } - if (commas + semicolons + pipes == 0) { - rc = blanks > 0 ? ' ' : '\0'; - } else if (semicolons >= commas && semicolons >= pipes) - rc = ';'; - else if (commas > semicolons && commas > pipes) - rc = ','; - else if (pipes > commas && pipes > semicolons) - rc = '|'; - } - return rc; -} - -/** - * @brief Determines the length and vlaue of an integer. - * - * @param text the number as text - * @param radix the base of the number system: 8 (octal), 10 or 16 - * @param pValue OUT: the value of the integer. May be NULL - * - * @return <=0: no integer found - * otherwise: the length of the integer - */ -int RplString::lengthOfUInt64(const char* text, int radix, quint64* pValue) -{ - qint64 value = 0; - int length = 0; - int cc; - if (radix == 10){ - while ( (cc = text[length]) >= '0' && cc <= '9'){ - value = value * 10 + cc - '0'; - length++; - } - } else if (radix == 16){ - while (true){ - if ( (cc = text[length]) >= '0' && cc <= '9') - value = value * 16 + cc - '0'; - else if (cc >= 'A' && cc <= 'F') - value = value * 16 + cc - 'A' + 10; - else if (cc >= 'a' && cc <= 'f') - value = value * 16 + cc - 'a' + 10; - else - break; - length++; - } - } else if (radix == 8){ - while (true){ - if ( (cc = text[length]) >= '0' && cc <= '7') - value = value * 8 + cc - '0'; - else - break; - length++; - } - } else { - throw RplException("RplString::lengthOfInt(): wrong radix: %d", radix); - } - if (pValue != NULL) - *pValue = value; - return length; -} - -/** - * @brief Determines the length and value of a floting point number. - * - * @param text the number as text - * @param pValue OUT: the value of the integer. May be NULL - * - * @return <=0: no real number found - * otherwise: the length of the floating point number - */ -int RplString::lengthOfReal(const char* text, qreal* pValue) -{ - qreal value = 0.0; - int cc; - int length = 0; - while (true){ - if ( (cc = text[length]) >= '0' && cc <= '9') - value = value * 10 + (cc - '0'); - else - break; - length++; - } - // found: a digit has been found (in front of or behind the '.' - bool found = length > 0; - if (text[length] == '.'){ - length++; - } - if (isdigit(text[length])){ - found = true; - qreal divisor = 1; - qreal precision = 0; - while ( (cc = text[length]) >= '0' && cc <= '9'){ - divisor *= 10; - precision = precision*10 + cc - '0'; - length++; - } - value += precision / divisor; - } else if (! found){ - length = 0; - } - if (found && toupper(text[length]) == 'E'){ - int savePoint = length; - length++; - bool negative = false; - if ( (cc = text[length]) == '+') - length++; - else if (cc == '-'){ - length++; - negative = true; - } - if (! isdigit(text[length])) - length = savePoint; - else{ - int exponent = 0; - while (isdigit(text[length])){ - exponent = exponent * 10 + text[length] - '0'; - length++; - } - if (negative) - value /= qPow(10, exponent); - else - value *= qPow(10, exponent); - } - } - if (pValue) - *pValue = value; - return found ? length : 0; -} - diff --git a/rplcore/rplstring.hpp b/rplcore/rplstring.hpp deleted file mode 100644 index ee8924c..0000000 --- a/rplcore/rplstring.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 RPLSTRING_HPP -#define RPLSTRING_HPP - -class RplString { -public: - static int countChar(const char* line, char cc); - static int count(const char* source, const char* item); - static const QByteArray& cutString(const QByteArray& source, int maxLength, - QByteArray& buffer, const char* appendix = "..."); - static const char* fileSeparator(); - static char fileSeparatorChar(); - static QByteArray hexDump(uint8_t* data, int length, int bytesPerLine = 16); - static QByteArray hexDump(const void* data, int length, int bytesPerLine = 16) { - return hexDump((uint8_t*) data, length, bytesPerLine); - } - static QByteArray read(const char* file, bool removeLastNewline = true); - static QByteArray replaceNode(const char* source, const char* newNode); - static bool write(const char* file, const char* content = NULL, - const char* mode = "w"); - 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); - static char findCsvSeparator(FILE* fp, char* buffer, size_t bufferSize); - static int lengthOfUInt64(const char* text, int radix, quint64* pValue); - static int lengthOfReal(const char* text, qreal* pValue); -}; - -#endif // RPLSTRING_HPP diff --git a/rplcore/rplterminator.cpp b/rplcore/rplterminator.cpp deleted file mode 100644 index 4fab794..0000000 --- a/rplcore/rplterminator.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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. - */ -/** @file - * @brief Implements a thread stopper. - */ -/** @file - * @brief Definitions for a thread stopper. - */ -#include "rplcore.hpp" - -enum { - LOC_CAUSE_TERMINATION_1 = RPL_FIRST_OF(RPLMODULE_TERMINATOR), // 10901 -}; - -/** - * @class RplTerminator rplterminator.hpp "rplcore/rplterminator.hpp" - * - * @brief Implements a thread stopper. - * - * Allows to terminate a thread avoiding unfreeing resources, deadlocks etc. - * - * The application must create one instance of a RplTerminator. - * All threads gets this instance and call them periodically if the application should stop. - * If yes they finish their work, frees the resources and stop. - * - */ - -/** - * @brief Constructor. - * @param logger NULL or the logger. Used to protocol the termination - */ -RplTerminator::RplTerminator(RplLogger* logger) : - m_stop(false), - m_logger(logger) { -} - -/** - * @brief Destructor. - */ -RplTerminator::~RplTerminator() { -} - -/** - * @brief Defines the stop of all threads. - * - * @param reason the reason of the termination. Will be logged (if a logger is defined) - * @param file NULL or the file of the caller. Normally set with __FILE__ - * @param lineNo 0 or the line number of the caller. Normally set with __LINE__ - * @param level log level - * @param location 0 or the location of the caller - */ -void RplTerminator::causeTermination(const char* reason, const char* file, - int lineNo, RplLoggerLevel level, int location) { - if(m_logger != NULL) { - QByteArray message(reason); - if(file != NULL) { - message.append(" [").append(file).append(lineNo).append("]"); - } - m_logger->log(level, location == 0 ? LOC_CAUSE_TERMINATION_1 : location, - message); - } - m_stop = true; -} - -/** - * @brief Tests whether the thread should be stopped. - * @return true: the thread should be stopped.
- * false: otherwise - */ -bool RplTerminator::isStopped() const { - return m_stop; -} - diff --git a/rplcore/rplterminator.hpp b/rplcore/rplterminator.hpp deleted file mode 100644 index f0e49c1..0000000 --- a/rplcore/rplterminator.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 RPLTERMINATOR_HPP -#define RPLTERMINATOR_HPP - -class RplTerminator { -public: - RplTerminator(RplLogger* logger = NULL); - virtual ~RplTerminator(); -private: - // No copy constructor: no implementation! - RplTerminator(const RplTerminator& source); - // Prohibits assignment operator: no implementation! - RplTerminator& operator =(const RplTerminator& source); -public: - void causeTermination(const char* reason, const char* file = NULL, - int lineNo = 0, RplLoggerLevel level = LOG_ERROR, int location = 0); - bool isStopped() const; -private: - bool m_stop; - RplLogger* m_logger; -}; - -#endif // RPLTERMINATOR_HPP diff --git a/rplcore/rpltest.cpp b/rplcore/rpltest.cpp deleted file mode 100644 index ea31b61..0000000 --- a/rplcore/rpltest.cpp +++ /dev/null @@ -1,489 +0,0 @@ -/* - * 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. - */ -/** @file - * @brief A testing tool like JUnit. - */ -/** @file rplcore/rpltest.hpp - * - * @brief Definitions for a testing tool like JUnit. - */ -#include "rplcore/rplcore.hpp" -#include "rpltest.hpp" - -/** @class RplTest rpltest.hpp "rplcore/repltest" - * - * @brief Implements an unit test base class similar JUnit for java. - * - * Example for usage: - * - * @see rplexample.cpp - */ -class RplTest; - -/** - * @brief Constructor. - * - * @param name - */ -RplTest::RplTest(const char* name) : - m_errors(0), - m_name(name), - m_logger(), - m_memoryAppender(1024), - m_memoryLogger() -{ - m_memoryAppender.setAutoDelete(false); - m_logger.buildStandardAppender(getTempDir("rpltest")); - log(QByteArray("Start of ") + m_name); - m_memoryLogger.addAppender(&m_memoryAppender); -} - -/** - * @brief Runs all tests of the test class. - */ -void RplTest::run() { - try { - doIt(); - } catch(RplException e) { - error("unexpected RplException: %s", e.getMessage().constData()); - } catch(...){ - error("unknown Exception"); - } - - if(m_errors > 0) { - error("Unit %s has %d error(s)", m_name.data(), m_errors); - // error() increments, we decrement: - m_errors--; - } -} - -/** - * @brief Destructor. - */ -RplTest::~RplTest() { -} - -/** - * Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(int expected, int current, const char* file, - int lineNo) { - if(expected != current) - error("%s-%d: error: %d != %d / %x != %x)", file, lineNo, expected, current, - (unsigned int) expected, (unsigned int) current); - return expected == current; -} - -/** - * Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(qint64 expected, qint64 current, const char* file, - int lineNo) { - if(expected != current) - error("%s-%d: error: %lld != %lld / %llx != %llx)", file, lineNo, - expected, current, (quint64) expected, (quint64) current); - return expected == current; -} - - -/** - * Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(qreal expected, qreal current, const char* file, - int lineNo) { - if(expected != current) - error("%s-%d: error: %d != %d / %x != %x)", file, lineNo, expected, current, - (unsigned int) expected, (unsigned int) current); - return expected == current; -} - -/** - * @brief Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(const char* expected, const QString& current, - const char* file, int lineNo) { - bool equal = assertEquals(expected, current.toUtf8().constData(), file, - lineNo); - return equal; -} - -/** - * @brief Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(const QString& expected, const QString& current, - const char* file, int lineNo) { - bool equal = assertEquals(expected.toUtf8().constData(), - current.toUtf8().constData(), file, lineNo); - return equal; -} - - -/** - * @brief Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(const char* expected, const char* current, - const char* file, int lineNo) { - bool equal = strcmp(expected, current) == 0; - if(! equal) { - if(strchr(expected, '\n') != NULL || strchr(current, '\n')) { - QList exp = RplString::toArray(expected, "\n"); - QList cur = RplString::toArray(current, "\n"); - equal = assertEquals(exp, cur, file, lineNo); - } else { - int ix = 0; - while(expected[ix] == current[ix] && expected[ix] != '\0') - ix++; - char pointer[12+1]; - char* ptr = pointer; - int maxIx = ix > 10 ? 10 : ix; - for(int ii = 0; ii < maxIx - 1; ii++) - *ptr++ = '-'; - *ptr++ = '^'; - *ptr = '\0'; - if(ix < 10) - error("%s-%d: error: diff at index %d\n%s\n%s\n%s", - file, lineNo, ix, expected, current, pointer); - else - error("%s-%d: error: diff at index %d\n%s\n...%s\n...%s\n%s", - file, lineNo, ix, current, - expected + ix - 10 + 3, - current + ix - 10 + 3, - pointer); - } - } - return equal; -} - -/** - * @brief Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -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) - nMax = current.size(); - for(int ix = 0; ix < nMax; ix++) { - if(expected.at(ix) != current.at(ix)) { - error("%s-%d: difference in line %d", file, lineNo, ix+1); - m_errors--; - assertEquals(expected.at(ix).constData(), current.at(ix).constData(), - file, lineNo); - rc = false; - break; - } - } - if(rc) { - if(expected.size() > nMax) - error("%s-%d: less lines than expected (%d):\n%s", - file, lineNo, nMax, expected.at(nMax).constData()); - else if(expected.size() < nMax) - error("%s-%d: more lines than expected (%d):\n%s", - file, lineNo, nMax, current.at(nMax).constData()); - } - return rc; -} -/** - * @brief Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(const QByteArray& expected, - const QByteArray& current, const char* file, int lineNo) { - return assertEquals(expected.data(), current.data(), file, lineNo); -} - -/** - * @brief Tests the equality of two values. - * - * Differences will be logged. - * - * @param expected the expected value - * @param current the current value - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: equal - */ -bool RplTest::assertEquals(const char* expected, const QByteArray& current, - const char* file, int lineNo) { - return assertEquals(expected, current.constData(), file, lineNo); -} - -/** - * @brief Tests whether a value is true. - * - * A value of false will be logged. - * - * @param condition value to test - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return condition - */ -bool RplTest::assertTrue(bool condition, const char* file, int lineNo) { - if(! condition) - error("%s-%d: not TRUE", file, lineNo); - return condition; -} - -/** - * @brief Tests whether a value is false. - * - * A value of true will be logged. - * - * @param condition value to test - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return ! condition - */ -bool RplTest::assertFalse(bool condition, const char* file, int lineNo) { - if(condition) - error("%s-%d: not FALSE", file, lineNo); - return ! condition; -} - -/** - * @brief Tests whether a value is NULL. - * - * A value of not NULL will be logged. - * - * @param ptr value to test - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: ptr is NULL - */ -bool RplTest::assertNull(const void* ptr, const char* file, int lineNo) { - if(ptr != NULL) - error("%s-%d: not NULL", file, lineNo); - return ptr == NULL; -} - -/** - * @brief Tests whether a value is not NULL. - * - * A value of NULL will be logged. - * - * @param ptr value to test - * @param file the file containing the test - * @param lineNo the line number containing the test - * @return true: ptr is not NULL - */ -bool RplTest::assertNotNull(const void* ptr, const char* file, int lineNo) { - if(ptr == NULL) - error("%s-%d: is NULL", file, lineNo); - return ptr != NULL; -} - -/** - * @brief Compares two files line by line. - * - * @param expected the file with the expected content - * @param current the file with the current content - * @param file the source file (point of the comparison) - * @param lineNo the source position (point of the comparison) - * @return true: the files are equal
- * false: otherwise - */ -bool RplTest::assertEqualFiles(const char* expected, const char* current, - const char* file, int lineNo) -{ - bool rc = false; - QByteArray expectedContent = RplString::read(expected, true); - QByteArray currentContent = RplString::read(current, true); - if (expectedContent.isEmpty()){ - char buffer[512]; - qsnprintf(buffer, sizeof buffer, "%s has no content. Does it exist?", - expected); - error(buffer); - } else if (currentContent.isEmpty()){ - char buffer[512]; - qsnprintf(buffer, sizeof buffer, "%s has no content. Does it exist?", - current); - error(buffer); - } else { - QList expLines = expectedContent.split('\n'); - QList curLines = currentContent.split('\n'); - rc = assertEquals(expLines, curLines, file, lineNo); - } - return rc; -} - -/** - * @brief Writes an info. - * - * @param message message to show - * @return true (for chaining) - */ -bool RplTest::log(const char* message) { - m_logger.log(LOG_INFO, 0, message); - return true; -} - - -/** - * @brief Writes an error. - * - * @param format message to show. With placeholders like std::printf() - * @param ... the values for the placeholders in format - * @return false (for chaining) - */ -bool RplTest::error(const char* format, ...) { - m_errors++; - va_list ap; - va_start(ap, format); - m_logger.log(LOG_ERROR, 0, format, ap); - va_end(ap); - return false; -} - -/** - * @brief Tests whether the m_memoryLogger has a message containing a given pattern. - * - * @param pattern regular expression to search - * @return true: pattern has been found
- * false: otherwise - */ -bool RplTest::logContains(const char* pattern) -{ - const QList& lines = m_memoryAppender.getLines(); - QRegularExpression rexpr(pattern); - bool rc = false; - QRegularExpressionMatch match; - for (int ii = 0; ii < lines.size(); ii++){ - const QByteArray& line = lines.at(ii); - match = rexpr.match(line); - if (match.hasMatch()){ - rc = true; - break; - } - } - return rc; -} - -/** - * @brief Returns the name of a directory in the temp dir. - * - * If the named directory does not exist it will be created. - * - * @param node NULL or the node (name without path) - * @param parent NULL or a node of the parent - * @param withSeparator true: the result ends with slash/backslash - * @return the name of an existing directory - */ -QByteArray RplTest::getTempDir(const char* node, const char* parent, - bool withSeparator) { - QByteArray temp("c:\\temp"); - struct stat info; - const char* ptr; - if((ptr = getenv("TMP")) != NULL) - temp = ptr; - else if((ptr = getenv("TEMP")) != NULL) - temp = ptr; - else if(stat("/tmp", &info) == 0) - temp = "/tmp"; - char sep = m_separator = temp.indexOf('/') >= 0 ? '/' : '\\'; - if(temp.at(temp.length() - 1) != sep) - temp += sep; - if(parent != NULL) { - temp += parent; - if(stat(temp.constData(), &info) != 0) - mkdir(temp.constData(), (-1)); - temp += sep; - } - if(node != NULL) { - temp += node; - temp += sep; - if(stat(temp.data(), &info) != 0) - mkdir(temp.data(), -1); - } - if(! withSeparator) - temp.resize(temp.length() - 1); - return temp; -} - -/** - * @brief Returns a name of a file in a temporary directory. - * - * @param node the file's name without path - * @param parent NULL or the name of a subdirectory the file will be inside - * @param deleteIfExists true: if the file exists it will be removed - * @return the full name of a temporary file - */ -QByteArray RplTest::getTempFile(const char* node, const char* parent, - bool deleteIfExists) { - QByteArray dir = getTempDir(parent); - QByteArray rc = dir; - if (! rc.endsWith(m_separator)) - rc += m_separator; - rc += node; - struct stat info; - if(deleteIfExists && stat(rc.constData(), &info) == 0) - unlink(rc.constData()); - return rc; -} diff --git a/rplcore/rpltest.hpp b/rplcore/rpltest.hpp deleted file mode 100644 index 5c7d3ac..0000000 --- a/rplcore/rpltest.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 RPLTEST_HPP -#define RPLTEST_HPP - -// the sources generated from QT include this file directly: -#ifndef RPLCORE_HPP -#include -#endif - -class RplTest { -public: - RplTest(const char* name); - virtual ~RplTest(); -private: - // No copy constructor: no implementation! - RplTest(const RplTest& source); - // Prohibits assignment operator: no implementation! - RplTest& operator =(const RplTest& source); -public: - bool assertEquals(int expected, int current, const char* file, int lineNo); - bool assertEquals(qint64 expected, qint64 current, const char* file, int lineNo); - bool assertEquals(qreal expected, qreal current, const char* file, int lineNo); - bool assertEquals(const char* expected, const QString& current, - const char* file, int lineNo); - bool assertEquals(const QString& expected, const QString& current, - const char* file, int lineNo); - bool assertEquals(const char* expected, const char* current, - const char* file, int lineNo); - bool assertEquals(const QByteArray& expected, const QByteArray& current, - const char* file, int lineNo); - bool assertEquals(const char* expected, const QByteArray& 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); - bool assertNotNull(const void* ptr, const char* file, int lineNo); - bool assertEqualFiles(const char* expected, const char* current, - const char* file, int lineNo); - bool log(const char* message); - bool error(const char* message, ...); - QByteArray getTempDir(const char* node, const char* parent = NULL, - bool withSeparator = true); - QByteArray getTempFile(const char* node, const char* parent = NULL, - bool deleteIfExists = true); - bool logContains(const char* pattern); - void run(); -protected: - virtual void doIt(void) = 0; - -protected: - int m_errors; - QByteArray m_name; - RplLogger m_logger; - // for testing of logging code: - RplMemoryAppender m_memoryAppender; - RplLogger m_memoryLogger; - char m_separator; -}; -#define checkE(expected, current) assertEquals(expected, current, __FILE__, __LINE__) -#define checkT(current) assertTrue(current, __FILE__, __LINE__) -#define checkF(current) assertFalse(current, __FILE__, __LINE__) -#define checkN(current) assertNull(current, __FILE__, __LINE__) -#define checkNN(current) assertNotNull(current, __FILE__, __LINE__) -#define checkFiles(expected, current) assertEqualFiles(expected, current, __FILE__, __LINE__) -#endif // RPLTEST_HPP diff --git a/rplcore/rplwriter.cpp b/rplcore/rplwriter.cpp deleted file mode 100644 index 53e018b..0000000 --- a/rplcore/rplwriter.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief A writer to an output media. - * - * Implementation of the abstract base class RplWriter and - * the concrete derivation RplFileWriter. - */ -/** @file rplcore/rplwriter.hpp - * - * @brief Definitions for a writer to an output media. - */ -#include "rplcore/rplcore.hpp" - -const char* RplWriter::m_tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; -int RplWriter::m_maxIndention = strlen(RplWriter::m_tabs); - -/** @class RplWriter rplwriter.hpp "rplcore/rplwriter.hpp" - * - * @brief Implements an abstract base class for producing text lines. - * - */ - -/** - * @brief Destructor. - * - * Closes the output medium. - * Ensures that the destructors of the derived classes are virtual. - */ -RplWriter::~RplWriter() -{ - close(); -} - -/** - * @brief Closes the output medium. - * - * This method does nothing, but overriding methods should free the resources. - * - * @note The method must be designed so that it can be called multiple times. - */ -void RplWriter::close() -{ -} -/** - * @brief Puts a given count of "\t" to the output medium. - * - * @param indent indention level, number of "\t" - */ -void RplWriter::indent(int indent) -{ - if (indent > m_maxIndention) - indent = m_maxIndention; - format("%.*s", indent, m_tabs); -} - -/** - * @brief Formats a string and write it to the output medium. - * - * @param format format string with placeholders like sprintf() - * @param ... variable arguments, values for the placeholders - */ -void RplWriter::format(const char* format, ...) -{ - va_list ap; - va_start(ap, format); - write(ap, format); - va_end(ap); -} -/** - * @brief Formats a line and write it to the output medium. - * - * @param format format string with placeholders like sprintf() - * @param ... variable arguments, values for the placeholders - */ -void RplWriter::formatLine(const char* format, ...) -{ - char buffer[64000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - writeLine(buffer); -} - -/** - * @brief Formats a message and writes it to the output medium. - * - * @param ap variable argument list (like in vsprintf) - * @param format format string with placeholders - */ -void RplWriter::write(va_list ap, const char* format) -{ - char buffer[64000]; - qvsnprintf(buffer, sizeof buffer, format, ap); - write(buffer); -} - -/** - * @brief Writes a line with indention to the output medium. - * - * @param indent indention level. Indention is limited to 20 - * @param line the line to write - */ -void RplWriter::writeIndented(int indent, const char* line) -{ - RplWriter::indent(indent); - writeLine(line); -} - -/** - * @brief Writes a line with indention to the output medium. - * - * @param indent indention level. Indention is limited to 20 - * @param format format string with placeholders like sprintf - * @param ... the values for the placeholders (variable arguments) - */ -void RplWriter::formatIndented(int indent, const char* format, ...) -{ - RplWriter::indent(indent); - - char buffer[64000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - writeLine(buffer); -} - -/** @class RplWriter rplwriter.hpp "rplcore/rplwriter.hpp" - * - * @brief Implements a class which writes lines into a file. - */ - -/** - * @brief Constructor. - * - * @param filename the file's name - * @param mode write mode, "w" for write or "a" for append - * @param additionalStream if not NULL the content will be written to this - * stream too. Normal usage: stdout or - * stderr - * @param eoln line end: "\n" or "\r\n" - */ -RplFileWriter::RplFileWriter(const char* filename, const char* mode, - FILE* additionalStream, const char* eoln) : - m_fp(fopen(filename, mode)), - m_name(filename), - m_eoln(eoln), - m_additionalStream(additionalStream) -{ -} - -/** - * @brief Writes a string to the file. - * @param message the string to write - */ -void RplFileWriter::write(const char* message) -{ - if (m_fp != NULL) - fputs(message, m_fp); - if (m_additionalStream != NULL) - fputs(message, m_additionalStream); -} - -/** - * @brief Writes a line to the file. - * @param line the line to write. If NULL an empty line will be written - */ -void RplFileWriter::writeLine(const char* line) -{ - if (m_fp != NULL){ - if (line != NULL) - fputs(line, m_fp); - fputs(m_eoln, m_fp); - } - if (m_additionalStream != NULL){ - if (line != NULL) - fputs(line, m_additionalStream); - fputc('\n', m_additionalStream); - } -} - -/** - * @brief Closes the output file. - */ -void RplFileWriter::close() -{ - if (m_fp != NULL){ - fclose(m_fp); - m_fp = NULL; - } - m_additionalStream = NULL; -} diff --git a/rplcore/rplwriter.hpp b/rplcore/rplwriter.hpp deleted file mode 100644 index 7b661e3..0000000 --- a/rplcore/rplwriter.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 RPLWRITER_HPP -#define RPLWRITER_HPP - -class RplWriter -{ -public: - virtual ~RplWriter(); -public: - /** - * @brief Writes a text to the output medium. - * - * @param message the message - */ - virtual void write(const char* message) = 0; - /** - * @brief Writes a text line to the output medium. - * - * @param line the text line. If NULL an empty line will be written - */ - virtual void writeLine(const char* line = NULL) = 0; - virtual void close(); -public: - void indent(int indent); - void format(const char* format, ...); - void formatLine(const char* format, ...); - void write(va_list ap, const char* format); - void writeIndented(int indent, const char* line); - void formatIndented(int indent, const char* format, ...); -protected: - static const char* m_tabs; - static int m_maxIndention; -}; - -class RplFileWriter : public RplWriter -{ -public: - RplFileWriter(const char* filename, const char* mode = "w", - FILE* additionalStream = NULL, const char* eoln = "\n"); -public: - virtual void write(const char* line); - virtual void writeLine(const char* line = NULL); - virtual void close(); -protected: - FILE* m_fp; - QByteArray m_name; - QByteArray m_eoln; - FILE* m_additionalStream; -}; - -#endif // RPLWRITER_HPP diff --git a/rplcore/testrplexample.cpp b/rplcore/testrplexample.cpp deleted file mode 100644 index 695fbae..0000000 --- a/rplcore/testrplexample.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 "project.hpp" - -#include "rplcore/rpltest.hpp" -// Code to test: -int add(int a, int b) { - return a+b; -} -QByteArray concat(const char* a, const char* b) { - return QByteArray(a) + " " + b; -} -const char* firstDot(const char* s) { - return strchr(s, '.'); -} -/** - * @brief Example for usage of the class RplTest. - */ -class TestRplExample : public RplTest { -public: - TestRplExample() : RplTest("RplExample") {} - -public: - void testInt() { - log("testing add..."); - // compare 2 integers: - checkE(2, add(1, 1)); - } - void testString() { - // compare 2 strings: - checkE("Be good", concat("Be", "good")); - // test for not NULL: - checkN(firstDot("Hi.")); - // test for NULL: - checkNN(firstDot("Hi")); - } - virtual void doIt() { - testInt(); - testString(); - } -}; -void testRplExample() { - TestRplExample test; - test.run(); -} diff --git a/rplexpr/rplasclasses.cpp b/rplexpr/rplasclasses.cpp deleted file mode 100644 index 11f21d6..0000000 --- a/rplexpr/rplasclasses.cpp +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Predefined classes of the virtual machine, e.g RplASInteger. - */ -/** @file rplexpr/rplasclasses.hpp - * - * @brief Definitions for predefined classes of the virtual machine. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" - -RplASList* RplASList::m_instance = NULL; -RplASMap* RplASMap::m_instance = NULL; -RplASFloat* RplASFloat::m_instance = NULL; -RplASInteger* RplASInteger::m_instance = NULL; -RplASString* RplASString::m_instance = NULL; -RplASBoolean* RplASBoolean::m_instance = NULL; -RplASVoid* RplASVoid::m_instance = NULL; -RplASFormula* RplASFormula::m_instance = NULL; - -/** @class RplSymbolSpace rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a symbol space for the parser. - * - * 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, only for the global symbol space. - * - * @param name the symbol space's name - * @param parent the parent of the symbol space - */ -RplSymbolSpace::RplSymbolSpace(RplASTree& tree) : - m_type(SST_GLOBAL), - m_name("$global"), - m_variables(), - m_classes(), - m_parent(NULL), - m_body(NULL), - m_listOfVars(), - m_tree(tree) -{ -} -/** - * @brief Constructor. - * - * @param type the type of the symbol space: SST_MODULE, ... - * @param name the symbol space's name - * @param parent the parent of the symbol space - */ -RplSymbolSpace::RplSymbolSpace(RplSymbolSpace::SymbolSpaceType type, - const QByteArray& name, - RplSymbolSpace* parent) : - m_type(type), - m_name(name), - m_variables(), - m_classes(), - m_parent(parent), - m_body(NULL), - m_listOfVars(), - m_tree(parent->m_tree) -{ -} - - -/** - * @brief Destructor. - */ -RplSymbolSpace::~RplSymbolSpace() -{ - ClassMap::iterator it; - for (it = m_classes.begin(); it != m_classes.end(); it++){ - delete it.value(); - } - MethodMap::iterator it2; - for (it2 = m_methods.begin(); it2 != m_methods.end(); it2++){ - delete it2.value(); - } -} - -/** - * @brief Starts a scope. - * - * A scope is an "area" where variables can be defined. - * A variable "lives" in a scope. - * - * Saves the status to restore it in finishScope(). - * - * @param scope OUT: status info - */ -void RplSymbolSpace::startScope(RplASScope& scope) -{ - scope.m_varNoAtStart = m_listOfVars.size(); -} - -/** - * @brief Finishes a scope. - * - * Finishes the "live" of the variables created in the ending scope. - * - * @param endOfScope line (inside the current source unit) which finishes the - * scope - * @param scope the status of the scope at start. - */ -void RplSymbolSpace::finishScope(int endOfScope, RplASScope& scope) -{ - // in methods/classes not needed: - int ix = scope.m_varNoAtStart - scope.m_builtInVars; - int last = m_listOfVars.size(); - for (; ix < last; ix++){ - RplASVarDefinition* var = m_listOfVars[ix]; - var->setEndOfScope(endOfScope); - const QByteArray& name = var->name(); - if (m_variables.contains(name)) - m_variables.remove(name); - } -} - -/** - * @brief Search a variable in the symbol space. - * - * @param name variable to find - * - * @return NULL: not found
- * otherwise: the variable - */ -RplASVarDefinition* RplSymbolSpace::findVariable(const QByteArray& name) const -{ - RplASVarDefinition* rc = NULL; - if (m_variables.contains(name)) - rc = m_variables[name]; - else if (m_parent != NULL) - rc = m_parent->findVariable(name); - return rc; -} - -/** - * @brief Search the class in the symbol space hierarchy. - * - * @param name Name of the class - * @return NULL: not found
- * otherwise: the class - */ -RplASClass* RplSymbolSpace::findClass(const QByteArray& name) const -{ - RplASClass* rc = NULL; - if (m_classes.contains(name)) - rc = m_classes[name]; - else if (m_parent != NULL) - rc = m_parent->findClass(name); - return rc; -} - -/** - * @brief Find a method in the instance. - * - * @param name the method's name - * @return NULL: method not found - * otherwise: the method description - */ -RplASMethod* RplSymbolSpace::findMethod(const QByteArray& name) const -{ - RplASMethod* rc = NULL; - if (m_methods.contains(name)) - rc = m_methods[name]; - return rc; -} - -/** - * @brief Writes the content of the instance into a file. - * - * @param writer writes to output - * @param indent nesting level: so many tabs will be used as prefix - * @param header NULL or the headline - */ -void RplSymbolSpace::dump(RplWriter& writer, int indent, const char* header) -{ - if (header != NULL) - writer.writeLine(header); - writer.formatIndented(indent, "= %s (%s) parent: %s", m_name.constData(), - spaceTypeName(m_type), - m_parent == NULL ? "" : m_parent->name().constData()); - QList sorted; - if (m_classes.size() > 0){ - writer.writeIndented(indent, "== Classes:"); - sorted.reserve(m_classes.size()); - ClassMap::iterator it; - for (it = m_classes.begin(); it != m_classes.end(); it++){ - sorted.append(it.key()); - } - qSort(sorted.begin(), sorted.end(), qLess()); - QList::iterator it2; - for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ - RplASClass* clazz = m_classes[*it2]; - clazz->dump(writer, indent); - } - } - if (m_methods.size() > 0){ - writer.writeIndented(indent, "== Methods:"); - sorted.clear(); - sorted.reserve(m_variables.size()); - MethodMap::iterator it3; - for (it3 = m_methods.begin(); it3 != m_methods.end(); it3++){ - sorted.append(it3.key()); - } - qSort(sorted.begin(), sorted.end(), qLess()); - QList::iterator it4; - for (it4 = sorted.begin(); it4 != sorted.end(); it4++){ - RplASMethod* method = m_methods[*it4]; - do { - method->dump(writer, indent); - method = method->sibling(); - } while (method != NULL); - } - } - - if (m_listOfVars.size() > 0){ - writer.writeIndented(indent, "== Variables:"); - QList::iterator it6; - for (int ix = 0; ix < m_listOfVars.size(); ix++){ - RplASVarDefinition* var = m_listOfVars[ix]; - var->dump(writer, indent); - } - } - if (m_body != NULL){ - writer.writeIndented(indent, "== Body:"); - RplASNode1::dumpStatements(writer, indent, m_body); - } -} - -/** - * @brief Returns the name of a space type. - * - * @param type type to inspect - * @return the name of the type - */ -const char*RplSymbolSpace::spaceTypeName( - RplSymbolSpace::SymbolSpaceType type) -{ - const char* rc = NULL; - switch(type){ - case SST_UNDEF: - rc = "undef"; - break; - case SST_GLOBAL: - rc = "global"; - break; - case SST_MODULE: - rc = "module"; - break; - case SST_CLASS: - rc = "class"; - break; - case SST_METHOD: - rc = "method"; - break; - default: - rc = "?"; - break; - } - return rc; -} - -/** - * @brief Initilizes the global symbol space. - * - * @param tree the abstract syntax tree - * @return the global symbol space - */ -RplSymbolSpace* RplSymbolSpace::createGlobal(RplASTree& tree) -{ - RplSymbolSpace* rc = new RplSymbolSpace(tree); - rc->m_tree = tree; - RplASInteger::m_instance = new RplASInteger(tree); - rc->m_classes[RplASInteger::m_instance->name()] = RplASInteger::m_instance; - RplASBoolean::m_instance = new RplASBoolean(tree); - rc->m_classes[RplASBoolean::m_instance->name()] = RplASBoolean::m_instance; - RplASFloat::m_instance = new RplASFloat(tree); - rc->m_classes[RplASFloat::m_instance->name()] = RplASFloat::m_instance; - RplASString::m_instance = new RplASString(tree); - rc->m_classes[RplASString::m_instance->name()] = RplASString::m_instance; - RplASList::m_instance = new RplASList(tree); - rc->m_classes[RplASList::m_instance->name()] = RplASList::m_instance; - RplASMap::m_instance = new RplASMap(tree); - rc->m_classes[RplASMap::m_instance->name()] = RplASMap::m_instance; - RplASVoid::m_instance = new RplASVoid(tree); - rc->m_classes[RplASVoid::m_instance->name()] = RplASVoid::m_instance; - RplASFormula::m_instance = new RplASFormula(tree); - rc->m_classes[RplASFormula::m_instance->name()] = RplASFormula::m_instance; - return rc; -} -/** - * @brief Returns the list of the variables. - * - * @return the list of the variables - */ -RplSymbolSpace::VariableList RplSymbolSpace::listOfVars() const -{ - return m_listOfVars; -} - -/** - * @brief Returns the parent of the symbol space. - * - * @return the symbolspace of the object (module, method, class..) containing - * the object belonging to the instance - */ -RplSymbolSpace* RplSymbolSpace::parent() const -{ - return m_parent; -} - -/** - * @brief Returns the body (an abstract syntax tree) of the symbol space. - * - * @return NULL: no body available
- * othewise: the body of the instance - */ -RplASItem* RplSymbolSpace::body() const -{ - return m_body; -} - -/** - * @brief Sets the body (an abstract syntax tree) of the symbol space. - * - * @param body the new body - */ -void RplSymbolSpace::setBody(RplASItem* body) -{ - m_body = body; -} - -/** - * @brief Adds a variable to the symbol space. - * - * @param variable the variable to add - * @param varNo OUT: variable number, current number in the symbol space - * @return NULL: success
- * otherwise: the already defined variable/method - */ -RplASItem* RplSymbolSpace::addVariable(RplASVarDefinition* variable, int& varNo) -{ - RplASItem* rc = NULL; - const QByteArray& name = variable->name(); - if (m_variables.contains(name)) - rc = m_variables[name]; - else if (m_methods.contains(name)) - rc = m_methods[name]; - else { - m_variables[name] = variable; - varNo = m_listOfVars.size(); - m_listOfVars.append(variable); - } - return rc; -} - -/** - * @brief Adds a method to the symbol space. - * - * @param method the method to add - * @return NULL: success
- * otherwise: the already defined variable/method - */ -RplASItem* RplSymbolSpace::addMethod(RplASMethod* method) -{ - RplASItem* rc = NULL; - const QByteArray& name = method->name(); - if (m_variables.contains(name)) - rc = m_variables[name]; - else if (! m_methods.contains(name)){ - m_methods[name] = method; - } else { - RplASMethod* first = m_methods[name]; - RplASMethod* oldMethod = first; - do { - if (oldMethod->equalSignature(*method)) - rc = oldMethod; - else - oldMethod = oldMethod->sibling(); - } while (rc == NULL && oldMethod != NULL); - if (rc == NULL){ - method->setChild(first); - m_methods[name] = method; - } - } - return rc; -} -/** - * @brief Adds a class to the instance. - * - * @param clazz the class to add - * @return NULL: success
- * otherwise: the already defined class - */ -RplASUserClass* RplSymbolSpace::addClass(RplASUserClass* clazz) -{ - RplASUserClass* rc = NULL; - const QByteArray& name = clazz->name(); - if (m_classes.contains(name)){ - rc = dynamic_cast(m_classes[name]); - } else { - m_classes[name] = clazz; - } - return rc; -} - -/** - * @brief Returns the name of the symbol space. - * - * @return the name - */ -const QByteArray& 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(RplASTree& tree) : - RplASClass("Bool", tree) -{ -} -/** - * @brief Creates a value object (used in RplASVariant). - * - * For Booleans nothing is to do! - * - * @param source NULL or a source to copy - * @return NULL - */ -void* RplASBoolean::newValueInstance(void*) const -{ - return NULL; -} - -/** - * @brief Destroys the given object. - * - * For Booleans nothing is to do! - * - * @param object object to destroy - */ -void RplASBoolean::destroyValueInstance(void*) const -{ -} - -/** - * @brief Calculates the boolean value of an class specific object. - * - * This method should never be called. - * - * @param object the object to test (with type QList*) - * @return false - */ -bool RplASBoolean::boolValueOf(void*) const -{ - return false; -} - -/** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength not used - * @return a string describing the object - */ -QByteArray RplASBoolean::toString(void* object, int) const -{ - return ((RplASVariant*) object)->asBool() ? "True" : "False"; -} - -/** @class RplASNumber rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements the class of a Boolean. - * - * A Boolean is one of the values true and false. - */ -/** - * @brief Constructor. - */ -RplASFloat::RplASFloat(RplASTree& tree) : - RplASClass("Float", tree) -{ -} - -RplASFloat::RplASFloat(const QByteArray& name, RplASTree& tree) : - RplASClass(name, tree) -{ - 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 NULL - */ -void* RplASFloat::newValueInstance(void*) const -{ - return NULL; -} - -/** - * @brief Destroys the given object. - * - * For Booleans nothing is to do! - * - * @param object object to destroy - */ -void RplASFloat::destroyValueInstance(void*) 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*) const -{ - return false; -} - -/** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength not used - * @return a string describing the object - */ -QByteArray RplASFloat::toString(void* object, int) const -{ - char buffer[256]; - - qsnprintf(buffer, sizeof buffer, "%f", ((RplASVariant *) object)->asFloat()); - return buffer; -} - -/** @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(RplASTree& tree) : - RplASFloat("Int", tree) -{ -} - -/** - * @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*) const -{ - return false; -} - -/** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength the maximum length of the result - * @return a string describing the object - */ -QByteArray RplASInteger::toString(void* object, int maxLength) const -{ - char buffer[64]; - qsnprintf(buffer, sizeof buffer, "%.*d", maxLength, - ((RplASVariant *) object)->asInt()); - return buffer; -} - - -/** @class RplASString rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements the class of a string. - * - * A string is a mutable character sequence. - */ -/** - * @brief Constructor. - */ -RplASString::RplASString(RplASTree& tree) : - RplASClass("Str", tree) -{ -} -/** - * @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* RplASString::newValueInstance(void* source) const -{ - QByteArray* rc = source == NULL ? new QByteArray() : new QByteArray(*(QByteArray*) source); - return (void*) rc; -} - -/** - * @brief Destroys the given object. - * - * The object must be created by newValueInstance(). - * - * @param object object to destroy - */ -void RplASString::destroyValueInstance(void* object) const -{ - delete (QByteArray*) object; -} - -/** - * @brief Calculates the boolean value of an class specific object. - * - * This method should never be called. - * - * @param object the object to test (a QByteArray* instance) - * @return false: the string is empty - * true: otherwise - */ -bool RplASString::boolValueOf(void* object) const -{ - bool rc = false; - if (object != NULL){ - QByteArray* string = static_cast(object); - if (string == NULL) - throw RplException("RplASString.boolValueOf(): not a string"); - rc = ! string->isEmpty(); - } - return rc; -} - -/** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength the maximum length of the result - * @return a string describing the object - */ -QByteArray RplASString::toString(void* object, int maxLength) const -{ - QByteArray rc; - QByteArray* string = reinterpret_cast (object); - int length = string->size(); - if (length + 2 > maxLength) - length = maxLength - 2; - rc.reserve(length); - rc += "'"; - if (string->size() < maxLength - 2) { - rc += *string; - } else { - rc += string->mid(0, maxLength - 2 - 3); - rc += "..."; - } - rc += "'"; - return rc; -} - -/** @class RplASList rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements the class of a list. - * - * A list is a container of any values. Values can be selected by the index. - */ -/** - * @brief Constructor. - */ -RplASList::RplASList(RplASTree& tree) : - RplASClass("List", tree) -{ -} - -/** - * @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* RplASList::newValueInstance(void* source) const -{ - RplASListOfVariants* rc = new RplASListOfVariants(); - if (source != NULL){ - RplASListOfVariants* source2 = (RplASListOfVariants*) source; - rc->reserve(source2->size()); - RplASListOfVariants::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){ - RplASListOfVariants* list = static_cast(object); - if (list == NULL) - throw RplException("RplASList.boolValueOf(): not a list"); - rc = ! list->empty(); - } - return rc; -} - -/** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength unused - * @return a string describing the object - */ -QByteArray RplASList::toString(void* object, int maxLength) const -{ - QByteArray rc; - rc.reserve(maxLength); - rc += "["; - RplASListOfVariants* list = reinterpret_cast(object); - RplASListOfVariants::iterator it; - bool first = true; - for(it = list->begin(); it != list->end(); it++){ - if (first) - first = false; - else - rc += ","; - QByteArray part = (*it)->toString(maxLength - rc.size() - 5); - if (maxLength - rc.size() - 5 - part.size() <= 0){ - rc += "..."; - break; - } else { - rc += part; - } - - } - rc += "]"; - 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. - */ -RplASMap::RplASMap(RplASTree& tree) : - RplASClass("Map", tree) -{ -} -/** - * @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 -{ - RplASMapOfVariants* rc = new RplASMapOfVariants(); - if (source != NULL){ - RplASMapOfVariants* source2 = - static_cast(source); - // rc->reserve(source2->size()); - RplASMapOfVariants::iterator it; - for (it = source2->begin(); it != source2->end(); it++){ - // deleting in destroyValue(): - const QByteArray& key = it.key(); - RplASVariant* value = new RplASVariant(*it.value()); - (*rc)[key] = value; - } - } - return (void*) rc; -} - -/** - * @brief Destroys the given object. - * - * The object must be created by newValueInstance(). - * - * @param object object to destroy - */ -void RplASMap::destroyValueInstance(void* object) const -{ - delete (RplASMapOfVariants*) 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){ - RplASMapOfVariants* map = reinterpret_cast(object); - if (map == NULL) - throw RplException("RplASMap.boolValueOf(): not a map"); - rc = map->empty() > 0; - } - return rc; -} - -/** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength maximal length of the result - * @return a string describing the object - */ -QByteArray RplASMap::toString(void* object, int maxLength) const -{ - QByteArray rc; - rc.reserve(maxLength); - rc += "["; - RplASMapOfVariants* map = reinterpret_cast(object); - RplASMapOfVariants::iterator it; - bool first = true; - for(it = map->begin(); it != map->end(); it++){ - if (first) - first = false; - else - rc += ","; - if (maxLength - rc.size() - 5 - 2 - it.key().size() <= 0){ - rc += "..."; - break; - } else { - rc += "'"; - rc += it.key(); - rc += "':"; - } - QByteArray part = it.value()->toString(maxLength - rc.size() - 5); - if (maxLength - rc.size() - 5 - part.size() <= 0){ - rc += "..."; - break; - } else { - rc += part; - } - - } - rc += "}"; - return rc; -} - - -/** @class RplVariable rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a variable of a symbol space. - */ - -/** - * @brief Constructor. - */ -RplVariable::RplVariable(const QByteArray& name) : - m_name(name), - m_namespace(NULL), - m_value(), - m_type(NULL) -{ - -} - -/** - * @brief Writes the content of the instance into an output media. - * - * @param writer writes to output - * @param indent nesting level: so many tabs will be used as prefix - */ -void RplVariable::dump(RplWriter& writer, int indent) -{ - const char* name1 = m_type == NULL ? "NoneType" : m_type->name().constData(); - QByteArray val = m_value.toString(); - writer.formatIndented(indent, "%s %s: value: %s", - name1, m_name.constData(), val.constData()); -} -/** - * @brief Returns the data type of the variable. - * - * @return the class of the variable - */ -RplASClass* RplVariable::type() const -{ - return m_type; -} - -/** - * @brief Sets the data type. - * @param type the class of the variable - */ -void RplVariable::setType(RplASClass* type) -{ - m_type = type; -} -/** - * @brief Returns the name of the variable. - * - * @return the name - */ -const QByteArray& RplVariable::name() const -{ - return m_name; -} - -/** @class RplVariable rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a data type representing a none type. - */ -RplASVoid::RplASVoid(RplASTree& tree) : - RplASClass("Void", tree) -{ -} - -/** - * @brief Instantiates a new object. - * - * In this case we do nothing. - * - * @param source ignored - * @return - */ -void*RplASVoid::newValueInstance(void*) const -{ - return NULL; -} - -/** - * @brief Destroys an object created by newValueInstance. - * - * In this case we do nothing. - * - * @param object not used - */ -void RplASVoid::destroyValueInstance(void*) const -{ -} - -/** - * @brief Returns the bool value of the given object - * @param object ignored - * @return false - */ -bool RplASVoid::boolValueOf(void*) const -{ - return false; -} - -/** - * @brief Converts the object into a string. - * - * @param object ignored - * @param maxLength ignored - * @return the empty string - */ -QByteArray RplASVoid::toString(void*, int) const -{ - return QByteArray(""); -} - -/** @class RplASFormula rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a data type representing a calculated value. - */ - -/** - * @brief Constructor. - * - * @param tree the abstract syntax tree - */ -RplASFormula::RplASFormula(RplASTree& tree) : - RplASClass("Formula", tree) -{ -} - -/** - * @brief Instantiates a new object. - * - * In this case we do nothing. - * - * @param expr the result - * @return - */ -void* RplASFormula::newValueInstance(void* expr) const -{ - return expr; -} - -/** - * @brief Destroys an object created by newValueInstance. - * - * In this case we do nothing. - * - * @param object not used - */ -void RplASFormula::destroyValueInstance(void*) const -{ -} - -/** - * @brief Returns the bool value of the given object - * @param object ignored - * @return false - */ -bool RplASFormula::boolValueOf(void*) const -{ - return false; -} - -/** - * @brief Converts the object into a string. - * - * @param object ignored - * @param maxLength ignored - * @return the empty string - */ -QByteArray RplASFormula::toString(void* object, int) const -{ - RplASExprStatement* expr = static_cast(object); - - char buffer[64]; - qsnprintf(buffer, sizeof buffer, "", expr->id()); - return buffer; -} - -/** @class RplASUserClass rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a data type representing an user defined class. - */ - - -/** - * @brief Constructor. - * - * @param name name of the user defined class - * @param position the position of the class definition - * @param tree the abstract syntax tree - */ -RplASUserClass::RplASUserClass(const QByteArray& name, - const RplSourcePosition* position, - RplASTree& tree) : - RplASClass(name, tree), - m_position(position) -{ -} - -/** - * @brief Creates an instance of an user defined class. - * - * @param source the type (user defined class) of the result - * @return an instance of an user defined class - */ -void*RplASUserClass::newValueInstance(void* source) const -{ - RplASUserClass* clazz = static_cast(source); - RplASUserObject* rc = new RplASUserObject(clazz); - return static_cast(rc); -} - -void RplASUserClass::destroyValueInstance(void* object) const -{ - RplASUserObject* obj = static_cast(object); - delete obj; -} - -/** - * @brief Returns the bool value of the object. - * - * @param object object to test - * @return true: object != NULL
- * false: object == NULL - */ -bool RplASUserClass::boolValueOf(void* object) const -{ - return object != NULL; -} - -/** - * @brief Returns a string representation an instance of a user defined class. - * - * @param object object to convert - * @param maxLength maximum length of the string - * @return - */ -QByteArray RplASUserClass::toString(void*, int) const -{ - return m_name; -} -/** - * @brief Returns the source position of the instance. - * - * @return the source position - */ -const RplSourcePosition* RplASUserClass::position() const -{ - return m_position; -} - - - -/** @class RplASUserObject rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an instance of an user defined class. - */ -RplASUserObject::RplASUserObject(RplASUserClass* clazz) : - m_class(clazz), - m_fields(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASUserObject::~RplASUserObject() -{ - delete[] m_fields; - m_fields = NULL; -} diff --git a/rplexpr/rplasclasses.hpp b/rplexpr/rplasclasses.hpp deleted file mode 100644 index fbb5df2..0000000 --- a/rplexpr/rplasclasses.hpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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 RPLASCLASSES_HPP -#define RPLASCLASSES_HPP - -class RplSymbolSpace; -class RplVariable { -public: - RplVariable(const QByteArray& name); -public: - void dump(RplWriter& writer, int indent); - - RplASClass* type() const; - void setType(RplASClass* type); - const QByteArray& name() const; - -protected: - QByteArray m_name; - // NULL for "simple" variables (int, float, bool) - RplSymbolSpace* m_namespace; - RplASVariant m_value; - RplASClass* m_type; -}; - -class RplASScope -{ -public: - int m_builtInVars; - int m_varNoAtStart; -}; - -class RplASUserClass; -class RplASTree; -class RplSymbolSpace -{ -public: - enum SymbolSpaceType { - SST_UNDEF, - SST_GLOBAL, - SST_MODULE, - SST_CLASS, - SST_METHOD - }; - -public: - typedef QMap VariableMap; - typedef QMap ClassMap; - typedef QMap MethodMap; - typedef QList VariableList; -private: - RplSymbolSpace(RplASTree& tree); -public: - RplSymbolSpace(SymbolSpaceType type, const QByteArray& name, - RplSymbolSpace* parent); - virtual ~RplSymbolSpace(); -public: - void startScope(RplASScope& scope); - void finishScope(int endOfScope, RplASScope& scope); - RplASVarDefinition* findVariable(const QByteArray& name) const; - RplASClass* findClass(const QByteArray& name) const; - RplASMethod* findMethod(const QByteArray& name) const; - void dump(RplWriter& writer, int indent, const char* header = NULL); - const QByteArray& name() const; - RplASItem* body() const; - void setBody(RplASItem* body); - RplASItem* addVariable(RplASVarDefinition* variable, int& varNo); - RplASItem* addMethod(RplASMethod* method); - RplASUserClass* addClass(RplASUserClass* clazz); - RplSymbolSpace* parent() const; - VariableList listOfVars() const; -public: - static const char* spaceTypeName(SymbolSpaceType type); - static RplSymbolSpace* createGlobal(RplASTree& tree); -private: - SymbolSpaceType m_type; - QByteArray m_name; - VariableMap m_variables; - ClassMap m_classes; - MethodMap m_methods; - RplSymbolSpace* m_parent; - RplASItem* m_body; - VariableList m_listOfVars; - RplASTree& m_tree; -}; - -class RplASBoolean : public RplASClass { -public: - RplASBoolean(RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASBoolean* m_instance; -}; - -class RplASFloat : public RplASClass { -public: - RplASFloat(RplASTree& tree); - RplASFloat(const QByteArray& name, RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASFloat* m_instance; -}; - -class RplASInteger : public RplASFloat { -public: - RplASInteger(RplASTree& tree); -public: - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASInteger* m_instance; -}; - -class RplASString : public RplASClass { -public: - RplASString(RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASString* m_instance; -}; - -class RplASList : public RplASClass { -public: - RplASList(RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASList* m_instance; -}; - -class RplASMap : public RplASClass { -public: - RplASMap(RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASMap* m_instance; -}; - -class RplASVoid : public RplASClass { -public: - RplASVoid(RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASVoid* m_instance; -}; - -class RplASFormula : public RplASClass { -public: - RplASFormula(RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; -public: - static RplASFormula* m_instance; -}; - -class RplASUserClass : public RplASClass { -public: - RplASUserClass(const QByteArray& name, const RplSourcePosition* position, - RplASTree& tree); -public: - void* newValueInstance(void* source = NULL) const; - void destroyValueInstance(void* object) const; - virtual bool boolValueOf(void* object) const; - virtual QByteArray toString(void *object, int maxLength = 80) const; - const RplSourcePosition* position() const; - -private: - const RplSourcePosition* m_position; -}; - -class RplASUserObject { -public: - RplASUserObject(RplASUserClass* clazz); - ~RplASUserObject(); -public: - void callMember(); -private: - RplASUserClass* m_class; - RplASVariant* m_fields; -}; - -#endif // RPLASCLASSES_HPP diff --git a/rplexpr/rplastree.cpp b/rplexpr/rplastree.cpp deleted file mode 100644 index de8e3e5..0000000 --- a/rplexpr/rplastree.cpp +++ /dev/null @@ -1,3694 +0,0 @@ -/* - * 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. -*/ -/** @file - * - * @brief Implementation of an Abstract Syntax Tree. - * - */ -/** @file rplexpr/rplastree.hpp - * - * @brief Definitions for an Abstract Syntax Tree. - * - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" - -enum { - LOC_VARDEF_EXEC_1 = RPL_FIRST_OF(RPL_MODULE_ASTREE), // 11001 - LOC_UNOP_CALC_1, - LOC_UNARY_CHECK_1, - LOC_UNARY_CHECK_2, - LOC_UNARY_CHECK_3, // 11005 - LOC_BINOP_1, - LOC_BINOP_CALC_1, - LOC_BINOP_CALC_2, - LOC_BINOP_CALC_3, - LOC_BINOP_CALC_4, // 11010 - LOC_BINOP_CALC_5, - LOC_BINOP_CALC_6, - LOC_BINOP_CALC_7, - LOC_BINOP_CALC_8, - LOC_BINOP_CALC_9, // 11015 - LOC_BINOP_CALC_10, - LOC_BINOP_CALC_11, - LOC_BINOP_CALC_12, - LOC_VARDEF_CHECK_1, - LOC_VARDEF_CHECK_2, // 11020 - LOC_ITEM_STATEM_LIST_1, - LOC_CONV_CHECK_1, - LOC_CONV_TRY_1, - LOC_ITEM_FORCE_ERROR_1, - LOC_UNARY_CHECK_4, // 11025 - LOC_IF_CHECK_1, - LOC_IF_CHECK_2, - LOC_FORC_CHECK_1, - LOC_FORC_CHECK_2, - LOC_FORC_CHECK_3, // 11030 - LOC_ITEM_AS_INT_1, - LOC_ITEM_AS_INT_2, - LOC_METHOD_CALL_CHECK_1, - LOC_MEHTOD_CALL_CHECK_2, - LOC_MEHTOD_CALL_CHECK_3, // 11035 - LOC_MEHTOD_CALL_CHECK_4, - LOC_COUNT -}; - -unsigned int RplASItem::m_nextId = 1; - -#define DEFINE_TABS(indent) \ - char tabs[32]; \ - memset(tabs, '\t', sizeof tabs); \ - tabs[(unsigned) indent < sizeof tabs ? indent : sizeof tabs - 1] = '\0' - -/** - * @brief Writes a map into a file. - * - * The output is sorted by key. - * - * @param writer writes to output - * @param map map to dump - * @param withEndOfLine true: '\n' will be written at the end - */ -void dumpMap(RplWriter& writer, RplASMapOfVariants& map, bool withEndOfLine) -{ - QList sorted; - sorted.reserve(map.size()); - RplASMapOfVariants::iterator it; - for (it = map.begin(); it != map.end(); it++){ - sorted.append(it.key()); - } - qSort(sorted.begin(), sorted.end(), qLess()); - QList::iterator it2; - bool first = true; - for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ - RplASVariant* value = map[*it2]; - writer.format("%c'%s':%s", first ? '{' : ',', (*it2).constData(), - value->toString().constData()); - first = false; - } - if (first) - writer.write("{"); - writer.write("}"); - if (withEndOfLine) - writer.writeLine(); -} - -/** @class RplASException rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a specific exception for the Abstract Syntax Tree. - */ - -/** - * @brief Builds the message. - * - * @param position describes the position of the error/warning - * @param format the reason of the exception - * @param varList the values for the placeholders in the format. - */ -void RplASException::build(const RplSourcePosition* position, - const char* format, va_list varList) -{ - char buffer[64000]; - if (position != NULL){ - m_message = position->toString().toUtf8(); - m_message += ": "; - } - qvsnprintf(buffer, sizeof buffer, format, varList); - m_message += buffer; -} - -/** - * @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("") -{ - va_list ap; - va_start(ap, format); - build(position, format, ap); - va_end(ap); -} - -/** - * @brief Constructor. - */ -RplASException::RplASException() : - RplException("") -{ -} - -/** @class RplASVariant rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a class which can hold the value of any type. - * - * The VM uses some tricks (performance): Therefore this class - * must not be virtual! - */ -/** - * @brief Constructor. - */ -RplASVariant::RplASVariant() : - m_variantType(VT_UNDEF), - m_flags(VF_UNDEF), - // m_value(), - m_class(NULL) -{ -} -/** - * @brief Destructor. - */ -RplASVariant::~RplASVariant() -{ - destroyValue(); - m_variantType = VT_UNDEF; -} - -/** - * @brief Copy constructor. - * @param source the source to copy - */ -RplASVariant::RplASVariant(const RplASVariant& source): - m_variantType(source.m_variantType), - m_flags(source.m_flags), - // 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_variantType = source.m_variantType; - m_flags = source.m_flags; - m_class = source.m_class; - copyValue(source); - return *this; -} - -/** - * @brief Copies the value. - * @param source the source to copy - */ -void RplASVariant::copyValue(const RplASVariant& source) -{ - destroyValue(); - m_variantType = source.m_variantType; - m_class = source.m_class; - - switch(source.m_variantType) - { - case VT_BOOL: - m_value.m_bool = source.m_value.m_bool; - break; - case VT_FLOAT: - m_value.m_float = source.m_value.m_float; - break; - case VT_INTEGER: - m_value.m_int = source.m_value.m_int; - break; - case VT_UNDEF: - break; - default: - m_value.m_object = m_class->newValueInstance(source.m_value.m_object); - break; - } - m_flags = source.m_flags; -} - -/** - * @brief Frees the resources of the instance. - */ -void RplASVariant::destroyValue() -{ - switch(m_variantType) - { - case VT_BOOL: - case VT_FLOAT: - case VT_INTEGER: - case VT_UNDEF: - break; - default: - if ((m_flags & VF_IS_COPY) == 0) - m_class->destroyValueInstance(m_value.m_object); - m_value.m_object = NULL; - break; - } - m_variantType = VT_UNDEF; -} -/** - * @brief Returns the variantType of the instance. - * - * @return the variant type - */ -RplASVariant::VariantType RplASVariant::variantType() const -{ - return m_variantType; -} - -/** - * @brief Return the name of the variant type. - * - * @return the type as string - */ -const char*RplASVariant::nameOfType() const -{ - const char* rc = "?"; - switch(m_variantType){ - case VT_UNDEF: - rc = ""; - break; - case VT_FLOAT: - rc = "Float"; - break; - case VT_INTEGER: - rc = "Int"; - break; - case VT_BOOL: - rc = "Bool"; - break; - case VT_OBJECT: - rc = "Obj"; - break; - default: - break; - } - return rc; -} - -/** - * @brief Returns the class (data type) of the instance. - * - * @return the variant type - */ -const RplASClass* RplASVariant::getClass() const -{ - return m_class; -} - - -/** - * @brief Returns the numeric value. - * - * @return the numeric value - * @throw RplException the instance is not a numberic value - * - */ -qreal RplASVariant::asFloat() const -{ - if (m_variantType != VT_FLOAT) - throw RplException("RplASVariant::asNumber: not a number: %d", - m_variantType); - 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_variantType != VT_INTEGER) - throw RplException("RplASVariant::asInt: not an integer: %d", - m_variantType); - 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_variantType != VT_BOOL) - throw RplException("RplASVariant::asBool: not a boolean: %d", - m_variantType); - 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_variantType != VT_OBJECT) - throw RplException("RplASVariant::asObject: not an object: %d", - m_variantType); - 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 QByteArray* RplASVariant::asString() const -{ - const RplASClass* clazz; - const QByteArray* rc = static_cast(asObject(&clazz)); - if (clazz != RplASString::m_instance){ - const QByteArray& name = clazz->name(); - throw RplException("RplASVariant::asString: not a string: %s", - name.constData()); - } - return rc; -} - -/** - * @brief Make the instance to a numeric value. - * - * @param number the numeric value. - */ -void RplASVariant::setFloat(qreal number) -{ - destroyValue(); - m_variantType = VT_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_variantType = VT_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_variantType = VT_BOOL; - m_value.m_bool = value; - m_class = RplASBoolean::m_instance; -} - -/** - * @brief Make the instance to a boolean value. - * - * @param string the string value. - */ -void RplASVariant::setString(const QByteArray& string) -{ - // deletion in RplASVariant::destroyValue(): - setObject(new QByteArray(string), RplASString::m_instance); -} - -/** - * @brief Builds a string. - * - * @param maxLength the maximum length of the result - * @return the value as string - */ -QByteArray RplASVariant::toString(int maxLength) const -{ - QByteArray rc; - char buffer[256]; - switch(m_variantType) - { - case VT_BOOL: - rc = m_value.m_bool ? "True" : "False"; - break; - case VT_FLOAT: - qsnprintf(buffer, sizeof buffer, "%f", m_value.m_float); - rc = buffer; - break; - case VT_INTEGER: - qsnprintf(buffer, sizeof buffer, "%lld", m_value.m_int); - rc = buffer; - break; - case VT_OBJECT: - rc = m_class->toString(m_value.m_object, maxLength); - break; - default: - case VT_UNDEF: - rc = "None"; - break; - } - return rc; -} - -/** - * @brief Make the instance to an object. - * - * @param object the class specific value object. - * @param clazz the data type of the object - */ -void RplASVariant::setObject(void* object, const RplASClass* clazz) -{ - destroyValue(); - m_variantType = VT_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. - * - */ - -/** - * @brief Constructor. - * - * @param type the type of the instance - */ -RplASItem::RplASItem(RplASItemType type) : - m_id(m_nextId++), - m_nodeType(type), - m_flags(0), - m_position(NULL) -{ -} -/** - * @brief Destructor. - */ - -RplASItem::~RplASItem() -{ -} - -/** - * @brief Checks a calculable node for correctness. - * - * @param description description of the meaning, e.g. "start value" - * @param expectedClass the node must have this type - * @param parser for error processing - * @return true: instance and children are correct
- * false: otherwise - */ -bool RplASItem::checkAsCalculable(const char* description, - RplASClass* expectedClass, RplParser& parser) -{ - bool rc = true; - if (! check(parser)) - rc = false; - if (rc){ - RplASCalculable* expr = dynamic_cast(this); - if (expr == NULL) - rc = error(LOC_ITEM_AS_INT_1, parser, "%s not calculable: %s", - description, nameOfItemType()); - else if (expr->clazz() != RplASInteger::m_instance) - rc = error(LOC_ITEM_AS_INT_2, parser, - "%s: wrong type %s instead of integer", - description, expr->clazz()->name().constData()); - } - return rc; -} - -/** - * @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; -} - -/** - * @brief Stores the position in the source code. - * - * @param position the position to store - */ -void RplASItem::setPosition(const RplSourcePosition* position) -{ - m_position = position; -} - -/** - * @brief Returns the id of the instance. - * - * @return the id - */ -unsigned int RplASItem::id() const -{ - return m_id; -} - -/** - * @brief Returns the position as a string. - * - * @param buffer OUT: the target buffer - * @param bufferSize size of the target buffer - * @return buffer - */ -char* RplASItem::positionStr(char buffer[], size_t bufferSize) const -{ - char* rc = (char*) ""; - if (m_position != NULL) - rc = m_position->utf8(buffer, bufferSize); - return rc; -} - -/** - * @brief Logs an internal error. - * - * @param logger can write to the output medium - * @param location identifies the error location - * @param format string with placeholders (optional) like sprintf() - * @param ... values for the placeholders - */ -void RplASItem::error(RplLogger* logger, int location, const char* format, ...) -{ - char buffer[1024]; - int halfBufferSize = (sizeof buffer) / 2; - qsnprintf(buffer, halfBufferSize, "id: %d [%s]:", m_id, - positionStr(buffer + halfBufferSize, halfBufferSize)); - int length = strlen(buffer); - va_list ap; - va_start(ap, format); - qvsnprintf(buffer + length, (sizeof buffer) - length, format, ap); - va_end(ap); - logger->log(LOG_ERROR, location, buffer); -} - -/** - * @brief Resets the static id counter. - */ -void RplASItem::reset() -{ - m_nextId = 1; -} -/** - * @brief Calculates an integer value. - * - * @param expr a calculable node - * @param thread the execution unit - * @return the value described by the node expr - */ -int RplASItem::calcAsInteger(RplASItem* expr, RplVMThread& thread) -{ - RplASCalculable* expr2 = dynamic_cast(expr); - expr2->calc(thread); - RplASVariant& value = thread.popValue(); - int rc = value.asInt(); - return rc; -} - -/** - * @brief Calculates an boolean value. - * - * @param expr a calculable node - * @param thread the execution unit - * @return the value described by the node expr - */ -bool RplASItem::calcAsBoolean(RplASItem* expr, RplVMThread& thread) -{ - RplASCalculable* expr2 = dynamic_cast(expr); - expr2->calc(thread); - RplASVariant& value = thread.popValue(); - bool rc = value.asBool(); - return rc; -} -/** - * @brief Checks the correctness of a statement list. - * - * @param list statement list to check - * @param parser for error processing - * @return true: all statements are correct
- * false: otherwise - */ -bool RplASItem::checkStatementList(RplASItem* list, RplParser& parser) -{ - bool rc = true; - - while(list != NULL){ - if (! list->check(parser)) - rc = false; - if (dynamic_cast(list) == NULL) - rc = list->error(LOC_ITEM_STATEM_LIST_1, parser, "not a statement: %s", - list->nameOfItemType()); - RplASNode1* node = dynamic_cast(list); - if (node == NULL){ - list->error(LOC_ITEM_STATEM_LIST_1, parser, "not a node: %s", - list->nameOfItemType()); - list = NULL; - } else { - list = node->child(); - } - } - return rc; -} -/** - * @brief Returns the node type. - * - * @return the node type - */ -RplASItemType RplASItem::nodeType() const -{ - return m_nodeType; -} - -/** - * @brief Returns the node type as a string. - * - * @return the node type as string - */ -const char*RplASItem::nameOfItemType() const -{ - const char* rc = "?"; - switch(m_nodeType){ - case AST_CONSTANT: - rc = "constant"; - break; - case AST_LIST_CONSTANT: - rc = "list"; - break; - case AST_LIST_ENTRY: - rc = "listEntry"; - break; - case AST_MAP_CONSTANT: - rc = "map"; - break; - case AST_MAP_ENTRY: - rc = "mapEntry"; - break; - case AST_NAMED_VALUE: - rc = "namedValue"; - break; - case AST_INDEXED_VALUE: - rc = "indexedValue"; - break; - case AST_FIELD: - rc = "field"; - break; - case AST_VAR_DEFINITION: - rc = "varDef"; - break; - case AST_EXPR_STATEMENT: - rc = "exprStatement"; - break; - case AST_METHOD: - rc = "method"; - break; - case AST_ARGUMENT: - rc = "arg"; - break; - case AST_INTRINSIC_METHOD: - rc = "intrinsicMethod"; - break; - case AST_PRE_UNARY_OP: - rc = "preUnary"; - break; - case AST_POST_UNARY_OP: - rc = "postUnary"; - break; - case AST_BINARY_OP: - rc = "binOp"; - break; - case AST_METHOD_CALL: - rc = "methodCall"; - break; - case AST_WHILE: - rc = "while"; - break; - case AST_REPEAT: - rc = "repeat"; - break; - case AST_IF: - rc = "if"; - break; - case AST_CONDITION: - rc = "condition"; - break; - case AST_ITERATED_FOR: - rc = "iFor"; - break; - case AST_COUNTED_FOR: - rc = "cFor"; - break; - case AST_SWITCH: - rc = "switch"; - break; - case AST_LEAVE: - rc = "leave"; - break; - case AST_CONTINUE: - rc = "continue"; - break; - default: - break; - } - return rc; -} -/** - * @brief Returns the flags of the node. - * - * @return the bitmask with the flags - */ -int RplASItem::flags() const -{ - return m_flags; -} -/** - * @brief Sets the flags of the node. - * - * @param flags the new value of the bitmask - */ -void RplASItem::setFlags(int flags) -{ - m_flags = flags; -} - -/** - * @brief Tests the compatibility of 2 data types. - * @param class1 1st class to inspect - * @param class2 2nd class to inspect - * @return true: classes are compatible
- * false: otherwise - */ -bool RplASItem::typeCheck(RplASClass* class1, RplASClass* class2){ - bool rc; - if (class1 == NULL || class2 == NULL) - rc = false; - else - //@ToDo: subclasses - rc = class1 == class2; - return rc; -} - -/** - * @brief Issues an error message. - * - * @param location an error specific id - * @param parser for error processing - * @param format the error message with placeholders (like printf) - * @param ... the values for the placeholders - * @return false (for chaining) - */ -bool RplASItem::error(int location, RplParser& parser, const char* format, ...) -{ - va_list varList; - va_start(varList, format); - parser.addMessage(RplParser::LT_ERROR, location, m_position, format, varList); - va_end(varList); - return false; -} - -/** - * @brief Ensures the occurrence of an error. - * - * When called a previous error should be happend. If not an internal error - * will be issued. - * - * @param parser for error processing - * @param info additional info - * @return false (for chaining) - */ -bool RplASItem::ensureError(RplParser& parser, const char* info) -{ - if (parser.errors() == 0) - error(LOC_ITEM_FORCE_ERROR_1, parser, "lost error (internal error): %s"); - return false; -} - -/** @class RplASCalculable rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief An abstract base class for items which calculates a value. - * - */ -/** - * @brief Constructor. - */ -RplASCalculable::RplASCalculable() : - m_class(NULL) -{ -} -/** - * @brief Returns the class of the node - * @return the class - */ - -RplASClass* RplASCalculable::clazz() const -{ - return m_class; -} -/** - * @brief Sets the class of the node. - * @param clazz the new class - */ -void RplASCalculable::setClass(RplASClass* clazz) -{ - m_class = clazz; -} - -/** @class RplASStorable rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements the abstract base class of value containing items. - * - */ - -/** @class RplASConstant rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a constant for the Abstract Syntax Tree. - * - */ - -/** - * @brief Constructor. - * - */ -RplASConstant::RplASConstant() : - RplASItem(AST_CONSTANT), - m_value() -{ -} - -/** - * @brief Copies the const value to the top of value stack. - * - * @param thread IN/OUT: the execution unit, a VM thread - */ -void RplASConstant::calc(RplVMThread& thread) -{ - RplASVariant& value = thread.reserveValue(); - value.copyValue(m_value); -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: a constant is always correct - */ -bool RplASConstant::check(RplParser& parser) -{ - return true; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASConstant::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "const id: %d value: %s %s", m_id, - m_value.toString().constData(), - positionStr(buffer, sizeof buffer)); -} - -/** - * @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 RplASListConstant rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a container for constant list entries. - * - */ - -/** - * @brief Constructor. - */ -RplASListConstant::RplASListConstant() : - RplASNode1(AST_LIST_CONSTANT), - RplASCalculable() -{ - m_value.setObject(RplASList::m_instance->newValueInstance(), - RplASList::m_instance); -} -/** - * @brief Returns the list. - * - * @return the list - */ -RplASListOfVariants* RplASListConstant::list(){ - RplASListOfVariants* rc = static_cast - (m_value.asObject(NULL)); - return rc; -} - -/** - * @brief Copies the list constant to the top of value stack. - * - * @param thread IN/OUT: the execution unit, a VM thread - */ -void RplASListConstant::calc(RplVMThread& thread) -{ - RplASVariant& value = thread.reserveValue(); - value.copyValue(m_value); -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: a constant is always correct - */ -bool RplASListConstant::check(RplParser& parser) -{ - return true; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASListConstant::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "listConst id: %d %s", m_id, - positionStr(buffer, sizeof buffer)); - - QByteArray sValue = m_value.toString(8092); - writer.writeIndented(indent + 1, sValue.constData()); -} - -/** - * @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& RplASListConstant::value() -{ - return m_value; -} - -/** @class RplASMapConstant rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a hash map for constant list entries. - * - */ -/** - * @brief RplASMapConstant::RplASMapConstant - */ -RplASMapConstant::RplASMapConstant() : - RplASNode1(AST_MAP_CONSTANT), - RplASCalculable(), - m_value() -{ - m_value.setObject(new RplASMapOfVariants, RplASMap::m_instance); -} - -/** - * @brief Copies the map constant to the top of value stack. - * - * @param thread IN/OUT: the execution unit, a VM thread - */ -void RplASMapConstant::calc(RplVMThread& thread) -{ - RplASVariant& value = thread.reserveValue(); - value.copyValue(m_value); -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: a constant is always correct - */ -bool RplASMapConstant::check(RplParser& parser) -{ - return true; -} -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASMapConstant::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "mapConst id: %d %s", - m_id, positionStr(buffer, sizeof buffer)); - writer.indent(indent); - dumpMap(writer, *map(), true); -} - - -/** - * @brief Returns the value of the constant, containing a map. - * - * @return the variant value - */ -RplASVariant& RplASMapConstant::value() -{ - return m_value; -} - -/** - * @brief Returns the (low level) map of the constant. - * - * @return the map of the constant - */ -RplASMapOfVariants* RplASMapConstant::map() -{ - RplASMapOfVariants* rc = static_cast( - m_value.asObject(NULL)); - return rc; -} - -/** @class RplASNamedValue rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a named values, a constant or a variable - */ - -/** - * @brief Constructor. - * - * @param clazz the data type (class) - * @param space the current symbol space - * @param name the name of the variable - * @param attributes the attributes of the variable - */ -RplASNamedValue::RplASNamedValue(RplASClass* clazz,RplSymbolSpace* space, - const QByteArray& name, int attributes) : - RplASItem(AST_NAMED_VALUE), - m_name(name), - m_attributes(attributes), - m_symbolSpace(space), - m_variableNo(-1) -{ - m_class = clazz; -} - -/** - * @brief Returns the name. - * - * @return the name - */ -const QByteArray& RplASNamedValue::name() const -{ - return m_name; -} - -/** - * @brief Sets the symbol space. - * @param space - * @param variableNo - */ -void RplASNamedValue::setSymbolSpace(RplSymbolSpace* space, int variableNo) -{ - m_symbolSpace = space; - m_variableNo = variableNo; -} -/** - * @brief Copies the value of the variable to the top of value stack. - * - * @param thread IN/OUT: the execution unit, a VM thread - */ -void RplASNamedValue::calc(RplVMThread& thread) -{ - thread.valueToTop(m_symbolSpace, m_variableNo); - if (thread.tracing()) - thread.vm()->traceWriter()->format("nVal %s=%.80s", - m_name.constData(), - thread.topOfValues().toString().constData()); -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASNamedValue::check(RplParser& parser) -{ - return true; -} -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASNamedValue::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "namedValue %s id: %d attr: 0x%x %s", - m_name.constData(), m_id, m_attributes, - positionStr(buffer, sizeof buffer)); -} - -/** - * @brief Returns the symbol space of the variable. - * - * @return the symbol space - */ -RplSymbolSpace*RplASNamedValue::symbolSpace() const -{ - return m_symbolSpace; -} - -/** - * @brief Sets the variable no in the instance. - * - * @param variableNo the variable number - */ -void RplASNamedValue::setVariableNo(int variableNo) -{ - m_variableNo = variableNo; -} - - -/** - * @brief Returns the variable no of the variable. - * - * @return the current number of the variable in the symbol space - */ -int RplASNamedValue::variableNo() const -{ - return m_variableNo; -} - -/** @class RplASConversion rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a data type conversion. - * - * m_child: the expression which will be converted - */ -/** - * @brief Constructor. - * @param expression the expression to convert - */ -RplASConversion::RplASConversion(RplASItem* expression) : - RplASNode1(AST_CONVERSION), - m_conversion(C_UNDEF) -{ - m_child = expression; - m_position = expression->position(); -} - -/** - * @brief Convert an expression to another data type. - * - * Possible conversions: @see RplASConversion::Conversion - * - * @param thread execution value - */ -void RplASConversion::calc(RplVMThread& thread) -{ - RplASCalculable* expr = dynamic_cast(m_child); - expr->calc(thread); - RplASVariant& value = thread.topOfValues(); - - switch(m_conversion){ - case C_INT_TO_FLOAT: - value.setFloat((qreal) value.asInt()); - break; - case C_FLOAT_TO_INT: - value.setInt((int) value.asFloat()); - break; - case C_BOOL_TO_INT: - value.setInt((int) value.asBool()); - break; - case C_BOOL_TO_FLOAT: - value.setFloat((qreal) value.asBool()); - break; - default: - break; - } - if (thread.tracing()) - thread.vm()->traceWriter()->format("(%s): %s", - m_class->name().constData(), - value.toString().constData()); -} - -/** - * @brief Returns the conversion type of two classes. - * - * @param from class to convert - * @param to result class of the conversion - * - * @return C_UNDEF: not convertable
- * otherwise: the conversion type - */ -RplASConversion::Conversion RplASConversion::findConversion(RplASClass* from, - RplASClass* to) -{ - Conversion rc = C_UNDEF; - if (from == RplASFloat::m_instance){ - if (to == RplASInteger::m_instance) - rc = C_FLOAT_TO_INT; - } else if (from == RplASInteger::m_instance){ - if (to == RplASFloat::m_instance) - rc = C_INT_TO_FLOAT; - } else if (from == RplASBoolean::m_instance){ - if (to == RplASInteger::m_instance) - rc = C_BOOL_TO_INT; - else if (to == RplASInteger::m_instance) - rc = C_BOOL_TO_INT; - else if (to == RplASFloat::m_instance) - rc = C_BOOL_TO_FLOAT; - } - return rc; -} - -/** - * @brief Checks the node. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASConversion::check(RplParser& parser) -{ - bool rc = m_child != NULL && m_child->check(parser); - RplASCalculable* expr = dynamic_cast(m_child); - if (! rc || expr == NULL) - ensureError(parser, "RplASConversion::check"); - else { - RplASClass* from = expr->clazz(); - m_conversion = findConversion(from, m_class); - if (m_conversion != C_UNDEF) - rc = true; - else - parser.error(LOC_CONV_CHECK_1, - "invalid data type conversion: %s -> %s", - from->name().constData(), - m_class->name().constData()); - } - return rc; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASConversion::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "conversion %s id: %d expr: %d %s", - m_class->name().constData(), m_id, m_child->id(), - positionStr(buffer, sizeof buffer)); -} - -/** - * @brief Tries to find a conversion to a given type. - * - * Checks if an expression has a given type. If not it will be tried to find - * a conversion. If this is not possible an error occurres. Otherwise the - * converter will be returned. - * - * @param expected the expected data type - * @param expr the expression to convert - * @param parser for error processing - * @param isCorrect OUT: false: error has been detected
- * No change if no error - * - * @return NULL: no conversion necessary
- * otherwise: a converter to the given type - */ -RplASConversion* RplASConversion::tryConversion(RplASClass* expected, - RplASItem* expr, RplParser& parser, bool& isCorrect) -{ - RplASConversion* rc = NULL; - if (! expr->check(parser)) - isCorrect = false; - else { - RplASCalculable* expr2 = dynamic_cast(expr); - if (expr2 != NULL){ - Conversion type = findConversion(expr2->clazz(), expected); - if (type == C_UNDEF){ - isCorrect = parser.error(LOC_CONV_TRY_1, - "invalid data type conversion: %s -> %s", - expr2->clazz()->name().constData(), - expected->name().constData()); - } else if (expr2->clazz() != expected){ - rc = new RplASConversion(expr); - rc->m_conversion = type; - rc->setClass(expected); - } - } - } - return rc; -} - -/** @class RplASIndexedValue rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an indexed values (member of a list) - * - * m_child: the parent: a list/map expression - * m_child2: the index expression - */ -RplASIndexedValue::RplASIndexedValue() : - RplASNode2(AST_INDEXED_VALUE) -{ -} - -/** - * @brief Calculates an indexed expression. - * - * Possible: list index or map index - * - * @param thread execution value - */ -void RplASIndexedValue::calc(RplVMThread& thread) -{ - RplASCalculable* expr = dynamic_cast(m_child2); - expr->calc(thread); - RplASVariant& ixValue = thread.popValue(); - int ix = ixValue.asInt(); - RplASCalculable* list = dynamic_cast(m_child); - list->calc(thread); - RplASVariant& listValue = thread.popValue(); - //@ToDo: access to the lists element: assignment or to stack - if (thread.tracing()) - thread.vm()->traceWriter()->format("[%d]: %.80s", - ix, thread.topOfValues().toString().constData()); -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASIndexedValue::check(RplParser& parser) -{ - RplASCalculable* list = dynamic_cast(m_child); - bool rc = m_child != NULL && m_child->check(parser); - if (! rc || list == NULL) - ensureError(parser, "RplASIndexedValue::check"); - else { - // index value: - // tryConversion() calls m_child2->check()! - RplASConversion* converter = RplASConversion::tryConversion( - RplASInteger::m_instance, m_child2, parser, rc); - if (rc && converter != NULL) - m_child = converter; - if (rc){ - //@ToDo: dynamic subclass of list / map - m_class = RplASString::m_instance; - rc = m_class != NULL && m_class == RplASInteger::m_instance; - } - } - return rc; -} - -/** - * @brief Writes the internals into an output media. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASIndexedValue::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "indexedValue id: %d index: %d parent: %d %s", - m_id, m_child2->id(), m_child->id(), - positionStr(buffer, sizeof buffer)); - m_child2->dump(writer, indent + 1); - m_child->dump(writer, indent + 1); -} - -/** @class RplVarDefinition rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements variable definition for the Abstract Syntax Tree. - * - * m_child: next statement
- * m_child2: named value (name + default value expression) - * m_child3: initial value or NULL - */ - -/** - * @brief Constructor. - */ -RplASVarDefinition::RplASVarDefinition() : - RplASNode3(AST_VAR_DEFINITION), - RplASStatement(), - m_endOfScope(0) -{ - m_flags |= NF_STATEMENT; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASVarDefinition::dump(RplWriter& writer, int indent) -{ - RplASNamedValue* namedValue = dynamic_cast(m_child2); - QByteArray name = namedValue->name(); - char endOfScope[32]; - endOfScope[0] = '\0'; - if (m_endOfScope > 0) - qsnprintf(endOfScope, sizeof endOfScope, "-%d:0", m_endOfScope); - char buffer[256]; - writer.formatIndented(indent, - "varDef %s %s id: %d namedValue: %d value: %d succ: %d %s%s", - clazz() == NULL ? "?" : clazz()->name().constData(), - name.constData(), m_id, - m_child2 == NULL ? 0 : m_child2->id(), - m_child3 == NULL ? 0 : m_child3->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer), endOfScope); - if (m_child2 != NULL) - m_child2->dump(writer, indent + 1); - if (m_child3 != NULL) - m_child3->dump(writer, indent + 1); -} - -/** - * @brief Returns the name of the variable. - * - * @return the name - */ -const QByteArray& RplASVarDefinition::name() const -{ - RplASNamedValue* namedValue = dynamic_cast(m_child2); - const QByteArray& rc = namedValue->name(); - return rc; -} - -/** - * @brief Returns the data type (class) of the variable. - * - * @return the data type - */ -RplASClass* RplASVarDefinition::clazz() const -{ - RplASNamedValue* namedValue = dynamic_cast(m_child2); - RplASClass* rc = namedValue == NULL ? NULL : namedValue->clazz(); - return rc; -} -/** - * @brief Returns the column of the scope end. - * - * 0 means end of method or end of class - * - * @return 0 or the column of the scope end - */ -int RplASVarDefinition::endOfScope() const -{ - return m_endOfScope; -} - -/** - * @brief Sets the column of the scope end. - * - * @param endOfScope the column of the scope end - */ -void RplASVarDefinition::setEndOfScope(int endOfScope) -{ - m_endOfScope = endOfScope; -} - - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASVarDefinition::check(RplParser& parser) -{ - RplASNamedValue* var = dynamic_cast(m_child2); - - bool rc = var != NULL && (m_child3 == NULL || m_child3->check(parser)); - if (! rc) - ensureError(parser, "RplASVarDefinition::check"); - else { - if (m_child3 != NULL){ - // with initialization: - RplASCalculable* expr = dynamic_cast(m_child3); - if (expr == NULL) - rc = error(LOC_VARDEF_CHECK_1, parser, - "Not a calculable expression: %s", - m_child3->nameOfItemType()); - else if (! typeCheck(var->clazz(), expr->clazz())) - rc = error(LOC_VARDEF_CHECK_2, parser, - "data types are not compatible: %s/%s", - var->clazz()->name().constData(), - expr->clazz() == NULL ? "?" - : expr->clazz()->name().constData()); - } - } - return rc; -} - -/** - * @brief Executes the statement. - * - * @return 0: continue the current statement list
- */ -int RplASVarDefinition::execute(RplVMThread& thread) -{ - if (m_child3 != NULL){ - // has an initialization: - RplASNamedValue* var = dynamic_cast(m_child2); - RplASCalculable* expr = dynamic_cast(m_child3); - expr->calc(thread); - RplASVariant& value = thread.popValue(); - RplASVariant& destination = thread.valueOfVariable( - var->m_symbolSpace, var->m_variableNo); - if (thread.tracing()) - thread.vm()->traceWriter()->format("%s = %.80s [%.80s]", - var->m_name.constData(), - value.toString().constData(), - destination.toString().constData()); - destination.copyValue(value); - } - return 0; -} - -/** @class RplASExprStatement rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an statement consisting of an expression. - * - * m_child: next statement
- * m_child2: expression - */ - -/** - * @brief Constructor. - */ -RplASExprStatement::RplASExprStatement() : - RplASNode2(AST_EXPR_STATEMENT), - RplASStatement() -{ - m_flags |= NF_STATEMENT; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASExprStatement::check(RplParser& parser) -{ - bool rc = m_child2->check(parser); - if (rc){ - RplASCalculable* expr = dynamic_cast (m_child2); - if (expr == NULL) - rc = ensureError(parser, "RplASExprStatement::check"); - } - return rc; -} -/** - * @brief Executes the statement. - * - * @return 0: continue the current statement list
- */ -int RplASExprStatement::execute(RplVMThread& thread) -{ - RplASCalculable* expr = dynamic_cast (m_child2); - expr->calc(thread); - RplASVariant& value = thread.popValue(); - if (thread.tracing()) - thread.vm()->traceWriter()->format("expr: %s", - value.toString().constData()); - value.destroyValue(); - return 0; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ - -void RplASExprStatement::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - if (m_id == 40) - m_id = 40; - writer.formatIndented(indent, "Expr id: %d expr: %d succ: %d %s", m_id, - m_child2 == NULL ? 0 : m_child2->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child2 != NULL) - m_child2->dump(writer, indent + 1); -} - -/** @class RplASNode1 rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an inner node of the abstract syntax tree with one child. - * - * This class is an abstract class. - */ - -/** - * @brief RplASNode1::RplASNode1 - * @param type - */ -RplASNode1::RplASNode1(RplASItemType type) : - RplASItem(type), - m_child(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASNode1::~RplASNode1() -{ - delete m_child; - m_child = NULL; -} -/** - * @brief Returns the child. - * - * @return the child of the instance - */ -RplASItem* RplASNode1::child() const -{ - return m_child; -} -/** - * @brief Sets the child. - */ -void RplASNode1::setChild(RplASItem* child) -{ - m_child = child; -} - -/** - * @brief Writes the internals of a statement list into a file. - * - * @param writer writes to output media - * @param indent the indent level of the statement list - * @param statements the chain of statements to dump - */ -void RplASNode1::dumpStatements(RplWriter& writer, int indent, - RplASItem* statements) -{ - RplASNode1* chain = dynamic_cast(statements); - while (chain != NULL){ - chain->dump(writer, indent); - chain = dynamic_cast(chain->m_child); - } -} - - -/** @class RplASNode2 rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an inner node of the abstract syntax tree with two childs. - * - * This class is an abstract class. - */ - -/** - * @brief RplASNode2::RplASNode2 - * @param type - */ -RplASNode2::RplASNode2(RplASItemType type) : - RplASNode1(type), - m_child2(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASNode2::~RplASNode2() -{ - delete m_child2; - m_child2 = NULL; -} -RplASItem* RplASNode2::child2() const -{ - return m_child2; -} - -void RplASNode2::setChild2(RplASItem* child2) -{ - m_child2 = child2; -} - - -/** @class RplASNode3 rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an inner node of the abstract syntax tree with 3 childs. - * - * This class is an abstract class. - */ - -/** - * @brief RplASNode3::RplASNode3 - * @param type - */ -RplASNode3::RplASNode3(RplASItemType type) : - RplASNode2(type), - m_child3(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASNode3::~RplASNode3() -{ - delete m_child3; - m_child3 = NULL; -} -/** - * @brief Returns the child3. - * - * @return the child 3 - */ -RplASItem* RplASNode3::child3() const -{ - return m_child3; -} - -/** - * @brief Sets the child3. - * - * @param child3 the new child3 - */ -void RplASNode3::setChild3(RplASItem* child3) -{ - m_child3 = child3; -} - - -/** @class RplASNode4 rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an inner node of the abstract syntax tree with 3 childs. - * - * This class is an abstract class. - */ - -/** - * @brief RplASNode4::RplASNode4 - * @param type - */ -RplASNode4::RplASNode4(RplASItemType type) : - RplASNode3(type), - m_child4(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASNode4::~RplASNode4() -{ - delete m_child4; - m_child4 = NULL; -} - -/** - * @brief Returns the child4. - * - * @return the child 4 - */ -RplASItem* RplASNode4::child4() const -{ - return m_child4; -} - -/** - * @brief Sets the child4. - * - * @param child4 the new child3 - */ -void RplASNode4::setChild4(RplASItem* child4) -{ - m_child4 = child4; -} - - -/** @class RplASNode5 rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an inner node of the abstract syntax tree with 4 childs. - * - * This class is an abstract class. - */ - -/** - * @brief RplASNode5::RplASNode5 - * @param type - */ -RplASNode5::RplASNode5(RplASItemType type) : - RplASNode4(type), - m_child5(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASNode5::~RplASNode5() -{ - delete m_child5; - m_child5 = NULL; -} - -/** - * @brief Returns the child5. - * - * @return the child 5 - */ -RplASItem* RplASNode5::child5() const -{ - return m_child5; -} - -/** - * @brief Sets the child5. - * - * @param child5 the new child3 - */ -void RplASNode5::setChild5(RplASItem* child5) -{ - m_child5 = child5; -} - -/** @class RplASNode6 rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an inner node of the abstract syntax tree with 4 childs. - * - * This class is an abstract class. - */ - -/** - * @brief RplASNode6::RplASNode6 - * @param type - */ -RplASNode6::RplASNode6(RplASItemType type) : - RplASNode5(type), - m_child6(NULL) -{ -} - -/** - * @brief Destructor. - */ -RplASNode6::~RplASNode6() -{ - delete m_child6; - m_child6 = NULL; -} - -/** - * @brief Returns the child5. - * - * @return the child 5 - */ -RplASItem* RplASNode6::child6() const -{ - return m_child6; -} - -/** - * @brief Sets the child6. - * - * @param child6 the new child6 - */ -void RplASNode6::setChild6(RplASItem* child6) -{ - m_child6 = child6; -} - -/** @class RplASUnaryOp rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an unary operation. - * - * This is an operation with one operand, e.g. the boolean 'not' operation. - * - * m_child: operand - */ - -/** - * @brief Constructor. - * - * @param op the operator id - * @param type the node type - */ -RplASUnaryOp::RplASUnaryOp(UnaryOp op, RplASItemType type) : - RplASNode1(type), - m_operator(op) -{ -} - -/** - * @brief Calculates the value of the unary operator. - * - * @param thread IN/OUT: the execution unit, a VM thread - */ -void RplASUnaryOp::calc(RplVMThread& thread) -{ - RplASVariant& value = thread.topOfValues(); - switch(m_operator){ - case UOP_PLUS: - break; - case UOP_MINUS_INT: - value.setInt(- value.asInt()); - break; - case UOP_MINUS_FLOAT: - value.setFloat(- value.asFloat()); - break; - case UOP_NOT_BOOL: - value.setBool(! value.asBool()); - break; - case UOP_NOT_INT: - value.setInt(~value.asInt()); - break; - case UOP_DEC: - case UOP_INC: - default: - error(thread.logger(), LOC_UNOP_CALC_1, "unknown operator: %d", m_operator); - break; - } - if (thread.tracing()) - thread.vm()->traceWriter()->format("unary %s: %s", - nameOfOp(m_operator), - value.toString().constData()); -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASUnaryOp::check(RplParser& parser) -{ - bool rc = m_child->check(parser); - if (rc){ - RplASCalculable* expr = dynamic_cast(m_child); - RplASClass* clazz = expr == NULL ? NULL : expr->clazz(); - if (clazz == NULL){ - rc = ensureError(parser, "RplASUnaryOp::check"); - } else { - switch(m_operator){ - case UOP_PLUS: - if (clazz != RplASInteger::m_instance - && clazz != RplASFloat::m_instance) - rc = error(LOC_UNARY_CHECK_1, parser, - "wrong data type for unary operator '+': %s", - clazz->name().constData()); - break; - case UOP_MINUS_INT: - if (clazz != RplASFloat::m_instance) - m_operator = UOP_MINUS_FLOAT; - else if (clazz != RplASInteger::m_instance) - rc = error(LOC_UNARY_CHECK_2, parser, - "wrong data type for unary operator '-': %s", - clazz->name().constData()); - break; - case UOP_NOT_BOOL: - if (clazz != RplASBoolean::m_instance) - rc = error(LOC_UNARY_CHECK_3, parser, - "wrong data type for unary operator '!': %s", - clazz->name().constData()); - break; - case UOP_NOT_INT: - if (clazz != RplASInteger::m_instance) - rc = error(LOC_UNARY_CHECK_4, parser, - "wrong data type for unary operator '!': %s", - clazz->name().constData()); - break; - case UOP_DEC: - break; - case UOP_INC: - break; - default: - throw RplASException(position(), "unknown operator: %d", m_operator); - break; - } - } - } - return rc; -} - -/** - * @brief Returns the operator of the unary operation. - * - * @return the operator - */ -int RplASUnaryOp::getOperator() const -{ - return m_operator; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASUnaryOp::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "Unary %d op: %s (%d) expr: %d %s", - m_id, - nameOfOp(m_operator), - m_operator, - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer) ); - if (m_child != NULL) - m_child->dump(writer, indent + 1); -} -/** - * @brief Returns the name (a string) of an unary operator. - * - * @param op the operand to convert - * @return the name of the operator - */ -const char*RplASUnaryOp::nameOfOp(RplASUnaryOp::UnaryOp op) -{ - const char* rc; - switch (op){ - case UOP_PLUS: - rc="+"; - break; - case UOP_MINUS_INT: - case UOP_MINUS_FLOAT: - rc="-"; - break; - case UOP_NOT_BOOL: - rc="!"; - break; - case UOP_NOT_INT: - rc="~"; - break; - case UOP_INC: - rc="++"; - break; - case UOP_DEC: - rc="--"; - break; - default: - throw RplException("unknown unary operator: %d", (int) op); - break; - } - return rc; -} - - -/** @class RplASStatement rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a base class for all statements. - * - * @note statements are always RplASNode1 and m_child is used - * for the successors (next statement). - */ - -/** - * @brief Constructor. - */ -RplASStatement::RplASStatement() -{ -} -/** - * @brief Executes the statements of a statement list. - * - * @param list statement list - * @param thread execution unit - * @return 0: continue the current statement list
- * n > 0: stop the n most inner statement lists (initialized by leave) - * n < 0: stop the -n most inner statement lists (initialized by continue) - */ -int RplASStatement::executeStatementList(RplASItem* list, RplVMThread& thread) -{ - int rc = 0; - while(rc == 0 && list != NULL){ - RplASStatement* statement = dynamic_cast(list); - rc =statement->execute(thread); - } - return rc; -} - - -/** @class RplASIf rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an if statement. - * - * The if statement has a condition, a then-part and an optional else-part. - * If the condition is evaluated to true, the then-part will be executed. - * Otherwise the else-part if it exists. - * - * m_child: next statement
- * m_child2: condition
- * m_child3: then part
- * m_child4: else part or NULL
- */ - -RplASIf::RplASIf() : - RplASNode4(AST_IF) -{ - m_flags |= NF_STATEMENT; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASIf::check(RplParser& parser) -{ - bool rc = true; - if (m_child2 == NULL) - rc = ensureError(parser, "'if' misses condition"); - else if (m_child2->checkAsCalculable("condition", RplASBoolean::m_instance, - parser)) - rc = false; - if (m_child3 != NULL && ! checkStatementList(m_child3, parser)) - rc = false; - if (m_child4 != NULL && ! checkStatementList(m_child4, parser)) - rc = false; - return rc; -} - -/** - * @brief Executes the statement. - * - * @return 0: continue the current statement list
- * n > 0: stop the n most inner statement lists (initialized by leave) - * n < 0: stop the -n most inner statement lists (initialized by continue) - */ -int RplASIf::execute(RplVMThread& thread) -{ - int rc = 0; - bool condition = calcAsBoolean(m_child2, thread); - if (thread.tracing()) - thread.vm()->traceWriter()->format("if %s", condition ? "true" : "false"); - - RplASItem* list = condition ? m_child3 : m_child4; - if (list != NULL){ - if ( (rc = executeStatementList(list, thread)) != 0){ - if (rc < 0) - rc--; - else if (rc > 0) - rc++; - } - } - return rc; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASIf::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, - "If id: %d condition: %d then: %d else: %d succ: %d%s", - m_id, - m_child2 == NULL ? 0 : m_child2->id(), - m_child3 == NULL ? 0 : m_child3->id(), - m_child4 == NULL ? 0 : m_child4->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - m_child2->dump(writer, indent + 1); - if (m_child3 != NULL) - m_child3->dump(writer, indent + 1); - if (m_child4 != NULL) - m_child4->dump(writer, indent + 1); -} - -/** @class RplASFor rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a for statement. - * - * The for statement has an initialization, a condition, a forwarding - * statement and a body. - * The initialization will be called first. - * Then the condition will be tested. If true the body will be executed - * and then the forwarding statement. - * - * m_child: next statement
- * m_child2: body
- * m_child3: iterator variable
- * m_child4: container variable
- */ - -/** - * @brief Constructor. - * - * @param variable NULL or the iterator variable - */ -RplASForIterated::RplASForIterated(RplASVarDefinition* variable) : - RplASNode4(AST_ITERATED_FOR), - RplASStatement() -{ - m_flags |= NF_STATEMENT; - m_child2 = variable; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASForIterated::check(RplParser& parser) -{ - return false; -} - -/** - * @brief Executes the statement. - * - * @return 0: continue the current statement list
- * n > 0: stop the n most inner statement lists (initialized by leave) - * n < 0: stop the -n most inner statement lists (initialized by continue) - */ -int RplASForIterated::execute(RplVMThread& thread) -{ - return 0; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASForIterated::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "forIt id: %d var: %d set: %d body: %d succ: %d %s", - m_id, - m_child3 == NULL ? 0 : m_child3->id(), - m_child4 == NULL ? 0 : m_child4->id(), - m_child2 == NULL ? 0 : m_child2->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child3 != NULL) - m_child3->dump(writer, indent + 1); - if (m_child4 != NULL) - m_child4->dump(writer, indent + 1); - dumpStatements(writer, indent + 1, m_child2); -} - -/** @class RplASForCounted rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a for statement. - * - * The for statement has an optional variable, an optional start value, - * an end value, an optional step and a body. - * - * The start and end value will be calculated. - * The body will be executed so many times given by the start and end value. - * - * m_child: next statement
- * m_child2: body
- * m_child3: variable or NULL
- * m_child4: start value or NULL
- * m_child5: end value
- * m_child6: step value or NULL - * - */ - -/** - * @brief Constructor. - * - * @param variable NULL or the counter variable - */ -RplASForCounted::RplASForCounted(RplASVarDefinition* variable) : - RplASNode6(AST_ITERATED_FOR), - RplASStatement() -{ - m_flags |= NF_STATEMENT; - m_child3 = variable; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASForCounted::check(RplParser& parser) -{ - bool rc = true; - RplASNamedValue* var = NULL; - if (m_child3 != NULL){ - var = dynamic_cast(m_child3); - if (! m_child3->check(parser)) - rc = false; - if (var == NULL) - rc = error(LOC_FORC_CHECK_1, parser, "not a variable: %s", - m_child3->nameOfItemType()); - } - RplASCalculable* expr; - if (m_child4 != NULL && ! m_child4->checkAsCalculable("start value", - RplASInteger::m_instance, parser)) - rc = false; - if (m_child5 != NULL && ! m_child5->checkAsCalculable("end value", - RplASInteger::m_instance, parser)) - rc = false; - if (m_child6 != NULL && ! m_child6->checkAsCalculable("step value", - RplASInteger::m_instance, parser)) - rc = false; - if (m_child2 != NULL && ! checkStatementList(m_child2, parser)) - rc = false; - return rc; -} - -/** - * @brief Executes the statement. - * - * @return 0: continue the current statement list
- * n > 0: stop the n most inner statement lists (initialized by leave) - * n < 0: stop the -n most inner statement lists (initialized by continue) - */ -int RplASForCounted::execute(RplVMThread& thread) -{ - int rc = 0; - RplASStatement* body = dynamic_cast(m_child2); - if (body == NULL) - throw RplASException(m_child2 == NULL ? m_position : m_child2->position(), - "forc statement: body is not a statement"); - int start = m_child4 == NULL ? 1 : calcAsInteger(m_child4, thread); - int end = m_child5 == NULL ? 0 : calcAsInteger(m_child5, thread); - int step = m_child6 == NULL ? 1 : calcAsInteger(m_child6, thread); - RplASNamedValue* var = m_child3 == NULL - ? NULL : dynamic_cast(m_child3); - if (thread.tracing()) - thread.vm()->traceWriter()->format("for %s from %d to %d step %d", - var == NULL ? "?" : var->name().constData(), - start, end, step); - - for(int ii = start; ii <= end; ii += step){ - //@ToDo: assign to the variable - int rc2 = body->execute(thread); - if (rc2 != 0){ - if (rc2 > 0){ - // rc comes from "break"; - rc = rc2 - 1; - } else { - // rc comes from "continue"; - if (rc2 == -1) - continue; - else - rc = rc2 + 1; - } - break; - } - } - return rc; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASForCounted::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "forC id: %d var: %d from: %d to: %d step: %d body: %d succ: %d %s", - m_id, - m_child3 == NULL ? 0 : m_child3->id(), - m_child4 == NULL ? 0 : m_child4->id(), - m_child5 == NULL ? 0 : m_child5->id(), - m_child6 == NULL ? 0 : m_child6->id(), - m_child2 == NULL ? 0 : m_child2->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child3 != NULL) - m_child3->dump(writer, indent + 1); - if (m_child4 != NULL) - m_child4->dump(writer, indent + 1); - if (m_child5 != NULL) - m_child5->dump(writer, indent + 1); - if (m_child6 != NULL) - m_child6->dump(writer, indent + 1); - dumpStatements(writer, indent + 1, m_child2); -} - -/** @class RplASWhile rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a while statement. - * - * The while statement has an a condition and a body. - * The body will be executed while the condition returns true. - * - * m_child: next statement
- * m_child2: condition
- * m_child3: body
- */ - -RplASWhile::RplASWhile() : - RplASNode3(AST_WHILE), - RplASStatement() -{ - m_flags |= NF_STATEMENT; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASWhile::check(RplParser& parser) -{ - bool rc = true; - if (m_child2 == NULL) - ensureError(parser, "missing condition for 'while''"); - else - rc = m_child2->checkAsCalculable("condition", RplASBoolean::m_instance, - parser); - if (m_child3 != NULL && ! checkStatementList(m_child3, parser)) - rc = false; - return rc; -} - -/** - * @brief Executes the statement. - * - * @return 0: continue the current statement list
- * n > 0: stop the n most inner statement lists (initialized by leave) - * n < 0: stop the -n most inner statement lists (initialized by continue) - */ -int RplASWhile::execute(RplVMThread& thread) -{ - int rc = 0; - RplASStatement* body = dynamic_cast(m_child3); - if (thread.tracing()) - thread.vm()->traceWriter()->write("while"); - while(calcAsBoolean(m_child2, thread)){ - int rc2 = body->execute(thread); - if (rc2 != 0){ - if (rc2 > 0){ - // rc comes from "break"; - rc = rc2 - 1; - } else { - // rc comes from "continue"; - if (rc2 == -1) - continue; - else - rc = rc2 + 1; - } - break; - } - } - return rc; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASWhile::dump(RplWriter& writer, int indent) -{ - - char buffer[256]; - writer.formatIndented(indent, "while id: %d condition: %d body: %d succ: %d %s", - m_id, - m_child2 == NULL ? 0 : m_child2->id(), - m_child3 == NULL ? 0 : m_child3->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child2 != NULL) - m_child2->dump(writer, indent + 1); - dumpStatements(writer, indent + 1, m_child3); -} - -/** @class RplASRepeat rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a while statement. - * - * The while statement has an a condition and a body. - * The body will be executed while the condition returns true. - * - * m_child: next statement
- * m_child2: condition
- * m_child3: body
- */ - -RplASRepeat::RplASRepeat() : - RplASNode3(AST_REPEAT), - RplASStatement() -{ - m_flags |= NF_STATEMENT; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASRepeat::check(RplParser& parser) -{ - bool rc = true; - if (m_child3 != NULL && ! checkStatementList(m_child3, parser)) - rc = false; - if (m_child2 == NULL) - ensureError(parser, "missing condition for 'repeat''"); - else if (! m_child2->checkAsCalculable("condition", RplASBoolean::m_instance, - parser)) - rc = false; - return rc; -} - -/** - * @brief Executes the statement. - * - * Meaning of the childs: - * m_child: body - * m_child2: condition - */ -int RplASRepeat::execute(RplVMThread& thread) -{ - int rc = 0; - RplASStatement* body = dynamic_cast(m_child3); - if (thread.tracing()) - thread.vm()->traceWriter()->write("repeat"); - do { - int rc2 = body->execute(thread); - if (rc2 != 0){ - if (rc2 > 0){ - // rc comes from "break"; - rc = rc2 - 1; - } else { - // rc comes from "continue"; - if (rc2 == -1) - continue; - else - rc = rc2 + 1; - } - break; - } - } while(! calcAsBoolean(m_child2, thread)); - return rc; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASRepeat::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "repeat id: %d condition: %d body: %d succ: %d %s", - m_id, - m_child2 == NULL ? 0 : m_child2->id(), - m_child3 == NULL ? 0 : m_child3->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child2 != NULL) - m_child2->dump(writer, indent + 1); - dumpStatements(writer, indent + 1, m_child3); -} - -/** @class RplASClass rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements the base class of an Abstract Syntax Tree class. - * - * This class is abstract. - */ -/** - * @brief Constructor. - */ -RplASClass::RplASClass(const QByteArray& name, RplASTree& tree) : - m_name(name), - m_symbols(NULL), - m_superClass(NULL), - m_tree(tree) -{ -} - -/** - * @brief Destructor. - * - * Does nothing but forces a virtual destructor of all derived classes. - * - */ -RplASClass::~RplASClass() -{ - -} - -/** - * @brief Return the class name. - * - * @return the class name - */ -const QByteArray& RplASClass::name() const -{ - return m_name; -} - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASClass::dump(RplWriter& writer, int indent) -{ - writer.formatIndented(indent, "class %s super: %s", m_name.constData(), - m_superClass == NULL - ? "" : m_superClass->name().constData()); - m_symbols->dump(writer, indent); -} - -/** - * @brief Sets the symbol space from the current in the tree. - */ -void RplASClass::setSymbols() -{ - m_symbols = m_tree.currentSpace(); -} - -/** @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() : - m_global(NULL), - m_modules(), - m_symbolSpaces(), - m_currentSpace(NULL), - m_store(128*1024) -{ - init(); -} - -/** - * @brief Destructor. - */ -RplASTree::~RplASTree() -{ - destroy(); -} - -/** - * @brief Initializes the instance. - * - * Used in the constructor and in clear. - */ -void RplASTree::init() -{ - m_global = RplSymbolSpace::createGlobal(*this); - m_symbolSpaces.append(m_global); - m_currentSpace = m_global; -} - -/** - * @brief Frees the resources of the instance. - */ -void RplASTree::destroy() -{ - SymbolSpaceMap::iterator it; - for (it = m_symbolSpaceHeap.begin(); it != m_symbolSpaceHeap.end(); it++){ - delete it.value(); - } - m_symbolSpaceHeap.clear(); -} -/** - * @brief Returns the string storage of the instance. - * - * @return the efficient allocator for C strings - */ -RplByteStorage& RplASTree::store() -{ - return m_store; -} - -/** - * @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(RplSourceUnitName name) -{ - bool rc = m_modules.contains(name); - if (! rc){ - // freed in ~RplASTree() - RplSymbolSpace* space = new RplSymbolSpace(RplSymbolSpace::SST_MODULE, - name, m_global); - m_symbolSpaceHeap[name] = space; - m_modules[name] = space; - m_symbolSpaces.append(space); - m_currentSpace = space; - } - return rc; -} -/** - * @brief Search for the symbol space of a given module. - * - * @param name the module's name - * @return NULL: not found
- * otherwise: the symbol space of the module - */ -RplSymbolSpace* RplASTree::findmodule(const QByteArray& name) -{ - RplSymbolSpace* rc = m_modules.contains(name) ? m_modules[name] : NULL; - return rc; -} - -/** - * @brief Handles the end of a module. - * @param name the module's name - */ -void RplASTree::finishModule(RplSourceUnitName name) -{ - RplSymbolSpace* top = m_symbolSpaces.at(m_symbolSpaces.size() - 1); - if (top->name() != name) - throw RplException("RplASTree::finishModule(): module is not top: %s", - name); - 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 name of the class/method - * @param type the symbol space type - * @return the new symbol space - */ -RplSymbolSpace* RplASTree::startClassOrMethod(const QByteArray& name, - RplSymbolSpace::SymbolSpaceType type) -{ - // the stack m_modules is never empty because of "global" and modules. - RplSymbolSpace* parent = m_symbolSpaces[m_symbolSpaces.size() - 1]; - QByteArray 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; - return space; -} - -/** - * @brief Handles the end of a class definition. - * - * @param name the name of the class (short form) - */ -void RplASTree::finishClassOrMethod(const QByteArray& 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.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; -} - -/** - * @brief Removes all content from the abstract syntax tree. - */ -void RplASTree::clear() -{ - destroy(); - //m_global->clear(); - m_modules.clear(); - m_symbolSpaces.clear(); - init(); -} - -/** - * @brief Writes the internals into a file. - * - * @param filename filename - * @param flags what to dump: sum of DMP_... flags - * @param header NULL or a text put on the top - */ -void RplASTree::dump(const char* filename, int flags, const char* header) -{ - RplFileWriter writer(filename); - if (header != NULL) - writer.writeLine(header); - if (flags & DMP_GLOBALS){ - m_global->dump(writer, 0, "=== Globals:"); - } - if (flags & DMP_MODULES){ - QList sorted; - sorted.reserve(m_modules.size()); - SymbolSpaceMap::iterator it; - for (it = m_modules.begin(); it != m_modules.end(); it++){ - sorted.append(it.key()); - } - qSort(sorted.begin(), sorted.end(), qLess()); - QList::iterator it2; - for (it2 = sorted.begin(); it2 != sorted.end(); it2++){ - RplSymbolSpace* space = m_modules[*it2]; - space->dump(writer, 0); - } - } - writer.close(); -} - -/** @class RplASMethodCall rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a method or function call for the Abstract Syntax Tree. - * - * m_child: next statement
- * m_child2: argument list
- * m_child3: parent (variable, field ...) - */ -/** - * @brief Constructor. - * - * @param name name of the method/function - * @param parent NULL: it is a function
- * otherwise: the parent (variable, field ...) - */ - -RplASMethodCall::RplASMethodCall(const QByteArray& name, RplASItem* parent) : - RplASNode3(AST_METHOD_CALL), - RplASStatement(), - m_name(name), - m_method(NULL) -{ - m_flags |= NF_STATEMENT; - m_child3 = parent; -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASMethodCall::check(RplParser& parser) -{ - bool rc = true; - RplASExprStatement* args = dynamic_cast(m_child2); - int argCount = 0; - RplASMethod* method = m_method; - RplASVarDefinition* params = dynamic_cast(method->child2()); - while (args != NULL && params != NULL){ - argCount++; - RplASCalculable* argExpr = dynamic_cast - (args->child2()); - if (argExpr == NULL) - rc = error(LOC_METHOD_CALL_CHECK_1, parser, - "argument %d misses expr", argCount); - else { - RplASNamedValue* var; - RplASItem* param = params->child2(); - if (param == NULL - || (var = dynamic_cast(param)) == NULL) - rc = error(LOC_MEHTOD_CALL_CHECK_2, parser, - "parameter %d misses named value: %s", argCount, - param == NULL ? "" : param->nameOfItemType()); - else { - // tryConversion() calls args->args->child2()->check()! - RplASConversion* converter = RplASConversion::tryConversion( - var->clazz(), args->child2(), parser, rc); - if (rc && converter != NULL) - args->setChild2(converter); - - } - } - args = dynamic_cast(args->child()); - params = dynamic_cast(params->child()); - } - if (args != NULL && params == NULL) - rc = error(LOC_MEHTOD_CALL_CHECK_3, parser, - "too many arguments: %d are enough", argCount); - else if (args == NULL && params != NULL && params->child3() != NULL) - rc = error(LOC_MEHTOD_CALL_CHECK_4, parser, - "too few arguments: %d are not enough", argCount); - return rc; -} - - -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASMethodCall::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "call %s Id: %d args: %d parent: %d succ: %d %s", - m_name.constData(), m_id, - m_child2 == NULL ? 0 : m_child2->id(), - m_child3 == NULL ? 0 : m_child3->id(), - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child2 != NULL) - m_child2->dump(writer, indent + 1); - if (m_child3 != NULL) - m_child3->dump(writer, indent + 1); -} - -/** - * @brief Executes the method call. - * - * @return 0: continue the current statement list - */ -int RplASMethodCall::execute(RplVMThread& thread) -{ - int rc = 0; - - return rc; -} - -RplASMethod* RplASMethodCall::method() const -{ - return m_method; -} - -/** - * @brief Sets the method. - * @param method method to set - */ -void RplASMethodCall::setMethod(RplASMethod* method) -{ - m_method = method; -} - -/** - * @brief Returns the argument list. - * - * @return the first element of an argument list - */ -RplASExprStatement* RplASMethodCall::arg1() const -{ - return dynamic_cast(m_child2); -} - -/** @class RplASException rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a call of a method or function. - * - * m_child: body - * m_child2: argument list (or NULL) - */ - -/** @class RplASBinaryOp rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements binary operator for the Abstract Syntax Tree. - * - * m_child: left operand
- * m_child2: right operand - */ -/** - * @brief Constructor. - */ -RplASBinaryOp::RplASBinaryOp() : - RplASNode2(AST_BINARY_OP), - m_operator(BOP_UNDEF) -{ -} - -/** - * @brief Calculates the binary operation. - * - * @param thread IN/OUT: the bool value of the condition - */ -void RplASBinaryOp::calc(RplVMThread& thread) -{ - if (isAssignment()) - assign(thread); - else{ - RplASCalculable* op1 = dynamic_cast(m_child); - RplASCalculable* op2 = dynamic_cast(m_child2); - if (op1 == NULL || op2 == NULL) - error(thread.logger(), LOC_BINOP_CALC_1, "operand is null: %d / %d", - m_child == NULL ? 0 : m_child->id(), - m_child2 == NULL ? 0 : m_child2->id()); - else{ - op1->calc(thread); - op2->calc(thread); - RplASVariant& val1 = thread.top2OfValues(); - RplASVariant& val2 = thread.topOfValues(); - switch(m_operator){ - case BOP_PLUS: - switch(val1.variantType()){ - case RplASVariant::VT_FLOAT: - val1.setFloat(val1.asFloat() + val2.asFloat()); - break; - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() + val2.asInt()); - break; - case RplASVariant::VT_OBJECT: - //if (val1.getClass() == RplASString::m_instance) - default: - error(thread.logger(), LOC_BINOP_CALC_2, "invalid type for '+': %s", - val1.nameOfType()); - break; - } - break; - case BOP_MINUS: - switch(val1.variantType()){ - case RplASVariant::VT_FLOAT: - val1.setFloat(val1.asFloat() - val2.asFloat()); - break; - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() - val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_3, "invalid type for '-': %s", - val1.nameOfType()); - break; - } - break; - case BOP_TIMES: - switch(val1.variantType()){ - case RplASVariant::VT_FLOAT: - val1.setFloat(val1.asFloat() * val2.asFloat()); - break; - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() * val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_4, "invalid type for '*': %s", - val1.nameOfType()); - break; - } - break; - case BOP_DIV: - switch(val1.variantType()){ - case RplASVariant::VT_FLOAT: - val1.setFloat(val1.asFloat() / val2.asFloat()); - break; - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() / val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_5, "invalid type for '/': %s", - val1.nameOfType()); - break; - } - break; - case BOP_MOD: - switch(val1.variantType()){ - case RplASVariant::VT_FLOAT: - val1.setFloat(fmod(val1.asFloat(), val2.asFloat())); - break; - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() % val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_6, "invalid type for '%': %s", - val1.nameOfType()); - break; - } - break; - case BOP_POWER: - switch(val1.variantType()){ - case RplASVariant::VT_FLOAT: - val1.setFloat(fmod(val1.asFloat(), val2.asFloat())); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_7, "invalid type for '**': %s", - val1.nameOfType()); - break; - } - break; - case BOP_LOG_OR: - switch(val1.variantType()){ - case RplASVariant::VT_BOOL: - val1.setBool(val1.asBool() || val2.asBool()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_8, "invalid type for '||': %s", - val1.nameOfType()); - break; - } - break; - case BOP_LOG_AND: - switch(val1.variantType()){ - case RplASVariant::VT_BOOL: - val1.setBool(val1.asBool() && val2.asBool()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_9, "invalid type for '&&': %s", - val1.nameOfType()); - break; - } - break; - case BOP_LOG_XOR: - switch(val1.variantType()){ - case RplASVariant::VT_BOOL: - val1.setBool(val1.asBool() != val2.asBool()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_9, "invalid type for '^^': %s", - val1.nameOfType()); - break; - } - break; - case BOP_BIT_OR: - switch(val1.variantType()){ - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() | val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_10, "invalid type for '|': %s", - val1.nameOfType()); - break; - } - break; - case BOP_BIT_AND: - switch(val1.variantType()){ - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() & val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_11, "invalid type for '&': %s", - val1.nameOfType()); - break; - } - break; - case BOP_BIT_XOR: - switch(val1.variantType()){ - case RplASVariant::VT_INTEGER: - val1.setInt(val1.asInt() ^ val2.asInt()); - break; - default: - error(thread.logger(), LOC_BINOP_CALC_12, "invalid type for '^': %s", - val1.nameOfType()); - break; - } - break; - default: - break; - } - thread.popValue(); - } - } -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASBinaryOp::check(RplParser& parser) -{ - return false; -} - -/** - * @brief Returns the operator. - * - * @return the operator - */ -RplASBinaryOp::BinOperator RplASBinaryOp::getOperator() const -{ - return m_operator; -} - -/** - * @brief Sets the operator. - * - * @param op the operator - */ -void RplASBinaryOp::setOperator(BinOperator op) -{ - m_operator = op; -} -/** - * @brief Writes the internals into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASBinaryOp::dump(RplWriter& writer, int indent) -{ - - const QByteArray& opName = nameOfOp(m_operator); - char buffer[256]; - writer.formatIndented(indent, "BinOp id: %d op: %s (%d) left: %d right: %d %s", - m_id, - opName.constData(), m_operator, - m_child == NULL ? 0 : m_child->id(), - m_child2 == NULL ? 0 : m_child2->id(), - positionStr(buffer, sizeof buffer)); - if (indent < 32 && m_child != NULL) - m_child->dump(writer, indent + 1); - if (indent < 32 && m_child2 != NULL) - m_child2->dump(writer, indent + 1); -} - -/** - * @brief Does an assignment. - * - * @param thread - */ -void RplASBinaryOp::assign(RplVMThread& thread) -{ - RplASVariant& rValue = thread.lValue(m_child); - RplASCalculable* expr = dynamic_cast(m_child2); - if (expr == NULL) - error(thread.logger(), LOC_BINOP_1, "not a calculable: id: %d", - m_child2 == NULL ? 0 : m_child2->id()); - else { - RplASVariant& value = thread.popValue(); - switch(m_operator){ - case BOP_ASSIGN: - break; - case BOP_PLUS_ASSIGN: - //switch(value.variantType()){ - - //} - break; - case BOP_MINUS_ASSIGN: - case BOP_TIMES_ASSIGN: - case BOP_DIV_ASSIGN: - case BOP_MOD_ASSIGN: - case BOP_POWER_ASSIGN: - case BOP_LOG_OR_ASSIGN: - case BOP_LOG_AND_ASSIGN: - case BOP_LOG_XOR_ASSIGN: - case BOP_BIT_OR_ASSIGN: - case BOP_BIT_AND_ASSIGN: - case BOP_BIT_XOR_ASSIGN: - break; - default: - break; - } - rValue.copyValue(value); - } -} -/** - * @brief Returns the name (a string) of a binary operator. - * - * @param op operator to convert - * - * @return the name of the operator - */ -const char* RplASBinaryOp::nameOfOp(RplASBinaryOp::BinOperator op) -{ - const char* rc; - switch (op){ - case BOP_ASSIGN: - rc = "="; - break; - case BOP_PLUS_ASSIGN: - rc = "+="; - break; - case BOP_MINUS_ASSIGN: - rc = "-="; - break; - case BOP_TIMES_ASSIGN: - rc = "*="; - break; - case BOP_DIV_ASSIGN: - rc = "/="; - break; - case BOP_MOD_ASSIGN: - rc = "%="; - break; - case BOP_POWER_ASSIGN: - rc = "**="; - break; - case BOP_LOG_OR_ASSIGN: - rc = "||="; - break; - case BOP_LOG_AND_ASSIGN: - rc = "&&="; - break; - case BOP_LOG_XOR_ASSIGN: - rc = "^^="; - break; - case BOP_BIT_OR_ASSIGN: - rc = "|="; - break; - case BOP_BIT_AND_ASSIGN: - rc = "&="; - break; - case BOP_BIT_XOR_ASSIGN: - rc = "^="; - break; - case BOP_LSHIFT_ASSIGN: - rc = "<<="; - break; - case BOP_LOG_RSHIFT_ASSIGN: - rc = ">>="; - break; - case BOP_ARTITH_RSHIFT_ASSIGN: - rc = ">>>="; - break; - case BOP_PLUS: - rc = "+"; - break; - case BOP_MINUS: - rc = "-"; - break; - case BOP_TIMES: - rc = "*"; - break; - case BOP_DIV: - rc = "/"; - break; - case BOP_MOD: - rc = "%"; - break; - case BOP_POWER: - rc = "**"; - break; - case BOP_LOG_OR: - rc = "||"; - break; - case BOP_LOG_AND: - rc = "&&"; - break; - case BOP_LOG_XOR: - rc = "^^"; - break; - case BOP_BIT_OR: - rc = "|"; - break; - case BOP_BIT_AND: - rc = "&"; - break; - case BOP_BIT_XOR: - rc = "^"; - break; - case BOP_LSHIFT: - rc = ""; break; - case BOP_LOG_RSHIFT: - rc = ">>"; - break; - case BOP_ARTITH_RSHIFT: - rc = ">>>"; - break; - case BOP_EQ: - rc = "=="; - break; - case BOP_NE: - rc = "!="; - break; - case BOP_LE: - rc = "<="; - break; - case BOP_LT: - rc = "<"; - break; - case BOP_GE: - rc = ">="; - break; - case BOP_GT: - rc = ">"; - break; - default: - throw RplException("unknown binary op %d", (int) op); - } - return rc; -} - -/** @class RplASMethod rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements a method definition for the Abstract Syntax Tree. - * - * The special case "function" (a method without class) is included. - * - * m_child: body
- * m_child2: parameter list (or NULL) - */ -/** - * @brief Constructor. - * - * @param name the method name - * @param tree the abstract syntax tree - */ -RplASMethod::RplASMethod(const QByteArray& name, RplASTree& tree) : - RplASNode2(AST_METHOD), - m_name(name), - m_resultType(NULL), - m_symbols(NULL), - m_sibling(NULL), - m_tree(tree) -{ -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASMethod::check(RplParser& parser) -{ - return false; -} - -/** - * @brief Executes the statement. - * - * This method will be never called. Must exit: Otherwise the class is abstract. - */ -int RplASMethod::execute(RplVMThread& thread) -{ - return 0; -} - - -/** - * @brief Writes the internals of the instance into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASMethod::dump(RplWriter& writer, int indent) -{ - - char buffer[256]; - writer.indent(indent); - writer.format("Method %s %s(", - m_resultType == NULL ? "" : m_resultType->name().constData(), - m_name.constData()); - RplSymbolSpace* parent = m_symbols->parent(); - writer.formatLine(") id: %d parent: %s args: %d body: %d %s", m_id, - parent == NULL ? "" : parent->name().constData(), - m_child2 == NULL ? 0 : m_child2->id(), - m_child->id(), - positionStr(buffer, sizeof buffer)); - if (m_child2 != NULL) - m_child2->dump(writer, indent + 1); - dumpStatements(writer, indent + 1, m_child); - m_symbols->dump(writer, indent + 1); -} -/** - * @brief Returns the symbol space of the instance. - * - * @return the symbol space - */ -RplSymbolSpace* RplASMethod::symbols() const -{ - return m_symbols; -} - -/** - * @brief Sets the symbol space from the current of the tree. - */ -void RplASMethod::setSymbols() -{ - m_symbols = m_tree.currentSpace(); -} -/** - * @brief Returns the name of the method - * @return the name - */ - -const QByteArray& RplASMethod::name() const -{ - return m_name; -} - -/** - * @brief Tests whether an other method has the same signature (parameterlist). - * @param other the method for comparison - * @return true: same signature
- * false: otherwise - */ -bool RplASMethod::equalSignature(RplASMethod& other) const -{ - bool rc = true; - RplASExprStatement* args = dynamic_cast(m_child2); - RplASExprStatement* otherArgs = dynamic_cast(other.child2()); - - while(rc && (args != NULL || otherArgs != NULL)){ - if (args == NULL || otherArgs == NULL) - rc = false; - else { - RplASVarDefinition* def = dynamic_cast(args->child2()); - RplASVarDefinition* defOther = dynamic_cast(otherArgs->child2()); - if (def->clazz() != defOther->clazz()) - rc = false; - } - } - return rc; -} -/** - * @brief Returns the next overloaded method. - * - * @return NULL: no other method available
- * otherwise: the next method with the same name but another signature - */ -RplASMethod* RplASMethod::sibling() const -{ - return m_sibling; -} - -/** - * @brief Sets the next overloaded method. - * - * @param sibling another method with the same name but another signature - */ -void RplASMethod::setSibling(RplASMethod* sibling) -{ - m_sibling = sibling; -} - - -/** @class RplASField rplastree.hpp "rplexpr/rplastree.hpp" - * - * @brief Implements an class field for the Abstract Syntax Tree. - * - * m_child: parent (variable, field, method) - */ - -/** - * @brief Constructor. - * - * @param name name of the field - */ -RplASField::RplASField(const QByteArray& name) : - RplASNode1(AST_FIELD), - m_name(name) -{ -} - -/** - * @brief Checks the correctness of the instance. - * - * @param parser for error processing - * @return true: node is correct
- * false: otherwise - */ -bool RplASField::check(RplParser& parser) -{ - return false; -} - -/** - * @brief Writes the internals of the instance into a file. - * - * @param writer writes to output - * @param indent nesting level - */ -void RplASField::dump(RplWriter& writer, int indent) -{ - char buffer[256]; - writer.formatIndented(indent, "field %s id: %d parent: %d succ: %s", - m_name.constData(), m_id, - m_child == NULL ? 0 : m_child->id(), - positionStr(buffer, sizeof buffer)); - m_child->dump(writer, indent + 1); -} - - diff --git a/rplexpr/rplastree.hpp b/rplexpr/rplastree.hpp deleted file mode 100644 index 899dfa4..0000000 --- a/rplexpr/rplastree.hpp +++ /dev/null @@ -1,755 +0,0 @@ -/* - * Licence: - * You can use and modify this file without any restriction. - * There is no warranty. - * You also can use the licence from http://www.wtwriterl.net/. - * The original sources can be found on https://github.com/republib. -*/ - - -#ifndef RPLASTREE_HPP -#define RPLASTREE_HPP - -enum RplASItemType { - AST_UNDEF, - AST_CONSTANT, - AST_LIST_CONSTANT, - AST_LIST_ENTRY, - AST_MAP_CONSTANT, - AST_MAP_ENTRY, - AST_NAMED_VALUE, - AST_CONVERSION, - AST_INDEXED_VALUE, - AST_FIELD, - AST_VAR_DEFINITION, - AST_EXPR_STATEMENT, - AST_METHOD, - AST_ARGUMENT, - AST_INTRINSIC_METHOD, - AST_PRE_UNARY_OP, - AST_POST_UNARY_OP, - AST_BINARY_OP, - AST_METHOD_CALL, - AST_WHILE, - AST_REPEAT, - AST_IF, - AST_CONDITION, - AST_ITERATED_FOR, - AST_COUNTED_FOR, - AST_SWITCH, - AST_LEAVE, - AST_CONTINUE -}; - -class RplASException : public RplException { -public: - RplASException(); - RplASException(const RplSourcePosition* position, const char* format, ...); -protected: - void build(const RplSourcePosition* position, const char* format, - va_list varList); -}; - -class RplASClass; -class RplASNamedValue; -class RplASItem; -class RplASCondition; - -class RplASVariant { - /* The VM uses some tricks (performance): Therefore this class - * must not be virtual! - */ -public: - enum VariantType { - VT_UNDEF, - VT_FLOAT, - VT_INTEGER, - VT_BOOL, - VT_OBJECT - }; - enum VariantFlags { - VF_UNDEF, - /// if DT_OBJECT: object is a copy, don't free at method end - VF_IS_COPY = 1 << 1, - /// debugger: action if changed - VF_WATCH_POINT = 1 << 2 - }; - - 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 QByteArray* asString() const; - void setFloat(qreal number); - void setInt(int integer); - void setBool(bool value); - void setObject(void* object, const RplASClass* clazz); - void setString(const QByteArray& string); - QByteArray toString(int maxLength = 80) const; - VariantType variantType() const; - const char* nameOfType() const; - const RplASClass* getClass() const; - void copyValue(const RplASVariant& source); - void destroyValue(); -private: - VariantType m_variantType:8; - /// bitmap of VF_... flags: - int m_flags:8; - union { - qreal m_float; - int m_int; - bool m_bool; - void* m_object; - } m_value; - const RplASClass* m_class; -}; - -class RplASTree; -class RplParser; -class RplVMThread; - -class RplASItem -{ -public: - enum NodeFlags { - NF_UNDEF, - /// the node calculates a value: - NF_CALCULABLE = 1 << 1, - /// the node calculates a value: - NF_STATEMENT = 1 << 2, - /// the tree under this node is complete checked for data type correctness - NF_TYPECHECK_COMPLETE = 1 << 3, - /// debugger: this node is a breakpoint - NF_BREAKPOINT = 1 << 5 - }; - -public: - friend class RplASTree; - RplASItem(RplASItemType type); - virtual ~RplASItem(); -public: - virtual bool check(RplParser& parser) = 0; -public: - bool checkAsCalculable(const char* description, RplASClass* expectedClass, - RplParser& parser); - const RplSourcePosition* position() const; - void setPosition(const RplSourcePosition* position); - unsigned int id() const; - char* positionStr(char buffer[], size_t bufferSize) const; - void error(RplLogger* logger, int location, const char* format, ...); -public: - /** - * @brief Writes the content of the instance into an output medium. - * - * @param writer writes to output media - * @param indent nesting level: so many tabs will be used as prefix - */ - virtual void dump(RplWriter& writer,int indent) = 0; -public: - static void reset(); - static bool checkStatementList(RplASItem* list, RplParser& parser); - static int calcAsInteger(RplASItem* expr, RplVMThread& thread); - static bool calcAsBoolean(RplASItem* expr, RplVMThread& thread); -public: - RplASItemType nodeType() const; - const char* nameOfItemType() const; - - int flags() const; - void setFlags(int flags); - - bool typeCheck(RplASClass* clazz1, RplASClass* clazz2); - bool error(int location, RplParser& parser, const char* format, ...); - bool ensureError(RplParser& parser, const char* info); -protected: - unsigned int m_id:16; - RplASItemType m_nodeType:8; - int m_flags:5; - int m_dataType: 3; - const RplSourcePosition* m_position; -private: - static unsigned int m_nextId; -}; - -class RplASNode1; -class RplASCalculable -{ -public: - RplASCalculable(); -public: - virtual void calc(RplVMThread& thread) = 0; -public: - RplASClass* clazz() const; - void setClass(RplASClass* clazz); -protected: - RplASClass* m_class; -}; - -class RplStackFrame; -class RplASStorable : public RplASCalculable { -}; -class RplVMThread; -class RplASConstant : public RplASItem, public RplASCalculable -{ -public: - RplASConstant(); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); -public: - virtual void dump(RplWriter& writer,int indent); - RplASVariant& value(); -private: - RplASVariant m_value; -}; - - -class RplASNode1 : public RplASItem -{ -public: - RplASNode1(RplASItemType type); - virtual ~RplASNode1(); -public: - RplASItem* child() const; - void setChild(RplASItem* child); -public: - static void dumpStatements(RplWriter& writer, int indent, RplASItem* statements); -protected: - RplASItem* m_child; -}; - -class RplASNode2 : public RplASNode1 -{ -public: - RplASNode2(RplASItemType type); - virtual ~RplASNode2(); -public: - RplASItem* child2() const; - void setChild2(RplASItem* child2); - -protected: - RplASItem* m_child2; -}; - -class RplASNode3 : public RplASNode2 -{ -public: - RplASNode3(RplASItemType type); - virtual ~RplASNode3(); -public: - RplASItem* child3() const; - void setChild3(RplASItem* child3); - -protected: - RplASItem* m_child3; -}; - -class RplASNode4 : public RplASNode3 -{ -public: - RplASNode4(RplASItemType type); - virtual ~RplASNode4(); -public: - RplASItem* child4() const; - void setChild4(RplASItem* child4); - -protected: - RplASItem* m_child4; -}; - -class RplASNode5 : public RplASNode4 -{ -public: - RplASNode5(RplASItemType type); - virtual ~RplASNode5(); -public: - RplASItem*child5() const; - void setChild5(RplASItem* child5); -protected: - RplASItem* m_child5; -}; -class RplASNode6 : public RplASNode5 -{ -public: - RplASNode6(RplASItemType type); - virtual ~RplASNode6(); -public: - RplASItem*child6() const; - void setChild6(RplASItem* child5); -protected: - RplASItem* m_child6; -}; - -typedef QList RplASListOfVariants; -typedef QMap RplASMapOfVariants; - -class RplASListConstant : public RplASNode1, public RplASCalculable -{ -public: - RplASListConstant(); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); - -public: - virtual void dump(RplWriter& writer,int indent); - RplASVariant& value(); - RplASListOfVariants* list(); -private: - RplASVariant m_value; -}; -class RplASMapConstant : public RplASNode1, public RplASCalculable -{ -public: - RplASMapConstant(); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); -public: - virtual void dump(RplWriter& writer,int indent); - RplASVariant& value(); - RplASMapOfVariants* map(); -private: - RplASVariant m_value; -}; - -class RplSymbolSpace; -class RplASNamedValue : public RplASItem, public RplASStorable -{ - friend class RplASVarDefinition; -public: - 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, - /// the evaluation should be lazy - A_LAZY = 1<<4, - /// parameter of a method - A_PARAM = 1<<5, - /// a automatic variable in counted for loops: - A_LOOP = 1<<6 - }; - -public: - RplASNamedValue(RplASClass* clazz, RplSymbolSpace* space, - const QByteArray& name, int attributes); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); -public: - const QByteArray& name() const; - void setSymbolSpace(RplSymbolSpace* space, int variableNo); - void dump(RplWriter& writer, int indent); - RplSymbolSpace* symbolSpace() const; - int variableNo() const; - void setVariableNo(int variableNo); -protected: - QByteArray m_name; - int m_attributes; - RplSymbolSpace* m_symbolSpace; - int m_variableNo; -}; -class RplASConversion : public RplASNode1, public RplASCalculable { -public: - enum Conversion { - C_UNDEF, - C_INT_TO_FLOAT, - C_FLOAT_TO_INT, - C_BOOL_TO_INT, - C_BOOL_TO_FLOAT - }; - -public: - RplASConversion(RplASItem* expression); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); - virtual void dump(RplWriter& writer,int indent); -public: - static RplASConversion* tryConversion(RplASClass* expected, RplASItem* expr, - RplParser& parser, bool& isCorrect); - static Conversion findConversion(RplASClass* from, RplASClass* to); -private: - Conversion m_conversion; -}; - -class RplASIndexedValue : public RplASNode2, public RplASCalculable { -public: - RplASIndexedValue(); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); -public: - void dump(RplWriter& writer, int indent); -}; - -class RplASStatement -{ -public: - RplASStatement(); -public: - virtual int execute(RplVMThread& thread) = 0; -public: - static int executeStatementList(RplASItem* list, RplVMThread& thread); -}; - - -class RplASVarDefinition : public RplASNode3, public RplASStatement -{ -public: - RplASVarDefinition(); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); -public: - void dump(RplWriter& writer, int indent); - const QByteArray& name() const; - int endOfScope() const; - void setEndOfScope(int endOfScope); - RplASClass* clazz() const; -private: - /// the column of the blockend containing the definition. - /// if 0: end is end of method or end of class - /// Note: the source unit is stored in RplASItem::m_sourcePosition - int m_endOfScope; -}; - -class RplASExprStatement : public RplASNode2, public RplASStatement -{ -public: - RplASExprStatement(); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); -public: - void dump(RplWriter& writer, int indent); -}; - -class RplASUnaryOp : public RplASNode1, RplASCalculable -{ -public: - enum UnaryOp { - UOP_UNDEF, - UOP_PLUS, - UOP_MINUS_INT, - UOP_MINUS_FLOAT, - UOP_NOT_BOOL, - UOP_NOT_INT, - UOP_INC, - UOP_DEC - }; -public: - RplASUnaryOp(UnaryOp op, RplASItemType type); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); -public: - int getOperator() const; - void dump(RplWriter& writer, int indent); -public: - static const char* nameOfOp(UnaryOp op); -private: - UnaryOp m_operator; -}; -class RplASBinaryOp : public RplASNode2, public RplASCalculable -{ -public: - enum BinOperator { - BOP_UNDEF, - BOP_ASSIGN, - BOP_PLUS_ASSIGN, - BOP_MINUS_ASSIGN, - BOP_TIMES_ASSIGN, - BOP_DIV_ASSIGN, - BOP_MOD_ASSIGN, - BOP_POWER_ASSIGN, - BOP_LOG_OR_ASSIGN, - BOP_LOG_AND_ASSIGN, - BOP_LOG_XOR_ASSIGN, - BOP_BIT_OR_ASSIGN, - BOP_BIT_AND_ASSIGN, - BOP_BIT_XOR_ASSIGN, - BOP_LSHIFT_ASSIGN, - BOP_LOG_RSHIFT_ASSIGN, - BOP_ARTITH_RSHIFT_ASSIGN, - BOP_PLUS, - BOP_MINUS, - BOP_TIMES, - BOP_DIV, - BOP_MOD, - BOP_POWER, - BOP_LOG_OR, - BOP_LOG_AND, - BOP_LOG_XOR, - BOP_BIT_OR, - BOP_BIT_AND, - BOP_BIT_XOR, - BOP_LSHIFT, - BOP_LOG_RSHIFT, - BOP_ARTITH_RSHIFT, - BOP_EQ, - BOP_NE, - BOP_LE, - BOP_LT, - BOP_GE, - BOP_GT, - BOB_COUNT - }; -private: - inline bool isAssignment() const { - return m_operator >= BOP_ASSIGN - && m_operator <= BOP_ARTITH_RSHIFT_ASSIGN; - } -public: - RplASBinaryOp(); -public: - virtual void calc(RplVMThread& thread); - virtual bool check(RplParser& parser); -public: - BinOperator getOperator() const; - void setOperator(BinOperator op); - void dump(RplWriter& writer, int indent); -private: - void assign(RplVMThread& thread); -public: - static const char* nameOfOp(BinOperator op); -private: - BinOperator m_operator; -}; - -class RplASIf : public RplASNode4, public RplASStatement -{ -public: - RplASIf(); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); - virtual void dump(RplWriter& writer, int indent); -}; - -class RplASForIterated : public RplASNode4, public RplASStatement -{ -public: - RplASForIterated(RplASVarDefinition* variable); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); - virtual void dump(RplWriter& writer, int indent); -}; - -class RplASForCounted : public RplASNode6, public RplASStatement -{ -public: - RplASForCounted(RplASVarDefinition* variable); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); - virtual void dump(RplWriter& writer, int indent); -}; - -class RplASWhile : public RplASNode3, public RplASStatement -{ -public: - RplASWhile(); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); - virtual void dump(RplWriter& writer, int indent); -}; - -class RplASRepeat : public RplASNode3, public RplASStatement -{ -public: - RplASRepeat(); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); - virtual void dump(RplWriter& writer, int indent); -}; - -class RplASMethod; -class RplASMethodCall : public RplASNode3, public RplASStatement -{ -public: - RplASMethodCall(const QByteArray& name, RplASItem* parent); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); -public: - void dump(RplWriter& writer, int indent); - -public: - RplASMethod* method() const; - void setMethod(RplASMethod* method); - - RplASExprStatement* arg1() const; -private: - QByteArray m_name; - RplASMethod* m_method; -}; - -class RplParameter : RplASItem -{ -public: - RplParameter(); - virtual ~RplParameter(); -private: - QByteArray m_name; - RplASNamedValue* m_default; -}; - -class RplASField : public RplASNode1 -{ -public: - RplASField(const QByteArray& name); -public: - virtual bool check(RplParser& parser); -public: - void dump(RplWriter& writer, int indent); -private: - QByteArray m_name; -}; - - -class RplASClass; -class RplSymbolSpace; -class RplASMethod : public RplASNode2 -{ -public: - RplASMethod(const QByteArray& name, RplASTree& tree); -public: - virtual bool check(RplParser& parser); - virtual int execute(RplVMThread& thread); -public: - void dump(RplWriter& writer, int indent); - RplSymbolSpace* symbols() const; - void setSymbols(); - const QByteArray& name() const; - bool equalSignature(RplASMethod& other) const; - RplASMethod* sibling() const; - void setSibling(RplASMethod* sibling); - -private: - QByteArray m_name; - RplASClass* m_resultType; - RplSymbolSpace* m_symbols; - // chain over all overloaded methods (same name, other signature): - RplASMethod* m_sibling; - RplASTree& m_tree; -}; - -class RplASClass { -public: - typedef QMap MethodMap; -public: - RplASClass(const QByteArray& name, RplASTree& m_tree); - virtual ~RplASClass(); -public: - /** - * @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) - */ - virtual void* newValueInstance(void* source = NULL) const = 0; - /** - * @brief Destroys the given object. - * - * The object must be created by newValueInstance(). - * - * @param object object to destroy - */ - 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; - /** - * @brief Returns a string representation of an instance. - * - * @param object the object to convert - * @param maxLength the maximum length of the result (string) - * @return a string describing the object - */ - virtual QByteArray toString(void *object, int maxLength = 80) const = 0; -public: - const QByteArray& name() const; - virtual void dump(RplWriter& writer, int indent); - void setSymbols(); -protected: - QByteArray m_name; - RplSymbolSpace* m_symbols; - const RplASClass* m_superClass; - RplASTree& m_tree; -}; - -#include "rplexpr/rplasclasses.hpp" - -#include "rplparser.hpp" -class RplSymbolSpace; -class RplASTree -{ -public: - enum { - DMP_NONE, - DMP_GLOBALS = 1<<1, - DMP_MODULES = 1<<2, - DMP_SPACE_STACK = 1<<3, - DMP_SPACE_HEAP = 1<<4, - DMP_ALL = DMP_GLOBALS | DMP_MODULES | DMP_SPACE_STACK | DMP_SPACE_HEAP, - DMP_NO_GLOBALS = DMP_MODULES | DMP_SPACE_STACK | DMP_SPACE_HEAP - }; - typedef QMap SymbolSpaceMap; - typedef QList SymbolSpaceStack; -public: - RplASTree(); - ~RplASTree(); -public: - bool startModule(RplSourceUnitName name); - void finishModule(RplSourceUnitName name); - RplSymbolSpace* startClassOrMethod(const QByteArray& name, - RplSymbolSpace::SymbolSpaceType type); - void finishClassOrMethod(const QByteArray& name); - SymbolSpaceStack& symbolSpaces(); - RplSymbolSpace* currentSpace() const; - RplASClass* findClass(const QByteArray& name); - void clear(); - void dump(const char* filename, int flags = DMP_ALL, - const char* header = NULL); - RplSymbolSpace*findmodule(const QByteArray& name); - RplSourcePosition* copyPosition(); - RplByteStorage& store(); - -protected: - void init(); - void destroy(); -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; - RplByteStorage m_store; -}; - -#endif // RPLASTREE_HPP diff --git a/rplexpr/rplexpr.hpp b/rplexpr/rplexpr.hpp deleted file mode 100644 index 7b04db0..0000000 --- a/rplexpr/rplexpr.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 RPLEXPR_HPP -#define RPLEXPR_HPP - -#include -#include -#include -#include -#include -#include -#include - -#include "rplexpr/rplsource.hpp" -#include "rplexpr/rpllexer.hpp" -#include "rplexpr/rplastree.hpp" -#include "rplexpr/rplvm.hpp" -#include "rplexpr/rplparser.hpp" -#include "rplexpr/rplmfparser.hpp" - -#endif // RPLEXPR_HPP diff --git a/rplexpr/rpllexer.cpp b/rplexpr/rpllexer.cpp deleted file mode 100644 index 1366cf8..0000000 --- a/rplexpr/rpllexer.cpp +++ /dev/null @@ -1,1234 +0,0 @@ -/* - * 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. -*/ -/** @file - * @brief Configurable scanner, wich separates syntactic symbols from an input media. - */ -/** @file rplexpr/rpllexer.hpp - * @brief Definitions for a configurable lexical analyser. - */ - -#include "rplcore/rplcore.hpp" -#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 specific exception for the lexer. - * - */ - -/** - * @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. - */ -RplLexException::RplLexException(const RplSourcePosition& position, - const char* format, ...) : - RplException("") -{ - char buffer[64000]; - m_message = position.toString().toUtf8(); - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - 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 - */ -RplToken::RplToken(RplTokenType type) : - m_tokenType(type), - m_string(), - m_printableString() - // m_value -{ - memset(&m_value, 0, sizeof m_value); -} - -/** - * @brief Destructor. - */ -RplToken::~RplToken() -{ -} -/** - * @brief Copy constructor. - * - * @param source source to copy - */ -RplToken::RplToken(const RplToken& source) : - m_tokenType(source.m_tokenType), - m_string(source.m_string), - m_printableString(source.m_printableString), - m_value(source.m_value) -{ -} -/** - * @brief Assignment operator. - * - * @param source source to copy - * @return - */ -RplToken& RplToken::operator =(const RplToken& source) -{ - m_tokenType = source.m_tokenType; - m_string = source.m_string; - m_value = source.m_value; - return *this; -} - -/** - * @brief Returns the string representation of the instance - * @return a string representing the instance - */ -const QByteArray& RplToken::toString() -{ - return m_string; -} - -/** - * @brief Returns the integer value of the token - * - * Only relevant if a TOKEN_NUMBER. - * - * @return the value of the token as integer - */ -int RplToken::asInteger() const -{ - return (int) m_value.m_integer; -} - -/** - * @brief Returns the integer value of the token - * - * Only relevant if a TOKEN_NUMBER. - * - * @return the value of the token as unsigned integer (64 bit) - */ -quint64 RplToken::asUInt64() const -{ - return m_value.m_integer; -} - -/** - * @brief Returns the floating point value of the token - * - * Only relevant if a TOKEN_REAL. - * - * @return the value of the token as floating point value - */ -qreal RplToken::asReal() const -{ - return m_value.m_real; -} - -/** - * @brief Returns the floating point value of the token - * - * Only relevant if a TOKEN_NUMBER. - * - * @return the value of the token as floating point value - */ -const QByteArray& RplToken::rawString() const -{ - return m_printableString; -} -/** - * @brief Returns the id of the token. - * - * Ids are more handy than string, e.g. allowing switch statements. - * - * Only relevant for TOKEN_KEYWORD and TOKEN_OPERATOR. - * - * @return the id of the token - */ -int RplToken::id() const -{ - return m_value.m_id; -} -/** - * @brief Returns the token type. - * @return the token type - */ -RplTokenType RplToken::tokenType() const -{ - return m_tokenType; -} - -/** - * @brief Checks whether the instance has a given token type. - * - * @param expected the token type to compare - * - * @return true: the expected type is the current
- * false: otherwise - */ -bool RplToken::isTokenType(RplTokenType expected) const -{ - return m_tokenType == expected; -} - -/** - * @brief Checks whether the instance is a given operator. - * - * @param expected the expected operator - * @param alternative 0 or a second possibility - * - * @return true: the instance is an operator and the expected or the alternative
- * false: otherwise - */ -bool RplToken::isOperator(int expected, int alternative) const -{ - return m_tokenType == TOKEN_OPERATOR && (m_value.m_id == expected - || m_value.m_id == alternative); -} - -/** - * @brief Checks whether the instance is a given keyword. - * - * @param expected the expected keyword - * @param alternative 0 or a second possibility - * - * @return true: the instance is a keyword and the expected or the alternative
- * false: otherwise - */ - -bool RplToken::isKeyword(int expected, int alternative) const -{ - return m_tokenType == TOKEN_KEYWORD && (m_value.m_id == expected - || m_value.m_id == alternative); -} - -/** - * @brief Makes all members undefined. - */ -void RplToken::clear() -{ - m_string.clear(); - m_printableString.clear(); - m_tokenType = TOKEN_UNDEF; - m_value.m_integer = 0; -} - -/** - * @brief Returns whether the token is a capitalized id - * - * @return true: the token is an id and the first char is an upper case char
- * false: otherwise - */ -bool RplToken::isCapitalizedId() const -{ - bool rc = m_tokenType == TOKEN_ID && isupper(m_string.at(0)) - && (m_string.length() == 1 || islower(m_string.at(1))); - return rc; -} - -/** - * @brief Returns the description of the current token. - * - * @return a description of the instance - */ -QByteArray RplToken::dump() const -{ - QByteArray rc; - rc = nameOfType(m_tokenType); - rc.append(": ").append(this->asUtf8()); - return rc; -} -QByteArray RplToken::asUtf8() const -{ - char buffer[4096]; - buffer[0] = '\0'; - - switch(m_tokenType){ - case TOKEN_UNDEF: - break; - case TOKEN_STRING: - qsnprintf(buffer, sizeof buffer, "'%.*s'", int(sizeof buffer) - 1, - m_printableString.constData()); - break; - case TOKEN_NUMBER: - qsnprintf(buffer, sizeof buffer, "%lld", m_value.m_integer); - break; - case TOKEN_REAL: - qsnprintf(buffer, sizeof buffer, "%f", m_value.m_real); - break; - case TOKEN_KEYWORD: - case TOKEN_OPERATOR: - qsnprintf(buffer, sizeof buffer, "%lld", (int) m_value.m_id); - break; - case TOKEN_ID: - qsnprintf(buffer, sizeof buffer, "'%.*s'", int(sizeof buffer) - 1, - m_string.constData()); - break; - case TOKEN_COMMENT_REST_OF_LINE: - case TOKEN_COMMENT_START: - case TOKEN_COMMENT_END: - case TOKEN_SPACE: - case TOKEN_END_OF_SOURCE: - default: - break; - } - return buffer; -} -/** - * @brief Returns then name of a token type. - * @param type the type to convert - * @return the token type name - */ -const char* RplToken::nameOfType(RplTokenType type) -{ - const char* rc = "?"; - - switch(type){ - case TOKEN_UNDEF: - rc = "undef"; - break; - case TOKEN_STRING: - rc = "String"; - break; - case TOKEN_NUMBER: - rc = "Number"; - break; - case TOKEN_REAL: - rc = "Real"; - break; - case TOKEN_KEYWORD: - rc = "Keyword"; - break; - case TOKEN_OPERATOR: - rc = "Operator"; - break; - case TOKEN_ID: - rc = "Id"; - break; - case TOKEN_COMMENT_REST_OF_LINE: - rc = "Comment-1-line"; - break; - case TOKEN_COMMENT_START: - rc = "Comment-m-line"; - break; - case TOKEN_COMMENT_END: - rc = "end of comment"; - break; - case TOKEN_SPACE: - rc = "space"; - break; - case TOKEN_END_OF_SOURCE: - rc = "end of source"; - break; - default: - break; - } - return rc; -} - - -/** @class RplLexer rpllexer.hpp "rplexpr/rpllexer.hpp" - * - * @brief Implements a lexical analyser. - * - * A lexical analyser reads a text source and separates the tokens for a parser. - * Tokens are the smallest elements of a parsing process. - * - */ - -static void itemsToVector(const char* items, RplLexer::StringList& vector, - int firstCharFlag, int secondCharFlag, - int thirdCharFlag, int restCharFlag, - int charInfo[]) -{ - QByteArray array2(items); - QList list = array2.split(' '); - QList::iterator it; - int id = 0; - for (it = list.begin(); it < list.end(); it++){ - QByteArray& item2 = *it; - QByteArray item(item2); - id++; - item.append(' ').append(id % 256).append(id / 256); - vector.append(item); - unsigned char cc = item2.at(0); - if (cc < 128) - charInfo[cc] |= firstCharFlag; - if(item2.size() > 1){ - cc = item2.at(1); - if (cc < 128) - charInfo[cc] |= secondCharFlag; - } - if(item2.size() > 2){ - cc = item2.at(2); - if (cc < 128) - charInfo[cc] |= thirdCharFlag; - } - if(item2.size() > 3){ - const char* ptr = item2.constData() + 3; - while( (cc = *ptr++) != '\0'){ - if (cc < 128) - charInfo[cc] |= restCharFlag; - } - } - } - qSort(vector.begin(), vector.end(), qLess()); -} - -static void charClassToCharInfo(const char* charClass, int flag, - int charInfo[]) -{ - for (int ix = 0; charClass[ix] != '\0'; ix++){ - unsigned char cc = (unsigned char) charClass[ix]; - if (cc < 128) - charInfo[cc] |= flag; - if (charClass[ix+1] == '-'){ - unsigned char ubound = charClass[ix+2]; - if (ubound == '\0') - charInfo['-'] |= flag; - else if (cc >= ubound) - throw new RplException("wrong character class range: %c-%c (%s)", - cc, ubound, charClass); - else { - for (int ii = cc + 1; ii <= ubound; ii++){ - charInfo[ii] |= flag; - } - } - ix += 2; - } - } -} - -/** - * @brief Constructor. - * - * @param source the input source handler - * @param keywords a string with all keywords delimited by " ". - * Example: "if then else fi while do done" - * @param operators a string with the operators separated by blank or "\n". - * "\n" separates the operators with the same priority. - * Lower position means lower priority - * @param rightAssociatives - * the operators which are right associative - * @param comments a string with pairs of comment begin and end delimited - * by " ". The comment end can be "\n" for line end. - * Example: "/ * * / // \n" (ignore the blank in "* /") - * @param firstCharsId string with the characters which are allowed as first - * characters of an id - * @param restCharsId string with the characters which are allowed as non - * first characters of an id - * @param numericTypes bit mask of allowed numeric, - * e.g. NUMTYPE_DECIMAL | NUMTYPE_HEXADECIMAL - * @param stringFeatures bit mask of the string features, - * e.g. SF_QUOTE | SF_TICK - * @param storageFlags describes the things which should be stored, e.g. - * S_ORG_STRINGS | S_COMMENTS | S_BLANKS - */ -RplLexer::RplLexer(RplSource* source, - const char* keywords, - const char* operators, const char* rightAssociatives, - const char* comments, - const char* firstCharsId, const char* restCharsId, int numericTypes, - int stringFeatures, int storageFlags) : - m_source(source), - m_keywords(), - m_operators(), - m_commentStarts(), - m_commentEnds(), - //m_charInfo() - m_idFirstRare(), - m_idRestRare(), - m_numericTypes(numericTypes), - m_idRest2(), - m_currentToken(&m_token1), - m_waitingToken(NULL), - m_waitingToken2(NULL), - m_token1(TOKEN_UNDEF), - m_token2(TOKEN_UNDEF), - m_currentPosition(NULL), - m_waitingPosition1(NULL), - m_waitingPosition2(NULL), - m_maxTokenLength(64), - m_input(), - m_currentCol(0), - m_hasMoreInput(false), - m_stringFeatures(stringFeatures), - m_storageFlags(storageFlags), - // m_prioOfOp - // m_assocOfOp - #if defined (RPL_LEXER_TRACE) - m_trace(true), - #endif - m_opNames() -{ - memset(m_prioOfOp, 0, sizeof m_prioOfOp); - memset(m_assocOfOp, 0, sizeof m_assocOfOp); - - memset(m_charInfo, 0, sizeof m_charInfo); - itemsToVector(keywords, m_keywords, CC_FIRST_KEYWORD, CC_2nd_KEYWORD, - CC_3rd_KEYWORD, CC_REST_KEYWORD, m_charInfo); - prepareOperators(operators, rightAssociatives); - charClassToCharInfo(firstCharsId, CC_FIRST_ID, m_charInfo); - charClassToCharInfo(restCharsId, CC_REST_ID, m_charInfo); - initializeComments(comments); - m_input.reserve(m_maxTokenLength*2); -} -/** - * @brief Destructor. - */ -RplLexer::~RplLexer() -{ -} - -/** - * @brief Returns the count of blanks in a given range of a string. - * - * @param start pointer to the first char to check - * @param end pointer to the last char to check - * @return the count of blanks - */ -int countBlanks(const char* start, const char* end){ - int rc = 0; - while(start != end){ - if (*start++ == ' '){ - rc++; - } - } - return rc; -} - -/** - * @brief Stores the operators in the internal members - * - * @param operators a string with the operators separated by blank or '\n'. - * '\n' separates the operators with the same priority. - * Lower position means lower priority - */ -void RplLexer::prepareOperators(const char* operators, - const char* rightAssociatives){ - QByteArray op2(operators); - QByteArray rightAssociatives2(" "); - rightAssociatives2 += rightAssociatives; - op2.replace("\n", " "); - itemsToVector(op2.constData(), m_operators, CC_FIRST_OP, CC_2nd_OP, - CC_3rd_OP, CC_REST_OP, m_charInfo); - // m_operators is now sorted: - // test whether the successor of 1 char operators is starting with this char: - // if not this operator will be marked with CC_OP_1_ONLY: - for (int ix = 0; ix < m_operators.size() - 1; ix++){ - // the entries of m_operators end with ' ' and id: - if (m_operators.at(ix).size() == 1 + 2 - && m_operators.at(ix).at(0) != m_operators.at(ix+1).at(0)){ - int cc = (char) m_operators[ix].at(0); - m_charInfo[cc] |= CC_OP_1_ONLY; - } - - } - m_opNames.reserve(m_operators.size() + 1); - op2 = " " + op2; - m_opNames = op2.split(' '); - QByteArray rAssoc = QByteArray(" ") + rightAssociatives + " "; - for (int opId = m_opNames.size() - 1; opId >= 1; opId--){ - QByteArray item = " " + m_opNames[opId] + " "; - if (rAssoc.indexOf(item) >= 0) - m_assocOfOp[opId] = true; - } - const char* start = operators; - const char* end; - int prio = 0; - int endId = 0; - int startId = 1; - bool again = true; - while (again){ - if ( (end = strchr(start, '\n')) == NULL){ - end = strchr(start, '\0'); - again = false; - } - prio++; - endId = startId + countBlanks(start, end) + 1 - 1; - while(startId <= endId){ - m_prioOfOp[startId++] = prio; - } - start = end + 1; - } -} - -void RplLexer::initializeComments(const char* comments) -{ - if (comments != NULL) - { - QByteArray starters; - QByteArray comments2(comments); - int ix = comments2.indexOf(" "); - if (ix >= 0) - throw RplException("more than one blank between comment pair(s): col %d %s", - ix + 1, comments + ix); - // the index of m_commentEnds is the position number: we need a dummy entry: - m_commentEnds.append(""); - - QList items = comments2.split(' '); - QList::iterator it; - ix = 0; - for (it = items.begin(); it != items.end(); it++, ix++){ - if (ix % 2 == 0){ - if (ix > 0) - starters += " "; - starters += *it; - }else{ - m_commentEnds.append(*it); - } - } - if (ix % 2 != 0) - throw RplException("not only pairs in the comment list"); - itemsToVector(starters, m_commentStarts, CC_FIRST_COMMENT_START, - CC_2nd_COMMENT_START, CC_3rd_COMMENT_START, CC_REST_COMMENT_START, - m_charInfo); - } -} -/** - * @brief Searches the prefix of m_input in the vector. - * - * @param tokenLength the length of the prefix in m_input - * @param vector the vector to search. Each element contains the id - * as last entry - * @param id the id of the entry in the vector. Only set if found - * @return - */ -int RplLexer::findInVector(int tokenLength, const StringList& vector) -{ - int id = 0; - int lbound = 0; - int ubound = vector.size() - 1; - // binary search over the sorted vector: - while(lbound <= ubound){ - int half = (ubound + lbound) / 2; - int compareRc = 0; - int ix = 0; - const QByteArray& current = vector[half]; - // vector items end with ' ' and id (2 byte): - int currentLength = current.size() - 3; - while(ix < tokenLength && compareRc == 0){ - if (ix >= currentLength) - // current is shorter: - compareRc = 1; - else - compareRc = m_input.at(ix) - (int) current.at(ix); - ix++; - } - if (compareRc == 0 && current.at(ix) != ' ') - // token.size() < current.size(): - compareRc = -1; - if (compareRc < 0) - ubound = half - 1; - else if (compareRc > 0) - lbound = half + 1; - else { - id = current[currentLength + 1] + current[currentLength + 2] * 256; - break; - } - } - return id; -} -/** - * @brief Reads data until enough data are available for one token. - * - * Data will be read from the current source unit. - * If this unit does not contain more data the next source unit from the stack - * will be used until the stack is empty. - * - * @return false: no more input is available
- * true: data are available - */ -bool RplLexer::fillInput() -{ - if (m_hasMoreInput){ - if (m_input.size() < m_maxTokenLength){ - m_source->currentReader()->fillBuffer(m_maxTokenLength, m_input, - m_hasMoreInput); - } - } - - while (m_input.size() == 0 && m_source->currentReader() != NULL){ - if (m_source->currentReader()->nextLine(m_maxTokenLength, - m_input, m_hasMoreInput)){ - m_currentCol = 0; - } - } - return m_input.size() > 0; -} - -/** - * @brief Finds a token with an id: TOKEN_OP, TOKEN_KEYWORD, ... - * - * @post the token is removed from the input - * - * @param tokenType the token type - * @param flag2 the flag of the 2nd char - * @param names the vector with the names, sorted - * @return NULL: not found
- * otherwise: the token - */ -RplToken* RplLexer::findTokenWithId(RplTokenType tokenType, int flag2, - StringList& names) -{ - int length = 1; - int inputLength = m_input.size(); - int cc; - if (inputLength > 1){ - cc = m_input[1]; - if (cc < CHAR_INFO_SIZE && (m_charInfo[cc] & flag2)){ - length++; - if (inputLength > 2){ - cc = m_input[2]; - // the 3rd char flag is the "successor" of the 2nd char flag: - int flag = (flag2 << 1); - if (cc < CHAR_INFO_SIZE && (m_charInfo[cc] & flag)){ - length++; - // the rest char flag is the "successor" of the 3nd char flag: - flag <<= 1; - while (length < inputLength){ - cc = m_input[length]; - if (cc < CHAR_INFO_SIZE && (m_charInfo[cc] & flag)) - length++; - else - break; - } - } - } - } - } - RplToken* rc = NULL; - if (! (tokenType == TOKEN_KEYWORD && length < inputLength - && (cc = m_input[length]) < CHAR_INFO_SIZE - && (m_charInfo[cc] & CC_REST_ID))) { - int id; - // the length could be too long: the CC_2nd_.. flag could be ambigous - while( (id = findInVector(length, names)) <= 0){ - if (length == 1 || tokenType == TOKEN_KEYWORD){ - break; - } - length--; - } - if (id > 0){ - rc = m_currentToken; - rc->m_tokenType = tokenType; - rc->m_value.m_id = id; - if (tokenType == TOKEN_COMMENT_START - && (m_storageFlags & STORE_COMMENT) != 0) - rc->m_string.append(m_input.mid(0, length)); - m_input.remove(0, length); - m_currentCol += length; - } - } - return rc; -} - -/** - * @brief Converts the number into a token. - * - * @return the token with the number - */ -RplToken* RplLexer::scanNumber() -{ - int inputLength = m_input.size(); - int cc; - int length; - quint64 value = 0; - if ( (cc = m_input[0]) == '0' && inputLength > 1 - && (m_numericTypes & NUMTYPE_HEXADECIMAL) - && (m_input[1] == 'x' || m_input[1] == 'X')){ - length = RplString::lengthOfUInt64(m_input.constData() + 2, 16, &value); - if (length > 0) - length += 2; - else - throw RplException("invalid hexadecimal number: no digit behind 'x"); - } else if (cc == '0' && (m_numericTypes & NUMTYPE_OCTAL) - && inputLength > 1){ - length = 1; - while (length < inputLength){ - if ( (cc = m_input[length]) >= '0' && cc <= '7') - value = value * 8 + cc - '0'; - else if (cc >= '8' && cc <= '9') - throw RplLexException(*m_currentPosition, - "invalid octal digit: %c", cc); - else - break; - length++; - } - } else { - length = 1; - value = cc - '0'; - while (length < inputLength){ - if ( (cc = m_input[length]) >= '0' && cc <= '9') - value = value * 10 + cc - '0'; - else - break; - length++; - } - } - m_currentToken->m_value.m_integer = value; - m_currentToken->m_tokenType = TOKEN_NUMBER; - if (length + 1 < inputLength - && ((cc = m_input[length]) == '.' || toupper(cc) == 'E')){ - qreal realValue; - int realLength = RplString::lengthOfReal(m_input.constData(), &realValue); - if (realLength > length){ - m_currentToken->m_tokenType = TOKEN_REAL; - m_currentToken->m_value.m_real = realValue; - length = realLength; - } - } - m_input.remove(0, length); - m_currentCol += length; - return m_currentToken; -} - -/** - * @brief Reads a string into the internal data. - * @return the token with the string - */ -RplToken*RplLexer::scanString() -{ - int delim = m_input[0]; - int inputLength = m_input.size(); - int cc; - int length = 1; - m_currentToken->m_tokenType = TOKEN_STRING; - m_currentToken->m_value.m_id = delim; - bool again = false; - do { - while(length < inputLength && (cc = m_input[length]) != delim){ - length++; - if (cc != '\\' - || (m_stringFeatures - & (SF_C_ESCAPING | SF_C_HEX_CHARS | SF_C_SPECIAL)) == 0){ - m_currentToken->m_string.append(QChar(cc)); - } else { - if (length >= inputLength) - throw RplLexException(*m_currentPosition, - "backslash without following character"); - cc = m_input[length++]; - if ( (m_stringFeatures & SF_C_HEX_CHARS) && toupper(cc) == 'X'){ - if (length >= inputLength) - throw RplLexException(*m_currentPosition, - "missing hexadecimal digit behind \\x"); - cc = m_input[length++]; - int hexVal = RplQString::valueOfHexDigit(cc); - if (hexVal < 0) - throw RplLexException(*m_currentPosition, - "not a hexadecimal digit behind \\x: %lc", - QChar(cc)); - if (length < inputLength){ - cc = m_input[length]; - int nibble = RplQString::valueOfHexDigit(cc); - if (nibble >= 0){ - length++; - hexVal = hexVal * 16 + nibble; - } - } - m_currentToken->m_string.append(QChar(hexVal)); - } else if ( (m_stringFeatures & SF_C_SPECIAL)){ - switch(cc){ - case 'r': - cc = '\r'; - break; - case 'n': - cc = '\n'; - break; - case 't': - cc = '\t'; - break; - case 'a': - cc = '\a'; - break; - case 'v': - cc = '\v'; - break; - case 'f': - cc = '\f'; - break; - default: - break; - } - m_currentToken->m_string.append(QChar(cc)); - } else { - m_currentToken->m_string.append(QChar(cc)); - } - } - } - if (cc == delim){ - length++; - } - if ( (m_stringFeatures & SF_DOUBLE_DELIM) && length < inputLength - && m_input[length] == (char) delim){ - m_currentToken->m_printableString.append(delim); - length++; - again = true; - } - } - while(again); - if (m_storageFlags & STORE_ORG_STRING) - m_currentToken->m_printableString.append(m_input.mid(0, length)); - m_input.remove(0, length); - m_currentCol += length; - return m_currentToken; -} - -/** - * @brief Reads a comment into the internal data. - * - * precondition: the current token is prepared yet - */ -void RplLexer::scanComment() -{ - int inputLength = m_input.size(); - int length = 1; - QByteArray& commentEnd = m_commentEnds[m_currentToken->id()]; - int ix; - if (commentEnd[0] == '\n'){ - // single line comment: - if (m_storageFlags & STORE_COMMENT) - m_currentToken->m_string.append(m_input); - length = inputLength; - } else { - // multiline comment: - while( (ix = m_input.indexOf(commentEnd)) < 0){ - if (m_storageFlags & STORE_COMMENT) - m_currentToken->m_string.append(m_input); - m_input.clear(); - if (! fillInput()) - throw RplLexException(*m_currentPosition, - "comment end not found"); - } - length = ix + commentEnd.size(); - if (m_storageFlags & STORE_COMMENT) - m_currentToken->m_string - .append(m_input.mid(0, length)); - } - m_input.remove(0, length); - m_currentCol += length; -} -#if defined (RPL_LEXER_TRACE) -bool RplLexer::trace() const -{ - return m_trace; -} - -void RplLexer::setTrace(bool trace) -{ - m_trace = trace; -} -#endif -/** - * @brief Returns the last read token. - * - * @return the current token - */ -RplToken* RplLexer::currentToken() const -{ - return m_currentToken; -} - -/** - * @brief Returns the current position. - * - * @return the current source code position - */ -const RplSourcePosition* RplLexer::currentPosition() const -{ - return m_currentPosition; -} - -/** - * @brief Returns the next token. - * - * @return the next token - */ -RplToken* RplLexer::nextToken() -{ - RplToken* rc = NULL; - int ix; - if (m_waitingToken != NULL){ - rc = m_currentToken = m_waitingToken; - m_waitingToken = m_waitingToken2; - m_waitingToken2 = NULL; - m_currentPosition = m_waitingPosition1; - m_waitingPosition1 = m_waitingPosition2; - m_waitingPosition2 = NULL; - } else { - m_currentToken->clear(); - RplReader* reader = m_source->currentReader(); - if (reader == NULL) - m_currentToken->m_tokenType = TOKEN_END_OF_SOURCE; - else { - m_waitingPosition2 = m_waitingPosition1; - m_waitingPosition1 = m_currentPosition; - m_currentPosition = m_source->newPosition(m_currentCol); - if (! fillInput()){ - m_currentToken->m_tokenType = TOKEN_END_OF_SOURCE; - } else { - int cc = m_input.at(0); - if (isspace(cc)){ - //waitingPosition = m_currentPosition; - m_currentToken->m_tokenType = TOKEN_SPACE; - ix = 1; - while(ix < m_input.size() && isspace(m_input.at(ix))) - ix++; - if (m_storageFlags & STORE_BLANK){ - m_currentToken->m_string.append(m_input.mid(0, ix)); - } - m_currentCol += ix; - m_input.remove(0, ix); - rc = m_currentToken; - } else if (isdigit(cc)){ - rc = scanNumber(); - } else if ( (cc == '"' && (m_stringFeatures & SF_QUOTE) != 0) - || (cc == '\'' && (m_stringFeatures & SF_TICK) != 0)){ - rc = scanString(); - } else { - if (cc >= CHAR_INFO_SIZE) - throw RplLexException(*m_currentPosition, - "no lexical symbol can start with this char: %lc", - cc); - else - { - if (rc == NULL && (m_charInfo[cc] & CC_FIRST_COMMENT_START)){ - rc = findTokenWithId(TOKEN_COMMENT_START, - CC_2nd_COMMENT_START, m_commentStarts); - if (rc != NULL) - scanComment(); - //waitingPosition = m_currentPosition; - } - - if (rc == NULL && (m_charInfo[cc] & CC_FIRST_OP)){ - if ( (m_charInfo[cc] & CC_OP_1_ONLY) == 0){ - rc = findTokenWithId(TOKEN_OPERATOR, - CC_2nd_OP, m_operators); - } else { - rc = m_currentToken; - rc->m_tokenType = TOKEN_OPERATOR; - rc->m_value.m_id = findInVector(1, m_operators); - m_input.remove(0, 1); - m_currentCol += 1; - } - } - if (rc == NULL && (m_charInfo[cc] & CC_FIRST_KEYWORD)){ - rc = findTokenWithId(TOKEN_KEYWORD, - CC_2nd_KEYWORD, m_keywords); - } - if (rc == NULL && (m_charInfo[cc] & CC_FIRST_ID)){ - int length = 1; - while(length < m_input.size() - && (cc = m_input[length]) < CHAR_INFO_SIZE - && (m_charInfo[cc] & CC_REST_ID) != 0) - length++; - rc = m_currentToken; - rc->m_tokenType = TOKEN_ID; - rc->m_string.append(m_input.mid(0, length)); - m_input.remove(0, length); - m_currentCol += length; - } - - } - } - } - } - } - if (rc == NULL || rc->tokenType() == TOKEN_UNDEF){ - if (m_input.size() == 0){ - rc = m_currentToken; - rc->m_tokenType = TOKEN_END_OF_SOURCE; - } else { - QByteArray symbol = m_input.mid(0, qMin(20, m_input.size()-1)); - throw RplLexException(*m_currentPosition, - "unknown lexical symbol: %s", symbol.constData()); - } - } -#if defined (RPL_LEXER_TRACE) - if (m_trace){ - char buffer[256]; - printf("token: %s pos: %s\n", m_currentToken->dump().constData(), - m_currentPosition->utf8(buffer, sizeof buffer)); - if (strstr(buffer, "0:28") != NULL) - buffer[0] = 0; - } -#endif - return rc; -} -/** - * @brief Reverses the last nextToken(). - * - * 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_waitingPosition1 = m_currentPosition; -#if defined (RPL_LEXER_TRACE) - if (m_trace){ - char buffer[256]; - printf("undo last token: waiting-token: %s pos: %s\n", - m_waitingToken->dump().constData(), - m_waitingPosition1->utf8(buffer, sizeof buffer)); - if (strcmp(buffer, ":2:6") == 0) - buffer[0] = 0; - } -#endif -} - -/** - * @brief Reverses the last nextToken(). - * - * Makes that nextToken() returns the current token again. - */ -void RplLexer::undoLastToken2() -{ - m_waitingToken2 = m_currentToken; - m_waitingToken = m_currentToken == &m_token1 ? &m_token2 : &m_token1; - m_waitingPosition2 = m_waitingPosition1; - m_waitingPosition1 = m_currentPosition; -} - -/** - * @brief Prevents the current token from data loss. - * - * Usage: - *

- * token1 = nextToken();
- * saveLastToken();
- * token2 = nextToken();
- * 
- * Then token1 and token2 contains the wanted content. - */ -void RplLexer::saveLastToken() -{ - if (m_waitingToken == NULL) - m_currentToken = m_currentToken == &m_token1 ? &m_token2 : &m_token1; -} - -/** - * @brief Returns the next relevant token, but the token remains "unread". - * - * @return the next token which is not a space/comment - */ -RplToken*RplLexer::peekNonSpaceToken() -{ - RplToken* token = nextNonSpaceToken(); - undoLastToken(); - return token; -} -/** - * @brief Returns the maximal length of a token - * @return the maximal length of a token - */ -size_t RplLexer::maxTokenLength() const -{ - return m_maxTokenLength; -} - -/** - * @brief Sets the maximal length of a token - * - * @param maxTokenLength the new maximal length of a token - */ -void RplLexer::setMaxTokenLength(size_t maxTokenLength) -{ - m_maxTokenLength = maxTokenLength; -} - -/** - * @brief RplLexer::nextNonSpaceToken - * @return - */ -RplToken* RplLexer::nextNonSpaceToken() -{ - RplToken* rc = NULL; - RplTokenType type; - do{ - rc = nextToken(); - } while( (type = m_currentToken->tokenType()) == TOKEN_SPACE - || type == TOKEN_COMMENT_START || type == TOKEN_COMMENT_END - || type == TOKEN_COMMENT_REST_OF_LINE); - return rc; -} - -/** - * @brief Prepares a given source unit for reading. - * - * Saves the current source position onto the top of stack. - * Pushes the source unit onto the top of stack. - * - * Precondition: the unit must be known by exactly one reader - * - * @param unit the new source unit - */ -void RplLexer::startUnit(RplSourceUnitName unit) -{ - m_source->startUnit(unit, *m_currentPosition); -} -/** - * @brief Returns the source of the instance. - * - * @return the source of the instance - */ -RplSource* RplLexer::source() -{ - return m_source; -} - -/** - * @brief Returns the priority of a given operator. - * - * @param op the operator - * @return the priority of the op - */ -int RplLexer::prioOfOp(int op) const -{ - int rc = op > 0 && (unsigned) op < sizeof m_prioOfOp / sizeof m_prioOfOp[0] - ? m_prioOfOp[op] : 0; - return rc; -} - -/** - * @brief Returns the name of an operator. - * - * @param op the operator id - * @return the name of the operator - */ -const QByteArray&RplLexer::nameOfOp(int op) const -{ - const QByteArray& rc = m_opNames.at(op); - return rc; -} - -/** - * @brief Returns whether an operator is right associative - * @param op op to test - * @return true: the operator is right associative
- * false: otherwise - */ -bool RplLexer::isRightAssociative(int op) const -{ - bool rc = false; - if (op >= 0 && (unsigned) op < sizeof m_assocOfOp / sizeof m_assocOfOp[0]){ - rc = m_assocOfOp[op]; - } - return rc; -} - - diff --git a/rplexpr/rpllexer.hpp b/rplexpr/rpllexer.hpp deleted file mode 100644 index cf27d9b..0000000 --- a/rplexpr/rpllexer.hpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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 RPLLEXER_HPP -#define RPLLEXER_HPP - -//#define RPL_LEXER_TRACE - -enum RplTokenType { - TOKEN_UNDEF, - TOKEN_STRING, - TOKEN_NUMBER, - TOKEN_REAL, - TOKEN_KEYWORD, - TOKEN_OPERATOR, - TOKEN_ID, - TOKEN_COMMENT_REST_OF_LINE, - TOKEN_COMMENT_START, - TOKEN_COMMENT_END, - TOKEN_SPACE, - TOKEN_END_OF_SOURCE, - TOKEN_COUNT -}; - -class RplLexException : public RplException { -public: - RplLexException(const RplSourcePosition& position, const char* message, ...); -}; - -class RplLexer; - -class RplToken { -public: - RplToken(RplTokenType type); - ~RplToken(); - RplToken(const RplToken& source); - RplToken& operator =(const RplToken& source); -public: - friend class RplLexer; - const QByteArray& toString(); - bool isInteger(); - int asInteger() const; - quint64 asUInt64() const; - qreal asReal() const; - const QByteArray& rawString() const; - int id() const; - RplTokenType tokenType() const; - bool isTokenType(RplTokenType expected) const; - bool isOperator(int expected, int alternative = 0) const; - bool isKeyword(int expected, int alternative = 0) const; - void clear(); - bool isCapitalizedId() const; - QByteArray dump() const; - static const char* nameOfType(RplTokenType type); - QByteArray asUtf8() const; -protected: - RplTokenType m_tokenType; - QByteArray m_string; - // only for TOKEN_STRING: copy from source but with escaped chars like "\\n" - QByteArray m_printableString; - union { - // only for TOKEN_KEYWORD and TOKEN_OPERATOR - int m_id; - quint64 m_integer; - qreal m_real; - } m_value; -}; - - -class RplSource; -class RplLexer -{ -public: - typedef QList StringList; - enum NumericType { - NUMTYPE_UNDEF, - NUMTYPE_DECIMAL = 1 << 0, - NUMTYPE_OCTAL = 1 << 1, - NUMTYPE_HEXADECIMAL = 1 << 2, - NUMTYPE_FLOAT = 1 << 3, - /// - NUMTYPE_ALL_INTEGER = NUMTYPE_DECIMAL | NUMTYPE_OCTAL |NUMTYPE_HEXADECIMAL, - NUMTYPE_ALL = NUMTYPE_ALL_INTEGER | NUMTYPE_FLOAT - }; - enum CharClassTag { - CC_UNDEF = 0, - /// this char is possible as first char of an id - CC_FIRST_ID = 1 << 0, - /// this char is possible as 2nd char of an id - CC_2nd_ID = 1 << 1, - /// this char is possible as 3rd char of an id - CC_3rd_ID = 1 << 2, - /// this char is possible as 4th... char of an id - CC_REST_ID = 1 << 3, - /// this char can start a comment - CC_FIRST_COMMENT_START = 1 << 4, - /// this char can be the 2nd char of a comment start - CC_2nd_COMMENT_START = 1 << 5, - /// this char can be the 3rd char of a comment start - CC_3rd_COMMENT_START = 1 << 6, - /// this char can be the 4th ... of a comment start - CC_REST_COMMENT_START = 1 << 7, - /// this char can start a keyword - CC_FIRST_KEYWORD = 1 << 8, - /// this char can be the 2nd char of a keyword - CC_2nd_KEYWORD = 1 << 9, - /// this char can be the 3rd char of a keyword - CC_3rd_KEYWORD = 1 << 10, - /// this char can be the 4th... char of a keyword - CC_REST_KEYWORD = 1 << 11, - /// this char can be the 1st char of an operator - CC_FIRST_OP = 1 << 12, - /// this char can be the 2nd char of an operator - CC_2nd_OP = 1 << 13, - /// this char can be the 3rd char of an operator - CC_3rd_OP = 1 << 14, - /// this char can be the 4th... char of an operator - CC_REST_OP = 1 << 15, - /// there is an operator with exactly this char - /// and there is no other operator starting with this char - CC_OP_1_ONLY = 1 << 16 - }; - enum StringFeatures { - SF_UNDEF, - /// ' can be a string delimiter - SF_TICK = 1 << 1, - /// " can be a string delimiter - SF_QUOTE = 1 << 2, - /// character escaping like in C: "\x" is "x" - SF_C_ESCAPING = 1 << 3, - /// 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, - /// A delimiter inside a string must be doubled (like in Pascal) - SF_DOUBLE_DELIM = 1 << 6, - // Redefinitions for better reading: - SF_LIKE_C = SF_TICK | SF_QUOTE | SF_C_ESCAPING | SF_C_SPECIAL - | SF_C_HEX_CHARS - }; - enum StorageFlags { - S_UNDEF, - /// the original string will be stored in m_string too - /// (not only m_rawString) - STORE_ORG_STRING = 1 << 1, - /// comments will be stored in m_string - STORE_COMMENT = 1 << 2, - /// blanks will be stored in m_string - STORE_BLANK = 1 << 3, - /// redefinitions for better reading: - STORE_NOTHING = 0, - STORE_ALL = STORE_ORG_STRING | STORE_COMMENT | STORE_BLANK - }; - -public: - RplLexer(RplSource* source, - const char* keywords, - const char* operators, - const char* rightAssociatives, - const char* comments, - const char* firstCharsId = "a-zA-Z_", - const char* restCharsId = "a-zA-Z0-9_", - int numericTypes = NUMTYPE_DECIMAL | NUMTYPE_HEXADECIMAL | NUMTYPE_FLOAT, - int stringFeatures = SF_TICK | SF_QUOTE | SF_C_ESCAPING | SF_C_SPECIAL - | SF_C_HEX_CHARS, - int storageFlags = STORE_NOTHING); - virtual ~RplLexer(); -public: - RplToken* nextToken(); - void undoLastToken(); - void undoLastToken2(); - void saveLastToken(); - RplToken* peekNonSpaceToken(); - RplToken* nextNonSpaceToken(); - size_t maxTokenLength() const; - void setMaxTokenLength(size_t maxTokenLength); - void startUnit(RplSourceUnitName unit); - RplSource* source(); - int prioOfOp(int op) const; - const QByteArray& nameOfOp(int op) const; - bool isRightAssociative(int op) const; - const RplSourcePosition* currentPosition() const; - RplToken* currentToken() const; -#if defined RPL_LEXER_TRACE - bool trace() const; - void setTrace(bool trace); -#endif -private: - void prepareOperators(const char* operators, const char* rightAssociatives); - void initializeComments(const char* comments); - bool fillInput(); - int findInVector(int tokenLength, const StringList& vector); - RplToken* findTokenWithId(RplTokenType tokenType, int flag2, - StringList& names); - RplToken* scanNumber(); - RplToken* scanString(); - void scanComment(); -protected: - RplSource* m_source; - /// sorted, string ends with the id of the keyword - StringList m_keywords; - // sorted, string ends with the id of the operator - StringList m_operators; - // sorted, each entry ends with the id of the comment start - StringList m_commentStarts; - // index: id content: comment_end - 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 - QByteArray m_idFirstRare; - // index: ord(char) content: chr(ix) can be the non first char of an id - QByteArray m_idRestRare; - // a list of QChars with ord(cc) > 127 and which can be the first char - int m_numericTypes; - QByteArray m_idRest2; - RplToken* m_currentToken; - RplToken* m_waitingToken; - RplToken* m_waitingToken2; - RplToken m_token1; - RplToken m_token2; - const RplSourcePosition* m_currentPosition; - const RplSourcePosition* m_waitingPosition1; - const RplSourcePosition* m_waitingPosition2; - int m_maxTokenLength; - QByteArray m_input; - int m_currentCol; - bool m_hasMoreInput; - int m_stringFeatures; - int m_storageFlags; - /// priority of the operators: index: id of the operator. content: prio - char m_prioOfOp[128]; - char m_assocOfOp[128]; - QList m_opNames; -#if defined (RPL_LEXER_TRACE) - bool m_trace; -#endif -}; - - -#endif // RPLLEXER_HPP diff --git a/rplexpr/rplmfparser.cpp b/rplexpr/rplmfparser.cpp deleted file mode 100644 index 9f3f5ec..0000000 --- a/rplexpr/rplmfparser.cpp +++ /dev/null @@ -1,1286 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief A parser for the language ML. - */ -/** @file - * @brief Definition for a parser for the language ML. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" - -enum MFLocations{ - L_PARSE_OPERAND_RPARENTH = 2001, - L_PARSE_OPERAND_RPARENTH_INFO, - L_TERM_WRONG_STRING, - L_TERM_WRONG_NUMBER, - L_PARSE_OPERAND_WRONG = 2005, - L_DEFINITION_NO_ID, - L_DEFINITION_WRONG_ID, - L_DEFINITION_UNKNOWN_CLASS, - L_DEFINITION_MISSING_ID, - L_DEFINITION_NO_OP = 2010, - L_DEFINITION_NO_SEMICOLON, - L_PARSE_IF_NO_THEN, - L_PARSE_IF_NO_ELSE, - L_PARSE_IF_NO_FI, - L_PARSE_WHILE_NO_DO = 2015, - L_PARSE_WHILE_NO_OD, - L_PARSE_REPEAT_NO_UNTIL, - L_PARSE_REPEAT_NO_SEMI, - L_PARSE_BODY_WRONG_ITEM, - L_PARSE_FOR_NO_TO = 2020, - L_PARSE_LIST_NO_COMMA, - L_PARSE_MAP_BOOL, - L_PARSE_MAP_NONE, - L_PARSE_MAP_NUMERIC, - L_PARSE_MAP_EXPR = 2025, - L_PARSE_MAP_EXPR2, - L_PARSE_MAP_NO_COLON, - L_PARSE_MAP_NO_COMMA, - L_PARSE_OPERAND_NOT_OPERAND = 2030, - L_PARSE_BODY_NO_START, - L_PARSE_OPERAND_NO_BRACKET, - L_PARSE_ARGS_NO_COMMA_OR_PARENT, - L_PARSE_OPERAND_NO_FIELD2, - L_PARSE_OPERAND_NO_BRACKET2 = 2035, - L_PARSE_OPERAND_NO_FIELD, - L_PARSE_METH_NO_CLASS, - L_PARSE_METH_NO_CLASS2, - L_PARSE_METH_NO_NAME, - L_PARSE_METH_NO_NAME2 = 2040, - L_PARSE_METH_NO_LPARENTH, - L_PARSE_METH_NO_COLON, - L_PARSE_PARAMLIST_NO_PARENTH, - L_PARSE_PARAMLIST_NO_PARENTH2, - L_PARSE_METH_NO_END = 2045, - L_PARSE_METH_NO_END2, - L_PARSE_VAR_DEF_ALREADY_DEFINED, - L_PARSE_VAR_DEF_ALREADY_DEFINED2, - L_PARSE_CLASS_NO_NAME, - L_PARSE_CLASS_LOWERCASE = 2050, - L_PARSE_CLASS_ALREADY_DEFINED, - L_PARSE_CLASS_ALREADY_DEFINED2 - -}; - -/** @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, RplASTree& abstractSyntaxTree) : - RplParser(m_lexer, abstractSyntaxTree), - m_lexer(&source, - MF_KEYWORDS, MF_OPERATORS, MF_RIGHT_ASSOCIATIVES, - "/* */ // \n", - "a-zA-Z_", "a-zA-Z0-9_", - RplLexer::NUMTYPE_ALL, RplLexer::SF_LIKE_C) -{ -} - -/** - * @brief Parses an if statement. - */ -RplASItem* RplMFParser::parseIf() -{ - RplASIf* rc = new RplASIf(); - rc->setPosition(m_lexer.currentPosition()); - - RplASItem* condition = parseExpr(0); - if (! m_lexer.currentToken()->isKeyword(K_THEN)) - syntaxError(L_PARSE_IF_NO_THEN, "'then' expected"); - rc->setChild2(condition); - RplASItem* body = parseBody(K_ELSE, K_FI); - rc->setChild3(body); - if (! m_lexer.currentToken()->isKeyword(K_ELSE, K_FI)) - syntaxError(L_PARSE_IF_NO_ELSE, "'else' or 'fi' expected"); - if ( m_lexer.currentToken()->isKeyword(K_ELSE)){ - RplASItem* body = parseBody(K_FI); - rc->setChild4(body); - } - if (! m_lexer.currentToken()->isKeyword(K_FI)) - syntaxError(L_PARSE_IF_NO_FI, "'fi' expected"); - m_lexer.nextNonSpaceToken(); - return rc; -} -/** - * @brief Parses a while statement. - */ - -RplASItem* RplMFParser::parseWhile() -{ - RplASWhile* rc = new RplASWhile(); - rc->setPosition(m_lexer.currentPosition()); - - RplASItem* condition = parseExpr(0); - if (! m_lexer.currentToken()->isKeyword(K_DO)) - syntaxError(L_PARSE_WHILE_NO_DO, "'do' expected"); - rc->setChild2(condition); - RplASItem* body = parseBody(K_OD); - rc->setChild3(body); - if (! m_lexer.currentToken()->isKeyword(K_OD)) - syntaxError(L_PARSE_WHILE_NO_OD, "'od' expected"); - m_lexer.nextNonSpaceToken(); - return rc; -} - -/** - * @brief Parses a repeat statement. - */ -RplASItem* RplMFParser::parseRepeat() -{ - RplASRepeat* rc = new RplASRepeat(); - rc->setPosition(m_lexer.currentPosition()); - - RplASItem* body = parseBody(K_UNTIL); - rc->setChild3(body); - if (! m_lexer.currentToken()->isKeyword(K_UNTIL)) - syntaxError(L_PARSE_REPEAT_NO_UNTIL, "'until' expected"); - - RplASItem* condition = parseExpr(0); - if (! m_lexer.currentToken()->isOperator(O_SEMICOLON)) - syntaxError(L_PARSE_REPEAT_NO_SEMI, "';' expected"); - rc->setChild2(condition); - m_lexer.nextNonSpaceToken(); - return rc; -} - -/** - * @brief Creates a variable definition for a builtin variable. - * @param var the basic variable data - * @return - */ -RplASVarDefinition* RplMFParser::buildVarDef(RplASNamedValue* var) -{ - RplASVarDefinition* rc = new RplASVarDefinition(); - rc->setPosition(var->position()); - rc->setChild2(var); - RplSymbolSpace* symbols = m_tree.currentSpace(); - int varNo; - symbols->addVariable(rc, varNo); - var->setVariableNo(varNo); - return rc; -} - -/** - * @brief Parses a for statement. - * - * Syntax: - * for [ VAR ] [ from START_EXPR ] to END_EXPR [ step STEP_EXPR ] do - * BODY od - * - * for VAR in ITERABLE_EXPR do BODY od - * - * @post the token behind the do is read - * @return the abstract syntax tree of the for statement - */ -RplASItem* RplMFParser::parseFor() -{ - int builtinVars = 1; - RplASNode2* rc = NULL; - const RplSourcePosition* startPosition = m_lexer.currentPosition(); - RplToken* token = m_lexer.nextNonSpaceToken(); - RplASNamedValue* var = NULL; - if (token->isTokenType(TOKEN_ID)){ - var = new RplASNamedValue(RplASInteger::m_instance, - m_tree.currentSpace(), token->toString(), - RplASNamedValue::A_LOOP); - var->setPosition(m_lexer.currentPosition()); - token = m_lexer.nextNonSpaceToken(); - } - if (token->isKeyword(K_IN)){ - RplASVarDefinition* varDef = buildVarDef(var); - RplASForIterated* node = new RplASForIterated(varDef); - rc = node; - node->setPosition(startPosition); - node->setChild3(var); - RplASItem* iterable = parseExpr(0); - node->setChild4(iterable); - } else { - if (var == NULL){ - char name[32]; - // Build a unique name inside the scope: - qsnprintf(name, sizeof name, "$%d_%d", startPosition->lineNo(), - startPosition->column()); - var = new RplASNamedValue(RplASInteger::m_instance, - m_tree.currentSpace(), name, - RplASNamedValue::A_LOOP); - var->setPosition(startPosition); - } - RplASVarDefinition* varDef = buildVarDef(var); - RplASForCounted* node = new RplASForCounted(varDef); - rc = node; - node->setPosition(startPosition); - node->setChild3(var); - if (token->isKeyword(K_FROM)){ - node->setChild4(parseExpr(0)); - } - if (! m_lexer.currentToken()->isKeyword(K_TO)){ - syntaxError(L_PARSE_FOR_NO_TO, "'to' expected"); - } - node->setChild5(parseExpr(0)); - if (m_lexer.currentToken()->isKeyword(K_STEP)){ - node->setChild6(parseExpr(0)); - } - } - if (! m_lexer.currentToken()->isKeyword(K_DO)) - syntaxError(L_PARSE_FOR_NO_TO, "'to' expected"); - rc->setChild2(parseBody(K_OD, K_UNDEF, builtinVars)); - m_lexer.nextNonSpaceToken(); - return rc; -} - -/** - * @brief Parses a variable definition. - * - * Syntax: - * Variable: { "const" | "lazy" }* TYPE id [ = EXPR ] [ ";" ] - * Parameter: { "const" | "lazy" }* TYPE id [ = EXPR ] { "," | ")" } - * - * @pre first token of the definition is read - * @post token behind the definition is read: ';', ',', ')' - * @param attribute attribute of the variable: A_PARAM... - * @return a variable/parameter definition - */ -RplASVarDefinition* RplMFParser::parseVarDefinition( - RplASNamedValue::Attributes attribute) -{ - int attributes = attribute; - RplToken* token = m_lexer.currentToken(); - while(token->isKeyword(K_CONST, K_LAZY)){ - switch(token->id()){ - case K_CONST: - attributes += RplASNamedValue::A_CONST; - break; - case K_LAZY: - attributes += RplASNamedValue::A_LAZY; - break; - default: - break; - } - token = m_lexer.nextNonSpaceToken(); - } - if (! token->isTokenType(TOKEN_ID)) - syntaxError(L_DEFINITION_NO_ID, "class name expected, but no id found"); - if (! token->isCapitalizedId()) - syntaxError(L_DEFINITION_WRONG_ID, - "a class name must start with an upper case character"); - RplASClass* clazz = m_tree.currentSpace()->findClass(token->toString()); - if (clazz == NULL) - syntaxError(L_DEFINITION_UNKNOWN_CLASS, "unknown class"); - token = m_lexer.nextNonSpaceToken(); - if (! token->isTokenType(TOKEN_ID)) - syntaxError(L_DEFINITION_MISSING_ID, "variable name expected"); - RplSymbolSpace* symbols = m_tree.currentSpace(); - // freed in the destructor of the nodes: - RplASNamedValue* namedValue = new RplASNamedValue(clazz, symbols, - token->toString(), - attributes); - namedValue->setPosition(m_lexer.currentPosition()); - RplASVarDefinition* rc = new RplASVarDefinition(); - rc->setPosition(m_lexer.currentPosition()); - rc->setChild2(namedValue); - token = m_lexer.nextNonSpaceToken(); - if (token->id() == O_ASSIGN){ - RplASItem* value = parseExpr(0); - rc->setChild3(value); - token = m_lexer.currentToken(); - } - int varNo = 0; - RplASItem* oldSymbol = symbols->addVariable(rc, varNo); - if (oldSymbol != NULL) - error(L_PARSE_VAR_DEF_ALREADY_DEFINED, oldSymbol->position(), - "symbol already defined", "previous definition"); - namedValue->setVariableNo(varNo); - return rc; -} - -/** - * @brief Reads the current tokens as an formula and returns the variant. - * - * @post the token behind the formula is read - * @param parent the parent for the formula: because of the destroying - * the new expression is chained into the parent - * @return the variant containing the formula - */ -RplASVariant* RplMFParser::createFormula(RplASNode1* parent) -{ - RplASVariant* variant = NULL; - m_lexer.undoLastToken2(); - RplASExprStatement* expr = dynamic_cast - (parseExprStatement(false)); - if (expr != NULL){ - // chaining per m_child (= next statement) is for freeing while destruction: - expr->setChild(parent->child()); - parent->setChild(expr); - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setObject(expr, RplASFormula::m_instance); - } - return variant; -} - -/** - * @brief Converts the current expression into a RplASVariant. - * - * If the expression is a constant, the constant value will be the content - * of the variant. Otherwise a formula will be stored. - * - * @pre the first token of the variant expression is read - * @post the token behind the variant expression is read - * @param token the token to convert - * @param endsWithComma true: the 2nd token is a ',' - * @param parent the parent node of the expression - * @return the variant with the token's content - */ -RplASVariant* RplMFParser::tokenToVariant(RplToken* token, - bool endsWithComma, RplASNode1* parent) -{ - RplASVariant* variant = NULL; - if (endsWithComma){ - switch(token->tokenType()){ - case TOKEN_NUMBER: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setInt(token->asInteger()); - break; - case TOKEN_STRING: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setString(token->toString()); - break; - case TOKEN_REAL: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setFloat(token->asReal()); - break; - case TOKEN_KEYWORD: - switch(token->id()){ - case K_TRUE: - case K_FALSE: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - variant->setBool(token->id() == K_TRUE); - break; - case K_NONE: - // freed in the destructor of varList (~RplASVariant()): - variant = new RplASVariant(); - break; - default: - break; - } - break; - default: - break; - } - } - if (variant == NULL) - variant = createFormula(parent); - return variant; -} - -/** - * @brief Parses a list. - * - * Syntax:
- * '[' [ EXPR_1 [ ',' EXPR_2 ...]] ']' - * - * @pre '[' is the current token - * @post the token behind the ']' is read - * @return a node of the abstract syntax tree - */ -RplASItem* RplMFParser::parseList() -{ - RplASListConstant* rc = new RplASListConstant(); - RplASVariant& varList = rc->value(); - RplASListOfVariants* list = static_cast - (varList.asObject(NULL)); - rc->setPosition(m_lexer.currentPosition()); - RplASVariant* variant; - bool again = true; - RplToken* token; - RplToken* token2; - // read the token behind '[': - token = m_lexer.nextNonSpaceToken(); - if (token->isOperator(O_RBRACKET)){ - // read token behind ']': - m_lexer.nextNonSpaceToken(); - } else{ - while(again){ - m_lexer.saveLastToken(); - token2 = m_lexer.nextNonSpaceToken(); - variant = tokenToVariant(token, token2->isOperator(O_COMMA), rc); - token = m_lexer.currentToken(); - if (token->isOperator(O_RBRACKET)) - again = false; - else if (! token->isOperator(O_COMMA)) - syntaxError(L_PARSE_LIST_NO_COMMA, "',' or ']' expected"); - // read token behind ',' or ']' - token = m_lexer.nextNonSpaceToken(); - if (variant != NULL) - list->append(variant); - } - } - return rc; -} - - -/** - * @brief Parses a map. - * - * Syntax:
- * '{' [ STRING_EXPR_1 ':' EXPR_1 [ ',' STRING_EXPR_2 ': EXPR_2 ...]] '}' - * - * @pre '{' is the current token - * @post the token behind the '}' is read - * @return a node of the abstract syntax tree - */ -RplASItem* RplMFParser::parseMap() -{ - RplASMapConstant* rc = new RplASMapConstant(); - RplASVariant& varMap = rc->value(); - RplASMapOfVariants* map = static_cast - (varMap.asObject(NULL)); - rc->setPosition(m_lexer.currentPosition()); - RplASVariant* variant; - bool again = true; - RplToken* token; - RplToken* token2; - QByteArray key; - while(again){ - token = m_lexer.nextNonSpaceToken(); - if (token->isOperator(O_RBRACE)) - again = false; - else{ - key.clear(); - switch(token->tokenType()){ - case TOKEN_STRING: - // freed in the destructor of varList (~RplASVariant()): - key = token->toString(); - break; - case TOKEN_KEYWORD: - switch(token->id()){ - case K_TRUE: - case K_FALSE: - syntaxError(L_PARSE_MAP_BOOL, - "boolean value not allowed as key type. Use a string"); - break; - case K_NONE: - syntaxError(L_PARSE_MAP_NONE, - "'none' is not allowed as key type. Use a string"); - break; - default: - syntaxError(L_PARSE_MAP_EXPR, - "a non constant expression is not allowed as key type. Use a string"); - break; - } - break; - case TOKEN_NUMBER: - case TOKEN_REAL: - syntaxError(L_PARSE_MAP_NUMERIC, - "numeric values not allowed as key type. Use a string"); - break; - default: - syntaxError(L_PARSE_MAP_EXPR2, - "a non constant expression is not allowed as key type. Use a string"); - break; - } - token = m_lexer.nextNonSpaceToken(); - if (! token->isOperator(O_COLON)){ - syntaxError(L_PARSE_MAP_NO_COLON, "':' expected"); - } else { - token = m_lexer.nextNonSpaceToken(); - m_lexer.saveLastToken(); - token2 = m_lexer.nextNonSpaceToken(); - variant = tokenToVariant(token, token2->isOperator(O_COMMA), rc); - (*map)[key] = variant; - variant = NULL; - token = m_lexer.currentToken(); - if (token->isOperator(O_RBRACE)) - again = false; - else if (! token->isOperator(O_COMMA)) - syntaxError(L_PARSE_MAP_NO_COMMA, "',' or '}' expected"); - } - } - } - m_lexer.nextNonSpaceToken(); - return rc; -} - -/** - * @brief Builds a node of a variable or a field (as an operand). - * - * @param name name of the variable/field - * @param position source position - * @param parent NULL: result is a variable - * - * @return a RplASNamedValue or a - * RplASNamedValue instance - */ -RplASItem* RplMFParser::buildVarOrField(const QByteArray& name, - const RplSourcePosition* position, RplASItem* parent) -{ - RplASItem* rc = NULL; - if (parent == NULL){ - RplSymbolSpace* space = m_tree.currentSpace(); - RplASVarDefinition* var = space->findVariable(name); - RplASClass* clazz = NULL; - if (var != NULL) - clazz = var->clazz(); - RplASNamedValue* var2 = new RplASNamedValue(clazz, space, - name, RplASNamedValue::A_NONE); - var2->setPosition(position); - rc = var2; - } else { - RplASField* field = new RplASField(name); - field->setPosition(position); - rc = field; - field->setChild(parent); - } - return rc; -} -/** - * @brief Converts a MF specific unary operator into one of AST. - * - * @param op operator known by MF parser - * - * @return operator known by the abstract syntax tree - */ -RplASUnaryOp::UnaryOp RplMFParser::convertUnaryOp(int op) -{ - RplASUnaryOp::UnaryOp rc; - switch(op){ - case O_PLUS: - rc = RplASUnaryOp::UOP_PLUS; - break; - case O_MINUS: - rc = RplASUnaryOp::UOP_MINUS_INT; - break; - case O_NOT: - rc = RplASUnaryOp::UOP_NOT_BOOL; - break; - case O_BIT_NOT: - rc = RplASUnaryOp::UOP_NOT_INT; - break; - case O_INC: - rc = RplASUnaryOp::UOP_INC; - break; - case O_DEC: - rc = RplASUnaryOp::UOP_DEC; - break; - default: - throw RplException("unknown unary operator %d", op); - break; - } - return rc; -} - -/** - * @brief Parses an operand. - * - * An operand is the first and the third part of a binary operation. - * Examples: constant, variable, method call, an expression in parentheses - * - * @post the token behind the operand is read - * @return the node with the operand - */ -RplASItem* RplMFParser::parseOperand(int level, RplASItem* parent) -{ - RplToken* token = m_lexer.nextNonSpaceToken(); - const RplSourcePosition* startPosition = m_lexer.currentPosition(); - RplASItem* rc = NULL; - bool readNext = true; - switch(token->tokenType()){ - case TOKEN_OPERATOR: - { - Operator opId = (Operator) token->id(); - if (parent != NULL && opId != O_LBRACKET) - syntaxError(L_PARSE_OPERAND_NO_FIELD, - "field expected (behind a '.')"); - if (opId == O_LBRACKET){ - if (parent == NULL){ - rc = parseList(); - readNext = false; - } else { - RplASIndexedValue* value = new RplASIndexedValue(); - value->setPosition(startPosition); - value->setChild(parent); - rc = value; - value->setChild2(parseExpr(level + 1)); - if (! m_lexer.currentToken()->isOperator(O_RBRACKET)) - syntaxError(L_PARSE_OPERAND_NO_BRACKET2, - "']' expected"); - } - } else if (opId == O_LBRACE){ - rc = parseMap(); - readNext = false; - } else if (opId == O_LPARENTH){ - rc = parseExpr(level + 1); - token = m_lexer.currentToken(); - if(! token->isOperator(O_RPARENTH)){ - // this call never comes back (exception!) - syntaxError(L_PARSE_OPERAND_RPARENTH, - "')' expected", "(", startPosition); - } - } else if (IS_UNARY_OP(opId)){ - RplASUnaryOp* op = new RplASUnaryOp(convertUnaryOp(token->id()), - AST_PRE_UNARY_OP); - op->setPosition(m_lexer.currentPosition()); - op->setChild(parseOperand(level)); - readNext = false; - rc = op; - } else - syntaxError(L_PARSE_OPERAND_NOT_OPERAND, - "operand expected, not an operator"); - break; - } - case TOKEN_STRING: - case TOKEN_NUMBER: - case TOKEN_REAL: - { - if (parent != NULL) - syntaxError(L_PARSE_OPERAND_NO_FIELD2, - "field expected (behind a '.')"); - RplASConstant* constant = new RplASConstant(); - constant->setPosition(m_lexer.currentPosition()); - rc = constant; - switch(token->tokenType()){ - case TOKEN_STRING: - constant->value().setString(token->toString()); - break; - case TOKEN_NUMBER: - constant->value().setInt(token->asInteger()); - break; - case TOKEN_REAL: - constant->value().setFloat(token->asReal()); - break; - default: - break; - } - break; - } - case TOKEN_ID: - { - QByteArray name = token->toString(); - if (name == "a") - name = "a"; - token = m_lexer.nextNonSpaceToken(); - startPosition = m_lexer.currentPosition(); - if (token->tokenType() != TOKEN_OPERATOR){ - rc = buildVarOrField(name, startPosition, parent); - readNext = false; - } else { - if (token->id() == O_LPARENTH){ - RplASMethodCall* call = new RplASMethodCall(name, parent); - call->setPosition(startPosition); - - rc = call; - token = m_lexer.nextNonSpaceToken(); - if (! token->isOperator(O_RPARENTH)){ - m_lexer.undoLastToken(); - RplASExprStatement* args = parseArguments(); - call->setChild2(args); - readNext = false; - } - } else { - rc = buildVarOrField(name, startPosition, parent); - if (token->id() == O_LBRACKET){ - RplASItem* indexExpr = parseExpr(0); - if (! m_lexer.currentToken()->isOperator(O_RBRACKET)) - syntaxError(L_PARSE_OPERAND_NO_BRACKET, "']' expected"); - dynamic_cast(rc)->setChild(indexExpr); - } else { - if (token->id() == O_INC || token->id() == O_DEC){ - RplASUnaryOp* op = new RplASUnaryOp(convertUnaryOp( - token->id()), - AST_POST_UNARY_OP); - op->setChild(rc); - rc = op; - } else { - readNext = false; - } - } - } - } - break; - } - case TOKEN_END_OF_SOURCE: - readNext = false; - break; - default: - // this call never comes back (exception!) - syntaxError(L_PARSE_OPERAND_WRONG, - "unexpected symbol detected. Operand expected"); - break; - } - if (readNext) - m_lexer.nextNonSpaceToken(); - if (m_lexer.currentToken()->isOperator(O_DOT, O_LBRACKET)){ - if (m_lexer.currentToken()->asInteger() == O_LBRACKET) - m_lexer.undoLastToken(); - rc = parseOperand(level, rc); - } - return rc; -} -/** - * @brief Converts a MF specific binary operator into one of AST. - * - * @param op operator known by MF parser - * - * @return operator known by the abstract syntax tree - */ -RplASBinaryOp::BinOperator RplMFParser::convertBinaryOp(int op){ - RplASBinaryOp::BinOperator rc; - switch(op){ - case RplMFParser::O_ASSIGN: - rc = RplASBinaryOp::BOP_ASSIGN; - break; - case RplMFParser::O_PLUS_ASSIGN: - rc = RplASBinaryOp::BOP_PLUS_ASSIGN; - break; - case RplMFParser::O_MINUS_ASSIGN: - rc = RplASBinaryOp::BOP_MINUS_ASSIGN; - break; - case RplMFParser::O_DIV_ASSIGN: - rc = RplASBinaryOp::BOP_DIV_ASSIGN; - break; - case RplMFParser::O_TIMES_ASSIGN: - rc = RplASBinaryOp::BOP_TIMES_ASSIGN; - break; - case RplMFParser::O_MOD_ASSIGN: - rc = RplASBinaryOp::BOP_MOD_ASSIGN; - break; - case RplMFParser::O_POWER_ASSIGN: - rc = RplASBinaryOp::BOP_POWER_ASSIGN; - break; - case RplMFParser::O_OR_ASSIGN: - rc = RplASBinaryOp::BOP_LOG_OR_ASSIGN; - break; - case RplMFParser::O_AND_ASSIGN: - rc = RplASBinaryOp::BOP_LOG_AND_ASSIGN; - break; - case RplMFParser::O_LSHIFT_ASSIGN: - rc = RplASBinaryOp::BOP_LSHIFT_ASSIGN; - break; - case RplMFParser::O_RSHIFT_ASSIGN: - rc = RplASBinaryOp::BOP_LOG_RSHIFT_ASSIGN; - break; - case RplMFParser::O_RSHIFT2_ASSIGN: - rc = RplASBinaryOp::BOP_ARTITH_RSHIFT_ASSIGN; - break; - case RplMFParser::O_OR: - rc = RplASBinaryOp::BOP_LOG_OR; - break; - case RplMFParser::O_AND: - rc = RplASBinaryOp::BOP_LOG_AND; - break; - case RplMFParser::O_EQ: - rc = RplASBinaryOp::BOP_EQ; - break; - case RplMFParser::O_NE: - rc = RplASBinaryOp::BOP_NE; - break; - case RplMFParser::O_LT: - rc = RplASBinaryOp::BOP_LT; - break; - case RplMFParser::O_GT: - rc = RplASBinaryOp::BOP_GT; - break; - case RplMFParser::O_LE: - rc = RplASBinaryOp::BOP_LE; - break; - case RplMFParser::O_GE: - rc = RplASBinaryOp::BOP_GE; - break; - case RplMFParser::O_PLUS: - rc = RplASBinaryOp::BOP_PLUS; - break; - case RplMFParser::O_MINUS: - rc = RplASBinaryOp::BOP_MINUS; - break; - case RplMFParser::O_DIV: - rc = RplASBinaryOp::BOP_DIV; - break; - case RplMFParser::O_MOD: - rc = RplASBinaryOp::BOP_MOD; - break; - case RplMFParser::O_TIMES: - rc = RplASBinaryOp::BOP_TIMES; - break; - case RplMFParser::O_POWER: - rc = RplASBinaryOp::BOP_POWER; - break; - case RplMFParser::O_XOR: - rc = RplASBinaryOp::BOP_LOG_XOR; - break; - case RplMFParser::O_BIT_OR: - rc = RplASBinaryOp::BOP_BIT_OR; - break; - case RplMFParser::O_BIT_AND: - rc = RplASBinaryOp::BOP_BIT_AND; - break; - case RplMFParser::O_LSHIFT: - rc = RplASBinaryOp::BOP_LSHIFT; - break; - case RplMFParser::O_RSHIFT: - rc = RplASBinaryOp::BOP_LOG_RSHIFT; - break; - case RplMFParser::O_RSHIFT2: - rc = RplASBinaryOp::BOP_ARTITH_RSHIFT; - break; - default: - throw RplException("unknown binary operator %d", op); - break; - } - return rc; -} - -/** - * @brief Parses an expression. - * - * This method parses the part of an expression with the same parenthesis level. - * The nested parts will be processed recursivly by calling parseOperand which - * calls parseExpr in the case of inner parentheses. - * - * Example: a + (3 * 7 - 2)
- * expr with level 1: 3*7-2
- * expr with level 0: a + expr1
- * - * @pre the nextNonSpaceToken() will return the first token of the expr. - * @post the token behind the expression is read - * - * @param depth the level of the parenthesis - * @return the abstract syntax tree representing the parsed expression - */ -RplASItem* RplMFParser::parseExpr(int depth){ - RplToken* token; - RplASItem* top = parseOperand(depth); - if (top != NULL){ - int lastPrio = INT_MAX; - bool again = true; - do { - token = m_lexer.currentToken(); - switch(token->tokenType()){ - case TOKEN_OPERATOR: - { - Operator opId = (Operator) token->id(); - if (IS_BINARY_OP(opId)){ - RplASBinaryOp* op = new RplASBinaryOp(); - op->setPosition(m_lexer.currentPosition()); - op->setOperator(convertBinaryOp(opId)); - - int prio = m_lexer.prioOfOp(token->id()); - if (prio < lastPrio - || (prio == lastPrio - && ! m_lexer.isRightAssociative(opId))){ - op->setChild(top); - top = op; - } else { - // right assoc or higher priority: - RplASBinaryOp* top2 = dynamic_cast(top); - op->setChild(top2->child2()); - top2->setChild2(op); - } - lastPrio = prio; - op->setChild2(parseOperand(depth)); - } else - again = false; - break; - } - case TOKEN_STRING: - syntaxError(L_TERM_WRONG_STRING, "Operator expected, not a string"); - break; - case TOKEN_NUMBER: - case TOKEN_REAL: - syntaxError(L_TERM_WRONG_NUMBER, "Operator expected, not a number"); - break; - case TOKEN_KEYWORD: - case TOKEN_ID: - case TOKEN_END_OF_SOURCE: - default: - again = false; - break; - } - } while(again); - } - return top; -} - - -/** - * @brief Parses an expression as a statement. - * - * @pre the nextNonSpaceToken() will return the first token of the expr.
- * Note: a ';' is part of the expression statement - * @post the token behind the expression is read - * @param eatSemicolon true: a trailing ';' will be read - * @return the abstract syntax tree of the expression statement - */ -RplASItem* RplMFParser::parseExprStatement(bool eatSemicolon) -{ - RplASItem* item = parseExpr(0); - RplASExprStatement* statement = NULL; - if (item != NULL){ - statement = new RplASExprStatement(); - statement->setPosition(item->position()); - statement->setChild2(item); - } - if (eatSemicolon && m_lexer.currentToken()->isOperator(O_SEMICOLON)) - m_lexer.nextNonSpaceToken(); - return statement; -} - -/** - * @brief Parses a local variable. - * - * @return the variable definition - */ -RplASItem* RplMFParser::parseLocalVar(){ - RplASItem* rc = parseVarDefinition(RplASNamedValue::A_NONE); - return rc; -} - -/** - * @brief Parses the body. - * - * A body is a module, a class region or a method body. - * - * @param keywordStop the first possible keyword which finishes the stm. list - * @param keywordStop2 the 2nd possible keyword which finishes the statements - * @param builtinVars number of variables valid only in this body - * @return the first element of the statement list - */ -RplASItem* RplMFParser::parseBody(Keyword keywordStop, Keyword keywordStop2, - int builtinVars) -{ - RplToken* token = m_lexer.nextNonSpaceToken(); - RplASItem* item = NULL; - RplASItem* body = NULL; - RplASNode1* lastStatement = NULL; - RplASScope scope; - m_tree.currentSpace()->startScope(scope); - scope.m_builtInVars = builtinVars; - bool again = true; - const RplSourcePosition* lastPos = NULL; - do { - token = m_lexer.currentToken(); - if (lastPos == m_lexer.currentPosition()) - syntaxError(L_PARSE_BODY_NO_START, "no statement starts with this symbol"); - lastPos = m_lexer.currentPosition(); - // eat a superflous ';' - if (token->isOperator(O_SEMICOLON)) - token = m_lexer.nextNonSpaceToken(); - try { - switch(token->tokenType()) - { - case TOKEN_OPERATOR: - case TOKEN_STRING: - case TOKEN_NUMBER: - case TOKEN_REAL: - m_lexer.undoLastToken(); - item = parseExprStatement(); - break; - case TOKEN_KEYWORD: - switch (token->id()){ - case K_IF: - item = parseIf(); - break; - case K_WHILE: - item = parseWhile(); - break; - case K_REPEAT: - item = parseRepeat(); - break; - case K_FOR: - item = parseFor(); - break; - case K_CLASS: - parseClass(); - item = NULL; - break; - case K_FUNCTION: - case K_GENERATOR: - parseMethod(); - item = NULL; - break; - case K_IMPORT: - parseImport(); - item = NULL; - break; - case K_CONST: - case K_LAZY: - item = parseLocalVar(); - break; - default: - if (token->isKeyword(keywordStop, keywordStop2)) - again = false; - break; - } - break; - case TOKEN_ID: - { - if (token->isCapitalizedId()){ - item = parseLocalVar(); - } else { - m_lexer.undoLastToken(); - item = parseExprStatement(); - } - break; - } - case TOKEN_END_OF_SOURCE: - again = false; - break; - default: - break; - } - if (again && item != NULL){ - if (body == NULL){ - body = item; - } else { - lastStatement->setChild(item); - } - lastStatement = dynamic_cast(item); - if (lastStatement == NULL) - error(L_PARSE_BODY_WRONG_ITEM, "wrong item type: %d", - item == NULL ? 0 : item->nodeType()); - token = m_lexer.currentToken(); - if (keywordStop != K_UNDEF - && token->isKeyword(keywordStop, keywordStop2)) - again = false; - } - } catch(RplSyntaxError exc){ - // we look for the end of the statement: - token = m_lexer.currentToken(); - RplTokenType type; - Operator op; - Keyword key; - while( (type = token->tokenType()) != TOKEN_END_OF_SOURCE) - if (type == TOKEN_OPERATOR - && ((op = Operator(token->id())) == O_SEMICOLON - || op == O_SEMI_SEMICOLON)) - break; - else if (type == TOKEN_KEYWORD){ - key = Keyword(token->id()); - if (key == K_ENDC || key == K_ENDF){ - // we need the token again! - m_lexer.undoLastToken(); - break; - } else if (key == K_FI || key == K_OD){ - break; - } else { - token = m_lexer.nextNonSpaceToken(); - } - } else { - token = m_lexer.nextNonSpaceToken(); - } - } - } while(again); - - if (keywordStop != K_ENDF && keywordStop != K_ENDC - && keywordStop != K_UNDEF) - m_tree.currentSpace()->finishScope(m_lexer.currentPosition()->lineNo(), - scope); - return body; -} - -/** - * @brief Parses a parameter list of a method/function. - * - * @pre token behind '(' is read - * @post token behind ')' is read - * @return - */ -RplASVarDefinition* RplMFParser::parseParameterList(){ - RplASVarDefinition* rc = NULL; - RplASVarDefinition* last = NULL; - const RplSourcePosition* startPos = m_lexer.currentPosition(); - RplASItem* definition = NULL; - do { - if (definition != NULL) - m_lexer.nextNonSpaceToken(); - RplASVarDefinition* current = parseVarDefinition(RplASNamedValue::A_PARAM); - if (rc == NULL){ - rc = current; - } else { - last->setChild(current); - } - last = current; - } while(m_lexer.currentToken()->isOperator(O_COMMA)); - if (! m_lexer.currentToken()->isOperator(O_RPARENTH)) - syntaxError(L_PARSE_PARAMLIST_NO_PARENTH, ") expected", ")", startPos); - m_lexer.nextNonSpaceToken(); - return rc; -} - -/** - * @brief Parses a class definition. - * - * The method definition will be stored in the symbol space of the parent - * - * @pre token "func" is read - * @post token behind "endf" is read - * @return NULL - */ -void RplMFParser::parseMethod() -{ - RplASMethod* method = NULL; - const RplSourcePosition* startPos = m_lexer.currentPosition(); - RplToken* token = m_lexer.nextNonSpaceToken(); - if (! token->isTokenType(TOKEN_ID)) - syntaxError(L_PARSE_METH_NO_CLASS, "type name expected"); - QByteArray type = token->toString(); - if (! isupper(type.at(0))) - syntaxError(L_PARSE_METH_NO_CLASS2, - "type name expected (must start with an upper case character)"); - token = m_lexer.nextNonSpaceToken(); - if (! token->isTokenType(TOKEN_ID)) - syntaxError(L_PARSE_METH_NO_NAME, "method name expected"); - QByteArray name = token->toString(); - if (! isupper(type.at(0))) - syntaxError(L_PARSE_METH_NO_CLASS2, - "method name expected (must start with an lower case character)"); - token = m_lexer.nextNonSpaceToken(); - if (! token->isOperator(O_LPARENTH, O_COLON)) - syntaxError(L_PARSE_METH_NO_LPARENTH, "'(' or ':' expected"); - - RplASVarDefinition* parameterList = NULL; - method = new RplASMethod(name, m_tree); - method->setPosition(startPos); - RplSymbolSpace* symbols = m_tree.currentSpace(); - symbols->addMethod(method); - m_tree.startClassOrMethod(name, RplSymbolSpace::SST_METHOD); - method->setSymbols(); - if (token->isOperator(O_LPARENTH)){ - token = m_lexer.nextNonSpaceToken(); - if (token->isOperator(O_RPARENTH)){ - token = m_lexer.nextNonSpaceToken(); - } else { - parameterList = parseParameterList(); - method->setChild2(parameterList); - } - } - if (! token->isOperator(O_COLON)) - syntaxError(L_PARSE_METH_NO_COLON, "':' expected"); - - method->setChild(parseBody(K_ENDF)); - if (! m_lexer.currentToken()->isKeyword(K_ENDF)) - syntaxError(L_PARSE_METH_NO_END, "end of function not found", "endf", - startPos); - m_lexer.nextNonSpaceToken(); - m_tree.finishClassOrMethod(name); -} - -/** - * @brief Parses a class definition. - * - * @pre "class" is read - * @post token behind "endc" is read - */ -void RplMFParser::parseClass() -{ - const RplSourcePosition* startPos = m_lexer.currentPosition(); - RplToken* token = m_lexer.nextNonSpaceToken(); - if (! token->isTokenType(TOKEN_ID)) - syntaxError(L_PARSE_CLASS_NO_NAME, "class name expected"); - if (! token->isCapitalizedId()) - syntaxError(L_PARSE_CLASS_LOWERCASE, "class name must start with an uppercase character"); - QByteArray name = token->toString(); - RplASUserClass* clazz = new RplASUserClass(name, startPos, m_tree); - RplSymbolSpace* parent = m_tree.currentSpace(); - RplASUserClass* alreadyDefined = parent->addClass(clazz); - if (alreadyDefined != NULL){ - error(L_PARSE_CLASS_ALREADY_DEFINED, alreadyDefined->position(), - "class already defined", "previous defined class"); - } - m_tree.startClassOrMethod(name, RplSymbolSpace::SST_CLASS); - clazz->setSymbols(); - - m_tree.finishClassOrMethod(name); -} - -/** - * @brief Parses a the import statement - */ -void RplMFParser::parseImport() -{ - -} - -/** - * @brief Parses a module. - * - * @pre the first char of the module is the next char to read. - * @post the total module is read - * - * @param name the name of the module (without path) - */ -RplASItem* RplMFParser::parseModule(RplSourceUnitName name) -{ - m_tree.startModule(name); - // parse until EOF: - RplASItem* body = parseBody(K_UNDEF); - m_tree.finishModule(name); - return body; -} -/** - * @brief Parse the input given by the source. - */ -void RplMFParser::parse() -{ - RplSource* source = m_lexer.source(); - RplSourceUnit* mainModule = source->currentReader()->currentSourceUnit(); - RplSourceUnitName mainModuleName = mainModule->name(); - try { - RplASItem* body = parseModule(mainModuleName); - RplSymbolSpace* module = m_tree.findmodule(mainModuleName); - if (module != NULL) - module->setBody(body); - } catch(RplParserStop exc){ - printf("compiling aborted: %s\n", exc.reason()); - } -} - -/** - * @brief Parses an argument list in a method call. - * - * @pre the token '(' is read - * @post the token behind the ')' is read - * @return the first element of the argument list - */ -RplASExprStatement* RplMFParser::parseArguments() -{ - RplASExprStatement* first = NULL; - RplASExprStatement* last = NULL; - bool again = false; - do { - RplASItem* expr = parseExpr(0); - if (! m_lexer.currentToken()->isOperator(O_COMMA, O_RPARENTH)) - syntaxError(L_PARSE_ARGS_NO_COMMA_OR_PARENT, "',' or ')' expected"); - again = m_lexer.currentToken()->isOperator(O_COMMA); - RplASExprStatement* current = new RplASExprStatement(); - current->setPosition(expr->position()); - current->setChild2(expr); - if (first == NULL) - first = last = current; - else{ - last->setChild(current); - last = current; - } - } while (again); - // skip ')': - m_lexer.nextNonSpaceToken(); - return first; -} - diff --git a/rplexpr/rplmfparser.hpp b/rplexpr/rplmfparser.hpp deleted file mode 100644 index d1d9d61..0000000 --- a/rplexpr/rplmfparser.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 RplParser -{ -public: - enum Keyword { - K_UNDEF, K_IF, K_THEN, K_ELSE, K_FI, K_WHILE, // 5 - K_DO, K_OD, K_REPEAT, K_UNTIL, K_FOR, // 10 - K_FROM, K_TO, K_STEP, K_IN, K_CASE, // 15 - K_OF, K_ESAC, K_LEAVE, K_CONTINUE, K_PASS, // 20 - K_CLASS, K_ENDC, K_ENDF, K_FUNCTION, K_GENERATOR, // 25 - K_IMPORT, K_CONST, K_LAZY, K_NONE, K_TRUE, // 30 - K_FALSE - }; -#define MF_KEYWORDS "if then else fi while do od repeat until" \ - " for from to step in case of esac leave continue pass" \ - " class endc endf func generator import" \ - " const lazy none true false" - enum Operator { - O_UNDEF, O_SEMI_SEMICOLON, O_SEMICOLON, O_COMMA, O_COLON, // 4 - O_ASSIGN, O_PLUS_ASSIGN, O_MINUS_ASSIGN, O_DIV_ASSIGN, O_TIMES_ASSIGN, // 8 - O_MOD_ASSIGN, O_POWER_ASSIGN, O_OR_ASSIGN, O_AND_ASSIGN, // 13 - O_LSHIFT_ASSIGN, O_RSHIFT_ASSIGN, O_RSHIFT2_ASSIGN, // 16 - O_OR, O_AND, // 18 - O_EQ, O_NE, // 20 - O_LT, O_GT, O_LE, O_GE, // 24 - O_QUESTION, // 25 - O_PLUS, O_MINUS, // 27 - O_DIV, O_MOD, O_TIMES, // 30 - O_POWER, // 31 - O_XOR, O_BIT_OR, O_BIT_AND, // 34 - O_LSHIFT, O_RSHIFT, O_RSHIFT2, // 37 - O_NOT, O_BIT_NOT, // 39 - O_INC, O_DEC, // 41 - O_DOT, O_LPARENTH, O_RPARENTH, O_LBRACKET, O_RBRACKET, O_LBRACE, O_RBRACE // 48 - }; -#define IS_BINARY_OP(op) (Operator(op) >= O_ASSIGN && Operator(op) <= O_DOT) -#define IS_UNARY_OP(op) (op==O_PLUS || op==O_MINUS || (op>=O_NOT && op<=O_DEC)) - -/// \n separates the priority classes -#define MF_OPERATORS ";; ; , :\n" \ - "= += -= /= *= %= **= |= &= <<= >>= >>>=\n" \ - "||\n" \ - "&&\n" \ - "== !=\n" \ - "< > <= >=\n" \ - "?\n" \ - "+ -\n" \ - "/ % *\n" \ - "**\n" \ - "^ | &\n" \ - "<< >> >>>\n" \ - "! ~\n" \ - "++ --\n" \ - ". ( ) [ ] { }" -#define MF_RIGHT_ASSOCIATIVES "= += -= /= *= %= **= |= &= <<= >>= >>>= ** ." -public: - RplMFParser(RplSource& source, RplASTree& ast); -public: - RplASItem* parseIf(); - RplASItem* parseWhile(); - RplASItem* parseRepeat(); - RplASItem* parseFor(); - RplASVarDefinition* parseVarDefinition(RplASNamedValue::Attributes attribute); - RplASItem* parseExpr(int depth); - RplASItem* parseBody(Keyword keywordStop, Keyword keywordStop2 = K_UNDEF, - int builtinVars = 0); - void parseMethod(); - void parseClass(); - void parseImport(); - RplASItem* parseModule(RplSourceUnitName name); - void parse(); - RplASItem*parseExprStatement(bool eatSemicolon = true); - RplASItem*parseList(); - RplASItem*parseMap(); -protected: - RplASExprStatement* parseArguments(); - RplASItem* parseOperand(int level, RplASItem* parent = NULL); - RplASVariant* tokenToVariant(RplToken* token, bool endsWithComma, - RplASNode1* parent); - RplASVariant*createFormula(RplASNode1* parent); - RplASItem* buildVarOrField(const QByteArray& name, - const RplSourcePosition* position, - RplASItem* parent); - RplASVarDefinition* parseParameterList(); - RplASItem* parseLocalVar(); - RplASVarDefinition* buildVarDef(RplASNamedValue* var); -protected: - static RplASBinaryOp::BinOperator convertBinaryOp(int op); - static RplASUnaryOp::UnaryOp convertUnaryOp(int op); -private: - ///syntax token builder. - /// Note: the super class contains a reference with the same name - RplLexer m_lexer; -}; - -#endif // RPLMFPARSER_HPP diff --git a/rplexpr/rplparser.cpp b/rplexpr/rplparser.cpp deleted file mode 100644 index de01de5..0000000 --- a/rplexpr/rplparser.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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. -*/ - -/** @file - * - * @brief Generally usable parts of an parser, e.g. error handling. - */ -/** @file rplexpr/rplparser.hpp - * - * @brief Definition of a generally usable parts of an parser. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" - - -/** @class RplSyntaxError rplparser.hpp "rplexpr/rplparser.hpp" - * - * @brief Implements an exception used for jumping out from many nested calls. - * - * We don't want to cancel the parse process if an syntax error has been - * occurred. Therefore we want to recreate after it. - * A relative simple solution: - * Ignoring the rest of the statement and start again with the next statement. - * Often the detection is done deep in an expression and we must jump out. - * This allows this exception. - */ - -/** - * @brief Constructor. - * @param reason the reason of the exception - * @return - */ -RplSyntaxError::RplSyntaxError(const char* reason) : - m_reason(reason) -{ -} -/** - * @brief Returns the description of the exception. - * - * @return the reason - */ -const char* RplSyntaxError::reason() const -{ - return m_reason; -} - -/** @class RplParserStop rplparser.hpp "rplexpr/rplparser.hpp" - * - * @brief Implements an exception used for jumping out from many nested calls. - * - * In some situation we want to abort the parsing process. - * This exception allows this without high costs even the abort position - * is in a deep nested call. - */ - -/** - * @brief Constructor. - * @param reason the reason of the exception - */ -RplParserStop::RplParserStop(const char* reason) : - RplSyntaxError(reason) -{ -} - -/** @class RplParser rplparser.hpp "rplexpr/rplparser.hpp" - * - * @brief Implements a base class for parsers. - * - * This class offers common things for all parsers, e.g. error handling. - */ -/** - * @brief Constructor. - * - * @param lexer the tokenizer - * @param tree the abstract syntax tree - */ -RplParser::RplParser(RplLexer& lexer, RplASTree& tree) : - m_lexer(lexer), - m_tree(tree), - m_messages(), - m_errors(0), - m_warnings(0), - m_maxErrors(20), - m_maxWarnings(20) -{ -} - -/** - * @brief Common actions for the error/warning functions. - * - * @param prefix first char in the message: 'E' (error) or 'W' (warning) - * @param location unique id of the error/warning message - * @param position position of the error/warning - * @param message message with placeholdes like sprintf() - * @return false (for chaining) - */ -bool RplParser::addSimpleMessage(LevelTag prefix, int location, - const RplSourcePosition* position, - const char* message){ - char buffer[2048]; - QByteArray msg; - qsnprintf(buffer, sizeof buffer, "%c%04d %s:%d-%d: ", prefix, location, - position->sourceUnit()->name(), - position->lineNo(), position->column()); - int used = strlen(buffer); - int length = strlen(message); - if (length >= (int) sizeof buffer - used) - length = sizeof buffer - used - 1; - memcpy(buffer + used, message, length); - buffer[used + length] = '\0'; - m_messages.append(buffer); - return false; -} - -/** - * @brief Common actions for the error/warning functions. - * - * @param prefix first char in the message: 'E' (error) or 'W' (warning) - * @param location unique id of the error/warning message - * @param position position of the error/warning - * @param format message with placeholdes like sprintf() - * @param varList the variable argument list - * @return false (for chaining) - */ -bool RplParser::addMessage(LevelTag prefix, int location, - const RplSourcePosition* position, - const char* format, va_list varList){ - char buffer[2048]; - qvsnprintf(buffer, sizeof buffer, format, varList); - return addSimpleMessage(prefix, location, position, buffer); -} - -/** - * @brief Adds an error message and throws an exception. - * - * The exception will be catched at a position where error recovery can take place. - * - * @param location unique id of the error/warning message - * @param message error message - */ - -void RplParser::syntaxError(int location, const char* message) -{ - addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message); - throw RplSyntaxError(message); -} - -/** - * @brief Adds an error message and throws an exception. - * - * This method is used if a closing symbol (e.g. a ')' or 'end') is missed. - * The message contains a %s as an placeholder for the position of the - * starting symbol. - * The exception will be catched at a position where error recovery - * can take place. - * - * @param location unique id of the error - * @param message message describing the error - * @param symbol starting symbol corresponding to the missed closing symbol - * @param position position of the starting symbol - */ - -void RplParser::syntaxError(int location, const char* message, - const char* symbol, - const RplSourcePosition* position) -{ - char buffer[256]; - char buffer2[512]; - qsnprintf(buffer2, sizeof buffer2, - "The starting symbol %s is located here. Missing point: %s", - symbol, m_lexer.currentPosition()->utf8(buffer, sizeof buffer)); - - addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message); - addSimpleMessage(LT_INFO, location + 1, position, buffer2); - throw RplSyntaxError(message); -} - -/** - * @brief Adds an error message. - * - * If too much errors an exception will be thrown to stop parsing. - * - * @param location unique id of the error/warning message - * @param format message with placeholdes like sprintf() - * @param ... optional: the variable argument list - * @return false (for chaining) - */ -bool RplParser::error(int location, const char* format, ...) -{ - va_list ap; - va_start(ap, format); - addMessage(LT_ERROR, location, m_lexer.currentPosition(), format, ap); - va_end(ap); - if (++m_errors >= m_maxErrors) - throw RplParserStop("too many errors"); - return false; -} -/** - * @brief Adds an error message with an additional info message. - * - * If too much errors an exception will be thrown to stop parsing. - * - * @param location unique id of the error/warning message - * @param position source position of the additional message - * @param message describes the error - * @param message2 the additional message - * @param ... optional: the variable argument list - * @return false (for chaining) - */ -bool RplParser::error(int location, const RplSourcePosition* position, - const char* message, const char* message2) -{ - addSimpleMessage(LT_ERROR, location, m_lexer.currentPosition(), message); - addSimpleMessage(LT_INFO, location + 1, position, message2); - if (++m_errors >= m_maxErrors) - throw RplParserStop("too many errors"); - return false; -} - -/** - * @brief Adds a warning message. - * - * If too much warnings an exception will be thrown to stop parsing. - * - * @param location unique id of the error/warning message - * @param format message with placeholdes like sprintf() - * @param ... optional: the variable argument list - */ -void RplParser::warning(int location, const char* format, ...) -{ - va_list ap; - va_start(ap, format); - addMessage(LT_WARNING, location, m_lexer.currentPosition(), format, ap); - va_end(ap); - if (++m_warnings >= m_maxWarnings) - throw RplParserStop("too many warnings"); -} -/** - * @brief Return the number of errors. - * - * @return the count of errors occurred until now - */ -int RplParser::errors() const -{ - return m_errors; -} -/** - * @brief Return the number of warnings. - * - * @return the count of errors occurred until now - */ -int RplParser::warnings() const -{ - return m_warnings; -} - - diff --git a/rplexpr/rplparser.hpp b/rplexpr/rplparser.hpp deleted file mode 100644 index cb21c4c..0000000 --- a/rplexpr/rplparser.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 RPLPARSER_HPP -#define RPLPARSER_HPP - -class RplSyntaxError -{ -public: - RplSyntaxError(const char* reason); -public: - const char* reason() const; -private: - const char* m_reason; -}; - -class RplParserStop : public RplSyntaxError { -public: - RplParserStop(const char* reason); -}; - -class RplParser { -public: - enum LevelTag { - LT_ERROR = 'E', - LT_WARNING = 'W', - LT_INFO = 'I' - }; - -public: - typedef QList MessageList; -public: - RplParser(RplLexer& lexer, RplASTree& ast); -public: - bool addSimpleMessage(LevelTag prefix, int location, - const RplSourcePosition* pos, - const char* message); - bool addMessage(LevelTag prefix, int location, - const RplSourcePosition* pos, - const char* format, va_list varList); - void syntaxError(int location, const char* message); - void syntaxError(int location, const char* message, const char* symbol, - const RplSourcePosition* position); - bool error(int location, const char* format, ...); - bool error(int location, const RplSourcePosition* position, - const char* message, const char* message2); - void warning(int location, const char* format, ...); - int errors() const; - int warnings() const; -protected: - RplLexer& m_lexer; - RplASTree& m_tree; - MessageList m_messages; - int m_errors; - int m_warnings; - int m_maxErrors; - int m_maxWarnings; -}; - -#endif // RPLPARSER_HPP diff --git a/rplexpr/rplsource.cpp b/rplexpr/rplsource.cpp deleted file mode 100644 index b10feac..0000000 --- a/rplexpr/rplsource.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Reading from several input media. - * - * The abstract base class RplReader and its concrete derivations - * RplStringReader, RplFileReader are used to read - * from one medium. - * The RplSource combines several readers and build an uniquely - * usable input stream. - */ -/** @file rplexpr/rplsource.hpp - * - * @brief Definitions for reading from several input media. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" - - -/** @class RplSource rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Stores all source positions. - * - */ - -/** @class RplSourceUnit rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Implements the base class of input source units. - * - * A source unit is set of input lines with a name, e.g. a file. - */ - -/** - * @brief Constructor - * @param name name of the unit - * @param reader the reader which can read the unit - */ -RplSourceUnit::RplSourceUnit(RplSourceUnitName name, RplReader* reader) : - m_name(name), - m_lineNo(0), - m_reader(reader) { -} - -/** - * @brief Destructor. - */ -RplSourceUnit::~RplSourceUnit() { -} - -/** - * @brief Returns the name. - * @return the name - */ -RplSourceUnitName RplSourceUnit::name() const { - return m_name.constData(); -} - -/** - * @brief Returns the line number. - * - * @return the line number - */ -int RplSourceUnit::lineNo() const { - return m_lineNo; -} - -/** - * @brief Sets the line number. - * - * @param lineNo the new line number - */ -void RplSourceUnit::setLineNo(int lineNo) { - m_lineNo = lineNo; -} -/** - * @brief Returns the reader of the instance. - * - * @return the reader belonging to the instance - */ -RplReader* RplSourceUnit::reader() const { - return m_reader; -} - -/** @class RplSourcePosition rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Stores a precise position in the input source. - * - * The input source contains normally a lot of nested source units. - * - * Each source unit owns a name and a sequence of lines. - * - * The mostly used source unit is a text file. - * - * A precice position is the stack of source unit positions. - */ - -/** - * @brief Constructor. - */ -RplSourcePosition::RplSourcePosition() : - m_sourceUnit(NULL), - m_lineNo(0), - m_column(0), - m_caller(NULL) -{ -} - -/** - * @brief Constructor. - * - * @param unit name of the input source (normally a file) - * @param lineNo line number inside the input source - * @param colNo distance to the line start - */ -RplSourcePosition::RplSourcePosition(RplSourceUnit* unit, int lineNo, - int colNo) : - m_sourceUnit(unit), - m_lineNo(lineNo), - m_column(colNo), - m_caller(NULL) -{ - RplReader* reader = dynamic_cast(unit->reader()); - m_caller = reader->source().caller(); -} -/** - * @brief Destructor - */ -RplSourcePosition::~ RplSourcePosition(){ - // That should never occure! - assert(false); -} - -/** - * @brief Placement new operator (for our own memory management). - * - * @param cbSize size of the instance - * @param buffer buffer for the instance - * @return buffer - */ -void*RplSourcePosition::operator new(size_t, void* buffer) -{ - return buffer; -} - -/** - * @brief Returns a description of the source position: "- ():". - * - * @return a description of the instance - */ -QString RplSourcePosition::toString() const -{ - char buffer[512]; - utf8(buffer, sizeof buffer); - - return QString(buffer); -} - -/** - * @brief Returns the position as a C string. - * - * @param buffer OUT: the target buffer - * @param bufferSize the size of the buffer - * @return buffer - */ -char* RplSourcePosition::utf8(char buffer[], size_t bufferSize) const -{ - qsnprintf(buffer, bufferSize, "%s:%d:%d", - m_sourceUnit == NULL ? "" : m_sourceUnit->name(), - m_lineNo, m_column); - return buffer; -} - -/** - * @brief Returns the line number. - * @return the line number - */ -int RplSourcePosition::lineNo() const -{ - return m_lineNo; -} - -/** - * @brief Sets the line number. - * - * @param lineNo the new lineNo - */ -void RplSourcePosition::setLineNo(int lineNo) -{ - m_lineNo = lineNo; -} -/** - * @brief Returns the column. - * - * @return the column of instance. - */ -int RplSourcePosition::column() const -{ - return m_column; -} - -/** - * @brief Sets the column. - * - * @param column the new column - */ -void RplSourcePosition::setColumn(int column) -{ - m_column = column; -} - -/** - * @brief Returns the source unit belonging to the instance. - * - * @return the source unit of the instance - */ -RplSourceUnit* RplSourcePosition::sourceUnit() const -{ - return m_sourceUnit; -} - -/** - * @brief Sets the source unit. - * - * @param sourceUnit the new source unit of the instance - */ -void RplSourcePosition::setSourceUnit(RplSourceUnit* sourceUnit) -{ - m_sourceUnit = sourceUnit; - m_lineNo = sourceUnit->lineNo(); -} - - -/** @class RplReader rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Implements a base class for readers of different media. - */ - - -/** - * @brief Constructor. - * - * @param source the parent - */ -RplReader::RplReader(RplSource& source) : - m_currentSourceUnit(NULL), - m_units(), - m_source(source) { -} - -/** - * @brief Destructor. - */ -RplReader::~RplReader() -{ - clear(); -} - -/** - * @brief Frees the resources. - */ -void RplReader::clear() -{ - UnitMap::iterator it; - for(it = m_units.begin(); it != m_units.end(); it++) { - RplStringSourceUnit* unit = (RplStringSourceUnit*)(*it); - delete unit; - } - m_units.clear(); - m_currentSourceUnit = NULL; -} - -/** - * @brief Returns the source of the reader. - * - * @return the parent, a source instance - */ -RplSource&RplReader::source() -{ - return m_source; -} -/** - * @brief Returns the current source unit. - * - * @return the source unit - */ -RplSourceUnit* RplReader::currentSourceUnit() const { - return m_currentSourceUnit; -} - -/** - * @brief Sets the current source unit. - * - * @param sourceUnit the name of the new source unit - * @return true: source unit exists
- * false: source unit not found - */ -bool RplReader::setCurrentSourceUnit(RplSourceUnitName& sourceUnit) { - bool rc = m_units.contains(sourceUnit); - if(rc) { - m_currentSourceUnit = m_units.value(sourceUnit); - m_source.pushSourceUnit(m_currentSourceUnit); - } - return rc; -} - -/** - * @brief Removes the "latest" sourceUnit. - */ -void RplReader::removeSourceUnit() { - - m_currentSourceUnit = m_source.popSourceUnit(this);; -} - -/** @class RplSourcePositionBlock rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Efficient heap of RplSourcePosition instances. - * - * The RplSourcePosition heap is only growing. The deletion is - * done for all entries together. - * Therefore a simple allocation is possible with blocks. - */ -RplSourcePositionBlock::RplSourcePositionBlock() : - m_successor(NULL) - // m_positions -{ - memset(m_positions, 0, sizeof m_positions); -} - -/** @class RplSource rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Administrates a set of input sources with different readers. - * - * An input stream can be built by some different resources, e.g. files - * and memory buffers. The RplSource can administrate these resources. - */ -/** - * @brief Constructor. - */ -RplSource::RplSource() : - m_sourcePositionStack(), - m_sourcePositionBlock(NULL), - m_countPositionBlock(RPL_POSITIONS_PER_BLOCK + 1), - m_readers(), - m_sourceUnits(), - m_unitStack(), - m_currentReader(NULL) -{ - // the stack should never be empty: - m_sourcePositionStack.push(NULL); -} - -/** - * @brief Destructor. - */ -RplSource::~RplSource() { - destroy(); -} - -/** - * @brief Frees the resources of the instance. - */ -void RplSource::destroy() -{ - m_sourcePositionStack.clear(); - m_readers.clear(); - m_sourceUnits.clear(); - m_currentReader = NULL; - RplSourcePositionBlock* block = m_sourcePositionBlock; - m_sourcePositionBlock = NULL; - m_countPositionBlock = RPL_POSITIONS_PER_BLOCK + 1; - while(block != NULL){ - RplSourcePositionBlock* last = block; - block = block->m_successor; - delete last; - } -} - -/** - * @brief Returns the permanently valid source unit name. - * - * If unit names can be local objects (not string constants - * like __FILE__) then this method must be overridden by - * a method which builds a permanently valid string. - * - * @param unit unit to find - * @return a permanently valid unit name - */ -RplSourceUnitName RplSource::permanentUnitName(RplSourceUnitName unit) { - return unit; -} - -/** - * @brief Returns the stack with the positions of the open input resources. - * - * @return the stack - */ -QStack RplSource::sourcePositionStack() const { - return m_sourcePositionStack; -} - -/** - * @brief Returns the source unit stack. - * @return the stack of the source units - */ -QStack& RplSource::sourceUnitStack() -{ - return m_unitStack; -} - -/** - * @brief Adds a source reader. - * - * @param reader the new reader. Will be freed in the destructor - */ -void RplSource::addReader(RplReader* reader) { - m_readers.push_back(reader); - if (m_currentReader == NULL) - m_currentReader = reader; -} - -/** - * @brief Adds a source unit. - * - * @param unit the new unit. Will be freed in the destructor - */ -void RplSource::addSourceUnit(RplSourceUnit* unit) { - m_sourceUnits.push_back(unit); -} - -/** - * @brief Starts a new source unit. - * - * Saves the current source position onto the top of stack. - * Pushes the source unit onto the top of stack. - * - * @param unit the source unit - * @param caller the position of the include - * - */ -bool RplSource::startUnit(RplSourceUnitName unit, - const RplSourcePosition& caller) { - m_sourcePositionStack.push_back(&caller); - RplReader* reader = NULL; - QList::iterator it; - for(it = m_readers.begin(); - reader == NULL && it != m_readers.end(); - it++) { - RplReader* current = *it; - if(current->openSourceUnit(unit)) { - reader = current; - m_currentReader = current; - break; - } - } - return reader != NULL; -} - -/** - * @brief Pushes a source unit onto the stack. - * - * @param unit the source unit - */ -void RplSource::pushSourceUnit(RplSourceUnit* unit) { - m_unitStack.push(unit); -} - -/** - * @brief Removes the latest source unit from the stack. - * - * @param reader the current reader - * @return NULL: the current reader does not have an open source unit
- * otherwise: the last entry from the source unit stack - */ -RplSourceUnit* RplSource::popSourceUnit(RplReader* reader) { - RplSourceUnit* rc = NULL; - if(m_unitStack.size() > 0) - m_unitStack.pop(); - m_currentReader = m_unitStack.size() <= 0 - ? NULL : m_unitStack.top()->reader(); - if(m_currentReader == reader) - rc = m_unitStack.top(); - else { - for(int ix = m_unitStack.size() - 2; ix >= 0; ix--){ - if(m_unitStack[ix]->reader() == reader){ - rc = m_unitStack[ix]; - break; - } - } - } - return rc; -} - -/** - * @brief Returns the reader of the current source unit. - * - * @return NULL: no reader active
- * otherwise: the current reader - */ -RplReader* RplSource::currentReader() { - return m_currentReader; -} - -/** - * @brief Returns a new instance of the current source position. - * - * The storage is done in a block (efficency). - * - * @param colNo the column in the line - * @return a new instance of a source position - */ -const RplSourcePosition* RplSource::newPosition(int colNo) -{ - if (m_countPositionBlock >= RPL_POSITIONS_PER_BLOCK){ - RplSourcePositionBlock* newBlock = new RplSourcePositionBlock; - newBlock->m_successor = m_sourcePositionBlock; - m_sourcePositionBlock = newBlock; - m_countPositionBlock = 0; - } - unsigned offset = m_countPositionBlock * sizeof(RplSourcePosition); - m_countPositionBlock++; - char* posInBlock = &m_sourcePositionBlock->m_positions[offset]; - RplSourceUnit* unit = dynamic_cast( - m_currentReader->currentSourceUnit()); - RplSourcePosition* rc = new (posInBlock) RplSourcePosition( - unit, unit->lineNo(), colNo); - return rc; -} - -/** - * @brief Resets all states in the source. - */ -void RplSource::clear() -{ - destroy(); -} - -/** - * @brief Returns the top position of the source unit stack. - * - * @return NULL: stack is empty
- * the top of the source unit stack - */ -const RplSourcePosition* RplSource::caller() const -{ - return m_sourcePositionStack.size() == 0 - ? NULL : m_sourcePositionStack.top(); -} - -/** @class RplStringSourceUnit rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Stores the state of a string based source unit. - * - */ - -/** - * @brief Constructor. - * - * @param name name of the unit - * @param content content of the unit - * @param reader the parent - */ -RplStringSourceUnit::RplStringSourceUnit(RplSourceUnitName name, - const RplSourceUnitContent& content, RplStringReader* reader) : - RplSourceUnit(name, reader), - m_currentPosition(0), - m_content(content) { -} - -/** - * @brief Destructor. - */ -RplStringSourceUnit::~RplStringSourceUnit() { -} -/** - * @brief Returns the current read position. - * - * @return the offset (count of QChars) of the end of the last read block - * inside m_content - */ -int RplStringSourceUnit::currentPosition() const { - return m_currentPosition; -} - -/** - * @brief Sets the current read position. - * - * @param currentPosition the offset (count of QChars) of the end of - * the last read block inside m_content - */ -void RplStringSourceUnit::setCurrentPosition(int currentPosition) { - m_currentPosition = currentPosition; -} -/** - * @brief Returns the content of the source unit. - * - * @return the content - */ -RplSourceUnitContent RplStringSourceUnit::content() const { - return m_content.constData(); -} - -/** @class RplStringReader rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Implements a source which provides reading from memory based buffers. - * - * Examples for usage: interactive input - */ - -/** - * @brief Constructor. - * - * @param source the parent - */ -RplStringReader::RplStringReader(RplSource& source) : - RplReader(source) { -} - -/** - * @brief Destructor. - */ -RplStringReader::~RplStringReader() { - clear(); -} - -/** - * @brief Opens a new source unit. - * - * @param unit name of the source - * @return NULL: unknown source
- * otherwise: an instance of a sub class of - * RplSourceUnit - */ -RplSourceUnit* RplStringReader::openSourceUnit(RplSourceUnitName unit) { - RplSourceUnit* rc = NULL; - if(setCurrentSourceUnit(unit)) { - rc = m_currentSourceUnit; - ((RplStringSourceUnit*) rc)->setCurrentPosition(0); - } - return rc; -} -/** - * @brief Delivers the next line from the input medium or the first part of it. - * - * @param maxSize if the line length is longer than this value, only the - * first part of the line will be returned - * @param buffer OUT: the line will be put here - * @param hasMore true: the line was longer than maxSize, only - * the first part of the line is put into the buffer - * @return false: no more input available
- * true: success - */ -bool RplStringReader::nextLine(int maxSize, QByteArray& buffer, bool& hasMore) { - bool rc = m_currentSourceUnit != NULL; - if (rc){ - m_currentSourceUnit->setLineNo(m_currentSourceUnit->lineNo() + 1); - rc = fillBuffer(maxSize, buffer, hasMore); - } - return rc; -} - -/** - * @brief Delivers the next part of a long line. - * - * @param maxSize if the remaining line length is longer than this value, - * only the a part of the line will be returned - * @param buffer OUT: the part of line will be put here - * @param hasMore true: the line was longer than maxSize, only - * the first part of the line is put into the buffer - * @return false: no more input available
- * true: success - */ -bool RplStringReader::fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore) { - RplStringSourceUnit* unit = (RplStringSourceUnit*) m_currentSourceUnit; - RplSourceUnitContent content = unit->content(); - int startPos = unit->currentPosition(); - const char* start = content + startPos; - const char* end = strchr(start, '\n'); - hasMore = false; - int size = end == NULL ? strlen(start) : end - start + 1; - hasMore = false; - if(size > maxSize) { - size = maxSize; - hasMore = true; - } - if(size > 0) { - buffer.append(start, size); - unit->setCurrentPosition(startPos + size); - } else { - removeSourceUnit(); - } - return size > 0; -} - -/** - * @brief Adds a source buffer to the reader - * - * @param name name of the medium - * @param content - */ -void RplStringReader::addSource(RplSourceUnitName name, - RplSourceUnitContent content) { - // Deletion in the destructor of the base class RplReader - RplStringSourceUnit* unit = new RplStringSourceUnit(name, content, this); - m_units.insert(m_units.begin(), unit->name(), unit); - m_currentSourceUnit = unit; -} - -/** - * @brief Replaces the content of a source unit. - * - * @param name name of the source unit - * @param content new content - */ -void RplStringReader::replaceSource(RplSourceUnitName name, - RplSourceUnitContent content) -{ - if (m_units.contains(name)){ - RplStringSourceUnit* unit = dynamic_cast(m_units[name]); - unit->m_content = content; - } -} - -/** @class RplFileSourceUnit rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Stores the state of a file based source unit. - * - * This is the mostly used implementation of the RplSourceUnit/RplReader. - */ - - -RplFileSourceUnit::RplFileSourceUnit(RplSourceUnitName filename, - RplFileReader* reader) : - RplSourceUnit(filename, reader), - m_currentPosition(0), - m_fp(fopen(filename, "r")), - m_textStream(m_fp, QIODevice::ReadOnly), - m_line() -{ -} - - -/** - * @brief Destructor. - */ -RplFileSourceUnit::~RplFileSourceUnit() -{ - fclose(m_fp); -} - -bool RplFileSourceUnit::isOpen() const -{ - return m_fp != NULL; -} -/** @class RplFileReader rplsource.hpp "rplexpr/rplsource.hpp" - * - * @brief Implements a source which provides reading from memory based buffers. - * - * Examples for usage: interactive input - */ - -/** - * @brief Constructor. - */ -RplFileReader::RplFileReader(RplSource& source) : - RplReader(source) -{ -} - -/** - * @brief Destructor. - */ -RplFileReader::~RplFileReader() { -} - -/** - * @brief Opens a new source unit. - * - * @param unit name of the source - * @return NULL: unknown source
- * otherwise: an instance of a sub class of - * RplSourceUnit - */ -RplSourceUnit* RplFileReader::openSourceUnit(RplSourceUnitName unit) { - RplSourceUnit* rc = NULL; - if(m_units.contains(unit)) { - rc = *m_units.find(unit); - m_currentSourceUnit = static_cast(rc); - } - return rc; -} -/** - * @brief Delivers the next line from the input medium or the first part of it. - * - * @param maxSize if the line length is longer than this value, only the - * first part of the line will be returned - * @param buffer OUT: the line will be put here - * @param hasMore true: the line was longer than maxSize, only - * the first part of the line is put into the buffer - * @return false: no more input available
- * true: success - */ -bool RplFileReader::nextLine(int maxSize, QByteArray& buffer, bool& hasMore) { - RplFileSourceUnit* unit = static_cast - (m_currentSourceUnit); - bool rc = ! feof(unit->m_fp); - if(! rc) { - m_source.popSourceUnit(this); - } else { - m_currentSourceUnit->setLineNo(m_currentSourceUnit->lineNo() + 1); - unit->m_currentPosition = 0; - QByteArray& line = unit->m_line; - line.reserve(maxSize+1); - if (fgets(line.data(), maxSize, unit->m_fp) == NULL) - rc = false; - else{ - line[maxSize] = '\0'; - line.resize(strlen(line.constData())); - rc = fillBuffer(maxSize, buffer, hasMore); - } - } - return rc; -} - -/** - * @brief Delivers the next part of a long line. - * - * @param maxSize if the remaining line length is longer than this value, - * only the a part of the line will be returned - * @param buffer OUT: the part of line will be put here - * @param hasMore true: the line was longer than maxSize, only - * the first part of the line is put into the buffer - * @return false: no more input available
- * true: success - */ -bool RplFileReader::fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore) { - RplFileSourceUnit* unit = static_cast(m_currentSourceUnit); - int start = unit->m_currentPosition; - QByteArray& content = unit->m_line; - int size = content.size() - start; - if(size > maxSize) - size = maxSize; - buffer += content.mid(start, size); - unit->m_currentPosition = (start += size); - hasMore = start < content.size(); - return size > 0; -} - -/** - * @brief Adds a source file to the reader - * - * @param filename the file' name (relative or absolute) - */ -void RplFileReader::addSource(RplSourceUnitName filename) { - // Deleting in ~RplSourceUnit(): - RplFileSourceUnit* unit = new RplFileSourceUnit(filename, this); - m_units.insert(m_units.begin(), unit->name(), unit); - m_currentSourceUnit = unit; -} - - diff --git a/rplexpr/rplsource.hpp b/rplexpr/rplsource.hpp deleted file mode 100644 index 40f80b9..0000000 --- a/rplexpr/rplsource.hpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 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 RPLSOURCE_HPP -#define RPLSOURCE_HPP - -// type of buffer names and filenames. Codec: UTF-8 -typedef const char* RplSourceUnitName; - -typedef const char* RplSourceUnitContent; - -class RplSource; -class RplReader; - -class RplSourceUnit { -public: - RplSourceUnit(const char* name, RplReader* reader); - virtual ~RplSourceUnit(); -public: - const char* name() const; - int lineNo() const; - void setLineNo(int lineNo); - RplReader* reader() const; - -protected: - QByteArray m_name; - int m_lineNo; - RplReader* m_reader; -}; - -class RplSourcePosition { -public: - RplSourcePosition(); - RplSourcePosition(RplSourceUnit* unit, int lineNo, int colNo); - ~ RplSourcePosition(); - void* operator new(size_t cbSize, void* buffer); -private: - /// forbid usage of the copy constructor! - RplSourcePosition(const RplSourcePosition& source); - /// forbid usage of the the assignment! - RplSourcePosition& operator=(const RplSourcePosition& source); -public: - QString toString() const; - int lineNo() const; - void setLineNo(int lineNo); - - int column() const; - void setColumn(int column); - - RplSourceUnit* sourceUnit() const; - void setSourceUnit(RplSourceUnit* sourceUnit); - char*utf8(char buffer[], size_t bufferSize) const; -private: - RplSourceUnit* m_sourceUnit; - int m_lineNo; - int m_column; - const RplSourcePosition* m_caller; -}; - -class RplReader { -public: - typedef RplCharPtrMap UnitMap; -public: - RplReader(RplSource& source); - ~RplReader(); -public: - /** - * @brief Prepares the reading from a given source unit. - * - * @param unit name of the unit - * @return NULL: unit not known
- * otherwise: an instance with the state of the reader - * for the source. This is normally a sub class of - * RplSourceUnit - */ - virtual RplSourceUnit* openSourceUnit(const char* unit) = 0; - /** - * @brief Reads the first part of the next line into a given buffer. - * - * @param maxSize the maximum length of the read input. - * If a line is longer the next part must be read - * by fillBuffer() - * @param buffer IN/OUT: the read input will be appended here - * @param hasMore OUT: true: the line is longer than maxSize - * @return true: the read was successful
- * false: no more input is available - */ - virtual bool nextLine(int maxSize, QByteArray& buffer, bool& hasMore) = 0; - /** - * @brief Reads the next part of the current line into a given buffer. - * - * @param maxSize the maximum length of the read input. - * @param buffer IN/OUT: the read input will be appended here - * @param hasMore OUT: true: the rest of line is longer than maxSize - * @return true: the read was successful
- * false: no more input is available - */ - virtual bool fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore) = 0; -public: - virtual void clear(); - RplSource& source(); - RplSourceUnit* currentSourceUnit() const; - bool setCurrentSourceUnit(RplSourceUnitName& currentSourceUnit); -protected: - void removeSourceUnit(); - -protected: - RplSourceUnit* m_currentSourceUnit; - /// name -> source - UnitMap m_units; - RplSource& m_source; -}; - -#define RPL_POSITIONS_PER_BLOCK 512 -class RplSourcePositionBlock{ - friend class RplSource; -public: - RplSourcePositionBlock(); -private: - RplSourcePositionBlock* m_successor; - char m_positions[RPL_POSITIONS_PER_BLOCK * sizeof(RplSourcePosition)]; -}; - -class RplSource { -public: - RplSource(); - virtual ~RplSource(); -public: - virtual const char* permanentUnitName(const char* unit); - void finishSourceUnit(); - void addReader(RplReader* reader); - void addSourceUnit(RplSourceUnit* unit); - QStack sourcePositionStack() const; - QStack& sourceUnitStack(); - - bool startUnit(const char* unit, const RplSourcePosition& caller); - void pushSourceUnit(RplSourceUnit* unit); - RplSourceUnit* popSourceUnit(RplReader* reader); - RplReader* currentReader(); - const RplSourcePosition* newPosition(int colNo); - void clear(); - const RplSourcePosition* caller() const; -protected: - void destroy(); -protected: - // stack of the info about the stacked (open) source units: - QStack m_sourcePositionStack; - RplSourcePositionBlock* m_sourcePositionBlock; - int m_countPositionBlock; - QList m_readers; - QList m_sourceUnits; - // setCurrentSourceUnit() pushes one entry, removeSourceUnit() pops it - // (when end of input has been reached). - QStack m_unitStack; - RplReader* m_currentReader; -}; - -class RplStringReader; - -class RplStringSourceUnit : public RplSourceUnit { - friend class RplStringReader; -public: - RplStringSourceUnit(RplSourceUnitName name, - const RplSourceUnitContent& content, - RplStringReader* reader); - virtual ~RplStringSourceUnit(); -public: - int currentPosition() const; - void setCurrentPosition(int currentPosition); - RplSourceUnitContent content() const; - -private: - int m_currentPosition; - QByteArray m_content; -}; - -class RplStringReader : public RplReader{ -public: - RplStringReader(RplSource& source); - virtual ~RplStringReader(); - // RplReader interface -public: - virtual RplSourceUnit* openSourceUnit(RplSourceUnitName unit); - virtual bool nextLine(int maxSize, QByteArray& buffer, bool& hasMore); - virtual bool fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore); -public: - void addSource(RplSourceUnitName name, RplSourceUnitContent content); - void replaceSource(RplSourceUnitName name, RplSourceUnitContent content); -}; - -class RplFileReader; - -class RplFileSourceUnit : public RplSourceUnit { - friend class RplFileReader; -public: - RplFileSourceUnit(RplSourceUnitName filename, RplFileReader* reader); - virtual ~RplFileSourceUnit(); -public: - bool isOpen() const; -private: - int m_currentPosition; - FILE* m_fp; - QTextStream m_textStream; - QByteArray m_line; -}; - -class RplFileReader : public RplReader{ -public: - RplFileReader(RplSource& source); - virtual ~RplFileReader(); - // RplReader interface -public: - virtual RplSourceUnit* openSourceUnit(RplSourceUnitName unit); - virtual bool nextLine(int maxSize, QByteArray& buffer, bool& hasMore); - virtual bool fillBuffer(int maxSize, QByteArray& buffer, bool& hasMore); -public: - void addSource(RplSourceUnitName filename); -}; - - -#endif // RPLSOURCE_HPP diff --git a/rplexpr/rplvm.cpp b/rplexpr/rplvm.cpp deleted file mode 100644 index 9598c23..0000000 --- a/rplexpr/rplvm.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* - * 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. -*/ - -/** @file - * - * @brief Implements an interpreter of an abstract syntax tree. - */ - -/** @file rplexpr/rplvm.hpp - * - * @brief Definitions for an interpreter of an abstract syntax tree. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" - -enum { - LOC_VAL_OF_VAR_1 = RPL_FIRST_OF(RPL_MODULE_VM), // 11401 - LOC_UNOP_1, - LOC_UNOP_2, - LOC_UNOP_3, - LOC_UNOP_4, // 10005 - LOC_BINOP_1, - LOC_COUNT -}; - -int RplVMThread::m_nextId = 1; - -/** @class RplVmException rplvm.hpp "rplexpr/rplvm.hpp" - * - * @brief Implements an exception for the virtual machine. - * - */ -/** - * @brief Constructor - * @param format the message with placeholders - * @param ... the values for the placeholders - */ -RplVmException::RplVmException(const char* format, ...) : - RplException("") -{ - char buffer[16000]; - va_list ap; - va_start(ap, format); - qvsnprintf(buffer, sizeof buffer, format, ap); - va_end(ap); - m_message = buffer; -} - -/** @class RplStackFrame rplvm.hpp "rplexpr/rplvm.hpp" - * - * @brief Implements the storage for a symbol space. - * - * The owner of a symbol space can be "global", a module, a class, or a method. - * Some symbol spaces have more than one stack frame, e.g. a recursive called - * method. - */ - - -/** - * @brief Constructor. - * - * @param symbols the symbol space belonging to the stack frame - */ - -RplStackFrame::RplStackFrame(RplSymbolSpace* symbols) : - m_countVariables(symbols->listOfVars().size()), - m_variables(NULL), - m_symbols(symbols) -{ - if (m_countVariables > 0) - m_variables = new RplASVariant[m_countVariables]; -} - -/** - * @brief Destructor. - */ -RplStackFrame::~RplStackFrame() -{ - delete[] m_variables; - m_variables = NULL; -} - -/** - * @brief Returns the storage of a variable given by the index. - * - * @param index the index of the variable - * - * @return the storage of the variable - */ -RplASVariant& RplStackFrame::valueOfVariable(int index) -{ - if (index < 0 || index >= m_countVariables) - throw RplVmException("valueOfVariable(): invalid index: %d", index); - return m_variables[index]; -} -/** - * @brief Returns the symbol space of the frame. - * - * @return the symbol space - */ -RplSymbolSpace* RplStackFrame::symbols() const -{ - return m_symbols; -} - -/** @class RplVMThread rplvm.hpp "rplexpr/rplvm.hpp" - * - * @brief Implements a thread of the virtual machine. - * - * The virtual machine can execute many threads at the same time. - * Each thread has its own stack. - */ - -/** - * @brief Constructor. - * - * @param maxStack the maximal number of nested stack frames - * @param vm the parent, the virtual machine - */ -RplVMThread::RplVMThread(int maxStack, RplVirtualMachine* vm) : - m_id(m_nextId++), - m_debugMode(false), - m_singleStep(false), - m_tracing(false), - m_maxStack(maxStack), - m_frameStack(), - // the stack is never empty! - m_topOfFrames(0), - m_valueStack(), - // the stack is never empty! - m_topOfValues(0), - m_vm(vm), - m_logger(new RplLogger()) -{ - QByteArray prefix = "vm_thread_" + QByteArray::number(m_id); - m_logger->buildStandardAppender(prefix); - m_frameStack.reserve(maxStack); - // the stack is never empty! - m_frameStack.append(new RplStackFrame(vm->tree().symbolSpaces()[0])); - // the stack is never empty! - m_valueStack.append(new RplASVariant); -} - -/** - * @brief Executes a statement list. - * - * @param statements the first statement of a statement list chained by - * m_child - * @param space the current symbol space - */ -void RplVMThread::execute(RplASNode1* statements, RplSymbolSpace* space) -{ - bool debugMode = m_debugMode; - RplASNode1 *next; - while(statements != NULL){ - if (debugMode - && (m_singleStep - || (statements->flags() & RplASItem::NF_BREAKPOINT) != 0)) - debug(statements); - RplASStatement* statement = dynamic_cast(statements); - if (statement != NULL) - statement->execute(*this); - statements = dynamic_cast(statements->child()); - } -} - -/** - * @brief Handles a debugger break. - * - * @param statement the current statement (not yet executed) - */ -void RplVMThread::debug(RplASNode1* statement) -{ - -} - -/** - * @brief Returns the logger of the instance. - * - * @return the logger - */ -RplLogger* RplVMThread::logger() const -{ - return m_logger; -} -/** - * @brief Reserves a value in the thread's value stack. - * - * @return the reserved value - */ -RplASVariant&RplVMThread::reserveValue() -{ - RplASVariant* rc; - if (++m_topOfValues < m_valueStack.size()){ - rc = m_valueStack[m_topOfValues]; - rc->destroyValue(); - } else { - rc = new RplASVariant(); - m_valueStack.append(rc); - } - return *rc; -} - -/** - * @brief Returns the top of the value stack. - * - * @return the top of the value stack - */ -RplASVariant& RplVMThread::topOfValues() -{ - RplASVariant& rc = *m_valueStack[m_topOfValues]; - return rc; -} - - -/** - * @brief Returns the entry under the top of the value stack. - * - * @return the 2nd value the value stack - */ -RplASVariant& RplVMThread::top2OfValues() -{ - RplASVariant& rc = *m_valueStack[m_topOfValues-1]; - return rc; -} - -/** - * @brief Returns the top of stack and removes it. - * - * @return the old top of stack - */ -RplASVariant& RplVMThread::popValue() -{ - RplASVariant& rc = *m_valueStack[m_topOfValues]; - if (m_topOfValues > 0) - m_topOfValues--; - return rc; -} - -/** - * @brief Copies a variable value to the top of the stack. - * - * @param symbolSpace the symbol space of the variable - * @param variableNo the current no of the variable in the symbol space - */ -void RplVMThread::valueToTop(RplSymbolSpace* symbolSpace, int variableNo) -{ - //@ToDo -} - -/** - * @brief Returns the "left value" (of an assignment). - * @param item - * @return - */ -RplASVariant& RplVMThread::lValue(RplASItem* item) -{ - RplASVariant* rc; - switch(item->nodeType()){ - case AST_NAMED_VALUE: - { - RplASNamedValue* var = dynamic_cast(item); - rc = &valueOfVariable(var->symbolSpace(), var->variableNo()); - break; - } - default: - break; - } - return *rc; -} - -/** - * @brief Returns the reference of the value of a variable. - * - * @param symbolSpace the symbol space - * @param variableNo the current number in the symbol space - * @return - */ -RplASVariant& RplVMThread::valueOfVariable(RplSymbolSpace* symbolSpace, - int variableNo) -{ - RplASVariant& rc = *m_valueStack[0]; - int ix = m_topOfFrames; - RplStackFrame* frame = NULL; - for(int ix = m_topOfFrames; ix >= 0; ix--){ - frame = m_frameStack[ix]; - if (frame->symbols() == symbolSpace){ - rc = frame->valueOfVariable(variableNo); - break; - } - } - if (frame == NULL) - m_logger->logv(LOG_ERROR, LOC_VAL_OF_VAR_1, "no frame has symbolspace %s", - symbolSpace->name().constData()); - - return rc; -} -/** - * @brief Returns whether each execution step should be dumped. - * @return true: tracing is on
- * false: otherwise - */ -bool RplVMThread::tracing() const -{ - return m_tracing; -} - -/** - * @brief Sets the tracing flag: if set each execution step is dumped. - * @param tracing true: tracing will be done
- * false: tracing will not be done - */ -void RplVMThread::setTracing(bool tracing) -{ - m_tracing = tracing; -} - -/** - * @brief Returns the parent, a virtual machine. - * - * @return the virtual machine - */ -RplVirtualMachine* RplVMThread::vm() const -{ - return m_vm; -} - -/** @class RplVirtualMachine rplvm.hpp "rplexpr/rplvm.hpp" - * - * @brief Implements a virtual machine. - * - * This is an execution unit which interprets an abstract syntax tree. - */ -RplVirtualMachine::RplVirtualMachine(RplASTree& tree, RplSource& source, - int maxStack) : - m_maxStack(maxStack), - m_threads(), - m_flags(VF_UNDEF), - m_source(source), - m_tree(tree), - m_trace() -{ - m_threads.reserve(8); - m_trace.reserve(1024); -} - -/** - * @brief Executes the program in a module. - * - * @param module the module's name - */ -void RplVirtualMachine::executeModule(const char* module) -{ - RplSymbolSpace* space = m_tree.findmodule(module); - if (space == NULL) - throw RplVmException("module not found: %s", module); - RplStackFrame frame(space); - RplSymbolSpace* mainSpace = NULL; - RplASItem* mainStatements = NULL; - RplASMethod* method = space->findMethod("main"); - if (method != NULL){ - mainStatements = method->child(); - mainSpace = method->symbols(); - } - addThread(space->body(), space, mainStatements, mainSpace); -} - -/** - * @brief Adds a thread to the instance. - * - * @param initialization the statements for initialization - * @param spaceInitialization the symbol space of the initialization - * @param statements the statement list to execute. This is normally - * the body of the main program - * @param space the symbol space of the statements, normally - * the symbol space of the main program - * @param maxStack the maximal number of nested stack frames. - * <= 0: use the default - */ -void RplVirtualMachine::addThread(RplASItem* initialization, - RplSymbolSpace* spaceInitialization, - RplASItem* statements, - RplSymbolSpace* space, - int maxStack) -{ - RplVMThread* thread = new RplVMThread( - maxStack <= 0 ? m_maxStack : maxStack, this); - m_threads.append(thread); - if (initialization != NULL){ - thread->execute(dynamic_cast(initialization), - spaceInitialization); - } - if (statements != NULL) - thread->execute(dynamic_cast(statements), space); -} -/** - * @brief Tests whether a given flag is set. - * - * @param flag flag to test - * @return true: the flag is set
- * false: otherwise - */ -bool RplVirtualMachine::hasFlag(RplVirtualMachine::VMFlag flag) const -{ - bool rc = (m_flags & flag) != 0; - return rc; -} -/** - * @brief Adds a flag to the current flags. - * - * @param flag - * @return - */ -void RplVirtualMachine::setFlag(RplVirtualMachine::VMFlag flag) -{ - m_flags |= flag; -} - -/** - * @brief Adds a flag to the current flags. - * - * @param flag - * @return - */ -void RplVirtualMachine::clearFlag(RplVirtualMachine::VMFlag flag) -{ - m_flags &= ~flag; -} - -/** - * @brief Returns the trace writer. - * @return the trace writer - */ -RplWriter* RplVirtualMachine::traceWriter() const -{ - return m_traceWriter; -} - -/** - * @brief Sets the trace writer. - * - * @param traceWriter the new writer - */ -void RplVirtualMachine::setTraceWriter(RplWriter* traceWriter) -{ - m_traceWriter = traceWriter; -} -/** - * @brief Returns the abstract symbol tree. - * - * @return the abstract symbol tree - */ -RplASTree& RplVirtualMachine::tree() const -{ - return m_tree; -} - diff --git a/rplexpr/rplvm.hpp b/rplexpr/rplvm.hpp deleted file mode 100644 index a22d860..0000000 --- a/rplexpr/rplvm.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 RplVmException : public RplException { -public: - RplVmException(const char* message, ...); -}; -class RplStackFrame { -public: - RplStackFrame(RplSymbolSpace* symbols); - ~RplStackFrame(); -public: - RplASVariant& valueOfVariable(int index); - RplSymbolSpace* symbols() const; - -private: - int m_countVariables; - RplASVariant* m_variables; - RplSymbolSpace* m_symbols; -}; - -class RplVirtualMachine; -class RplVMThread -{ - friend class RplASItem; - friend class RplASStatement; - friend class RplASCalculable; - friend class RplASCondition; -public: - typedef QList StackFrameList; -public: - RplVMThread(int maxStack, RplVirtualMachine* vm); -public: - void execute(RplASNode1* statements, RplSymbolSpace* space); - virtual void debug(RplASNode1* statement); - RplWriter* errorWriter() const; - void setErrorWriter(RplWriter* errorWriter); - RplLogger* logger() const; - RplASVariant& reserveValue(); - RplASVariant& topOfValues(); - RplASVariant& top2OfValues(); - RplASVariant& popValue(); - void valueToTop(RplSymbolSpace* symbolSpace, int variableNo); - RplASVariant& lValue(RplASItem* item); - RplASVariant& valueOfVariable(RplSymbolSpace* symbolSpace, int variableNo); - bool tracing() const; - void setTracing(bool tracing); - RplVirtualMachine* vm() const; - -protected: - int m_id; - bool m_debugMode; - bool m_singleStep; - bool m_tracing; - int m_maxStack; - StackFrameList m_frameStack; - int m_topOfFrames; - QList m_valueStack; - int m_topOfValues; - RplVirtualMachine* m_vm; - RplLogger* m_logger; -private: - static int m_nextId; -}; - -class RplVirtualMachine -{ -public: - enum VMFlag { - VF_UNDEF, - VF_TRACE_STATEMENTS = 1<<1, - VF_TRACE_LOCALS = 1<<2, - VF_TRACE_AUTO_VARIABLES = 1<<3 - - }; - typedef QList LineList; -public: - RplVirtualMachine(RplASTree& tree, RplSource& source, int maxStack = 1024); -public: - void executeModule(const char* module); - void addThread(RplASItem* initialization, - RplSymbolSpace* spaceInitialization, - RplASItem* statements, RplSymbolSpace* space, - int maxStack = 0); - bool hasFlag(VMFlag flag) const; - void setFlag(VMFlag flag); - void clearFlag(VMFlag flag); - RplWriter* traceWriter() const; - void setTraceWriter(RplWriter* traceWriter); - RplASTree& tree() const; - -private: - int m_maxStack; - QList m_threads; - int m_flags; - RplSource& m_source; - RplASTree& m_tree; - LineList m_trace; - RplWriter* m_traceWriter; -}; - -#endif // RPLVM_HPP diff --git a/rplmath/rplenigma.cpp b/rplmath/rplenigma.cpp deleted file mode 100644 index eefa89b..0000000 --- a/rplmath/rplenigma.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* - * - * 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. - */ -/** @file - * - * @brief Implements encryption and decryption engines. - */ -/** @file rplmath/rplenigma.hpp - * - * @brief Definitions for encryption and decryption engines. - */ - -#include "rplmath/rplmath.hpp" - -/** @class RplEnigma::secret_t rplenigma.hpp "rplmath/rplenigma.hpp" - * - * @brief Stores the internal structure of a secret. - * - * A secret can be a password, a salt, a certificate or simlar - * which makes an encryption individually. - */ -/** @class RplEnigma rplenigma.hpp "rplmath/rplenigma.hpp" - * - * @brief Implements a portable en/decryption engine. - * - * It is used a symetric encryption. - * Therefore the encrypt methods can be used for decryption too. - * - * The encryption can be unique by using certificates. - * A certificate is a series of bytes affecting the pseudo random sequence. - * - * More than one certificate can be used. - * - * The encryption is done with an pseudo random generator. - * - * The portability (over byte order) is guaranteed if the - * pseudo random generator is portable (nomalizing big/little endian) - * and a 64 bit integer arithmetic is available. - * - */ -const char* RplEnigma::SET_DECIMALS = "0123456789"; -const char* RplEnigma::SET_HEXDIGITS = "0123456789abcdef"; -const char* RplEnigma::SET_ALPHANUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz_"; -const char* RplEnigma::SET_FILENAME = " !^°$%&=+~#-.0123456789ABCDEFGHIJKLM" - "NOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; -const char* RplEnigma::SET_32_127 = " !\"#$%&'()*+,-./0123456789:;<=>?@" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f"; -const char* RplEnigma::SET_32_255 = " !\"#$%&'()*+,-./0123456789:;<=>?@" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; -const char* RplEnigma::SET_PRINTABLE_127 = - "\t\r\n !\"#$%&'()*+,-./0123456789:;<=>?@" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f"; -const char* RplEnigma::SET_PRINTABLE_255 = - "\t\r\n !\"#$%&'()*+,-./0123456789:;<=>?@" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f" - "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" - "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" - "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" - "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; - -/** - * @brief Constructor. - * - * @param random pseudo random generator - */ -RplEnigma::RplEnigma(RplRandom* random) : - m_random(random), - m_ownsRandom(false), - m_secrets(), - m_randomCalls(0), - m_randomSource("4711") { - m_randomSource.reserve(8096); - if(random == NULL) { - m_random = new RplRandom(); - m_ownsRandom = true; - } -} -/** - * @brief Destructor. - */ -RplEnigma::~RplEnigma() { - if(m_ownsRandom) { - delete m_random; - m_random = NULL; - } -} - -/** - * @brief Reads a certificate. - * - * @param filename the name of the certificate - * - * @return empty string: error while reading
- * otherwise: the certificate as byte array - */ -QByteArray RplEnigma::readCertificate(const char* filename) { - QByteArray rc = "not implemented: readCertificate(): "; - rc += filename; - assert(rc.isEmpty()); - return rc; -} - -inline void buildBooster(QByteArray& booster, const char* charSet) { - int size = 257; - booster.fill(0, size); - int ix = 0; - unsigned char cc; - while((cc = (unsigned char) *charSet++) != '\0') { - booster[cc] = ++ix; - } - booster[0] = ix; -} - -/** - * @brief Encodes a string in place with a given character set. - * - * If a character of data is not included in the character set - * it remains unchanged - * - * @param data IN: data to encode
- * OUT: data encoded - * @param size length of data. If 0 - * @param charSet a string containing all valid characters - * @param booster a performance booster. Once built it can be shared - * between all calls of encodeChar() and decodeChar() - * with the same character set
- * IN: "": not initialized otherwise: ready for work - * OUT: ready for work - */ -void RplEnigma::encode(char* data, int size, const char* charSet, - QByteArray& booster) { - if(booster.length() == 0) { - buildBooster(booster, charSet); - } - int lengthOfCharSet = (int)(unsigned char) booster.at(0); - for(int ii = 0; ii < size; ii++) { - unsigned char cc = (unsigned char) data[ii]; - int ix = booster.at(cc); - if(ix != 0) { - int next = nextInt(lengthOfCharSet); - int ix2 = (ix - 1 + next) % lengthOfCharSet; - data[ii] = charSet[ix2]; - } - } -} - -/** - * @brief Decodes a string in place with a given character set. - * - * If a character of data is not included in the character set - * it remains unchanged - * - * @param data IN: data to decode
- * OUT: data decoded - * @param size length of data. If 0 - * @param charSet a string containing all valid characters - * @param booster a performance booster. Once built it can be shared - * between all calls of encodeChar() and decodeChar() - * with the same character set
- * IN: "": not initialized otherwise: ready for work - * OUT: ready for work - */ -void RplEnigma::decode(char* data, int size, const char* charSet, - QByteArray& booster) { - if(booster.length() == 0) { - buildBooster(booster, charSet); - } - int lengthOfCharSet = (int)(unsigned char) booster.at(0); - for(int ii = 0; ii < size; ii++) { - unsigned char cc = (unsigned char) data[ii]; - int ix = booster.at(cc); - if(ix != 0) { - int next = nextInt(lengthOfCharSet); - int ix2 = (lengthOfCharSet + ix -1 - next) % lengthOfCharSet; - data[ii] = charSet[ix2]; - } - } -} -/** - * @brief Encodes or decode a byte array. - * - * The encoding method is symetric. Therefore it can encode and decode. - * - * @param data IN: data to encode/decoded
- * OUT: data encoded/decoded - */ -void RplEnigma::change(QByteArray& data) { - int randomLength = m_randomSource.length(); - for(int ix = data.length() - 1; ix >= 0; ix--) { - char item = data.at(ix); - item = (item ^ nextInt(0xff) ^ m_randomSource.at(ix % randomLength)); - data[ix] = item; - } -} - -/** - * @brief Adds a random source given by an byte array. - * - * A random source can be the binary form of an certificate, - * a salt, a password or similar. - * - * @param byteSecret a byte sequence which influences the random generation - */ -void RplEnigma::addByteSecret(QByteArray byteSecret) { - // we expand it to a multiple of 64 bit: - int oldSize = byteSecret.length(); - int newSize = (oldSize + 7) / 8 * 8; - int ix; - if(newSize > oldSize) { - byteSecret.resize(newSize); - int sum = 0; - int start = oldSize > 8 ? oldSize - 8 : 0; - for(ix = start; ix < oldSize; ix++) { - sum += ix + byteSecret.at(ix); - } - for(ix = oldSize; ix < newSize; ix++) { - sum += ix + 7; - byteSecret[ix] = (char)(sum + byteSecret.at(ix-1)); - } - } - int count = newSize / 8; - secret_t* secret = new secret_t(); - secret->m_count = count; - secret->m_list = new u_int64_t[count]; - m_secrets.append(secret); - for(ix = 0; ix < count; ix++) { - u_int64_t value = 0; - for(int ix2 = 0; ix2 < 8; ix2++) - value = (value << 8) + byteSecret.at(ix * 8 + ix2); - secret->m_list[ix] = value; - } - QByteArray value; - QCryptographicHash hash(QCryptographicHash::Md5); - RplRandom rand; - hash.addData(m_randomSource.constData(), 4); - for(ix = 0; ix < byteSecret.length(); ix++) { - hash.addData(byteSecret.constData() + ix, 1); - QByteArray current = hash.result(); - int ix2 = rand.nextInt(0, m_randomSource.length() - 1); - m_randomSource[ix2] = (m_randomSource.at(ix2) ^ current.at(0)); - m_randomSource.insert(0, current); - } -} - -/** - * @brief Returns the next random integer - * @param maxValue - * @return - */ -int RplEnigma::nextInt(int maxValue) { - u_int64_t seed = 0; - QList::const_iterator it; - int ixSecret = m_randomCalls++; - int ix = ixSecret; - for(it = m_secrets.constBegin(); it != m_secrets.constEnd(); ++it) { - secret_t* secret = *it; - seed |= ((secret->m_list[ixSecret % secret->m_count]) >> (ix % 8)); - } - m_random->xorSeed(seed); - int rc = m_random->nextInt(0, maxValue); - return rc; -} - -/** - * @brief Sets the pseudo random to a define state. - * - * @param seed the initial state - */ -void RplEnigma::setSeed(u_int64_t seed) { - m_random->setSeed(seed); - m_randomCalls = 0; -} - -// ------------------ -#if ! defined RPL_TEST -#define RPL_TEST -#endif -#ifdef RPL_TEST -#include "rplcore/rpltest.hpp" -/** - * @brief Unit test for RplEnigma. - */ -class TestRplEnigma : public RplTest { -public: - TestRplEnigma() : RplTest("RplEnigma") {} - -public: - void testOneCharset(const char* value, const char* charSet, - const char* expected) { - RplEnigma enigma; - enigma.addByteSecret(QByteArray("Geheim")); - enigma.setSeed(0); - QByteArray encoded = value; - QByteArray booster; - enigma.encode(encoded.data(), encoded.length(), charSet, booster); - //printString(encoded.constData()); - QByteArray decoded = encoded; - enigma.setSeed(0); - enigma.decode(decoded.data(), decoded.length(), charSet, booster); - checkE(value, decoded.constData()); - checkE(expected, encoded); - } - - void printCharSets() { - QByteArray value; - value.reserve(256); - unsigned char cc; - for(cc = ' '; cc <= 127; cc++) { - if(cc == '"' || cc == '\\') - value.append('\\'); - value.append(cc); - } - printf("%s\n", value.constData()); - value.resize(0); - for(cc = 128; cc >= 128; cc++) { - char buf[10]; - if(cc % 32 == 0) - value.append("\n"); - sprintf(buf, "\\x%02x", cc); - value.append(buf); - } - printf("%s\n", value.constData()); - } - void printString(const char* value) { - QByteArray v; - unsigned char cc; - while((cc = (unsigned char) *value++) != 0) { - if(cc == '\\' || cc == '"') { - v.append('\\'); - v.append(cc); - } else if(cc >= 127) { - char buffer[10]; - sprintf(buffer, "\\x%02x", cc); - v.append(buffer); - } else { - v.append(cc); - } - } - printf("%s\n", v.constData()); - } - void testOneBytes(const char* bytes) { - RplEnigma enigma; - enigma.addByteSecret("Hello World"); - enigma.setSeed(0x1234); - - QByteArray encoded(bytes); - enigma.change(encoded); - - enigma.setSeed(0x1234); - QByteArray decoded(encoded); - enigma.change(decoded); - checkE(bytes, decoded); - } - - void testBytes() { - testOneBytes("abcdefg"); - testOneBytes("01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - } - - void testCharSet() { - //testOneCharset("&()[]{}Weiß der Geier/Kuckuck?", RplEnigma::SET_32_255, "2Kc\x9a\xfeQ\xd7\xa84sx)*\xfb\xd2z\xf4\"W\xb0\xee\xb0\xd1\x84\xace\xf8_u*T"); - testOneCharset("\\Weiß der Geier/Kuckuck?", RplEnigma::SET_32_127, - "(Z?hßaZ_#/QZ+Oi|SI^=<,)A"); - testOneCharset("01234567890abcdef", RplEnigma::SET_HEXDIGITS, - "c4c25b08735c53a63"); - testOneCharset("data$1%3.^~", RplEnigma::SET_FILENAME, "^voazo-n%$b"); - testOneCharset("Weiß der Geier!", RplEnigma::SET_ALPHANUM, "weyß BCk 19NoO!"); - testOneCharset("12345678901234567890", RplEnigma::SET_DECIMALS, - "97394833084815683977"); - testOneCharset("000000000000000000000000000", RplEnigma::SET_DECIMALS, - "850592651836811261879625929"); - } - - virtual void doIt() { - testBytes(); - testCharSet(); - } -}; - -#endif -void testRplEnigma() { -#ifdef RPL_TEST - TestRplEnigma test; - test.run(); -#endif -} diff --git a/rplmath/rplenigma.hpp b/rplmath/rplenigma.hpp deleted file mode 100644 index e2ed1d3..0000000 --- a/rplmath/rplenigma.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef RPLENIGMA_HPP -#define RPLENIGMA_HPP - -class RplEnigma { -public: - ///> '0'..'9' - static const char* SET_DECIMALS; - ///> '0'..'9' 'a'..'f' - static const char* SET_HEXDIGITS; - ///> '0'..'9' 'A'..'Z' a'..'z' '_' - static const char* SET_ALPHANUM; - ///> '0'..'9' 'A'..'Z' z'..'z' '_' - static const char* SET_FILENAME; - ///> ' ' .. chr(127) - static const char* SET_32_127; - ///> ' ' .. chr(255) - static const char* SET_32_255; - ///> TAB, CR, NL, ' '..chr(127) - static const char* SET_PRINTABLE_127; - ///> TAB, CR, NL, ' '..chr(255) - static const char* SET_PRINTABLE_255; - -protected: - typedef struct { - int m_count; - u_int64_t* m_list; - } secret_t; -public: - RplEnigma(RplRandom* random = NULL); - virtual ~RplEnigma(); -private: - // No copy constructor: no implementation! - RplEnigma(const RplEnigma& source); - // Prohibits assignment operator: no implementation! - RplEnigma& operator =(const RplEnigma& source); -public: - void encode(char* data, int size, const char* charSet, QByteArray& cache); - void decode(char* data, int size, const char* charSet, QByteArray& cache); - void change(QByteArray& data); - void addByteSecret(QByteArray byteSeed); - void setSeed(u_int64_t seed); - QByteArray readCertificate(const char* filename); -protected: - int nextInt(int maxValue); - -protected: - ///> a pseudo random generator - RplRandom* m_random; - ///> true: m_random must be destroyed (in the destructor). - bool m_ownsRandom; - ///> This values will be mixed with m_random' seed - QList m_secrets; - ///> each call of setSeed sets this value to 0. - int m_randomCalls; - ///> a byte sequence derived from the secrets - QByteArray m_randomSource; -}; - -#endif // RPLENIGMA_HPP diff --git a/rplmath/rplmath.hpp b/rplmath/rplmath.hpp deleted file mode 100644 index 81819e9..0000000 --- a/rplmath/rplmath.hpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 RPLMATH_HPP -#define RPLMATH_HPP - -#ifndef RPLCORE_HPP -#include "rplcore/rplcore.hpp" -#endif -#include "rplmath/rplrandom.hpp" -#include "rplmath/rplenigma.hpp" -#include "rplmath/rplmatrix.hpp" - -#endif // RPLMATH_HPP diff --git a/rplmath/rplmatrix.cpp b/rplmath/rplmatrix.cpp deleted file mode 100644 index 872af7b..0000000 --- a/rplmath/rplmatrix.cpp +++ /dev/null @@ -1,517 +0,0 @@ -/* - * RplMatrix.cpp - * - * Created on: 29.05.2014 - * Author: hm - */ - -/** @file - * @brief Implements 2 dimensional matrices. - */ -/** @file rplmath/rplmatrix.hpp - * - * @brief Definitions for 2 dimensional matrices. - */ - -#include "rplmath/rplmath.hpp" - -RplMatrixException::RplMatrixException(const RplMatrix& RplMatrix, - const char* format, ...) : - RplException() -{ - if (! RplMatrix.getName().isEmpty()) - m_message = RplMatrix.getName() + ": "; - char buffer[16*1024]; - - va_list args; - va_start(args, format); - qvsnprintf(buffer, sizeof buffer, format, args); - va_end(args); - m_message += buffer; -} - -/** - * Constructor. - */ -RplMatrix::RplMatrix(const char* name) : - m_rows(0), - m_cols(0), - m_values(NULL), - m_name(name) -{ -} - -/** - * Constructor. - * - * @param rows number of rows - * @param cols number of columns - * @param name the name of the matrix - */ -RplMatrix::RplMatrix(int rows, int cols, const char* name): - m_rows(rows), - m_cols(cols), - m_values(new MatVal[rows*cols]), - m_name(name) -{ -} -/** - * Destructor. - */ -RplMatrix::~RplMatrix() { - delete m_values; - m_values = NULL; -} - -/** - * Copy constructor. - * - * @param source source to copy - */ -RplMatrix::RplMatrix(const RplMatrix& source) : - m_rows(0), - m_cols(0), - m_values(NULL), - m_name(source.m_name + QByteArray("-copy")) -{ - resize(source.m_rows, source.m_cols, source.m_values); -} - -/** - * Checks the validity of the definition parameters. - * - * @param rows the row number - * @param cols the column number - * @throws RplMatrixException - */ -void RplMatrix::checkDefinition(int rows, int cols) const -{ - if (rows < 0) - throw RplMatrixException(*this, "row number negative: %d", rows); - if (cols < 0) - throw RplMatrixException(*this, "column number negative: %d", cols); - if (double(rows) * cols > 1.0*1000*1000) - throw RplMatrixException(*this, "too many elements: %d*%d", rows, cols); -} - -/** - * Checks the validity of the indexes. - * - * @param row the RplMatrix row number: 0..N-1 - * @param col the RplMatrix column number: 0..M-1 - * @throws RplMatrixException - */ -void RplMatrix::check(int row, int col) const -{ - if (row < 0 || row >= m_rows) - throw RplMatrixException(*this, "invalid row: %d not in [0,%d[", row, - m_rows); - if (col < 0 || col >= m_cols) - throw RplMatrixException(*this, "invalid column: %d not in [0,%d[", col, - m_cols); -} -/** - * Checks whether a given Matrix has the same dimensions. - * - * @param operand Matrix to compare - * @throws RplMatrixException - */ -void RplMatrix::checkSameDimension(const RplMatrix& operand) const -{ - if (m_rows != operand.getRows()) - throw RplMatrixException(*this, - "%s has a different row count: %d / %d", - operand.getName().constData(), m_rows, operand.getRows()); - if (m_cols != operand.getCols()) - throw RplMatrixException(*this, - "%s has a different column count: %d / %d", - operand.getName().constData(), m_cols, operand.getCols()); -} - -/** - * Assignment operator. - * - * @param source the source to copy - */ -RplMatrix& RplMatrix::operator =(const RplMatrix& source) -{ - resize(source.m_rows, source.m_cols, source.m_values); - return *this; -} -/** - * Adds a Matrix to the instance. - * - * @param operand Matrix to add - * @return the instance itself - */ -RplMatrix& RplMatrix::operator +=(const RplMatrix& operand) -{ - checkSameDimension(operand); - for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ - m_values[ix] += operand.m_values[ix]; - } - return *this; -} -/** - * Subtracts a matrix from the instance. - * - * @param operand matrix to subtract - * @return the instance itself - */ -RplMatrix& RplMatrix::operator -=(const RplMatrix& operand) -{ - checkSameDimension(operand); - for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ - m_values[ix] -= operand.m_values[ix]; - } - return *this; -} -/** - * Builds the sum of the instance and a given matrix. - * - * @param operand RplMatrix to add - * @return a new RplMatrix with the sum - */ -RplMatrix RplMatrix::operator +(const RplMatrix& operand) -{ - RplMatrix rc(*this); - rc += operand; - return rc; -} -/** - * Builds the difference of the instance and a given matrix. - * - * @param operand matrix to subtract - * @return a new matrix with the difference - */ -RplMatrix RplMatrix::operator -(const RplMatrix& operand) -{ - RplMatrix rc(*this); - rc -= operand; - return rc; -} -/** - * Adds a scalar to the instance. - * - * @param scalar scalar to add - * @return the instance itself - */ -RplMatrix& RplMatrix::operator +=(MatVal scalar) -{ - for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ - m_values[ix] += scalar; - } - return *this; -} -/** - * Adds a scalar to the instance. - * - * @param scalar scalar to add - * @return the instance itself - */ -RplMatrix& RplMatrix::operator -=(MatVal scalar) -{ - for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ - m_values[ix] -= scalar; - } - return *this; -} -/** - * Builds the sum of the instance and a given scalar. - * - * @param scalar scalar to add - * @return a new matrix with the sum - */ -RplMatrix RplMatrix::operator +(MatVal scalar) -{ - RplMatrix rc(*this); - rc += scalar; - return rc; -} -/** - * Builds the difference of the instance and a given scalar. - * - * @param scalar scalar to subtract - * @return a new matrix with the sum - */ -RplMatrix RplMatrix::operator -(MatVal scalar) -{ - RplMatrix rc(*this); - rc -= scalar; - return rc; -} -/** - * Tests the equiness of the instance with a given matrix. - * - * @param operand the matrix to compare - * @return true: the matrices are equal
- * false: otherwise - */ -bool RplMatrix::operator ==(const RplMatrix& operand) const -{ - checkSameDimension(operand); - bool rc = true; - for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ - if (m_values[ix] != operand.m_values[ix]){ - rc = false; - break; - } - } - return rc; -} -/** - * Compares the instance with a given scalar. - * - * @param scalar the scalar to compare - * @return true: all elements are equal to the scalar
- * false: otherwise - */ -bool RplMatrix::operator ==(MatVal scalar) const -{ - bool rc = true; - for (int ix = m_rows * m_cols - 1; ix >= 0; ix--){ - if (m_values[ix] != scalar){ - rc = false; - break; - } - } - return rc; -} -/** - * Sets a new row-column pair. - */ -RplMatrix& RplMatrix::resize(int rows, int cols, const MatVal values[], - MatVal defaultValue) -{ - checkDefinition(rows, cols); - if (rows != m_rows || cols != m_cols){ - delete m_values; - m_values = new MatVal[rows * cols]; - m_rows = rows; - m_cols = cols; - } - if (values == NULL) - { - for (int ix = rows*cols - 1; ix >= 0; ix--){ - m_values[ix] = defaultValue; - } - } else { - for (int ix = rows*cols - 1; ix >= 0; ix--){ - m_values[ix] = values[ix]; - } - } - return *this; -} -/** - * Returns the minimum and the maximum of the instance. - * - * @return a tuple with the minimum and the maximum - */ -Tuple2 RplMatrix::minMax() const -{ -#ifndef DBL_MAX -#define DBL_MAX std::numeric_limits::max() -#define DBL_MIN std::numeric_limits::min() -#endif - Tuple2 rc(DBL_MAX, DBL_MIN); - - for (int ix = m_rows*m_cols - 1; ix >= 0; ix--){ - MatVal x; - if ( (x = m_values[ix]) < rc.m_value1) - rc.m_value1 = x; - if (x > rc.m_value2) - rc.m_value2 = x; - } - return rc; -} - -/** - * Builds a matrix with exchanged rows and cols. - * - * @return the transposed matrix - */ -RplMatrix RplMatrix::transpose() const -{ - RplMatrix rc(m_cols, m_rows); - - for (int row = 0; row < m_rows; row++){ - for (int col = 0; col < m_cols; col++){ - rc.m_values[m_rows*col + row] = m_values[row * m_cols + col]; - } - } - return rc; -} -QByteArray RplMatrix::toString(const char* prefix, const char* format, - const char* rowSeparator, const char* colSeparator) const -{ - char buffer[128]; - Tuple2 minMaxi(minMax()); - QByteArray rc; - qsnprintf(buffer, sizeof buffer, format, minMaxi.m_value1); - int length = strlen(buffer); - qsnprintf(buffer, sizeof buffer, format, minMaxi.m_value2); - int length2 = strlen(buffer); - if (length < length2) - length = length2; - qsnprintf(buffer, sizeof buffer, format, - (minMaxi.m_value1 + minMaxi.m_value2) / 2); - length2 = strlen(buffer); - if (length < length2) - length = length2; - if (prefix == NULL) - prefix = ""; - length = m_rows * m_cols * (length + strlen(colSeparator)) - + m_rows * strlen(rowSeparator) + strlen(prefix) + 20; - rc.reserve(length); - rc += prefix; - rc += "["; - for (int row = 0; row < m_rows; row++){ - for (int col = 0; col < m_cols; col++){ - qsnprintf(buffer, sizeof buffer, format, m_values[m_cols*row + col]); - rc += buffer; - rc += colSeparator; - } - rc += rowSeparator; - } - rc += "]"; - return rc; -} -/** - * Finds the length of a column. - * - * @param text the text to inspect - * @param separator the column separator - * @return the count of chars between start and the next separator - */ -static int lengthOfColumn(const char* text, char separator){ - const char* ptr = text; - while(*ptr == ' ') - ptr++; - char delimiter = 0; - if (*ptr == '"' || *ptr == '\''){ - delimiter = *ptr++; - } - while(*ptr){ - if (*ptr == '\\'){ - ptr++; - if (*ptr != '\0') - ptr++; - } else if (*ptr == separator) - break; - else if (*ptr != delimiter){ - ptr++; - while(*ptr && *ptr != separator) - ptr++; - } - } - int rc = ptr - text; - return rc; -} -/** - * Skips all columns with a content other than a numeric value. - * - * @param line the text line - * @param separator the column separator - * @return the start of a number or "" - */ -static const char* skipNonNumbers(const char* line, char separator) -{ - int len1, len2 = 0; - - while ( (len1 = RplString::lengthOfNumber(line)) == 0 - && (len2 = lengthOfColumn(line, separator)) > 0) - line += len2; - if (*line == separator) - line++; - return line; -} -/** - * Returns the count of numeric numbers in a CSV line. - * - * @param line the line from a CSV file - * @return 0: not only numbers are in the line
- * otherwise: the count of numeric columns in the line - */ -static int countNumbers(const char* line, char separator){ - line = skipNonNumbers(line, separator); - bool again = true; - int rc = 0; - char cc; - while(again && (cc = *line) != '\0' && cc != '\n' && cc != '\r'){ - int length = RplString::lengthOfNumber(line, true); - if (length == 0){ - rc = 0; - again = false; - } else { - line += length; - rc++; - if (*line == separator) - line++; - } - } - return rc; -} -/** - * Reads a file with the CSV (comma separated values) format - * into the instance. - */ -void RplMatrix::readFromCvs(const char* filename, int maxLineLength) -{ - FILE* fp = fopen(filename, "r"); - if (fp == NULL) - throw RplMatrixException(*this, "Cannot open %s (%d)", filename, errno); - char* buffer = new char[maxLineLength + 1]; - const char* line; - char separator = RplString::findCsvSeparator(fp, buffer, maxLineLength); - int rows = 0; - int cols = 0; - int nCols; - // find the count of rows and columns: - while( (line = fgets(buffer, maxLineLength, fp)) != NULL) - { - if ( (nCols = countNumbers(line, separator)) > 0){ - rows++; - if (nCols > cols) - cols = nCols; - } - } - resize(rows, cols); - // find the values - fseek(fp, 0, SEEK_SET); - int row = -1; - while( (line = fgets(buffer, maxLineLength, fp)) != NULL) - { - int nCols; - if ( (nCols = countNumbers(line, separator)) > 0){ - row++; - line = skipNonNumbers(line, separator); - int col = -1; - int length; - const char* ptr; - while( (length = RplString::lengthOfNumber(line, true)) > 0){ - col++; - ptr = line; - line += length; - while(*ptr == ' ') - ptr++; - MatVal value = atof(ptr); - m_values[m_cols*row + col] = value; - if (*line == separator) - line++; - else - break; - } - } - } - - fclose(fp); - delete buffer; -} -void RplMatrix::readFromXml(const char* filename, const char* tagCol, - const char* tagRow, const char* tagTable, - int maxLineLength) -{ - throw RplMatrixException(*this, "readFromXml not implementes: %s %s %s %s %d", - filename, tagCol, tagRow, tagTable, maxLineLength); -} - diff --git a/rplmath/rplmatrix.hpp b/rplmath/rplmatrix.hpp deleted file mode 100644 index 9082408..0000000 --- a/rplmath/rplmatrix.hpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * RplMatrix.hpp - * - * Created on: 29.05.2014 - * Author: hm - */ - -#ifndef RplMatrix_HPP_ -#define RplMatrix_HPP_ - - -class RplMatrix; -/** - * Implements a RplMatrix specific exception. - */ -class RplMatrixException : public RplException -{ -public: - RplMatrixException(const RplMatrix& RplMatrix, const char* format, ...); -}; - -/** - * The type of a RplMatrix element. - */ -typedef qreal MatVal; - -class Tuple2 { -public: - Tuple2(MatVal value1, MatVal value2) : - m_value1(value1), - m_value2(value2) - {} -public: - MatVal m_value1; - MatVal m_value2; -}; -/** - * Implements a RplMatrix with 2 dimensions. - */ -class RplMatrix { -public: - RplMatrix(const char* name = NULL); - RplMatrix(int rows, int cols, const char* name = NULL); - virtual ~RplMatrix(); - RplMatrix(const RplMatrix& source); - RplMatrix& operator =(const RplMatrix& source); -public: - RplMatrix& operator +=(const RplMatrix& operand); - RplMatrix& operator -=(const RplMatrix& operand); - RplMatrix operator +(const RplMatrix& operand); - RplMatrix operator -(const RplMatrix& operand); - RplMatrix& operator +=(MatVal scalar); - RplMatrix& operator -=(MatVal scalar); - RplMatrix operator +(MatVal scalar); - RplMatrix operator -(MatVal scalar); - bool operator ==(const RplMatrix& operand) const; - bool operator ==(MatVal scalar) const; - inline bool operator !=(const RplMatrix& operand) const - { return ! (*this == operand); } - inline bool operator !=(MatVal operand) - { return ! (*this == operand); } -public: - inline const QByteArray& getName() const - { return m_name; } - inline MatVal get(int row, int col) const - { check(row, col); return m_values[row*m_cols + col]; } - inline RplMatrix& set(int row, int col, MatVal value) - { check(row, col); m_values[row*m_cols + col] = value; return *this; } - inline int getRows() const - { return m_rows; } - inline int getCols() const - { return m_cols; } -public: - void checkDefinition(int rows, int cols) const; - void check(int row, int col) const; - void checkSameDimension(const RplMatrix& operand) const; - RplMatrix& resize(int rows, int cols, const MatVal values[] = NULL, - MatVal defaultValue = 0.0); - Tuple2 minMax() const; - RplMatrix transpose() const; - QByteArray toString(const char* prefix = NULL, - const char* format = "%f", - const char* rowSeparator = "\n", - const char* colSeparator = ",") const; - void readFromCvs(const char* filename, int maxLineLength = 1024*1024); - void readFromXml(const char* filename, const char* tagCol, - const char* tagRow, const char* tagTable, - int maxLineLength = 1024*1024); -protected: - int m_rows; - int m_cols; - MatVal* m_values; - QByteArray m_name; -}; - -#endif /* RplMatrix_HPP_ */ diff --git a/rplmath/rplrandom.cpp b/rplmath/rplrandom.cpp deleted file mode 100644 index 5662f0d..0000000 --- a/rplmath/rplrandom.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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. - */ - -/** @file - * - * @brief Implements pseudo random generators. - */ -/** @file rplmath/rplrandom.hpp - * - * @brief Definitions for pseudo random generators. - */ -#include "rplmath.hpp" - -/** @class RplRandom rplrandom.hpp "rplmath/rplrandom.hpp" - * - * @brief Implements a portable pseudo random generator. - * - */ - -/** - * @brief Constructor. - */ -RplRandom::RplRandom() : - m_seed(0) { -} - -/** - * @brief Destructor. - */ -RplRandom::~RplRandom() { -} - -/** - * @brief Returns the next random number as 64 bit unsigned integer. - * - * @return the next random number. - */ -u_int64_t RplRandom::nextInt64() { - // Donald Knuth recommands (for 64-Bit): - m_seed = m_seed * 6364136223846793005L + 1442695040888963407L; - return m_seed; -} -/** - * @brief Sets the random seed. - * - * @param seed the new seed. - */ -void RplRandom::setSeed(u_int64_t seed) { - m_seed = seed; -} -/** - * @brief Modifies the seed. - * - * @param seed the XOR operand. - */ -void RplRandom::xorSeed(u_int64_t seed) { - m_seed ^= seed; -} - -/** - * @brief nextByte returns the next random byte. - * - * @return a pseudo random value 0..255 - */ -quint8 RplRandom::nextByte() { - u_int64_t value = nextInt64(); - // forget the last 3 bits: - quint8 rc = (quint8)(value >> 3) % 256; - return rc; -} - -/** - * @brief Returns the next pseudo random integer. - * - * @param minValue the minimal result (including) - * @param maxValue the maximal result (includeing) - * @return the next integer - */ -int RplRandom::nextInt(int minValue, int maxValue) { - u_int64_t value = nextInt64(); - u_int64_t diff = maxValue - minValue; - int rc; - if(diff <= 0) - rc = minValue; - else - rc = (int)(minValue + value % diff); - return rc; -} - -/** - * @brief Returns a random string. - * - * @param length the length of the result - * @param minChar all characters of the result are greater or equal than this value - * @param maxChar all characters of the result are lower or equal than this value - * @return a random string - */ -QByteArray RplRandom::nextString(int length, char minChar, char maxChar) { - QByteArray rc; - rc.resize(length); - for(int ii = 0; ii < length; ii++) { - rc[ii] = nextInt(minChar, maxChar); - } - return rc; -} - -/** - * @brief Returns a random string. - * - * @param length the length of the result - * @param charSet a string with all allowed characters - * @return a random string with characters from the given set - */ -QByteArray RplRandom::nextString(int length, char* charSet) { - QByteArray rc; - rc.resize(length); - int ubound = strlen(charSet) - 1; - for(int ii = 0; ii < length; ii++) { - rc[ii] = charSet[nextInt(0, ubound)]; - } - return rc; -} - diff --git a/rplmath/rplrandom.hpp b/rplmath/rplrandom.hpp deleted file mode 100644 index a041fb6..0000000 --- a/rplmath/rplrandom.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef RPLRANDOM_HPP -#define RPLRANDOM_HPP - -#include -class RplRandom { -public: - RplRandom(); - virtual ~RplRandom(); -private: - // No copy constructor: no implementation! - RplRandom(const RplRandom& source); - // Prohibits assignment operator: no implementation! - RplRandom& operator =(const RplRandom& source); -public: - virtual u_int64_t nextInt64(); - virtual void setSeed(u_int64_t seed); - void xorSeed(u_int64_t seed); - quint8 nextByte(); - int nextInt(int minValue, int maxValue); - QByteArray nextString(int length = 8, char minChar = ' ', char maxChar = 127); - QByteArray nextString(int length, char* charSet); -protected: - u_int64_t m_seed; -}; - - -#endif // RPLRANDOM_HPP diff --git a/rplmodules.hpp b/rplmodules.hpp deleted file mode 100644 index 86ff3b9..0000000 --- a/rplmodules.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef RPLMODULES_HPP -#define RPLMODULES_HPP - -enum { - RPLMODULE_LOGGER = 101, - RPLMODULE_CONFIG, - RPLMODULE_CONTAINER, - RPLMODULE_EXCEPTION, - RPLMODULE_TEST, // 105 - RPLMODULE_TCPSERVER, - RPLMODULE_TCPCLIENT, - RPLMODULE_TCPPEER, - RPLMODULE_TERMINATOR, - RPL_MODULE_ASTREE, // 110 - RPL_MODULE_ASCLASSES, - RPL_MODULE_LEXER, - RPL_MODULE_SOURCE, - RPL_MODULE_VM, - RPL_MODULE_MFPARSER, // 115 - // last element: - RPLMODULE_COUNT -}; -#define RPL_FIRST_OF(moduleNo) (moduleNo*100+1) -class RplModules{ -public: - static int fileToNumber(const char* file); - static const char* numberToFile(int location); -}; - - -#endif // RPLMODULES_HPP diff --git a/rplnet/rplnet.hpp b/rplnet/rplnet.hpp deleted file mode 100644 index f9c4731..0000000 --- a/rplnet/rplnet.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 RPLNET_HPP -#define RPLNET_HPP - -#include -#include -#include -#include -#include -#include -#include - -//includes implicite rplcore.hpp: -#include "../rplmath/rplmath.hpp" -#include "rpltcppeer.hpp" -#include "rpltcpserver.hpp" -#include "rpltcpclient.hpp" -#include "rplnetconfig.hpp" - -#endif // RPLNET_HPP diff --git a/rplnet/rplnetconfig.cpp b/rplnet/rplnetconfig.cpp deleted file mode 100644 index bdb28d5..0000000 --- a/rplnet/rplnetconfig.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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 "rplnetconfig.hpp" - -const char* RplNetConfig::IP = "connection.ip"; -const char* RplNetConfig::PORT = "connection.port"; -const char* RplNetConfig::SLEEP_MILLISEC = "connection.sleepmillisec"; diff --git a/rplnet/rplnetconfig.hpp b/rplnet/rplnetconfig.hpp deleted file mode 100644 index 4d58ef0..0000000 --- a/rplnet/rplnetconfig.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 RPLNETCONFIG_HPP -#define RPLNETCONFIG_HPP - -class RplNetConfig -{ -public: - static const char* IP; - static const char* PORT; - static const char* SLEEP_MILLISEC; -}; - -#endif // RPLNETCONFIG_HPP diff --git a/rplnet/rpltcpclient.cpp b/rplnet/rpltcpclient.cpp deleted file mode 100644 index 7ae57df..0000000 --- a/rplnet/rpltcpclient.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 "rplnet.hpp" - -enum { - LOC_1 = RPL_FIRST_OF(RPLMODULE_TCPCLIENT), // 10701 - LOC_HANDLE_ERROR_1, - LOC_SET_REMOTE_ADDRESS_1, -}; - -/** @class RplTcpClient rpltcpclient.hpp "rplnet/rpltcpclient.hpp" - * - * @brief Implements a TCP client. - * - * Use the protocol defined at RplTcpServer. - */ -/** - * @brief Constructor. - * - * @param configurator some parameters will be get from this configurator - * @param thread current thread. Used for sleep() - * @param terminator NULL or for controlled termination - * @param logger a logger - */ -RplTcpClient::RplTcpClient(RplConfigurator& configurator, QThread* thread, - RplTerminator* terminator, - RplLogger* logger) : - m_peer(new RplTcpPeer(configurator, thread, terminator, false, logger)), - m_logger(logger), - m_configurator(configurator){ - QByteArray ip = configurator.asString(RplNetConfig::IP, "localhost"); - int port = configurator.asInt(RplNetConfig::PORT, 12345); - if(! ip.isEmpty() && port != 0) - setRemoteAddress(ip.constData(), port); -} - -/** - * @brief Destructor. - */ -RplTcpClient::~RplTcpClient() { - delete m_peer; - m_peer = NULL; -} - -/** - * @brief Defines the remote address for a client. - * @param ip NULL or the ip to connect - * @param port 0 or the port to connect - */ -void RplTcpClient::setRemoteAddress(const char* ip, int port) { - QTcpSocket* socket = (QTcpSocket*) m_peer->getSocket(); - delete socket; - if(ip == NULL || port == 0) - m_peer->setSocket(NULL); - else { - socket = new QTcpSocket(); - connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(handleError(QAbstractSocket::SocketError))); - m_peer->setSocket(socket); - m_peer->setAddress(ip, port); - m_logger->logv(LOG_INFO, LOC_SET_REMOTE_ADDRESS_1, - "connect with %s:%d", ip, port); - socket->connectToHost(QString(ip), port); - socket->waitForConnected(); - } -} - -/** - * @brief Returns the peer info. - * @return the peer info - */ -RplTcpPeer* RplTcpClient::getPeer() const { - return m_peer; -} - -/** - * @brief Handles a network error. - * - * @param socketError the error code - */ -void RplTcpClient::handleError(QAbstractSocket::SocketError socketError) { - if (m_logger != NULL) - m_logger->logv(LOG_ERROR, LOC_HANDLE_ERROR_1, "Network error %d", - socketError); -} - -/** @class RplClientThread rpltcpclient.hpp "rplnet/rpltcpclient.hpp" - * - * @brief Implements a thread usable for a tcp client. - * - * Each RplTcpPeer needs a thread. Therefore this class provides all things - * needed for a RplTcpClient which uses a RplTcpPeer. - */ - -/** - * @brief Constructor. - * - * @param configurator delivers some connection parameters - * @param logger the logger. If NULL a default logger will be used - */ -RplClientThread::RplClientThread(RplConfigurator& configurator, - RplLogger* logger) : - m_client(NULL), - m_logger(logger), - m_configurator(configurator), - m_ownsLogger(false) { - m_client = new RplTcpClient(configurator, this, NULL, logger); -} -/** - * @brief Destructor. - */ -RplClientThread::~RplClientThread() { - delete m_client; - m_client = NULL; - if(m_ownsLogger) { - delete m_logger; - m_logger = NULL; - } -} -/** - * @brief Returns the peer which can be used for sending and receiving messages. - * - * @return the peer - */ -RplTcpPeer* RplClientThread::getPeer() const { - return m_client->getPeer(); -} - -/** - * @brief Returns the logger of the thread. - * @return the logger - */ -RplLogger* RplClientThread::getLogger() const { - return m_logger; -} - -/** - * @brief Contains the main method of the thread. - * - * Calls doIt() for the real things. - */ -void RplClientThread::run() { - doIt(); -} diff --git a/rplnet/rpltcpclient.hpp b/rplnet/rpltcpclient.hpp deleted file mode 100644 index 7ba45a5..0000000 --- a/rplnet/rpltcpclient.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 RPLTCPCLIENT_HPP -#define RPLTCPCLIENT_HPP - -#ifndef RPLNET_HPP -#include "rplnet.hpp" -#endif - -class RplTcpPeer; - -class RplTcpClient : public QObject { - Q_OBJECT -public: - RplTcpClient(RplConfigurator& configurator, QThread* thread, - RplTerminator* terminator, - RplLogger* logger = NULL); - virtual ~RplTcpClient(); -private: - // No copy constructor: no implementation! - RplTcpClient(const RplTcpClient& source); - // Prohibits assignment operator: no implementation! - RplTcpClient& operator =(const RplTcpClient& source); -public: - RplTcpPeer* getPeer() const; -private: - void setRemoteAddress(const char* ip, int port); -public slots: - void handleError(QAbstractSocket::SocketError socketError); -private: - RplTcpPeer* m_peer; - RplLogger* m_logger; - RplConfigurator& m_configurator; -}; - -class RplClientThread : public QThread { -public: - RplClientThread(RplConfigurator& configurator, - RplLogger* logger = NULL); - virtual ~RplClientThread(); -private: - // No copy constructor: no implementation! - RplClientThread(const RplClientThread& source); - // Prohibits the assignment operator. Not implemented! - RplClientThread& operator=(const RplClientThread& source); -public: - /** - * @brief Does the main task of the thread. - * - * Will be called from QThread::run(). - * The implementations of this abstract method should be call getPeer() - * to send and receive messages. - */ - virtual void doIt() = 0; - RplTcpPeer* getPeer() const; - RplLogger* getLogger() const; -private: - virtual void run(); -protected: - RplTcpClient* m_client; - RplLogger* m_logger; - RplConfigurator& m_configurator; -private: - bool m_ownsLogger; -}; - -#endif // RPLTCPCLIENT_HPP diff --git a/rplnet/rpltcppeer.cpp b/rplnet/rpltcppeer.cpp deleted file mode 100644 index d45946f..0000000 --- a/rplnet/rpltcppeer.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* - * 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 "rplnet.hpp" - -enum { - LOC_SEND_1 = RPL_FIRST_OF(RPLMODULE_TCPPEER), // 10801 - LOC_READ_BYTES_1, - LOC_READ_BYTES_2, - LOC_READ_BYTES_3, - LOC_READ_BYTES_4, - LOC_HANDLE_ERROR_1, - LOC_SEND_2, -}; - -static int s_dummy = 0; - -/** @class RplTcpPeer rpltcppeer.hpp "rplnet/rpltcppeer.hpp" - * - * @brief Implements the common things for TCP server and client. - * - * The communication is done with the following protocol: - *
    - *
  • The data is transmitted via TCP.
  • - *
  • The data exchange is done with info units. - *
  • Each info unit contains a header and the data.
  • - *
- * The format of the header: - *
FLAGS [SALT] COMMAND SIZE
- * 
- *
    - *
  • FLAGS (1 byte): a XOR sum of the flags defined in rpltcppeer::flag_t.
  • - *
  • SALT (4 byte): a random value. Controls the encryption. Only available if FLAG_ENCRYPT is set.
  • - *
  • COMMAND (5 byte): define the task to do (client to server) or the answer (server to client). - *
  • SIZE (2 or 4 byte): the size of the data behind the header. 4 bytes if FLAG_4_BYTE_SIZE is set.
  • - *
- * - */ - -/** - * @brief Creates an instance of a RplTcpPeer. - * - * @param configurator delivers some connection parameters - * @param thread the current thread. Used for sleep() - * @param terminator NULL or for controlled thread termination - * @param logger logger. If Null the global logger will be taken (not thread safe!) - * @return an instance of RplTcpPeer - */ -RplTcpPeer* RplTcpPeer::createPeer(RplConfigurator& configurator, - QThread* thread, - RplTerminator* terminator, - RplLogger* logger) { - return new RplTcpPeer(configurator, thread, terminator, logger); -} - -/** - * @brief Constructor. - * - * @param configurator delivers some connection parameters - * @param thread the current thread. Used for sleep() - * @param terminator NULL or for controlled thread termination - * @param isServer true: the receiving does have a timeout - * @param logger logger. If Null the global logger will be taken (not thread safe!) - */ -RplTcpPeer::RplTcpPeer(RplConfigurator& configurator, QThread* thread, - RplTerminator* terminator, - bool isServer, - RplLogger* logger) : - m_socket(NULL), - m_logger(logger == NULL ? RplLogger::globalLogger() : logger), - m_thread(thread), - m_random(), - m_timeout(isServer ? 0 : configurator.asInt("connection.timeout", 60)), - m_terminator(terminator), - m_configurator(configurator), - m_isServer(isServer), - m_dataLocker(QMutex::NonRecursive), - m_waitForData() { - // Simulate a true random with time, and addresses from stack and code segment: - m_random.setSeed(time(NULL) + ((qint64) this << 8) + ((qint64) &s_dummy << 16) - + ((qint64) &createPeer << 24)); -} - -/** - * @brief Destructor. - */ -RplTcpPeer::~RplTcpPeer() { - -} - -/** - * @brief Sends a message via TCP. - * - * @param flags a sum of FLAGS_... constants - * @param command defines the content of the message - * @param data NULL or additional data - * @return true: success
- * false: error occurred - */ -bool RplTcpPeer::send(qint8 flags, const char* command, - const QByteArray& data) { - bool rc = false; - QByteArray header; - QByteArray data2 = RplString::toCString(data.constData(), 20); - m_logger->logv(LOG_INFO, LOC_SEND_1, "send: flags: %x %s %s (%d)", - flags, command, data2.constData(), data.length()); - header.reserve(16); - header.append((char) flags); - if(flags & FLAG_ENCRYPT) { - header.append((char) m_random.nextByte()); - header.append((char) m_random.nextByte()); - header.append((char) m_random.nextByte()); - header.append((char) m_random.nextByte()); - } - unsigned int length = data.length(); - header.append(char(length % 256)); - header.append(char((length >> 8) % 256)); - if(flags & FLAG_4_BYTE_SIZE) { - header.append(char((length >> 16) % 256)); - header.append(char((length >> 24) % 256)); - } - length = strlen(command); - header.append(command, length < 5 ? length : 5); - while(length++ < 5) { - header.append(' '); - } - qint64 written = m_socket->write(header.constData(), header.length()); - qint64 written2 = m_socket->write(data); - m_socket->flush(); - int count = 0; - if(written != header.length() || written2 != data.length()) { - int endTime = time(NULL) + m_timeout; - // wait until the data are sent or timeout or external termination: - while(m_socket->bytesToWrite() > 0) { - m_thread->msleep(1); - if(++count % 20 == 0) { - if(m_terminator == NULL || m_terminator->isStopped() - || time(NULL) > endTime) - break; - } - } - } - if(m_logger->isActive(LOG_DEBUG)) - m_logger->logv(LOG_DEBUG, LOC_SEND_1, "send %s: %s len=%d loops=%d %s", - m_address.constData(), command, data.length(), count, - RplString::hexDump((const void*) data.constData(), 16, 16).constData()); - return rc; -} - -/** - * @brief Reads an amount of bytes with a timeout. - * - * @param bytes count of bytes to read - * @param maxTime IN/OUT: last time the read must be ready - * @param loops IN/OUT: number of sleeps - * - * @return "": read not successful: timeout or termination or error
- * otherwise: the read bytes - */ -QByteArray RplTcpPeer::readBytes(int bytes, time_t maxTime, int& loops) { - QAbstractSocket* socket = getSocket(); - bool success = true; - qint64 available; - long msec = m_configurator.asInt(RplNetConfig::SLEEP_MILLISEC, 1); - int divider = 1000L / (msec == 0 ? 1 : msec); - if (divider < 1) - divider = 1; - QMutexLocker locker(&m_dataLocker); - m_dataLocker.lock(); - - while(! m_waitForData.wait(&m_dataLocker, 1000L)){ - if(loops == 0 && ! m_isServer) - maxTime = time(NULL) + m_timeout; - if(++loops % divider == 0 && ! m_isServer) { - if(time(NULL) > maxTime) { - m_logger->logv(LOG_ERROR, LOC_READ_BYTES_1, - "receive: timeout (%d)", m_timeout); - success = false; - break; - } - } - if(m_terminator != NULL && m_terminator->isStopped()) { - m_logger->log(LOG_ERROR, LOC_READ_BYTES_2, "receive: stopped"); - success = false; - break; - } - } - available = socket->bytesAvailable(); - m_logger->logv(LOG_DEBUG, LOC_READ_BYTES_4, - "readBytes(): available: %ld/%ld", available, bytes); - QByteArray rc; - if(success) { - rc = socket->read(bytes); - if(rc.length() != bytes) { - m_logger->logv(LOG_ERROR, LOC_READ_BYTES_3, - "receive: too few bytes: %d of %d", - rc.length(), bytes); - } - } - return rc; -} - -int getInt(const QByteArray& data, int offset, int size) { - int rc = ((int)(unsigned char) data.at(offset++)); - while(--size > 0) { - rc = rc * 256 + (unsigned char) data.at(offset++); - } - return rc; -} - -/** - * @brief Receives a message via TCP. - * - * @param command OUT: defines the content of the read message - * @param data OUT: "" or additional data - * @return true: success
- * false: error occurred - */ -bool RplTcpPeer::receive(QByteArray& command, QByteArray& data) { - bool rc = true; - command.clear(); - data.clear(); - QByteArray header; - header.reserve(16); - int minHeaderSize = 8; - int loops = 0; - time_t maxTime = 0; - quint8 flags = 0; - header = readBytes(minHeaderSize, maxTime, loops); - if(header.length() > 0) { - flags = header.at(0); - int headerSize = minHeaderSize; - if((flags & FLAG_4_BYTE_SIZE) != 0) - headerSize += 2; - if((flags & FLAG_ENCRYPT) != 0) - headerSize += 4; - if(headerSize != minHeaderSize) { - QByteArray restHeader = readBytes(headerSize - minHeaderSize, - maxTime, loops); - if(restHeader.length() == 0) - header.clear(); - else - header.append(restHeader); - } - } - rc = header.length() > 0; - if(rc) { - int offset = (flags & FLAG_ENCRYPT) == 0 ? 6 : 8; - int size = (flags & FLAG_4_BYTE_SIZE) == 0 ? 4 : 2; - int dataLength = getInt(header, offset, size); - command = header.mid(offset - 5, 5); - data = readBytes(dataLength, maxTime, loops); - rc = data.length() == dataLength; - } - return rc; - -} - -/** - * @brief Sends a message and receives an answer message via TCP. - * - * @param flags a sum of FLAGS_... constants - * @param command defines the content of the message to send - * @param data NULL or additional data - * @param answer OUT: the command of the answer - * @param answerData OUT: "" or additional data of the answer - * @return true: success
- * false: error occurred - */ -bool RplTcpPeer::sendAndReceive(quint8 flags, char command [4], - QByteArray* data, QByteArray& answer, - QByteArray& answerData) { - answer.clear(); - answerData.clear(); - bool rc = send(flags, command, data == NULL ? QByteArray("") : *data); - if(rc) - rc = receive(answer, answerData); - return rc; -} - -/** - * @brief Sets the socket. - * - * @param socket the socket to set - */ -void RplTcpPeer::setSocket(QAbstractSocket* socket) { - m_socket = socket; - if (socket != NULL) - connect( m_socket, SIGNAL(readyRead()), SLOT(readTcpData()) ); -} - -/** - * @brief Reads the (ready) data from the socket. - */ -void RplTcpPeer::readTcpData() { - m_waitForData.wakeOne(); -} - -/** - * @brief Handles a network error. - * - * @param socketError the error code - */ -void RplTcpPeer::handleError(QTcpSocket::SocketError socketError) { - m_logger->logv(LOG_ERROR, LOC_HANDLE_ERROR_1, "Network error %d", - socketError); -} - -/** - * @brief Returns a human readable peer address. - * @return a string with the peer address: e.g. "192.16.2.3:44335" - */ -QByteArray RplTcpPeer::getPeerAddress() { - QByteArray rc; - if(m_socket == NULL) - rc = ""; - else - rc = m_socket->peerAddress().toString().toUtf8(); - return rc; -} - -/** - * @brief Returns the socket. - * - * @return the socket - */ -QAbstractSocket* RplTcpPeer::getSocket() const { - return m_socket; -} - -/** - * @brief Returns the port. - * @return the port of the peer. - */ -int RplTcpPeer::getPort(){ - int port = m_configurator.asInt(RplNetConfig::PORT, 12345); - return port; -} -/** - * @brief Returns the ip address. - * @return "": all addresses (for listening)
- * otherwise: the address (e.g. 127.0.0.1) - */ -QByteArray RplTcpPeer::getIp(){ - QByteArray ip = m_configurator.asString(RplNetConfig::IP, ""); - return ip; -} -/** - * @brief Sets the address (ip:port). - * @param ip the ip address - * @param port the port - */ -void RplTcpPeer::setAddress(const char* ip, int port) -{ - m_address = QByteArray(ip) + ":" + QByteArray::number(port); -} diff --git a/rplnet/rpltcppeer.hpp b/rplnet/rpltcppeer.hpp deleted file mode 100644 index d040496..0000000 --- a/rplnet/rpltcppeer.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 RPLTCPPEER_HPP -#define RPLTCPPEER_HPP - -#ifndef RPLNET_HPP -#include "rplnet.hpp" -#endif - -class RplTcpPeer : public QObject { - Q_OBJECT -public: - enum { - ///> standard behaviour - FLAG_UNDEF, - ///> if set: the size field is 4 byte (max. 4 GByte) instead of 2 byte (64kByte) - FLAG_4_BYTE_SIZE = 1, - ///> if set the data are compressed by the gzip algorithm - FLAG_GZIP = 2, - ///> if set the data are encrypted. In front of the size field a 4 byte salt field exists. - ///> In this case all data behind the salt field are encrypted. - FLAG_ENCRYPT = 4, - ///> connection initialization of - } flag_t; -public: - static RplTcpPeer* createPeer(RplConfigurator& configurator, - QThread* thread, - RplTerminator* terminator, - RplLogger* logger = NULL); -public: - RplTcpPeer(RplConfigurator& configurator, QThread* thread, - RplTerminator* terminator, - bool isServer, - RplLogger* logger = NULL); - virtual ~RplTcpPeer(); -private: - // No copy constructor: no implementation! - RplTcpPeer(const RplTcpPeer& source); - // No assignment operator: no implementation! - RplTcpPeer& operator =(const RplTcpPeer& source); -public: - virtual bool send(qint8 flags, const char* command, const QByteArray& data); - virtual bool receive(QByteArray& command, QByteArray& data); - virtual bool sendAndReceive(quint8 flags, char command [4], - QByteArray* data, QByteArray& answer, QByteArray& answerData); - void setSocket(QAbstractSocket* socket); - QAbstractSocket* getSocket() const; - QByteArray getPeerAddress(); - void handleError(QTcpSocket::SocketError socketError); - int getPort(); - QByteArray getIp(); - void setAddress(const char* ip, int port); -private: - QByteArray readBytes(int bytes, time_t maxTime, int& loops); - -public slots: - void readTcpData(); - -private: - QAbstractSocket* m_socket; - // : - QByteArray m_address; - RplLogger* m_logger; - QByteArray m_received; - int m_expected; - QThread* m_thread; - // Only used for salt generation: - RplRandom m_random; - ///> maximum allowed time (in seconds) for sending/receiving one info unit - int m_timeout; - ///> for controlled termination - RplTerminator* m_terminator; - RplConfigurator& m_configurator; - bool m_isServer; - QMutex m_dataLocker; - QWaitCondition m_waitForData; -}; - -#endif // RPLTCPPEER_HPP diff --git a/rplnet/rpltcpserver.cpp b/rplnet/rpltcpserver.cpp deleted file mode 100644 index c9ba87f..0000000 --- a/rplnet/rpltcpserver.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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 "rplnet.hpp" - -enum { - LOC_RUN_1 = RPL_FIRST_OF(RPLMODULE_TCPSERVER), // 10601 - LOC_TCP_TREAD_RUN_1, - LOC_TCP_TREAD_RUN_2, - LOC_TCP_INCOMING_CONNECTION_1, -}; - -/** @class RplTcpThread rpltcpserver.hpp "rplcore/rpltcpserver.hpp" - * - * @brief Serves one connection of a multihreaded TCP server. - * - * Note: The real thing is done by the RplTaskHandler instance. - */ - -/** - * @brief Constructor. - * - * @param configurator delivers some connection parameters - * @param socketDescriptor socket of the connection to handle - * @param threadId an unique id for the thread - * @param handler does the work - */ -RplTcpThread::RplTcpThread(RplConfigurator& configurator, - qintptr socketDescriptor, int threadId, - RplTaskHandler* handler) : - m_threadId(threadId), - m_taskHandler(handler), - m_socketDescriptor(socketDescriptor), - m_configurator(configurator){ -} -/** - * @brief Destructor. - */ -RplTcpThread::~RplTcpThread() { - -} - -/** - * @brief Does the proper thread task. - * - * Initializes the socket and loops for incoming commands. - */ -void RplTcpThread::run() { - QTcpSocket socket; - if(!socket.setSocketDescriptor(getSocketDescriptor())) { - emit error(socket.error()); - } else { - RplTcpPeer peer(m_configurator, this, m_taskHandler->getTerminator(), - true, m_taskHandler->getLogger()); - peer.setSocket(&socket); - QByteArray addr = peer.getPeerAddress(); - m_taskHandler->getLogger()->logv(LOG_DEBUG, LOC_TCP_TREAD_RUN_1, - "RplTcpThread::run(): start Peer: %s", addr.constData()); - while(m_taskHandler->handle(&peer)) { - // do nothing - } - socket.disconnectFromHost(); - socket.waitForDisconnected(); - m_taskHandler->getLogger()->logv(LOG_DEBUG, LOC_TCP_TREAD_RUN_1, - "RplTcpThread::run(): end Peer: %s", addr.constData()); - } -} - -/** - * @brief Returns the thread id. - * - * @return the thread id - */ -int RplTcpThread::getThreadId() const { - return m_threadId; -} -/** - * @brief Returns the task handler. - * - * @return the task handler - */ -RplTaskHandler* RplTcpThread::getTaskHandler() const { - return m_taskHandler; -} -/** - * @brief Returns the tcp socket of the served connection. - * - * @return the socket - */ -qintptr RplTcpThread::getSocketDescriptor() const { - return m_socketDescriptor; -} - -/** @class RplTcpServer rpltcpserver.hpp "rplcore/rpltcpserver" - * - * @brief Implements a multithreaded TCP server. - */ - -/** - * @brief Constructor. - * - * @param configurator some parameters will be get from this configurator - * @param taskHandler this handler reads from the tcp and interprets the content - * @param threadFactory creates a thread for a new connection - * @param logger NULL or logger - * @param parent NULL or the parent which deletes the childen - */ -RplTcpServer::RplTcpServer(RplConfigurator& configurator, - RplTaskHandler* taskHandler, - RplVMThreadFactory& threadFactory, - RplLogger* logger, - QObject* parent) : - QTcpServer(parent), - m_taskHandler(taskHandler), - m_threadId(0), - m_threadFactory(threadFactory), - m_configurator(configurator){ -} - -/** - * @brief The slot handling a new tcp connection. - * - * @param socketDescriptor the tcp socket - */ -void RplTcpServer::incomingConnection(qintptr socketDescriptor) { - RplTcpThread* thread = m_threadFactory.create(m_configurator, - socketDescriptor, ++m_threadId, m_taskHandler); - m_taskHandler->getLogger()->log(LOG_DEBUG, LOC_TCP_INCOMING_CONNECTION_1, - "Connection detected"); - QTcpServer::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - thread->start(); -} - -/** @class RplTcpThread rpltcpserver.hpp "rplcore/rpltcpserver.hpp" - * - * @brief Defines a function pointer type to create a RplTcpThread instance. - * - */ - -/** @class RplTaskHandler rpltcpserver.hpp "rplcore/rpltcpserver.hpp" - * - * @brief An abstract base class for an handler processing an data unit. - * - * The handler knows the stucture of the data unit and can interpret this. - */ -/** - * @brief Constructor - * - * @param configurator delivers some connection parameters - * @param terminator external controller for thread termination - * @param logger the logger - */ -RplTaskHandler::RplTaskHandler(RplConfigurator& configurator, - RplTerminator* terminator, RplLogger* logger) : - m_answerFlags(0), - m_logger(logger), - m_terminator(terminator), - m_configurator(configurator){ -} - -/** - * @brief Destructor. - */ -RplTaskHandler::~RplTaskHandler() { -} - -/** - * @brief Reads one data unit, processes it and sends the answer. - * - * @param peer the communication partner - * @return false: the application should stop
- * true: processing remains - */ -bool RplTaskHandler::handle(RplTcpPeer* peer) { - QByteArray command; - QByteArray data; - QByteArray answer; - QByteArray answerData; - bool rc = true; - if(peer->receive(command, data)) { - rc = process(command, data, answer, answerData); - if(answer.length() > 0) { - peer->send(m_answerFlags, answer, answerData); - } - } - return rc; -} - -/** - * @brief Sets the thead id. - * - * @param id the thread id - */ -void RplTaskHandler::setThreadId(int id) { - m_threadId = id; -} - -/** - * @brief Gets the thread id. - * - * @return the thread id - */ -int RplTaskHandler::getThreadId() const { - return m_threadId; -} - -/** - * @brief Returns the logger. - * - * @return the logger - */ -RplLogger* RplTaskHandler::getLogger() const { - return m_logger; -} - -/** - * @brief Returns the termination controller. - * - * @return the termination controller - */ -RplTerminator* RplTaskHandler::getTerminator() const { - return m_terminator; -} - diff --git a/rplnet/rpltcpserver.hpp b/rplnet/rpltcpserver.hpp deleted file mode 100644 index 2683346..0000000 --- a/rplnet/rpltcpserver.hpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 RPLTCPSERVER_HPP -#define RPLTCPSERVER_HPP - -// the sources generated from QT include this file directly: -#ifndef RPLNET_HPP -#include "rplnet.hpp" -#endif - -class RplTcpPeer; -class RplTaskHandler { -public: - RplTaskHandler(RplConfigurator& configurator, - RplTerminator* terminator, - RplLogger* logger); - virtual ~RplTaskHandler(); -public: - - virtual bool handle(RplTcpPeer* peer); - /** - * @brief Processes one data unit from the socket. - * - * @param command defines the meaning of the information unit - * @param data "" or the data of the information unit - * @param answer OUT: "" or the answer to send back - * @param answerData OUT: "" or the answer data to send back - * @return true: the receiving loop should be continued
- * false: the process should be stopped - */ - virtual bool process(const QByteArray& command, const QByteArray& data, - QByteArray& answer, QByteArray& answerData) = 0; - void setThreadId(int id); - int getThreadId() const; - RplLogger* getLogger() const; - RplTerminator* getTerminator() const; -protected: - quint8 m_answerFlags; -private: - int m_threadId; - RplLogger* m_logger; - RplTerminator* m_terminator; - RplConfigurator& m_configurator; -}; - -class RplTcpThread : public QThread { - Q_OBJECT -public: - RplTcpThread(RplConfigurator& m_configurator, - qintptr socketDescriptor, int threadId, - RplTaskHandler* handler); - virtual ~RplTcpThread(); -private: - // No copy constructor: no implementation! - RplTcpThread(const RplTcpThread& source); - // No assignment operator: no implementation! - RplTcpThread& operator=(const RplTcpThread& source); -public: - void run(); - int getThreadId() const; - RplTaskHandler* getTaskHandler() const; - qintptr getSocketDescriptor() const; - -signals: - void error(QTcpSocket::SocketError socketError); - -private: - // a unique id for the thread - int m_threadId; - // this handler interprets the info from the TCP connection - RplTaskHandler* m_taskHandler; - // the assigned socket - qintptr m_socketDescriptor; - RplConfigurator& m_configurator; -}; -class RplThreadFactory { -public: - virtual RplTcpThread* create(RplConfigurator& configurator, - qintptr socketDescriptor, - int threadId, - RplTaskHandler* handler) = 0; -}; - -class RplTcpPeer; -class RplTcpServer : public QTcpServer, public RplTerminator { - Q_OBJECT -public: - explicit RplTcpServer(RplConfigurator& configurator, - RplTaskHandler* taskHandler, - RplThreadFactory& threadFactory, - RplLogger* logger = NULL, QObject* parent = 0); -private: - // No copy constructor: no implementation! - RplTcpServer(const RplTcpServer& source); - // No assignment operator: no implementation! - RplTcpServer& operator=(const RplTcpServer& source); -public: - RplTcpPeer* getPeer() const; - bool handleTask(); - -protected slots: - void incomingConnection(qintptr socketDescriptor); - -private: - RplTaskHandler* m_taskHandler; - int m_threadId; - RplTcpPeer* m_peer; - RplThreadFactory& m_threadFactory; - RplConfigurator& m_configurator; -}; - -#endif // RPLTCPSERVER_HPP diff --git a/rplstatic/getsrc.pl b/rplstatic/getsrc.pl deleted file mode 100644 index 7618a1f..0000000 --- a/rplstatic/getsrc.pl +++ /dev/null @@ -1,29 +0,0 @@ -#! /usr/bin/perl - -use strict; - -my @rc; -push @rc, &oneDir("../rplcore/*.hpp"); -push @rc, &oneDir("../rplmath/*.hpp"); -push @rc, &oneDir("../rplnet/*.hpp"); -push @rc, "\n"; -push @rc, &oneDir("../rplcore/*.cpp"); -push @rc, &oneDir("../rplmath/*.cpp"); -push @rc, &oneDir("../rplnet/*.cpp"); - -print @rc; -exit 0; - -sub oneDir{ - my $pattern = shift; - my @rc; - open (my $INP, "ls -1 $pattern|") || die "ls -1 $pattern: $!"; - while(<$INP>) { - if (/(\S+)/){ - push(@rc, " $1 \\\n"); - } - } - return @rc; -} - - diff --git a/rplstatic/rplstatic.pro b/rplstatic/rplstatic.pro deleted file mode 100644 index 2263c8d..0000000 --- a/rplstatic/rplstatic.pro +++ /dev/null @@ -1,84 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2014-05-30T21:36:15 -# -#------------------------------------------------- - -QT += network testlib - -QT -= gui - -TARGET = rplstatic -TEMPLATE = lib -CONFIG += staticlib - -INCLUDEPATH = .. - -SOURCES += \ - ../rplcore/rplconfig.cpp \ - ../rplcore/rplcontainer.cpp \ - ../rplcore/rplexception.cpp \ - ../rplcore/rpllogger.cpp \ - ../rplcore/rplstring.cpp \ - ../rplcore/rplterminator.cpp \ - ../rplcore/rpltest.cpp \ - ../rplmath/rplenigma.cpp \ - ../rplmath/rplmatrix.cpp \ - ../rplmath/rplmatrix_test.cpp \ - ../rplmath/rplrandom.cpp \ - ../rplnet/rplnetconfig.cpp \ - ../rplnet/rpltcpclient.cpp \ - ../rplnet/rpltcppeer.cpp \ - ../rplnet/rpltcpserver.cpp \ - ../rplexpr/rpllexer.cpp \ - ../rplexpr/rplsource.cpp \ - ../rplcore/rplqstring.cpp \ - ../rplexpr/rplastree.cpp \ - ../rplexpr/rplasclasses.cpp \ - ../rplexpr/rplmfparser.cpp \ - ../rplexpr/rplvm.cpp \ - ../rplexpr/rplparser.cpp \ - ../rplcore/rplbytestorage.cpp \ - ../rplcore/rplwriter.cpp \ - ../rplcore/rplcharptrmap.cpp - -HEADERS += ../rplmodules.hpp \ - ../rplcore/rplconfig.hpp \ - ../rplcore/rplconfigurator.hpp \ - ../rplcore/rplcontainer.hpp \ - ../rplcore/rplcore.hpp \ - ../rplcore/rplexception.hpp \ - ../rplcore/rpllogger.hpp \ - ../rplcore/rplstring.hpp \ - ../rplcore/rplterminator.hpp \ - ../rplcore/rpltest.hpp \ - ../rplmath/rplenigma.hpp \ - ../rplmath/rplmath.hpp \ - ../rplmath/rplmatrix.hpp \ - ../rplmath/rplrandom.hpp \ - ../rplnet/rplnetconfig.hpp \ - ../rplnet/rplnet.hpp \ - ../rplnet/rpltcpclient.hpp \ - ../rplnet/rpltcppeer.hpp \ - ../rplnet/rpltcpserver.hpp \ - ../rplexpr/rpllexer.hpp \ - ../rplexpr/rplexpr.hpp \ - ../rplexpr/rplsource.hpp \ - ../rplcore/rplqstring.hpp \ - ../rplexpr/rplastree.hpp \ - ../rplexpr/rplasclasses.hpp \ - ../rplexpr/rplmfparser.hpp \ - ../rplexpr/rplvm.hpp \ - ../rplexpr/rplparser.hpp \ - ../rplcore/rplbytestorage.hpp \ - ../rplcore/rplwriter.hpp \ - ../rplcore/rplcharptrmap.hpp - -unix:!symbian { - maemo5 { - target.path = /opt/usr/lib - } else { - target.path = /usr/lib - } - INSTALLS += target -} diff --git a/rplstatic/rplstaticlib.cpp b/rplstatic/rplstaticlib.cpp deleted file mode 100644 index e4c28bf..0000000 --- a/rplstatic/rplstaticlib.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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 "rplstaticlib.hpp" - - -RplStaticLib::RplStaticLib() -{ -} diff --git a/rplstatic/rplstaticlib.hpp b/rplstatic/rplstaticlib.hpp deleted file mode 100644 index 80a926b..0000000 --- a/rplstatic/rplstaticlib.hpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 RPLSTATICLIB_HPP -#define RPLSTATICLIB_HPP - - -class RplStaticLib -{ - -public: - RplStaticLib(); -}; - -#endif // RPLSTATICLIB_HPP diff --git a/static/rplstatic.pro b/static/rplstatic.pro new file mode 100644 index 0000000..82c9e9b --- /dev/null +++ b/static/rplstatic.pro @@ -0,0 +1,83 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-05-30T21:36:15 +# +#------------------------------------------------- + +QT += network testlib + +QT -= gui + +TARGET = rplstatic +TEMPLATE = lib +CONFIG += staticlib + +INCLUDEPATH = .. /usr/include/c++/4.9 + +SOURCES += \ + ../base/ReConfig.cpp \ + ../base/ReContainer.cpp \ + ../base/ReException.cpp \ + ../base/ReLogger.cpp \ + ../base/ReStringUtil.cpp \ + ../base/ReTerminator.cpp \ + ../base/ReTest.cpp \ + ../math/ReEnigma.cpp \ + ../math/ReMatrix.cpp \ + ../math/ReRandom.cpp \ + ../net/ReNetConfig.cpp \ + ../net/ReTCPClient.cpp \ + ../net/ReTCPPeer.cpp \ + ../net/ReTCPServer.cpp \ + ../expr/ReLexer.cpp \ + ../expr/ReSource.cpp \ + ../base/ReQString.cpp \ + ../expr/ReASTree.cpp \ + ../expr/ReASClasses.cpp \ + ../expr/ReMFParser.cpp \ + ../expr/ReVM.cpp \ + ../expr/ReParser.cpp \ + ../base/ReByteStorage.cpp \ + ../base/ReWriter.cpp \ + ../base/ReCharPtrMap.cpp + +HEADERS += ../remodules.hpp \ + ../base/ReConfig.hpp \ + ../base/ReConfigurator.hpp \ + ../base/ReContainer.hpp \ + ../base/rebase.hpp \ + ../base/ReException.hpp \ + ../base/ReLogger.hpp \ + ../base/ReStringUtil.hpp \ + ../base/ReTerminator.hpp \ + ../base/ReTest.hpp \ + ../math/ReEnigma.hpp \ + ../math/remath.hpp \ + ../math/ReMatrix.hpp \ + ../math/ReRandom.hpp \ + ../net/ReNetConfig.hpp \ + ../net/renet.hpp \ + ../net/ReTCPClient.hpp \ + ../net/ReTCPPeer.hpp \ + ../net/ReTCPServer.hpp \ + ../expr/ReLexer.hpp \ + ../expr/reexpr.hpp \ + ../expr/ReSource.hpp \ + ../base/ReQString.hpp \ + ../expr/ReASTree.hpp \ + ../expr/ReASClasses.hpp \ + ../expr/ReMFParser.hpp \ + ../expr/ReVM.hpp \ + ../expr/ReParser.hpp \ + ../base/ReByteStorage.hpp \ + ../base/ReWriter.hpp \ + ../base/ReCharPtrMap.hpp + +unix:!symbian { + maemo5 { + target.path = /opt/usr/lib + } else { + target.path = /usr/lib + } + INSTALLS += target +} diff --git a/static/rplstaticlib.cpp b/static/rplstaticlib.cpp new file mode 100644 index 0000000..dfb4013 --- /dev/null +++ b/static/rplstaticlib.cpp @@ -0,0 +1,15 @@ +/* + * 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 "../static/rplstaticlib.hpp" + + +RplStaticLib::RplStaticLib() +{ +} diff --git a/static/rplstaticlib.hpp b/static/rplstaticlib.hpp new file mode 100644 index 0000000..80a926b --- /dev/null +++ b/static/rplstaticlib.hpp @@ -0,0 +1,21 @@ +/* + * 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 RPLSTATICLIB_HPP +#define RPLSTATICLIB_HPP + + +class RplStaticLib +{ + +public: + RplStaticLib(); +}; + +#endif // RPLSTATICLIB_HPP diff --git a/unittests/main.cpp b/unittests/main.cpp deleted file mode 100644 index 588f9e8..0000000 --- a/unittests/main.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 "../rplmath/rplmath.hpp" - -#include - -void testCore(){ - extern void testRplString(); - testRplString(); - - extern void testRplCharPtrMap(); - testRplCharPtrMap(); - - extern void testRplWriter(); - testRplWriter(); - - extern void testRplByteStorage(); - testRplByteStorage(); - - extern void testRplQString(); - testRplQString(); - - extern void testRplString(); - testRplString(); - - extern void testRplException(); - testRplException(); -} - -void testExpr(){ - extern void testRplMFParser(); - testRplMFParser(); - - extern void testRplBenchmark(); - //testRplBenchmark(); - - extern void testRplVM(); - testRplVM(); - - extern void testRplSource(); - testRplSource(); - - extern void testRplLexer(); - testRplLexer(); - - extern void testRplMFParser(); - testRplMFParser(); - - extern void testRplASTree(); - testRplASTree(); - - extern void testRplVM(); - testRplVM(); - -} - -void testStandard(){ - testExpr(); - testCore(); - extern void testRplMatrix(); - testRplMatrix(); - -} - -void labor(){ -} - -int main(int argc, char *argv[]) -{ - //labor(); - if (argc > 1) - printf("not used: %s\n", argv[1]); - - testStandard(); - -} diff --git a/unittests/rplastree_test.cpp b/unittests/rplastree_test.cpp deleted file mode 100644 index 7ace6e3..0000000 --- a/unittests/rplastree_test.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Unit test of the abstract syntax tree. - */ - - -#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; - RplASTree m_tree; -public: - TestRplASTree() : - RplTest("RplASTree"), - m_source(), - m_reader(m_source), - m_unit("
", "", &m_reader), - m_tree() - {} -public: - void testRplASException() { - try{ - m_reader.addSource("
", "12"); - m_source.addReader(&m_reader); - m_source.addSourceUnit(m_reader.currentSourceUnit()); - const RplSourcePosition* pos = m_source.newPosition(2); - throw RplASException(pos, "simple string: %s", "Hi"); - checkF(true); - } catch (RplASException exc){ - checkE("
:0: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(NULL, m_tree.symbolSpaces()[0], "gugo", - RplASNamedValue::A_GLOBAL); - checkE("gugo", value.name()); - } - virtual void doIt() { - testRplASNamedValue(); - testRplASConstant(); - testRplASException(); - testRplASVariant(); - } -}; -void testRplASTree() { - TestRplASTree test; - test.run(); -} - - - - - - diff --git a/unittests/rplbench.cpp b/unittests/rplbench.cpp deleted file mode 100644 index 86b6674..0000000 --- a/unittests/rplbench.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Unit test of the abstract syntax tree. - */ - - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplBenchmark : public RplTest{ -private: - const char* m_filename; - RplSource m_source; - RplFileReader m_reader; - RplASTree m_tree; -public: - TestRplBenchmark() : - RplTest("RplBenchmark"), - m_filename("/home/ws/qt/rplqt/bench/mfbench.mf"), - m_source(), - m_reader(m_source), - m_tree() - { - m_source.addReader(&m_reader); - m_reader.addSource(m_filename); - } -public: - void benchmark() { - time_t start = time(NULL); - RplMFParser parser(m_source, m_tree); - parser.parse(); - time_t end = time(NULL); - printf("compilation: %d sec\n", int(end - start)); - } - virtual void doIt() { - try{ - RplFileSourceUnit* unit = dynamic_cast - (m_reader.currentSourceUnit()); - if (unit != NULL && ! unit->isOpen()) - throw RplException("file not found: %s", m_filename); - benchmark(); - } catch(RplException ex){ - printf("%s\n", ex.getMessage().constData()); - } - } -}; -void testRplBenchmark() { - TestRplBenchmark test; - test.run(); -} - - - diff --git a/unittests/rplbytestorage_test.cpp b/unittests/rplbytestorage_test.cpp deleted file mode 100644 index 1245694..0000000 --- a/unittests/rplbytestorage_test.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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. -*/ -/** @file - * @brief Unit test of the byte and C string storage. - */ - -#include "rplcore/rplcore.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplByteStorage : public RplTest{ -public: - TestRplByteStorage() : - RplTest("RplByteStorage") - {} -private: - void testChars(){ - RplByteStorage store(100); - char* s1 = store.allocateChars(4); - memcpy((void*) s1, "123", 4); - const char* s2 = store.allocateChars("abc"); - const char* s3 = store.allocateChars("defghij", 3); - checkE(s1, "123"); - checkE(s2, "abc"); - checkE(s3, "def"); - const char* ptr = s1 + 4; - checkT(ptr == s2); - ptr += 4; - checkT(ptr == s3); - } - - void testBytes(){ - RplByteStorage store(100); - uint8_t* s1 = store.allocateBytes(4); - memcpy((void*) s1, "1234", 4); - uint8_t* s2 = store.allocateBytes((void*) "abcd", 4); - uint8_t* s3 = store.allocateBytes((void*) "efghij", 4); - uint8_t* s4 = store.allocateZeros(4); - - checkE("1234abcdefgh", (const char*) s1); - uint8_t* ptr = s1 + 4; - checkT(ptr == s2); - ptr += 4; - checkT(ptr == s3); - ptr += 4; - checkT(ptr == s4); - for (int ii = 0; ii < 4; ii++) - checkE(0, (int) s4[ii]); - } - void testBufferChange(){ - RplByteStorage store(10); - char buffer[2]; - buffer[1] = '\0'; - for (int ii = 0; ii < 10000; ii++){ - buffer[1] = 'A' + ii % 26; - store.allocateBytes(buffer, 1); - } - int a = 1; - } - -public: - virtual void doIt() { - testBufferChange(); - testChars(); - testBytes(); - } -}; -void testRplByteStorage() { - TestRplByteStorage test; - test.run(); -} - - diff --git a/unittests/rplcharptrmap_test.cpp b/unittests/rplcharptrmap_test.cpp deleted file mode 100644 index 3600efb..0000000 --- a/unittests/rplcharptrmap_test.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 "rplcore/rpltest.hpp" - -class TestRplCharPtrMap : public RplTest{ -public: - TestRplCharPtrMap() : - RplTest("RplCharPtrMap") - { - } -protected: - void testBasic(){ - RplCharPtrMap map; - map["x"] = "x1"; - checkT(map.contains("x")); - checkF(map.contains("y")); - checkE("x1", map["x"]); - } - - virtual void doIt(void) { - testBasic(); - } -}; -void testRplCharPtrMap() { - TestRplCharPtrMap test; - test.run(); -} diff --git a/unittests/rplexception_test.cpp b/unittests/rplexception_test.cpp deleted file mode 100644 index 39e124a..0000000 --- a/unittests/rplexception_test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 "rplcore/rpltest.hpp" -/** @file - * @brief Unit test of the basic exceptions. - */ - -class TestRplException : public RplTest{ -public: - TestRplException() : RplTest("RplException") {} - -public: - void testBasic() { - try{ - throw RplException("simple"); - checkF(true); - } catch (RplException exc){ - checkE("simple", exc.getMessage().constData()); - } - try{ - throw RplException("String: %s and int %d", "Hi", -333); - checkF(true); - } catch (RplException exc){ - checkE("String: Hi and int -333", exc.getMessage().constData()); - } - try{ - throw RplException(LOG_INFO, 1234, &m_memoryLogger, - "String: %s and int %d", "Hi", -333); - checkF(true); - } catch (RplException exc){ - checkT(logContains("^ .*\\(1234\\): String: Hi and int -333")); - } - log("ok"); - } - virtual void doIt() { - testBasic(); - } -}; -void testRplException() { - TestRplException test; - test.run(); -} - - - diff --git a/unittests/rpllexer_test.cpp b/unittests/rpllexer_test.cpp deleted file mode 100644 index 3d9696e..0000000 --- a/unittests/rpllexer_test.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Unit test of the syntax symbol extractor. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplLexer : public RplTest, public RplToken{ -public: - TestRplLexer() : - RplTest("RplLexer"), - RplToken(TOKEN_ID) - {} - -public: - void testRplToken(){ - // test constructor values: - checkE(TOKEN_ID, tokenType()); - checkE(0, m_value.m_id); - checkT(m_string.isEmpty()); - checkT(m_printableString.isEmpty()); - - m_value.m_id = 7422; - checkE(7422, RplToken::id()); - m_string = "Wow!"; - checkE("Wow!", RplToken::toString()); - m_printableString = "GooGoo"; - checkE("GooGoo", rawString()); - m_tokenType = TOKEN_NUMBER; - checkE(TOKEN_NUMBER, tokenType()); - - clear(); - checkE(TOKEN_UNDEF, tokenType()); - checkE(0, m_value.m_id); - checkT(m_string.isEmpty()); - checkT(m_printableString.isEmpty()); - - m_value.m_integer = 773322; - checkE(773322, asInteger()); - m_value.m_real = 0.25; - checkE(0.25, asReal()); - } - - RplToken* checkToken(RplToken* token, RplTokenType type, int id = 0, - const char* string = NULL){ - checkE(type, token->tokenType()); - if (id != 0) - checkE(id, token->id()); - if (string != NULL) - checkE(string, token->toString()); - return token; - } - enum { KEY_UNDEF, KEY_IF, KEY_THEN, KEY_ELSE, KEY_FI - }; -# define KEYWORDS "if then else fi" - enum { OP_UNDEF, OP_PLUS, OP_TIMES, OP_DIV, OP_GT, - OP_LT, OP_GE, OP_LE, OP_EQ, OP_ASSIGN, OP_PLUS_ASSIGN, OP_DIV_ASSIGN, - OP_TIMES_ASSIGN - }; -# define OPERATORS "+\n* /\n> < >= <= ==\n= += /= *=" - enum { COMMENT_UNDEF, COMMENT_1, COMMENT_MULTILINE, COMMENT_2 - }; -# define COMMENTS "/* */ // \n" - void testSpace(){ - RplSource source; - RplStringReader reader(source); -# define BLANKS1 "\t\t \n" -# define BLANKS2 " \n" - reader.addSource("
", BLANKS1 BLANKS2); - source.addReader(&reader); - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_DECIMAL, - RplLexer::SF_TICK, RplLexer::STORE_ALL); - checkToken(lex.nextToken(), TOKEN_SPACE, 0, BLANKS1); - checkToken(lex.nextToken(), TOKEN_SPACE, 0, BLANKS2); - } - void testNumeric(){ - RplSource source; - RplStringReader reader(source); - const char* blanks = "321 0x73 7.8e+5"; - reader.addSource("
", blanks); - source.addReader(&reader); - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_TICK, RplLexer::STORE_ALL); - RplToken* token = checkToken(lex.nextToken(), TOKEN_NUMBER); - checkE(321, token->asInteger()); - token = checkToken(lex.nextNonSpaceToken(), TOKEN_NUMBER); - checkE(0x73, token->asInteger()); - token = checkToken(lex.nextNonSpaceToken(), TOKEN_REAL); - checkE(7.8e+5, token->asReal()); - } - - void testOperators(){ - RplSource source; - RplStringReader reader(source); - const char* ops = "<< < <<< <= == = ( ) [ ]"; - reader.addSource("
", ops); - source.addReader(&reader); - enum { UNDEF, SHIFT, LT, SHIFT2, LE, EQ, ASSIGN, - LPARENT, RPARENT, LBRACKET, RBRACKET }; - RplLexer lex(&source, KEYWORDS, ops, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_TICK, RplLexer::STORE_ALL); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, SHIFT); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LT); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, SHIFT2); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LE); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, EQ); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, ASSIGN); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LPARENT); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, RPARENT); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LBRACKET); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, RBRACKET); - checkToken(lex.nextNonSpaceToken(), TOKEN_END_OF_SOURCE); - reader.addSource("", "(([["); - lex.startUnit(""); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LPARENT); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LPARENT); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LBRACKET); - checkToken(lex.nextNonSpaceToken(), TOKEN_OPERATOR, LBRACKET); - checkToken(lex.nextNonSpaceToken(), TOKEN_END_OF_SOURCE); - } - - void testComments(){ - RplSource source; - RplStringReader reader(source); - - reader.addSource("
", "/**/9//\n8/***/7// wow\n/*\n*\n*\n**/"); - source.addReader(&reader); - - enum { COMMENT_UNDEF, COMMENT_MULTILINE, COMMENT_1 - }; - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_LIKE_C, RplLexer::STORE_ALL); - checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_MULTILINE, - "/**/"); - checkToken(lex.nextToken(), TOKEN_NUMBER); - checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_1, - "//\n"); - checkToken(lex.nextToken(), TOKEN_NUMBER); - checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_MULTILINE, - "/***/"); - checkToken(lex.nextToken(), TOKEN_NUMBER); - checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_1, - "// wow\n"); - checkToken(lex.nextToken(), TOKEN_COMMENT_START, COMMENT_MULTILINE, - "/*\n*\n*\n**/"); - } - void testStrings(){ - RplSource source; - RplStringReader reader(source); - - reader.addSource("
", "\"abc\\t\\r\\n\\a\\v\"'1\\x9Z\\x21A\\X9'"); - source.addReader(&reader); - - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_LIKE_C, RplLexer::STORE_ALL); - checkToken(lex.nextToken(), TOKEN_STRING, '"', "abc\t\r\n\a\v"); - checkToken(lex.nextToken(), TOKEN_STRING, '\'', "1\tZ!A\t"); - } - void testKeywords(){ - RplSource source; - RplStringReader reader(source); - - reader.addSource("
", "if\n\tthen else\nfi"); - source.addReader(&reader); - - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_LIKE_C, RplLexer::STORE_ALL); - checkToken(lex.nextToken(), TOKEN_KEYWORD, KEY_IF); - checkToken(lex.nextNonSpaceToken(), TOKEN_KEYWORD, KEY_THEN); - checkToken(lex.nextNonSpaceToken(), TOKEN_KEYWORD, KEY_ELSE); - checkToken(lex.nextNonSpaceToken(), TOKEN_KEYWORD, KEY_FI); - checkToken(lex.nextNonSpaceToken(), TOKEN_END_OF_SOURCE); - } - - void testIds(){ - RplSource source; - RplStringReader reader(source); - - reader.addSource("
", "i\n\tifs\n" - "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - source.addReader(&reader); - - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_LIKE_C, RplLexer::STORE_ALL); - checkToken(lex.nextToken(), TOKEN_ID, 0, "i"); - checkToken(lex.nextNonSpaceToken(), TOKEN_ID, 0, - "ifs"); - checkToken(lex.nextNonSpaceToken(), TOKEN_ID, 0, - "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - } - - void testBasic(){ - RplSource source; - RplStringReader reader(source); - source.addReader(&reader); - reader.addSource("
", "if i>1 then i=1+2*_x9 fi"); - RplLexer lex(&source, KEYWORDS, OPERATORS, "=", COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_LIKE_C, RplLexer::STORE_ALL); - RplToken* token; - checkToken(lex.nextToken(), TOKEN_KEYWORD, KEY_IF); - checkToken(lex.nextToken(), TOKEN_SPACE, 0); - checkToken(lex.nextToken(), TOKEN_ID, 0, "i"); - checkToken(lex.nextToken(), TOKEN_OPERATOR, OP_GT); - token = checkToken(lex.nextToken(), TOKEN_NUMBER); - checkE(1, token->asInteger()); - checkToken(lex.nextToken(), TOKEN_SPACE, 0); - checkToken(lex.nextToken(), TOKEN_KEYWORD, KEY_THEN); - checkToken(lex.nextToken(), TOKEN_SPACE, 0); - - } - void testPrio(){ - RplSource source; - RplStringReader reader(source); - source.addReader(&reader); - reader.addSource("x", ""); - enum { O_UNDEF, O_ASSIGN, O_PLUS, O_MINUS, O_TIMES, O_DIV - }; - RplLexer lex(&source, KEYWORDS, - "=\n+ -\n* /", "=", - COMMENTS, - "A-Za-z_", - "A-Za-z0-9_", - RplLexer::NUMTYPE_ALL, - RplLexer::SF_LIKE_C, RplLexer::STORE_ALL); - checkT(lex.prioOfOp(O_ASSIGN) < lex.prioOfOp(O_PLUS)); - checkE(lex.prioOfOp(O_PLUS), lex.prioOfOp(O_MINUS)); - checkT(lex.prioOfOp(O_MINUS) < lex.prioOfOp(O_TIMES)); - checkE(lex.prioOfOp(O_TIMES), lex.prioOfOp(O_DIV)); - } - - virtual void doIt(void) { - testPrio(); - testBasic(); - testIds(); - testKeywords(); - testComments(); - testStrings(); - testOperators(); - testNumeric(); - testSpace(); - testRplToken(); - } -}; -void testRplLexer() { - TestRplLexer test; - test.run(); -} diff --git a/unittests/rplmatrix_test.cpp b/unittests/rplmatrix_test.cpp deleted file mode 100644 index b9a651d..0000000 --- a/unittests/rplmatrix_test.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Matrix_test.cpp - * - * Created on: 29.05.2014 - * Author: hm - */ - -/** @file - * @brief Unit test of the matrices. - */ - -#include "rplcore/rplcore.hpp" -#include "rplmath/rplmath.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplMatrix : public RplTest{ -public: - TestRplMatrix() : RplTest("RplMatrix") {} - -public: - void fillMatrix(RplMatrix& mx, MatVal offset = 0){ - for(int row = 0; row < mx.getRows(); row++){ - for (int col = 0; col < mx.getCols(); col++){ - mx.set(row, col, 100.0*row + col + offset); - } - } - } - void checkMatrix(const RplMatrix& mx, MatVal offset = 0){ - int count = 0; - for(int row = 0; row < mx.getRows(); row++){ - for (int col = 0; col < mx.getCols(); col++){ - checkE(100.0*row + col + offset, mx.get(row, col)); - count++; - } - } - checkE(mx.getCols()*mx.getRows(), count); - } - void fillConst(RplMatrix& mx, MatVal value){ - for(int row = 0; row < mx.getRows(); row++){ - for (int col = 0; col < mx.getCols(); col++){ - mx.set(row, col, value); - } - } - } - void checkConst(const RplMatrix& mx, MatVal value){ - int count = 0; - for(int row = 0; row < mx.getRows(); row++){ - for (int col = 0; col < mx.getCols(); col++){ - checkE(value, mx.get(row, col)); - count++; - } - } - checkE(mx.getCols()*mx.getRows(), count); - } - - void testBasic() { - Tuple2 tuple(-2.0, 0.5); - checkE(-2.0, tuple.m_value1); - checkE(0.5, tuple.m_value2); - RplMatrix mat("mx"); - try{ - throw RplMatrixException(mat, "String: %s and int %d", "Hi", -333); - checkF(true); - } catch (RplMatrixException exc){ - checkE("mx: String: Hi and int -333", exc.getMessage()); - } - RplMatrix mat2; - try{ - throw RplMatrixException(mat2, "String: %s and int %d", "Hi", -333); - checkF(true); - } catch (RplMatrixException exc){ - checkE("String: Hi and int -333", exc.getMessage()); - } - checkE("mx", mat.getName()); - checkE("", mat2.getName()); - - RplMatrix m2x3(2, 3, "m2x3"); - checkE("m2x3", m2x3.getName()); - checkE(2, m2x3.getRows()); - checkE(3, m2x3.getCols()); - fillMatrix(m2x3); - checkMatrix(m2x3); - - RplMatrix mxCopy(m2x3); - checkE("m2x3-copy", mxCopy.getName()); - checkE(2, mxCopy.getRows()); - checkE(3, mxCopy.getCols()); - checkMatrix(mxCopy); - - RplMatrix mxCopy2("mxCopy2"); - mxCopy2 = m2x3; - checkE("mxCopy2", mxCopy2.getName()); - checkE(2, mxCopy2.getRows()); - checkE(3, mxCopy2.getCols()); - checkMatrix(mxCopy2); - } - void testAddOperators(){ - RplMatrix m1(3, 2, "m1"); - fillMatrix(m1); - checkMatrix(m1); - RplMatrix m2(3, 2, "m2"); - fillMatrix(m2, 42); - checkMatrix(m2, 42); - RplMatrix m3(3, 2, "m3"); - fillMatrix(m3, -42); - checkMatrix(m3, -42); - - m1 += 42; - checkMatrix(m1, 42); - - checkT(m1 == m2); - checkF(m1 == m3); - - m1 -= 42; - checkMatrix(m1); - m1 -= m1; - checkConst(m1, 0); - - fillMatrix(m1); - m1 -= m3; - checkConst(m1, 42); - m1 += m2; - checkMatrix(m1, 42*2); - } - void testCompareOperators(){ - RplMatrix m1(3, 2, "m1"); - fillMatrix(m1); - checkMatrix(m1); - RplMatrix m2(3, 2, "m2"); - fillMatrix(m2); - - checkT(m1 == m2); - checkF(m1 != m2); - // modify each element, comparism must return false: - int row, col; - for (row = 0; row < m2.getRows(); row++) - for (col = 0; col < m2.getCols(); col++){ - fillMatrix(m2); - m2.set(row, col, -1); - checkF(m1 == m2); - checkT(m1 != m2); - } - - fillConst(m1, 42); - checkT(m1 == 42); - checkF(m1 == 43); - checkT(m1 != 43); - for (row = 0; row < m1.getRows(); row++) - for (col = 0; col < m1.getCols(); col++){ - fillMatrix(m1, 42); - m1.set(row, col, -1); - checkF(m1 == 42); - checkT(m1 != 42); - } - } - - void testCheckDefinition(){ - RplMatrix m1(3, 2, "m1"); - fillMatrix(m1); - checkMatrix(m1); - RplMatrix m2(3, 2, "m2"); - fillMatrix(m2); - - m1.checkDefinition(1, 1); - m1.checkDefinition(1000, 1000); - m1.checkDefinition(0, 0); - try { - m1.checkDefinition(-1, 1); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: row number negative: -1", exc.getMessage()); - } - try { - m1.checkDefinition(1, -1); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: column number negative: -1", exc.getMessage()); - } - - } - void testCheck(){ - RplMatrix m1(3, 2, "m1"); - fillMatrix(m1); - checkMatrix(m1); - - m1.check(0, 0); - m1.check(3-1, 2-1); - try { - m1.check(-1, 1); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: invalid row: -1 not in [0,3[", exc.getMessage()); - } - try { - m1.check(3, 1); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: invalid row: 3 not in [0,3[", exc.getMessage()); - } - try { - m1.check(1, -1); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: invalid column: -1 not in [0,2[", exc.getMessage()); - } - try { - m1.check(1, 2); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: invalid column: 2 not in [0,2[", exc.getMessage()); - } - } - void testCheckSameDimension(){ - RplMatrix m1(3, 2, "m1"); - RplMatrix m2(3, 2, "m2"); - - m1.checkSameDimension(m2); - - m2.resize(2, 2); - try { - m1.checkSameDimension(m2); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: m2 has a different row count: 3 / 2", exc.getMessage()); - } - m2.resize(3, 3); - try { - m1.checkSameDimension(m2); - checkT(false); - } catch(RplMatrixException exc){ - checkE("m1: m2 has a different column count: 2 / 3", exc.getMessage()); - } - } - void testResize(){ - RplMatrix m1(3, 2, "m1"); - fillMatrix(m1); - checkMatrix(m1); - RplMatrix m2(2, 4, "m2"); - fillConst(m2, 0); - checkConst(m2, 0); - - m1.resize(2, 4); - checkE(2, m1.getRows()); - checkE(4, m1.getCols()); - checkT(m1 == m2); - } - - void testMinMax(){ - RplMatrix m1(4, 5, "m1"); - fillMatrix(m1); - checkMatrix(m1); - m1.set(0, 0, -98); - m1.set(3, 4, 9999); - Tuple2 miniMax = m1.minMax(); - checkE(-98.0, miniMax.m_value1); - checkE(9999.0, miniMax.m_value2); - - fillMatrix(m1); - checkMatrix(m1); - m1.set(1, 1, 7777); - m1.set(3, 4, -987); - miniMax = m1.minMax(); - checkE(-987.0, miniMax.m_value1); - checkE(7777.0, miniMax.m_value2); - } - - void testTranspose() - { - RplMatrix m1(1, 5, "m1"); - fillMatrix(m1); - RplMatrix m2(m1.transpose()); - - checkE(5, m2.getRows()); - checkE(1, m2.getCols()); - - int row, col; - col = 0; - for (row = 0; row < 5; row++){ - checkE(qreal(col*100+row), m2.get(row, 0)); - } - - m1.resize(35, 73); - fillMatrix(m1); - m2 = m1.transpose(); - - checkE(73, m2.getRows()); - checkE(35, m2.getCols()); - - int count = 0; - for (row = 0; row < m2.getRows(); row++){ - for (col = 0; col < m2.getCols(); col++){ - checkE(qreal(col*100+row), m2.get(row, col)); - count++; - } - } - checkE(73*35, count); - } - void testToString(){ - RplMatrix m1(1, 1, "m1"); - m1.set(0, 0, 2.34); - checkE("[2.340000,\n]", m1.toString().constData()); - checkE("jonny[2.34000 |]", m1.toString("jonny", "%.5f", "|", " ").constData()); - - m1.resize(2, 1); - m1.set(0, 0, 2.34); - m1.set(1, 0, 55.5); - - checkE("[2.340000,\n55.500000,\n]", m1.toString().constData()); - checkE("jonny[2.34000 |55.50000 |]", m1.toString("jonny", "%.5f", "|", " ").constData()); - log(""); - } - void testReadCsv(){ - QByteArray fn = getTempFile("rplmatrixtest.csv"); - const char* content; - RplMatrix m1(1,1,"m1"); - - fillMatrix(m1); - content = ",Port0,Port1,Port2\n" - "element1,5, -3E-99 , 0.5\n" - "element2,7,-22.3,44\n" - "\n" - "2 Elements, 3, Ports"; - RplString::write(fn, content); - m1.readFromCvs(fn, 256); - checkE(2, m1.getRows()); - checkE(3, m1.getCols()); - - checkE(5.0, m1.get(0, 0)); - checkE(-3.0E-99, m1.get(0, 1)); - checkE(0.5, m1.get(0, 2)); - - checkE(7.0, m1.get(1, 0)); - checkE(-22.3, m1.get(1, 1)); - checkE(44.0, m1.get(1, 2)); - - fillMatrix(m1); - content = "Port0,Port1,Port2\n" - "5, -3E-99 , 0.5\n"; - RplString::write(fn, content); - m1.readFromCvs(fn, 256); - checkE(1, m1.getRows()); - checkE(3, m1.getCols()); - checkE(5.0, m1.get(0, 0)); - checkE(-3.0E-99, m1.get(0, 1)); - checkE(0.5, m1.get(0, 2)); - - -/* - void readFromCvs(const char* filename, int maxLineLength = 1024*1024); - void readFromXml(const char* filename, const char* tagCol, - const char* tagRow, const char* tagTable, - int maxLineLength = 1024*1024); -*/ - } - virtual void doIt(void) { - testBasic(); - testAddOperators(); - testCompareOperators(); - testCheckDefinition(); - testCheck(); - testCheckSameDimension(); - testResize(); - testMinMax(); - testTranspose(); - testToString(); - testReadCsv(); - } -}; -void testRplMatrix() { - TestRplMatrix test; - test.run(); -} diff --git a/unittests/rplmfparser_test.cpp b/unittests/rplmfparser_test.cpp deleted file mode 100644 index 6266a48..0000000 --- a/unittests/rplmfparser_test.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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. -*/ -/** @file - * @brief Unit test of the parser for the language "MF". - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplMFParser : public RplTest{ -private: - RplSource m_source; - RplASTree m_tree; - RplStringReader m_reader; - RplFileReader m_fileReader; - QByteArray m_currentSource; -public: - TestRplMFParser() : - RplTest("RplMFParser"), - m_source(), - m_tree(), - m_reader(m_source), - m_fileReader(m_source) - { - m_source.addReader(&m_reader); - } -protected: - void setSource(const char* content){ - RplASItem::reset(); - m_currentSource = content; - m_tree.clear(); - m_source.clear(); - m_reader.clear(); - m_reader.addSource("", content); - m_source.addReader(&m_reader); - m_source.addSourceUnit(m_reader.currentSourceUnit()); - } - void setFileSource(const char* filename){ - RplASItem::reset(); - m_currentSource = RplString::read(filename); - m_tree.clear(); - m_source.clear(); - m_fileReader.clear(); - m_fileReader.addSource(filename); - m_source.addReader(&m_fileReader); - m_source.addSourceUnit(m_fileReader.currentSourceUnit()); - } - -private: - void checkAST(const char* fileExpected, int lineNo){ - QByteArray fnExpected = "test"; - fnExpected += QDir::separator().toLatin1(); - fnExpected += "mfparser"; - fnExpected += (char) QDir::separator().toLatin1(); - fnExpected += fileExpected; - QByteArray fnCurrent = getTempFile(fileExpected, "rplmfparser"); - m_tree.dump(fnCurrent, RplASTree::DMP_NO_GLOBALS, m_currentSource); - assertEqualFiles(fnExpected.constData(), fnCurrent.constData(), - __FILE__, lineNo); - } - -public: - void fileClassTest(){ - setFileSource("test/rplmfparser/string1.mf"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("string1.txt", __LINE__); - } - - void baseTest(){ - setSource("2+3*4"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("baseTest.txt", __LINE__); - } - - void varDefTest(){ - setSource("const lazy Str s = 'Hi';\nconst List l;\nInt i = 3;"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("varDefTest.txt", __LINE__); - } - - void ifTest(){ - setSource("Int a;\nInt b;\na = b = 2;\nif 11 < 12\nthen a = 13 * 14\nelse a = 15 / 16\nfi"); - // setSource("Int a; if 11 < 12 then a = 13 * 14 else a = 15 / 16 fi"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("ifTest1.txt", __LINE__); - setSource("Str x;\nif 7 < 6\nthen x = '123';\nfi"); - parser.parse(); - checkAST("ifTest2.txt", __LINE__); - } - void whileTest(){ - setSource("Int a = 20;\nwhile 3 < 5 do\n a = 7\nod"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("whileTest.txt", __LINE__); - } - - void repeatTest(){ - setSource("Int a;\nrepeat\na++;\nuntil a != 2 * 3;"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("repeatTest.txt", __LINE__); - } - void forCTest(){ - setSource("Int a;\nfor b from 10 to 1 step -2 do\na += 1;\nod"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("forC1.txt", __LINE__); - setSource("Int a; for to 10 do a += 1 od"); - parser.parse(); - checkAST("forC2.txt", __LINE__); - } - void opTest(){ - checkE(25, RplMFParser::O_QUESTION); - checkE(37, RplMFParser::O_RSHIFT2); - checkE(41, RplMFParser::O_DEC); - checkE(48, RplMFParser::O_RBRACE); - setSource("Int a = 1;\nInt b = 100;\n--a;\nb++;\na--*++b**(8-3);\na=b=(a+(b-2)*3)"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("opTest1.txt", __LINE__); - } - void forItTest(){ - setSource("Map a;\nfor x in a do\na += 1;\nod"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("forIt1.txt", __LINE__); - } - void listTest(){ - RplMFParser parser(m_source, m_tree); - setSource("List b = [];"); - parser.parse(); - checkAST("list1.txt", __LINE__); - setSource("List a = [2+3, 3.14, 7, 'hi', a]; List b = [];"); - parser.parse(); - checkAST("list2.txt", __LINE__); - } - void mapTest(){ - setSource("Map a = {};"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("map1.txt", __LINE__); - setSource("Map a = {'a': 2+3,'bcd':3.14,'ccc':7, 'hi':'world'};\nMap b = {};"); - parser.parse(); - checkAST("map2.txt", __LINE__); - } - void methodCallTest(){ - //setSource("max(4,3.14);"); - setSource("rand();\nsin(a);\nmax(1+2*3,4**(5-4));"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("methc1.txt", __LINE__); - } - void fieldTest(){ - setSource("file.find('*.c')[0].name;\n[1,2,3].join(' ');\n3.14.trunc;"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("field1.txt", __LINE__); - } - - void methodTest(){ - setSource("func Float pi: 3.1415; endf func Str delim(): '/' endf;"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("meth1.txt", __LINE__); - setSource("func Int fac(const Int n):\n" - "Int rc; if rc <= 1 then rc = 1 else rc = n*fac(n-1) fi\n" - "rc endf"); - parser.parse(); - checkAST("meth2.txt", __LINE__); - setSource("func Int max(Int a, Int b):\n Int rc = a;\n" - "if a < b then rc = b; fi\nrc\n" - "endf\n" - "func Int max(const Int a, Int b, Int c):\n" - "max(a, max(b, c))\n" - "endf"); - parser.parse(); - checkAST("meth3.txt", __LINE__); - setSource("func Int max(const Int a, Int b, Int c):\n" - "func Int max(Int a, Int b):\n Int rc = a;\n" - "if a < b then rc = b; fi\nrc\n" - "endf\n" - "max(a, max(b, c))\n" - "endf"); - parser.parse(); - checkAST("meth4.txt", __LINE__); - } - void mainTest(){ - setSource("Int a=2+3*4;\nfunc Void main():\na;\nendf"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - checkAST("main1.txt", __LINE__); - } - - virtual void doIt(void) { - mainTest(); - varDefTest(); - repeatTest(); - baseTest(); - whileTest(); - methodTest(); - fieldTest(); - methodCallTest(); - mapTest(); - forItTest(); - forCTest(); - listTest(); - opTest(); - fileClassTest(); - } -}; -void testRplMFParser() { - TestRplMFParser test; - test.run(); -} - - diff --git a/unittests/rplqstring_test.cpp b/unittests/rplqstring_test.cpp deleted file mode 100644 index bdadd09..0000000 --- a/unittests/rplqstring_test.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. -*/ -/** @file - * @brief Unit test of the QString tools. - */ - -#include "rplcore/rplcore.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplQString : public RplTest { -public: - TestRplQString() : - RplTest("RplQString") - {} - -public: - void testLengthOfUInt64(){ - quint64 value = -3; - checkE(1, RplQString::lengthOfUInt64(QString("0"), 0, 10, &value)); - checkE(0LL, value); - checkE(3, RplQString::lengthOfUInt64("x432", 1, 10, &value)); - checkE(432LL, value); - checkE(3, RplQString::lengthOfUInt64("x432 x", 1, 10, &value)); - checkE(432LL, value); - checkE(3, RplQString::lengthOfUInt64("x432fabc x", 1, 10, &value)); - checkE(432LL, value); - checkE(16, RplQString::lengthOfUInt64("a1234567890123567", 1, 10, &value)); - checkE(1234567890123567LL, value); - checkE(10, RplQString::lengthOfUInt64("x1234abcdef", 1, 16, &value)); - checkE(0x1234abcdefLL, value); - checkE(3, RplQString::lengthOfUInt64("432", 0, 8, &value)); - checkE(0432LL, value); - checkE(6, RplQString::lengthOfUInt64(" 765432 ", 1, 8, &value)); - checkE(0765432LL, value); - - checkE(0, RplQString::lengthOfUInt64("1 ", 1, 8, &value)); - checkE(0, RplQString::lengthOfUInt64("", 1, 8, &value)); - } - void testLengthOfUInt(){ - uint value = 3; - checkE(1, RplQString::lengthOfUInt(QString("0"), 0, 10, &value)); - checkE(0, value); - checkE(3, RplQString::lengthOfUInt("x432", 1, 10, &value)); - checkE(432, value); - checkE(3, RplQString::lengthOfUInt("x432 x", 1, 10, &value)); - checkE(432, value); - checkE(3, RplQString::lengthOfUInt("x432fabc x", 1, 10, &value)); - checkE(432, value); - checkE(3, RplQString::lengthOfUInt("432", 0, 8, &value)); - checkE(0432, value); - checkE(6, RplQString::lengthOfUInt(" 765432 ", 1, 8, &value)); - checkE(0765432, value); - - checkE(0, RplQString::lengthOfUInt("1 ", 1, 8, &value)); - checkE(0, RplQString::lengthOfUInt("", 1, 8, &value)); - } - void testLengthOfReal(){ - qreal value; - checkE(4, RplQString::lengthOfReal(QString("0.25"), 0, &value)); - checkE(0.25, value); - checkE(3, RplQString::lengthOfReal(QString("X.25"), 1, &value)); - checkE(0.25, value); - checkE(1, RplQString::lengthOfReal(QString(" 0"), 1, &value)); - checkE(0.0, value); - checkE(17, RplQString::lengthOfReal(QString("X12345678901234567"), 1, &value)); - checkE(12345678901234567.0, value); - checkE(2, RplQString::lengthOfReal(QString(".5"), 0, &value)); - checkE(0.5, value); - checkE(5, RplQString::lengthOfReal(QString("2.5e2x"), 0, &value)); - checkE(250.0, value); - checkE(6, RplQString::lengthOfReal(QString("2.5e+2"), 0, &value)); - checkE(250.0, value); - checkE(7, RplQString::lengthOfReal(QString("2.5E-33"), 0, &value)); - checkE(2.5e-33, value); - - checkE(3, RplQString::lengthOfReal(QString("2.5E"), 0, &value)); - checkE(2.5, value); - checkE(3, RplQString::lengthOfReal(QString("2.5E+"), 0, &value)); - checkE(2.5, value); - checkE(3, RplQString::lengthOfReal(QString("2.5E-a"), 0, &value)); - checkE(2.5, value); - } - - void testValueOfHexDigit(){ - checkE(0, RplQString::valueOfHexDigit('0')); - checkE(9, RplQString::valueOfHexDigit('9')); - checkE(10, RplQString::valueOfHexDigit('a')); - checkE(15, RplQString::valueOfHexDigit('f')); - checkE(10, RplQString::valueOfHexDigit('A')); - checkE(15, RplQString::valueOfHexDigit('F')); - - checkE(-1, RplQString::valueOfHexDigit('0' - 1)); - checkE(-1, RplQString::valueOfHexDigit('9' + 1)); - checkE(-1, RplQString::valueOfHexDigit('A' - 1)); - checkE(-1, RplQString::valueOfHexDigit('F' + 1)); - checkE(-1, RplQString::valueOfHexDigit('a' - 1)); - checkE(-1, RplQString::valueOfHexDigit('f' + 1)); - } - void testUtf8(){ - QString name = "Heinz Müller"; - char buffer[32]; - checkE("Heinz Müller", RplQString::utf8(name, buffer, sizeof buffer)); - memset(buffer, 'x', sizeof buffer); - checkE("Heinz", RplQString::utf8(name, buffer, (size_t) (5+1))); - checkE(buffer[6], 'x'); - } - - virtual void doIt(void) { - testUtf8(); - testLengthOfUInt64(); - testLengthOfUInt(); - testLengthOfReal(); - testValueOfHexDigit(); - } -}; -void testRplQString() { - TestRplQString test; - test.run(); -} - diff --git a/unittests/rplsource_test.cpp b/unittests/rplsource_test.cpp deleted file mode 100644 index 49eeeae..0000000 --- a/unittests/rplsource_test.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Unit test of the input media reader. - */ - -#include "rplcore/rplcore.hpp" -#include "rplexpr/rplexpr.hpp" -#include "rplcore/rpltest.hpp" - -class TestRplSource : public RplTest{ -public: - TestRplSource() : RplTest("TestRplSource") {} - -private: - QByteArray m_content1_1; - QByteArray m_content1_2; - QByteArray m_content2; - RplSource m_source; - -protected: - void init(){ - m_content1_1 = "# test\nimport source2\n"; - m_content1_2 = "a=1;\nveeeeeeeeery looooooooooooooong\n"; - m_content2 = "x=2"; - } - - void testRplStringSourceUnit(){ - RplStringReader reader(m_source); - QByteArray content("a=1;\nveeeeeeeeery looooooooooooooong\n"); - RplStringSourceUnit unit("test", content, &reader); - unit.setLineNo(144); - checkE(144, unit.lineNo()); - checkE("test", unit.name()); - } - void checkOne(int maxSize, RplReader& reader){ - QByteArray total; - QByteArray buffer; - int lineNo = 0; - bool hasMore; - checkF(reader.openSourceUnit("unknownSource")); - checkT(reader.openSourceUnit("source1")); - while(reader.nextLine(maxSize, buffer, hasMore)){ - lineNo++; - total += buffer; - buffer.clear(); - while(hasMore && reader.fillBuffer(maxSize, buffer, hasMore)){ - total += buffer; - buffer.clear(); - } - bool isImport = total.endsWith("source2\n"); - if (isImport){ - reader.openSourceUnit("source2"); - checkE("source2", reader.currentSourceUnit()->name()); - while(reader.nextLine(maxSize, buffer, hasMore)){ - lineNo++; - while(hasMore && reader.fillBuffer(maxSize, buffer, hasMore)){ - total += buffer; - buffer.clear(); - } - } - checkE("source1", reader.currentSourceUnit()->name()); - } - } - checkE(5, lineNo); - checkE(m_content1_1 + m_content2 + m_content1_2, total); - - } - - void testRplStringReader(){ - RplStringReader reader(m_source); - reader.addSource("source1", m_content1_1 + m_content1_2); - reader.addSource("source2", m_content2); - RplSourceUnit* unit = reader.openSourceUnit("source1"); - checkNN(unit); - checkE("source1", unit->name()); - checkE(0, unit->lineNo()); - checkOne(6, reader); - checkOne(100, reader); - reader.replaceSource("source2", "content2"); - - unit = reader.openSourceUnit("source2"); - QByteArray buffer; - bool hasMore; - checkT(reader.nextLine(50, buffer, hasMore)); - checkE("content2", buffer); - checkF(hasMore); - } - -public: - virtual void doIt(void) { - init(); - testRplStringSourceUnit(); - testRplStringReader(); - } -}; -void testRplSource() { - TestRplSource test; - test.run(); -} - - - diff --git a/unittests/rplstring_test.cpp b/unittests/rplstring_test.cpp deleted file mode 100644 index 8983dc0..0000000 --- a/unittests/rplstring_test.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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. -*/ - -/** @file - * @brief Unit test of the QByteArray tools. - */ -#include "rplcore/rplcore.hpp" -#include "rplcore/rpltest.hpp" -/** - * @brief Unit test for RplString. - */ -class TestRplString : public RplTest { -public: - TestRplString() : RplTest("RplString") {} - -public: - void testCountChar(){ - checkE(1, RplString::countChar("x", 'x')); - checkE(0, RplString::countChar("X", 'x')); - checkE(2, RplString::countChar("xbxxbxx", 'b')); - } - - void testCount() { - checkE(0, RplString::count("abc", " ")); - checkE(1, RplString::count("abc", "b")); - checkE(2, RplString::count("axx", "x")); - - checkE(0, RplString::count("abbc", "bbb")); - checkE(1, RplString::count("\n\n", "\n\n")); - checkE(2, RplString::count(" a ", " ")); - } - - void testCutString() { - QByteArray source("123"); - QByteArray buffer; - checkE(QByteArray("123"), RplString::cutString(source, 4, buffer)); - checkE(QByteArray("123"), RplString::cutString(source, 3, buffer)); - checkE(QByteArray("12..."), RplString::cutString(source, 2, buffer)); - checkE(QByteArray("12"), RplString::cutString(source, 2, buffer, "")); - } - - void testHexDump() { - QByteArray data("abc123\nxyz"); - checkE(QByteArray("61 62 63 31 abc1\n" - "32 33 0a 78 23.x\n" - "79 7a yz\n"), - RplString::hexDump((uint8_t*) data.constData(), data.length(), 4)); - checkE(QByteArray("61 62 63 31 32 33 0a 78 79 7a abc123.xyz"), - RplString::hexDump((uint8_t*) data.constData(), data.length(), 10)); - checkE(QByteArray("61 62 63 31 32 33 0a 78 79 7a abc123.xyz"), - RplString::hexDump((uint8_t*) data.constData(), data.length(), 12)); - } - - void testReadWrite() { - QByteArray fn = getTempFile("test.dat"); - const char* content = "Hello world\nLine2\n"; - checkT(RplString::write(fn, content)); - checkE(content, RplString::read(fn, false)); - checkE(content, RplString::read(fn, true) + "\n"); - } - - void testToArray() { - QList array = RplString::toArray("1 abc 3", " "); - checkE(3, array.size()); - checkE("1", array.at(0)); - checkE("abc", array.at(1)); - checkE("3", array.at(2)); - } - - void testToNumber() { - checkE("3", RplString::toNumber(3)); - checkE("-33", RplString::toNumber(-33)); - checkE("003", RplString::toNumber(3, "%03d")); - } - - void testLengthOfNumber(){ - checkE(3, RplString::lengthOfNumber("0.3xxx")); - checkE(5, RplString::lengthOfNumber(" \t0.3xxx")); - checkE(3, RplString::lengthOfNumber("-.3xxx")); - checkE(2, RplString::lengthOfNumber(".3exxx")); - checkE(2, RplString::lengthOfNumber(".3e+xxx")); - checkE(16, RplString::lengthOfNumber("1234567.9012E+77")); - checkE(17, RplString::lengthOfNumber("-1234567.9012E+77 ")); - checkE(18, RplString::lengthOfNumber("-1234567.9012E+77 ", true)); - checkE(18, RplString::lengthOfNumber("-1234567.9012E+77 x", true)); - checkE(20, RplString::lengthOfNumber(" -1234567.9012E+77 x", true)); - } - - void checkCsv(const char* content, char expected){ - QByteArray fn = getTempFile("testrplstring.csv"); - RplString::write(fn, content); - FILE* fp = fopen(fn, "r"); - checkNN(fp); - char buffer[256]; - checkE(expected, RplString::findCsvSeparator(fp, buffer, sizeof buffer)); - fclose(fp); - } - - void testFindCsvSeparator(){ - const char* content = ",,,\t;;;||||"; - checkCsv(content, '\t'); - - content = "col1,col2\n1.5,3,5\n"; - checkCsv(content, ','); - - content = "col1;col2\n1,50;3.5\n" - "7;8\n10;12\n13;14"; - checkCsv(content, ';'); - - content = "0.3 7.8 8.9\n7.8 9.4 8.3"; - checkCsv(content, ' '); - - content = "0.3|7.8|8.9\n7.8| 9.4|8.3"; - checkCsv(content, '|'); - - content = "0,3;7.8;8.9"; - checkCsv(content, ';'); - } - void testLengthOfUInt64(){ - quint64 value = -3; - checkE(1, RplString::lengthOfUInt64("0", 10, &value)); - checkE(0LL, value); - checkE(3, RplString::lengthOfUInt64("432", 10, &value)); - checkE(432LL, value); - checkE(3, RplString::lengthOfUInt64("432 x", 10, &value)); - checkE(432LL, value); - checkE(3, RplString::lengthOfUInt64("432fabc x", 10, &value)); - checkE(432LL, value); - checkE(16, RplString::lengthOfUInt64("1234567890123567", 10, &value)); - checkE(1234567890123567LL, value); - checkE(10, RplString::lengthOfUInt64("1234abcdef", 16, &value)); - checkE(0x1234abcdefLL, value); - checkE(3, RplString::lengthOfUInt64("432", 8, &value)); - checkE(0432LL, value); - checkE(6, RplString::lengthOfUInt64("765432 ", 8, &value)); - checkE(0765432LL, value); - - checkE(0, RplString::lengthOfUInt64(" ", 8, &value)); - checkE(0, RplString::lengthOfUInt64("", 8, &value)); - } - void testLengthOfReal(){ - qreal value; - checkE(1, RplString::lengthOfReal("0", &value)); - checkE(0.0, value); - checkE(1, RplString::lengthOfReal("0%", &value)); - checkE(0.0, value); - checkE(4, RplString::lengthOfReal("0.25", &value)); - checkE(0.25, value); - checkE(3, RplString::lengthOfReal(".25", &value)); - checkE(0.25, value); - checkE(17, RplString::lengthOfReal("12345678901234567", &value)); - checkE(12345678901234567.0, value); - checkE(2, RplString::lengthOfReal(".5", &value)); - checkE(0.5, value); - checkE(5, RplString::lengthOfReal("2.5e2x", &value)); - checkE(250.0, value); - checkE(6, RplString::lengthOfReal("2.5e+2", &value)); - checkE(250.0, value); - checkE(7, RplString::lengthOfReal("2.5E-33", &value)); - checkE(2.5e-33, value); - - checkE(3, RplString::lengthOfReal("2.5E", &value)); - checkE(2.5, value); - checkE(3, RplString::lengthOfReal("2.5E+", &value)); - checkE(2.5, value); - checkE(3, RplString::lengthOfReal("2.5E-a", &value)); - checkE(2.5, value); - } - - virtual void doIt() { - testLengthOfReal(); - testLengthOfUInt64(); - testCountChar(); - testCount(); - testCutString(); - testToNumber(); - testToArray(); - testHexDump(); - testReadWrite(); - testLengthOfNumber(); - testFindCsvSeparator(); - } -}; - -void testRplString() { - TestRplString test; - test.run(); -} - - - diff --git a/unittests/rplvm_test.cpp b/unittests/rplvm_test.cpp deleted file mode 100644 index 244058e..0000000 --- a/unittests/rplvm_test.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 TestRplVM : public RplTest{ -private: - RplSource m_source; - RplASTree m_tree; - RplStringReader m_reader; - const char* m_currentSource; -public: - TestRplVM() : - RplTest("RplVM"), - m_source(), - m_tree(), - m_reader(m_source) - { - m_source.addReader(&m_reader); - } -protected: - void setSource(const char* content){ - RplASItem::reset(); - m_currentSource = content; - m_tree.clear(); - m_source.clear(); - m_reader.clear(); - m_reader.addSource("", content); - m_source.addReader(&m_reader); - m_source.addSourceUnit(m_reader.currentSourceUnit()); - } - -private: - void checkAST(const char* fileExpected, int lineNo){ - QByteArray fnExpected = "test"; - fnExpected += QDir::separator().toLatin1(); - fnExpected += "rplvm"; - fnExpected += (char) QDir::separator().toLatin1(); - fnExpected += fileExpected; - QByteArray fnCurrent = getTempFile(fileExpected, "rplvm"); - RplMFParser parser(m_source, m_tree); - parser.parse(); - RplVirtualMachine vm(m_tree, m_source); - vm.setFlag(RplVirtualMachine::VF_TRACE_STATEMENTS); - RplFileWriter writer(fnCurrent); - vm.setTraceWriter(&writer); - writer.write(m_currentSource); - vm.executeModule(""); - assertEqualFiles(fnExpected.constData(), fnCurrent.constData(), - __FILE__, lineNo); - } -public: - void baseTest(){ - setSource("Int a=2+3*4;\nfunc Void main():\na;\nendf"); - checkAST("baseTest.txt", __LINE__); - } - virtual void doIt(void) { - baseTest(); - } -}; -void testRplVM() { - TestRplVM test; - test.run(); -} - diff --git a/unittests/rplwriter_test.cpp b/unittests/rplwriter_test.cpp deleted file mode 100644 index b348982..0000000 --- a/unittests/rplwriter_test.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * 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. -*/ - -/** @file - * @brief Unit test of the output media writers. - */ - -#include "rplcore/rplcore.hpp" -#include "rplcore/rpltest.hpp" -/** - * @brief Unit test for RplString. - */ -class TestRplWriter : public RplTest { -public: - TestRplWriter() : RplTest("RplWriter") {} - -private: - void testFileWriter(){ - QByteArray fn = getTempFile("rplwriter.txt"); - RplFileWriter writer(fn); - writer.writeLine("abc"); - writer.formatLine("%04d", 42); - writer.writeIndented(3, "123"); - writer.indent(2); - writer.write("pi"); - writer.format("%3c%.2f", ':', 3.1415); - writer.writeLine(); - writer.close(); - QByteArray current = RplString::read(fn, false); - checkE("abc\n0042\n\t\t\t123\n\t\tpi :3.14\n", current); - } - -public: - virtual void doIt(void) { - testFileWriter(); - } -}; -void testRplWriter() { - TestRplWriter test; - test.run(); -} - - - diff --git a/unittests/unittests.pro b/unittests/unittests.pro deleted file mode 100644 index 0104a97..0000000 --- a/unittests/unittests.pro +++ /dev/null @@ -1,49 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2014-05-31T00:01:23 -# -#------------------------------------------------- - -QT += core network - -QT -= gui - -TARGET = unittests -CONFIG += console -CONFIG -= app_bundle - -INCLUDEPATH = .. - -TEMPLATE = app - -SOURCES += main.cpp \ - ../rplcore/rpllogger.cpp \ - ../rplcore/rpltest.cpp \ - ../rplcore/rplstring.cpp \ - ../rplcore/rplexception.cpp \ - ../rplmath/rplmatrix.cpp \ - ../rplexpr/rplsource.cpp \ - ../rplexpr/rpllexer.cpp \ - ../rplexpr/rplastree.cpp \ - ../rplexpr/rplparser.cpp \ - ../rplexpr/rplmfparser.cpp \ - ../rplcore/rplqstring.cpp \ - ../rplexpr/rplasclasses.cpp \ - ../rplcore/rplbytestorage.cpp \ - ../rplexpr/rplvm.cpp \ - ../rplcore/rplwriter.cpp \ - rplmatrix_test.cpp \ - rplexception_test.cpp \ - rplstring_test.cpp \ - rplsource_test.cpp \ - rpllexer_test.cpp \ - rplqstring_test.cpp \ - rplastree_test.cpp \ - rplmfparser_test.cpp \ - rplvm_test.cpp \ - rplbytestorage_test.cpp \ - rplwriter_test.cpp \ - rplbench.cpp \ - rplcharptrmap_test.cpp \ - ../rplcore/rplcharptrmap.cpp -