From 610858b1ca2ae7766928a50b2f9c3faad2439c25 Mon Sep 17 00:00:00 2001 From: Daniel McCarthy Date: Wed, 19 Sep 2018 20:04:23 +0100 Subject: [PATCH 1/6] Made it so if you try to use ++ or -- a freindly error appears asking you to use +=1 or -=1 rather than the previous error which didn't make sense --- src/system/lexer.cpp | 176 ++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/src/system/lexer.cpp b/src/system/lexer.cpp index 5b77436..73d81b2 100755 --- a/src/system/lexer.cpp +++ b/src/system/lexer.cpp @@ -31,25 +31,24 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "config.h" const char keywords[][MAX_KEYWORD_SIZE] = {"public", "private", "protected", "function", "number", "string", "int", "boolean", "true", "false", "class", "return", "continue", "break", "void", "new", "if", "else", "extends", "throw", "try", "catch", "finally", "do", "while", "break", "continue", "for", "include", "include_once", "pure", "final", "permission", "null", "require", "limit", "scope", "filter", "output", "operator"}; const char valid_operators[][MAX_OPERATORS_SIZE] = {"+", "-", "*", "/", "++", "--", "+=", "-=", "/=", "*=", "-=", "=", ".", "&", "|", "%", "^", "~", "!", "==", "!=", ">=", ">", "<=", "<", "&&", "||"}; -const char symbols[] = {';',',','(', ')', '{', '}','[',']', ':', '@'}; -Lexer::Lexer(Logger* logger) +const char symbols[] = {';', ',', '(', ')', '{', '}', '[', ']', ':', '@'}; +Lexer::Lexer(Logger *logger) { this->logger = logger; this->root = NULL; } Lexer::~Lexer() { - } -void Lexer::setInput(const char* buf, int size) +void Lexer::setInput(const char *buf, int size) { this->buf = buf; this->size = size; - this->end = this->buf+this->size; + this->end = this->buf + this->size; } -bool Lexer::bounds_safe(const char* ptr) +bool Lexer::bounds_safe(const char *ptr) { return ptr < this->end; } @@ -59,7 +58,7 @@ bool Lexer::is_keyword(std::string value) int total_keywords = sizeof(keywords) / MAX_KEYWORD_SIZE; for (int i = 0; i < total_keywords; i++) { - const char* keyword = keywords[i]; + const char *keyword = keywords[i]; if (value == keyword) { return true; @@ -86,8 +85,8 @@ bool Lexer::is_operator(std::string str) int total_operators = sizeof(valid_operators) / MAX_OPERATORS_SIZE; for (int i = 0; i < total_operators; i++) { - const char* op = valid_operators[i]; - if(str == op) + const char *op = valid_operators[i]; + if (str == op) { return true; } @@ -118,7 +117,6 @@ bool Lexer::is_string_seperator(char c) return c == '"'; } - bool Lexer::is_symbol(char c) { int total_symbols = sizeof(symbols); @@ -133,7 +131,6 @@ bool Lexer::is_symbol(char c) return false; } - bool Lexer::is_comment(char c) { return c == '#'; @@ -154,29 +151,29 @@ void Lexer::error(std::string message, PosInfo posInfo) logger->error(message, posInfo); } -int Lexer::get_type_of_char(char c, PosInfo& posInfo) +int Lexer::get_type_of_char(char c, PosInfo &posInfo) { int type = -1; if (is_character(c)) { type = IS_CHARACTER; } - else if(is_operator(c)) + else if (is_operator(c)) { if (c == '~') error("The operator ~ has not yet been implemented in Marble as Marble is in beta testing there is a small lack of features that will exist in the stable. Please wait until the stable is released or if it is released upgrade to stable to use this operator", posInfo); - + type = IS_OPERATOR; } - else if(is_number(c)) + else if (is_number(c)) { type = IS_NUMBER; } - else if(is_whitespace(c)) + else if (is_whitespace(c)) { type = IS_WHITESPACE; } - else if(is_symbol(c)) + else if (is_symbol(c)) { type = IS_SYMBOL; } @@ -188,12 +185,12 @@ int Lexer::get_type_of_char(char c, PosInfo& posInfo) return type; } -std::string Lexer::get_operator(const char** ptr) +std::string Lexer::get_operator(const char **ptr) { - const char* our_ptr = *ptr; + const char *our_ptr = *ptr; char c = *our_ptr; std::string value = ""; - while(is_operator(c)) + while (is_operator(c)) { value += c; our_ptr++; @@ -204,10 +201,11 @@ std::string Lexer::get_operator(const char** ptr) { *ptr += value.length(); } + return value; } -std::string Lexer::get_number(const char** ptr, PosInfo& posInfo) +std::string Lexer::get_number(const char **ptr, PosInfo &posInfo) { int exiting_character = -1; std::string value = get_while(ptr, IS_NUMBER, posInfo, &exiting_character); @@ -217,7 +215,7 @@ std::string Lexer::get_number(const char** ptr, PosInfo& posInfo) if (exiting_character == 'x') { // It looks like we have hexadecimal let's forward the pointer twice to ignore "0x" - *ptr+=2; + *ptr += 2; posInfo.col += 1; value = get_while(ptr, IS_NUMBER | IS_CHARACTER, posInfo); @@ -247,32 +245,32 @@ std::string Lexer::get_number(const char** ptr, PosInfo& posInfo) char Lexer::get_char_for_sequence(char c) { char result = 0; - switch(c) + switch (c) { - case 'r': - result = '\r'; + case 'r': + result = '\r'; break; - case 'n': - result = '\n'; + case 'n': + result = '\n'; break; - case '0': - result = '\0'; + case '0': + result = '\0'; break; - case '"': - result = '"'; + case '"': + result = '"'; break; - case '\\': - result = '\\'; + case '\\': + result = '\\'; break; - default: - logger->error("Bad char sequence for: " + c); + default: + logger->error("Bad char sequence for: " + c); } return result; } -std::string Lexer::get_string(const char** ptr, PosInfo& posInfo) +std::string Lexer::get_string(const char **ptr, PosInfo &posInfo) { if (!is_string_seperator(**ptr)) { @@ -280,26 +278,26 @@ std::string Lexer::get_string(const char** ptr, PosInfo& posInfo) } // Ok this appears to be a valid string so far, lets move the pointer forward to ignore the string seperator - *ptr+=1; + *ptr += 1; // our_ptr will now point at the start of the string - const char* our_ptr = *ptr; + const char *our_ptr = *ptr; char c = *our_ptr; int length = 0; std::string value = ""; // Lets loop until we find an ending string seperator. - while(bounds_safe(our_ptr) && !is_string_seperator(c)) + while (bounds_safe(our_ptr) && !is_string_seperator(c)) { if (c == '\\') { /* Some characters are valid in strings such as carriage returns and new lines \r\n * Let's handle it here*/ - our_ptr+=1; + our_ptr += 1; c = *our_ptr; c = get_char_for_sequence(c); } value += c; - our_ptr+=1; + our_ptr += 1; c = *our_ptr; } @@ -308,8 +306,7 @@ std::string Lexer::get_string(const char** ptr, PosInfo& posInfo) return value; } - -std::string Lexer::get_while(const char** ptr, int expected, PosInfo& posInfo, int* exiting_character) +std::string Lexer::get_while(const char **ptr, int expected, PosInfo &posInfo, int *exiting_character) { std::string tokenValue = ""; char c = **ptr; @@ -318,7 +315,7 @@ std::string Lexer::get_while(const char** ptr, int expected, PosInfo& posInfo, i { error("While calling \"get_while\" the first character must be of the expected type", posInfo); } - while(bounds_safe(*ptr)) + while (bounds_safe(*ptr)) { c = **ptr; type = get_type_of_char(c, posInfo); @@ -331,34 +328,35 @@ std::string Lexer::get_while(const char** ptr, int expected, PosInfo& posInfo, i goto next; } // Restore the pointer to previous state - *ptr-=1; - posInfo.col-=1; + *ptr -= 1; + posInfo.col -= 1; if (exiting_character != NULL) *exiting_character = c; break; } -next: + next: tokenValue += c; - *ptr+=1; - posInfo.col+=1; + *ptr += 1; + posInfo.col += 1; } return tokenValue; } -void Lexer::ignore_line(const char** ptr) +void Lexer::ignore_line(const char **ptr) { - while(bounds_safe(*ptr) && **ptr != 0x0a) *ptr+=1; + while (bounds_safe(*ptr) && **ptr != 0x0a) + *ptr += 1; } /** * Stage 1 will remove all comments, and create tokens based on the input */ -Token* Lexer::stage1(PosInfo posInfo) +Token *Lexer::stage1(PosInfo posInfo) { - Token* root_token = NULL; - Token* last_token = NULL; - const char* ptr = this->buf; + Token *root_token = NULL; + Token *last_token = NULL; + const char *ptr = this->buf; int token_type = -1; std::string token_value = ""; // We will loop through the whole thing and when we reach a whitespace a token has been completed @@ -377,7 +375,7 @@ Token* Lexer::stage1(PosInfo posInfo) { posInfo.col += 1; } - ptr+=1; + ptr += 1; continue; } @@ -386,7 +384,7 @@ Token* Lexer::stage1(PosInfo posInfo) token_type = TOKEN_TYPE_STRING; token_value = get_string(&ptr, posInfo); } - else if(is_comment(c)) + else if (is_comment(c)) { ignore_line(&ptr); continue; @@ -394,35 +392,40 @@ Token* Lexer::stage1(PosInfo posInfo) else { int c_type = get_type_of_char(c, posInfo); - switch(c_type) + switch (c_type) { - case IS_OPERATOR: - token_type = TOKEN_TYPE_OPERATOR; - token_value = get_while(&ptr, IS_OPERATOR, posInfo); + case IS_OPERATOR: + token_type = TOKEN_TYPE_OPERATOR; + token_value = get_while(&ptr, IS_OPERATOR, posInfo); + if (token_value == "++") + error("Marble does not support unary \"++\". Please use \"+=1\"", posInfo); + else if (token_value == "--") + error("Marble does not support unary operator \"--\". Please use \"-=1\"", posInfo); + break; - case IS_CHARACTER: - token_value = get_while(&ptr, IS_CHARACTER | IS_NUMBER, posInfo); - if (is_keyword(token_value)) - { - token_type = TOKEN_TYPE_KEYWORD; - } - else - { - token_type = TOKEN_TYPE_IDENTIFIER; - } + case IS_CHARACTER: + token_value = get_while(&ptr, IS_CHARACTER | IS_NUMBER, posInfo); + if (is_keyword(token_value)) + { + token_type = TOKEN_TYPE_KEYWORD; + } + else + { + token_type = TOKEN_TYPE_IDENTIFIER; + } break; - case IS_NUMBER: - token_type = TOKEN_TYPE_NUMBER; - token_value = get_number(&ptr, posInfo); + case IS_NUMBER: + token_type = TOKEN_TYPE_NUMBER; + token_value = get_number(&ptr, posInfo); break; - case IS_SYMBOL: - token_type = TOKEN_TYPE_SYMBOL; - token_value += c; + case IS_SYMBOL: + token_type = TOKEN_TYPE_SYMBOL; + token_value += c; break; } } - Token* new_token = tokenFactory.createToken(token_type, posInfo); + Token *new_token = tokenFactory.createToken(token_type, posInfo); new_token->setValue(token_value); if (root_token == NULL) { @@ -434,14 +437,13 @@ Token* Lexer::stage1(PosInfo posInfo) last_token->next = new_token; last_token = new_token; } - + token_type = -1; token_value = ""; // Increment the pointer - ptr+=1; - posInfo.col+=1; + ptr += 1; + posInfo.col += 1; } - return root_token; } @@ -450,14 +452,14 @@ Token* Lexer::stage1(PosInfo posInfo) * Stage 2 will ensure that the tokens values are valid. * Such as a stacked value is correct, e.g a stacked operator value "++--" is clearly illegal. */ -void Lexer::stage2(Token* root_token) +void Lexer::stage2(Token *root_token) { - Token* token = root_token; - while(token != NULL) + Token *token = root_token; + while (token != NULL) { int token_type = token->getType(); std::string token_value = token->getValue(); - switch(token_type) + switch (token_type) { case TOKEN_TYPE_OPERATOR: { @@ -473,7 +475,7 @@ void Lexer::stage2(Token* root_token) } } -Token* Lexer::lex(PosInfo posInfo) +Token *Lexer::lex(PosInfo posInfo) { // Stage 1 - Remove comments; Create tokens this->root = stage1(posInfo); From af0c2ceb8dd673f8dd21e96ea4e3922dd03cc5f6 Mon Sep 17 00:00:00 2001 From: Daniel McCarthy Date: Wed, 19 Sep 2018 20:13:05 +0100 Subject: [PATCH 2/6] Made it so you no longer require {} for statements where you just want to run the next line --- src/system/parser.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/system/parser.cpp b/src/system/parser.cpp index dea4969..a01f137 100755 --- a/src/system/parser.cpp +++ b/src/system/parser.cpp @@ -1364,21 +1364,26 @@ void Parser::parse_class_body_next() void Parser::parse_body() { - if (!next()->isSymbol("{")) + BodyNode* body_node = (BodyNode*) factory.createNode(NODE_TYPE_BODY); + if (peek()->isSymbol("{")) { - parse_error("Expecting a left bracket \"{\" for given body"); + // Remove the "{" symbol + next(); + while(!peek()->isSymbol("}")) + { + parse_body_next(); + body_node->addNode((InterpretableNode*) pop_node()); + } + // Lets remove the "}" symbol + next(); } - - BodyNode* body_node = (BodyNode*) factory.createNode(NODE_TYPE_BODY); - while(!peek()->isSymbol("}")) + else { + // No "{" bracket provided therefore we will just parse the next body node parse_body_next(); body_node->addNode((InterpretableNode*) pop_node()); } - // Lets remove the "}" symbol - next(); - push_node(body_node); } From 4faa3b303684a1d076e2c002a990029ce3958911 Mon Sep 17 00:00:00 2001 From: Daniel McCarthy Date: Wed, 19 Sep 2018 20:26:30 +0100 Subject: [PATCH 3/6] Made it so you do not have to pass a Value to a vector and instead can pass the literal value and it will be automatically converted --- .../src/storage/commonmod_vector.cpp | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/commonmod/src/storage/commonmod_vector.cpp b/src/commonmod/src/storage/commonmod_vector.cpp index 6374324..751a03e 100755 --- a/src/commonmod/src/storage/commonmod_vector.cpp +++ b/src/commonmod/src/storage/commonmod_vector.cpp @@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "commonmod_vector.h" +#include "commonmod_value.h" #include "function.h" #include "variable.h" #include "array.h" @@ -62,6 +63,25 @@ Class *CommonModule_Vector::registerClass(ModuleSystem *moduleSystem) */ Function *push_function = c->registerFunction("push", {VarType::fromString("Value")}, VarType::fromString("void"), CommonModule_Vector::Vector_Push); + /** + * @class Vector + * + * Pushes the number "v" to this Vector + * + * function push(number v) : void + */ + push_function = c->registerFunction("push", {VarType::fromString("number")}, VarType::fromString("void"), CommonModule_Vector::Vector_Push); + + /** + * @class Vector + * + * Pushes the string "v" to this Vector + * + * function push(string v) : void + */ + push_function = c->registerFunction("push", {VarType::fromString("string")}, VarType::fromString("void"), CommonModule_Vector::Vector_Push); + + /** * @class Vector * @@ -112,6 +132,15 @@ Class *CommonModule_Vector::registerClass(ModuleSystem *moduleSystem) void CommonModule_Vector::Vector_Push(Interpreter *interpreter, std::vector values, Value *return_value, std::shared_ptr object, Scope *caller_scope) { std::shared_ptr vector_obj = std::dynamic_pointer_cast(object); + // If our value type is a string or number then we are going to have to create a Value object for it + // and set the Value's value to this string or number + if (values[0].type == VALUE_TYPE_STRING || values[0].type == VALUE_TYPE_NUMBER) + { + std::shared_ptr new_value = + std::dynamic_pointer_cast(Object::create(interpreter, interpreter->getClassSystem()->getClassByName("Value"), {values[0]})); + // Overwrite our value passed with the new object Value + values[0] = Value(new_value); + } values[0].holder = NULL; vector_obj->vec_values.push_back(Value(&values[0])); } From a5c1de22355fb60322e122c2ea65fb558cd020fc Mon Sep 17 00:00:00 2001 From: Daniel McCarthy Date: Wed, 19 Sep 2018 20:32:32 +0100 Subject: [PATCH 4/6] Made it so you no longer have to pass a Value to a Map you can now pass primitive types --- src/commonmod/src/storage/commonmod_map.cpp | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/commonmod/src/storage/commonmod_map.cpp b/src/commonmod/src/storage/commonmod_map.cpp index 4eed957..d90143a 100755 --- a/src/commonmod/src/storage/commonmod_map.cpp +++ b/src/commonmod/src/storage/commonmod_map.cpp @@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "commonmod_map.h" +#include "commonmod_value.h" #include "function.h" #include "variable.h" #include "array.h" @@ -58,6 +59,24 @@ Class* CommonModule_Map::registerClass(ModuleSystem* moduleSystem) */ c->registerFunction("set", {VarType::fromString("string"), VarType::fromString("Value")}, VarType::fromString("void"), CommonModule_Map::Map_Set); + /** + * @class Map + * function set(string index, number value) : void + * + * Sets the index value represented by the index to the new provided value + */ + c->registerFunction("set", {VarType::fromString("string"), VarType::fromString("number")}, VarType::fromString("void"), CommonModule_Map::Map_Set); + + /** + * @class Map + * function set(string index, string value) : void + * + * Sets the index value represented by the index to the new provided value + */ + c->registerFunction("set", {VarType::fromString("string"), VarType::fromString("string")}, VarType::fromString("void"), CommonModule_Map::Map_Set); + + + /** * @class Map * function get(string index) : Value @@ -69,6 +88,16 @@ Class* CommonModule_Map::registerClass(ModuleSystem* moduleSystem) void CommonModule_Map::Map_Set(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope) { + // If our value type is a string or number then we are going to have to create a Value object for it + // and set the Value's value to this string or number + if (values[1].type == VALUE_TYPE_STRING || values[0].type == VALUE_TYPE_NUMBER) + { + std::shared_ptr new_value = + std::dynamic_pointer_cast(Object::create(interpreter, interpreter->getClassSystem()->getClassByName("Value"), {values[0]})); + // Overwrite our value passed with the new object Value + values[1] = Value(new_value); + } + std::shared_ptr map_obj = std::dynamic_pointer_cast(object); map_obj->value_map[values[0].svalue] = std::make_unique(&values[1]); } From 2f394a1fc16f2c03581d7bec54702ae3ba5e2c7b Mon Sep 17 00:00:00 2001 From: Daniel McCarthy Date: Wed, 19 Sep 2018 21:08:10 +0100 Subject: [PATCH 5/6] Implemented strtoupper and strtolower functions --- src/commonmod/include/commonmod_stringutils.h | 2 + src/commonmod/src/commonmod_stringutils.cpp | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/commonmod/include/commonmod_stringutils.h b/src/commonmod/include/commonmod_stringutils.h index 9db948c..0cc8757 100755 --- a/src/commonmod/include/commonmod_stringutils.h +++ b/src/commonmod/include/commonmod_stringutils.h @@ -46,6 +46,8 @@ class CommonModule_StringUtils : public Object static void StringUtils_number_format(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope); static void StringUtils_number_to_string(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope); static void StringUtils_safe_tags(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope); + static void StringUtils_strtoupper(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope); + static void StringUtils_strtolower(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope); }; diff --git a/src/commonmod/src/commonmod_stringutils.cpp b/src/commonmod/src/commonmod_stringutils.cpp index 71e4b6a..c3e0c8a 100755 --- a/src/commonmod/src/commonmod_stringutils.cpp +++ b/src/commonmod/src/commonmod_stringutils.cpp @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "exceptionobject.h" #include #include +#include CommonModule_StringUtils::CommonModule_StringUtils(Class *c) : Object(c) { @@ -228,6 +229,32 @@ Class *CommonModule_StringUtils::registerClass(ModuleSystem *moduleSystem) */ c->registerFunction("safe_tags", {VarType::fromString("string")}, VarType::fromString("string"), CommonModule_StringUtils::StringUtils_safe_tags); moduleSystem->getFunctionSystem()->registerFunction("safe_tags", {VarType::fromString("string")}, VarType::fromString("string"), CommonModule_StringUtils::StringUtils_safe_tags); + + /** + * @class StringUtils + * + * Converts the given string to upper case and returns it. + * + * Since: V0.2.0 + * @works_without_class + * + * function strtoupper(string val) : string + */ + c->registerFunction("strtoupper", {VarType::fromString("string")}, VarType::fromString("string"), CommonModule_StringUtils::StringUtils_strtoupper); + moduleSystem->getFunctionSystem()->registerFunction("strtoupper", {VarType::fromString("string")}, VarType::fromString("string"), CommonModule_StringUtils::StringUtils_strtoupper); + + + /** + * @class StringUtils + * + * Converts the given string to lower case and returns it + * Since: V0.2.0 + * @works_without_class + * + * function strtolower(string val) : string + */ + c->registerFunction("strtolower", {VarType::fromString("string")}, VarType::fromString("string"), CommonModule_StringUtils::StringUtils_strtolower); + moduleSystem->getFunctionSystem()->registerFunction("strtolower", {VarType::fromString("string")}, VarType::fromString("string"), CommonModule_StringUtils::StringUtils_strtolower); } void CommonModule_StringUtils::StringUtils_getASCIIString(Interpreter *interpreter, std::vector values, Value *return_value, std::shared_ptr object, Scope *caller_scope) @@ -376,4 +403,18 @@ void CommonModule_StringUtils::StringUtils_safe_tags(Interpreter *interpreter, s result = str_replace(result, "<", "<"); result = str_replace(result, ">", ">"); return_value->set(result); +} + +void CommonModule_StringUtils::StringUtils_strtoupper(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope) +{ + std::string s = values[0].svalue; + std::transform(s.begin(), s.end(), s.begin(), ::toupper); + return_value->set(s); +} + +void CommonModule_StringUtils::StringUtils_strtolower(Interpreter* interpreter, std::vector values, Value* return_value, std::shared_ptr object, Scope* caller_scope) +{ + std::string s = values[0].svalue; + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return_value->set(s); } \ No newline at end of file From 0308631659ac781c54a2a0f2cb5b49f53391644b Mon Sep 17 00:00:00 2001 From: Daniel McCarthy Date: Wed, 19 Sep 2018 21:16:52 +0100 Subject: [PATCH 6/6] Updated new version --- include/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config.h b/include/config.h index 98bd825..342c768 100755 --- a/include/config.h +++ b/include/config.h @@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Marble versioning information #define MARBLE_MAJOR_CODENAME "Clearies" -#define MARBLE_VERSION "0.1.5" +#define MARBLE_VERSION "0.2.0" #define MAX_KEYWORD_SIZE 15 #define MAX_OPERATORS_SIZE 3