From 25288ae6783d3a44d80d19264a87dc3999eac0be Mon Sep 17 00:00:00 2001 From: ewowi Date: Mon, 3 Jan 2022 16:04:39 +0100 Subject: [PATCH] Custom Effects 0.2.2 to 0.2.3 (performance improvements) --- wled00/src/dependencies/arti/arti.h | 1225 +++++++++++++--------- wled00/src/dependencies/arti/arti_wled.h | 36 +- 2 files changed, 741 insertions(+), 520 deletions(-) diff --git a/wled00/src/dependencies/arti/arti.h b/wled00/src/dependencies/arti/arti.h index 29ef94c79d..8f0e30f075 100644 --- a/wled00/src/dependencies/arti/arti.h +++ b/wled00/src/dependencies/arti/arti.h @@ -1,8 +1,8 @@ /* @title Arduino Real Time Interpreter (ARTI) @file arti.h - @version 0.2.2 - @date 20211216 + @version 0.2.3 + @date 20220103 @author Ewoud Wijma @repo https://github.com/ewoudwijma/ARTI @remarks @@ -34,11 +34,12 @@ - Add unary operators: -1, ++, -- - support .12345 notation - bug fix != + - minimize value["x"] @todo - check why statement is not 'shrinked' - - minimize value["x"] - make default work in js (remove default as we have now load template) - save log after first run of loop to get runtime errors included (or 30 iterations to also capture any stack overflows) + - add PI */ #pragma once @@ -61,6 +62,7 @@ const char spaces[51] PROGMEM = " "; #define FREE_SIZE esp_get_free_heap_size() + // #define OPTIMIZED_TREE 1 #else //embedded #include "dependencies/ArduinoJson-recent.h" @@ -80,6 +82,7 @@ const char spaces[51] = " "; #define FREE_SIZE (unsigned int)0 + // #define OPTIMIZED_TREE 1 #endif bool logToFile = true; //print output to file (e.g. default.wled.log) @@ -201,7 +204,7 @@ char* strupr(char* s) return s; } -enum Operators +enum Tokens { F_integerConstant, F_realConstant, @@ -221,10 +224,11 @@ enum Operators F_and, F_or, F_plusplus, - F_minmin + F_minmin, + F_NoToken = 255 }; -const char * operatorToString(uint8_t key) +const char * tokenToString(uint8_t key) { switch (key) { case F_integerConstant: @@ -264,7 +268,7 @@ const char * operatorToString(uint8_t key) return "<"; break; case F_lessThenOrEqual: - return "<=>"; + return "<="; break; case F_greaterThen: return ">"; @@ -288,12 +292,65 @@ const char * operatorToString(uint8_t key) return "unknown key"; } -enum Constructs +uint8_t stringToToken(const char * token, const char * value) +{ + if (strcmp(token, "INTEGER_CONST") == 0) + return F_integerConstant; + else if (strcmp(token, "REAL_CONST") == 0) + return F_realConstant; + else if (strcmp(value, "+") == 0) + return F_plus; + else if (strcmp(value, "-") == 0) + return F_minus; + else if (strcmp(value, "*") == 0) + return F_multiplication; + else if (strcmp(value, "/") == 0) + return F_division; + else if (strcmp(value, "%") == 0) + return F_modulo; + else if (strcmp(value, "<<") == 0) + return F_bitShiftLeft; + else if (strcmp(value, ">>") == 0) + return F_bitShiftRight; + else if (strcmp(value, "==") == 0) + return F_equal; + else if (strcmp(value, "!=") == 0) + return F_notEqual; + else if (strcmp(value, ">") == 0) + return F_greaterThen; + else if (strcmp(value, ">=") == 0) + return F_greaterThenOrEqual; + else if (strcmp(value, "<") == 0) + return F_lessThen; + else if (strcmp(value, "<=") == 0) + return F_lessThenOrEqual; + else if (strcmp(value, "&&") == 0) + return F_and; + else if (strcmp(value, "||") == 0) + return F_or; + else if (strcmp(value, "+=") == 0) + return F_plus; + else if (strcmp(value, "-=") == 0) + return F_minus; + else if (strcmp(value, "*=") == 0) + return F_multiplication; + else if (strcmp(value, "/=") == 0) + return F_division; + else if (strcmp(value, "++") == 0) + return F_plusplus; + else if (strcmp(value, "--") == 0) + return F_minmin; + else + return F_NoToken; + +} + +enum Nodes { F_Program, F_Function, F_Call, - F_Var, + F_VarDef, F_Assign, F_Formal, F_VarRef, @@ -301,79 +358,128 @@ enum Constructs F_If, F_Cex, F_Expr, - F_Term + F_Term, + #ifdef OPTIMIZED_TREE + F_Statement, + F_Indices, + F_Formals, + F_Factor, + F_Block, + F_Actuals, + F_Increment, + F_AssignOperator, + #endif + F_NoNode = 255 }; -const char * constructToString(uint8_t key) +//Tokens +// ID + +//Optimizer +// level +// index +// external + +// block +// formals +// actuals +// increment +// assignoperator +// type (not used yet in wled) + +//expr +//indices + +const char * nodeToString(uint8_t key) { switch (key) { case F_Program: return "program"; - break; case F_Function: return "function"; - break; case F_Call: return "call"; - break; - case F_Var: + case F_VarDef: return "variable"; - break; case F_Assign: return "assign"; - break; case F_Formal: return "formal"; - break; case F_VarRef: return "varref"; - break; case F_For: return "for"; - break; case F_If: return "if"; - break; case F_Cex: return "cex"; - break; case F_Expr: return "expr"; - break; case F_Term: return "term"; - break; + #ifdef OPTIMIZED_TREEX + case F_Statement: + return "statement"; + case F_Indices: + return "indices"; + case F_Formals: + return "formals"; + case F_Factor: + return "factor"; + case F_Block: + return "block"; + case F_Actuals: + return "actuals"; + #endif } return "unknown key"; } -uint8_t stringToConstruct(const char * construct) +uint8_t stringToNode(const char * node) { - if (strcmp(construct, "program") == 0) + if (strcmp(node, "program") == 0) return F_Program; - else if (strcmp(construct, "function") == 0) + else if (strcmp(node, "function") == 0) return F_Function; - else if (strcmp(construct, "call") == 0) + else if (strcmp(node, "call") == 0) return F_Call; - else if (strcmp(construct, "variable") == 0) - return F_Var; - else if (strcmp(construct, "assign") == 0) + else if (strcmp(node, "variable") == 0) + return F_VarDef; + else if (strcmp(node, "assign") == 0) return F_Assign; - else if (strcmp(construct, "formal") == 0) + else if (strcmp(node, "formal") == 0) return F_Formal; - else if (strcmp(construct, "varref") == 0) + else if (strcmp(node, "varref") == 0) return F_VarRef; - else if (strcmp(construct, "for") == 0) + else if (strcmp(node, "for") == 0) return F_For; - else if (strcmp(construct, "if") == 0) + else if (strcmp(node, "if") == 0) return F_If; - else if (strcmp(construct, "cex") == 0) + else if (strcmp(node, "cex") == 0) return F_Cex; - else if (strcmp(construct, "expr") == 0) + else if (strcmp(node, "expr") == 0) return F_Expr; - else if (strcmp(construct, "term") == 0) + else if (strcmp(node, "term") == 0) return F_Term; - return 255; + #ifdef OPTIMIZED_TREE + else if (strcmp(node, "statement") == 0) + return F_Statement; + else if (strcmp(node, "indices") == 0) + return F_Indices; + else if (strcmp(node, "formals") == 0) + return F_Formals; + else if (strcmp(node, "factor") == 0) + return F_Factor; + else if (strcmp(node, "block") == 0) + return F_Block; + else if (strcmp(node, "actuals") == 0) + return F_Actuals; + else if (strcmp(node, "increment") == 0) + return F_Increment; + else if (strcmp(node, "assignoperator") == 0) + return F_AssignOperator; + #endif + return F_NoNode; } bool errorOccurred = false; @@ -422,170 +528,179 @@ class Lexer { DEBUG_ARTI("Destruct Lexer\n"); } - void advance() { - if (this->current_char == '\n') { - this->lineno += 1; - this->column = 0; - } - this->pos++; - - if (this->pos > strlen(this->text) - 1) - this->current_char = -1; - else { - this->current_char = this->text[this->pos]; - this->column++; - } + void advance() { + if (this->current_char == '\n') + { + this->lineno += 1; + this->column = 0; } + this->pos++; - void skip_whitespace() { - while (this->current_char != -1 && isspace(this->current_char)) - this->advance(); + if (this->pos > strlen(this->text) - 1) + this->current_char = -1; + else + { + this->current_char = this->text[this->pos]; + this->column++; } + } - void skip_comment(const char * endTokens) { - while (strncmp(this->text + this->pos, endTokens, strlen(endTokens)) != 0) - this->advance(); - for (int i=0; iadvance(); - } + void skip_whitespace() + { + while (this->current_char != -1 && isspace(this->current_char)) + this->advance(); + } + + void skip_comment(const char * endTokens) + { + while (strncmp(this->text + this->pos, endTokens, strlen(endTokens)) != 0) + this->advance(); + for (int i=0; iadvance(); + } - void number() + void number() + { + current_token.lineno = this->lineno; + current_token.column = this->column; + strcpy(current_token.type, ""); + strcpy(current_token.value, ""); + + char result[charLength] = ""; + while (this->current_char != -1 && isdigit(this->current_char)) { - current_token.lineno = this->lineno; - current_token.column = this->column; - strcpy(current_token.type, ""); - strcpy(current_token.value, ""); + result[strlen(result)] = this->current_char; + this->advance(); + } + if (this->current_char == '.') + { + result[strlen(result)] = this->current_char; + this->advance(); - char result[charLength] = ""; while (this->current_char != -1 && isdigit(this->current_char)) { result[strlen(result)] = this->current_char; this->advance(); } - if (this->current_char == '.') - { - result[strlen(result)] = this->current_char; - this->advance(); - - while (this->current_char != -1 && isdigit(this->current_char)) - { - result[strlen(result)] = this->current_char; - this->advance(); - } - - result[strlen(result)] = '\0'; - strcpy(current_token.type, "REAL_CONST"); - strcpy(current_token.value, result); - } - else - { - result[strlen(result)] = '\0'; - strcpy(current_token.type, "INTEGER_CONST"); - strcpy(current_token.value, result); - } + result[strlen(result)] = '\0'; + strcpy(current_token.type, "REAL_CONST"); + strcpy(current_token.value, result); } - - void id() + else { - current_token.lineno = this->lineno; - current_token.column = this->column; - strcpy(current_token.type, ""); - strcpy(current_token.value, ""); + result[strlen(result)] = '\0'; + strcpy(current_token.type, "INTEGER_CONST"); + strcpy(current_token.value, result); + } - char result[charLength] = ""; - while (this->current_char != -1 && (isalnum(this->current_char) || this->current_char == '_')) - { - result[strlen(result)] = this->current_char; - this->advance(); - } - result[strlen(result)] = '\0'; + } - char resultUpper[charLength]; - strcpy(resultUpper, result); - strupr(resultUpper); + void id() + { + current_token.lineno = this->lineno; + current_token.column = this->column; + strcpy(current_token.type, ""); + strcpy(current_token.value, ""); - if (definitionJson["TOKENS"][resultUpper].isNull()) - { - strcpy(current_token.type, "ID"); - strcpy(current_token.value, result); - } - else { - strcpy(current_token.type, definitionJson["TOKENS"][resultUpper]); - strcpy(current_token.value, resultUpper); - } + char result[charLength] = ""; + while (this->current_char != -1 && (isalnum(this->current_char) || this->current_char == '_')) + { + result[strlen(result)] = this->current_char; + this->advance(); } + result[strlen(result)] = '\0'; + + char resultUpper[charLength]; + strcpy(resultUpper, result); + strupr(resultUpper); - void get_next_token() + if (definitionJson["TOKENS"].containsKey(resultUpper)) { - current_token.lineno = this->lineno; - current_token.column = this->column; - strcpy(current_token.type, ""); - strcpy(current_token.value, ""); + strcpy(current_token.type, definitionJson["TOKENS"][resultUpper]); + strcpy(current_token.value, resultUpper); + } + else + { + strcpy(current_token.type, "ID"); + strcpy(current_token.value, result); + } + } - if (errorOccurred) return; + void get_next_token() + { + current_token.lineno = this->lineno; + current_token.column = this->column; + strcpy(current_token.type, ""); + strcpy(current_token.value, ""); - while (this->current_char != -1 && this->pos <= strlen(this->text) - 1 && !errorOccurred) - { - if (isspace(this->current_char)) { - this->skip_whitespace(); - continue; - } + if (errorOccurred) return; - if (strncmp(this->text + this->pos, "/*", 2) == 0) { - this->advance(); - skip_comment("*/"); - continue; - } + while (this->current_char != -1 && this->pos <= strlen(this->text) - 1 && !errorOccurred) + { + if (isspace(this->current_char)) { + this->skip_whitespace(); + continue; + } - if (strncmp(this->text + this->pos, "//", 2) == 0) { - this->advance(); - skip_comment("\n"); - continue; - } + if (strncmp(this->text + this->pos, "/*", 2) == 0) + { + this->advance(); + skip_comment("*/"); + continue; + } - if (isalpha(this->current_char)) { - this->id(); - return; - } - - if (isdigit(this->current_char) || this->current_char == '.') { - this->number(); - return; - } + if (strncmp(this->text + this->pos, "//", 2) == 0) + { + this->advance(); + skip_comment("\n"); + continue; + } - // findLongestMatchingToken - char token_type[charLength] = ""; - char token_value[charLength] = ""; - - uint8_t longestTokenLength = 0; - - for (JsonPair tokenPair: definitionJson["TOKENS"].as()) { - const char * value = tokenPair.value(); - char currentValue[charLength]; - strncpy(currentValue, this->text + this->pos, charLength); - currentValue[strlen(value)] = '\0'; - if (strcmp(value, currentValue) == 0 && strlen(value) > longestTokenLength) { - strcpy(token_type, tokenPair.key().c_str()); - strcpy(token_value, value); - longestTokenLength = strlen(value); - } - } + if (isalpha(this->current_char)) + { + this->id(); + return; + } + + if (isdigit(this->current_char) || (this->current_char == '.' && isdigit(this->text[this->pos+1]))) + { + this->number(); + return; + } - if (strcmp(token_type, "") != 0 && strcmp(token_value, "") != 0) - { - strcpy(current_token.type, token_type); - strcpy(current_token.value, token_value); - for (int i=0; iadvance(); - return; - } - else { - ERROR_ARTI("Lexer error on %c line %u col %u\n", this->current_char, this->lineno, this->column); - errorOccurred = true; + // findLongestMatchingToken + char token_type[charLength] = ""; + char token_value[charLength] = ""; + + uint8_t longestTokenLength = 0; + + for (JsonPair tokenPair: definitionJson["TOKENS"].as()) { + const char * value = tokenPair.value(); + char currentValue[charLength]; + strncpy(currentValue, this->text + this->pos, charLength); + currentValue[strlen(value)] = '\0'; + if (strcmp(value, currentValue) == 0 && strlen(value) > longestTokenLength) { + strcpy(token_type, tokenPair.key().c_str()); + strcpy(token_value, value); + longestTokenLength = strlen(value); } } - } //get_next_token + + if (strcmp(token_type, "") != 0 && strcmp(token_value, "") != 0) + { + strcpy(current_token.type, token_type); + strcpy(current_token.value, token_value); + for (int i=0; iadvance(); + return; + } + else { + ERROR_ARTI("Lexer error on %c line %u col %u\n", this->current_char, this->lineno, this->column); + errorOccurred = true; + } + } + } //get_next_token void eat(const char * token_type) { // DEBUG_ARTI("try to eat %s %s\n", lexer->current_token.type, token_type); @@ -645,7 +760,7 @@ class Symbol { uint8_t scope_level; uint8_t scope_index; ScopedSymbolTable* scope = nullptr; - ScopedSymbolTable* function_scope = nullptr; //used to find the formal parameters in the scope of a function symbol + ScopedSymbolTable* function_scope = nullptr; //used to find the formal parameters in the scope of a function node JsonVariant block; @@ -950,7 +1065,7 @@ class ARTI { void arti_set_external_variable(float value, uint8_t variable, float par1 = floatNull, float par2 = floatNull, float par3 = floatNull); bool loop(); - uint8_t parse(JsonVariant parseTree, const char * symbol_name, char operatorx, JsonVariant expression, uint8_t depth = 0) + uint8_t parse(JsonVariant parseTree, const char * node_name, char operatorx, JsonVariant expression, uint8_t depth = 0) { if (depth > 50) { @@ -963,40 +1078,34 @@ class ARTI { uint8_t resultChild = ResultContinue; - //check if unary or binary operator - // if (expression.size() > 1) { - // DEBUG_ARTI("%s\n", "array multiple 1 ", parseTree); - // DEBUG_ARTI("%s\n", "array multiple 2 ", expression); - // } - - if (expression.is()) { //should always be the case - for (JsonVariant arrayElement: expression.as()) + if (expression.is()) //should always be the case + { + for (JsonVariant expressionElement: expression.as()) //e.g. ["PROGRAM","ID","block"] { - JsonVariant nextExpression = arrayElement; - const char * nextSymbol_name = symbol_name; + const char * nextNode_name = node_name; //e.g. "program": + JsonVariant nextExpression = expressionElement; // e.g. block JsonVariant nextParseTree = parseTree; - JsonArray arr; - JsonVariant symbolExpression = lexer->definitionJson[arrayElement.as()]; + JsonVariant nodeExpression = lexer->definitionJson[expressionElement.as()]; - if (!symbolExpression.isNull()) //is arrayElement a Symbol e.g. "compound" : ["CURLYOPEN", "block*", "CURLYCLOSE"], + if (!nodeExpression.isNull()) //is expressionElement a Node e.g. "block" : ["LCURL",{"*": ["statement"]},"RCURL"] { - nextSymbol_name = arrayElement.as(); - nextExpression = symbolExpression; + nextNode_name = expressionElement; //e.g. block + nextExpression = nodeExpression; // e.g. ["LCURL",{"*": ["statement"]},"RCURL"] - // DEBUG_ARTI("%s %s %u\n", spaces+50-depth, nextSymbol_name, depth); //, parseTree.as().c_str() + // DEBUG_ARTI("%s %s %u\n", spaces+50-depth, nextNode_name, depth); //, parseTree.as().c_str() if (parseTree.is()) { - parseTree[parseTree.size()][nextSymbol_name]["connect"] = "array"; + parseTree[parseTree.size()][nextNode_name]["connect"] = "array"; nextParseTree = parseTree[parseTree.size()-1]; //nextparsetree is last element in the array (which is always an object) } else //no list, create object { - if (parseTree[symbol_name].isNull()) //no object yet - parseTree[symbol_name]["connect"] = "object"; //make the connection, new object item + if (parseTree[node_name].isNull()) //no object yet + parseTree[node_name]["connect"] = "object"; //make the connection, new object item - nextParseTree = parseTree[symbol_name]; + nextParseTree = parseTree[node_name]; } } @@ -1013,14 +1122,14 @@ class ARTI { { if (objectOperator == '*' || objectOperator == '+') { - nextParseTree[nextSymbol_name]["*"][0] = "multiple"; - nextParseTree = nextParseTree[nextSymbol_name]["*"]; + nextParseTree[nextNode_name]["*"][0] = "multiple"; // * is another object in the list of objects + nextParseTree = nextParseTree[nextNode_name]["*"]; } //and: see 'is array' if (objectOperator == '|') { - resultChild = parse(nextParseTree, nextSymbol_name, objectOperator, objectElement, depth + 1); + resultChild = parse(nextParseTree, nextNode_name, objectOperator, objectElement, depth + 1); if (resultChild != ResultFail) resultChild = ResultContinue; } else @@ -1029,7 +1138,7 @@ class ARTI { uint8_t counter = 0; while (resultChild2 == ResultContinue) { - resultChild2 = parse(nextParseTree, nextSymbol_name, objectOperator, objectElement, depth + 1); //no assign to result as optional + resultChild2 = parse(nextParseTree, nextNode_name, objectOperator, objectElement, depth + 1); //no assign to result as optional if (objectOperator == '?') { //zero or one iteration, also continue if parse not continue resultChild2 = ResultContinue; @@ -1068,81 +1177,104 @@ class ARTI { } //not or } // element is array else - ERROR_ARTI("%s Definition error: should be an array %s %c %s\n", spaces+50-depth, stringOrEmpty(nextSymbol_name), operatorx, objectElement.as().c_str()); + ERROR_ARTI("%s Definition error: should be an array %s %c %s\n", spaces+50-depth, stringOrEmpty(nextNode_name), operatorx, objectElement.as().c_str()); } else if (nextExpression.is()) // e.g. ["LPAREN", "expr*", "RPAREN"] { - resultChild = parse(nextParseTree, nextSymbol_name, '&', nextExpression, depth + 1); // every array element starts with '&' (operatorx is for result of all elements of array) + resultChild = parse(nextParseTree, nextNode_name, '&', nextExpression, depth + 1); // every array element starts with '&' (operatorx is for result of all elements of array) } - else if (!lexer->definitionJson["TOKENS"][nextExpression.as()].isNull()) // token e.g. "ID" + else if (lexer->definitionJson["TOKENS"].containsKey(nextExpression.as())) // token e.g. "ID" { const char * token_type = nextExpression; if (strcmp(lexer->current_token.type, token_type) == 0) { - if (strcmp(lexer->current_token.type, "") != 0) - DEBUG_ARTI("%s %s %s", spaces+50-depth, lexer->current_token.type, lexer->current_token.value); - - if (nextParseTree.is()) - { - JsonArray arr = nextParseTree.as(); - arr[arr.size()][lexer->current_token.type] = lexer->current_token.value; //add in last element of array - } + DEBUG_ARTI("%s %s %s", spaces+50-depth, lexer->current_token.type, lexer->current_token.value); + + //only add 'semantic tokens' + if (strcmp(lexer->current_token.type, "ID") != 0 && strcmp(lexer->current_token.type, "INTEGER_CONST") != 0 && strcmp(lexer->current_token.type, "REAL_CONST") != 0 && + strcmp(lexer->current_token.type, "INTEGER") != 0 && strcmp(lexer->current_token.type, "REAL") != 0 && + strcmp(lexer->current_token.value, "+") != 0 && strcmp(lexer->current_token.value, "-") != 0 && strcmp(lexer->current_token.value, "*") != 0 && strcmp(lexer->current_token.value, "/") != 0 && strcmp(lexer->current_token.value, "%") != 0 && + strcmp(lexer->current_token.value, "+=") != 0 && strcmp(lexer->current_token.value, "-=") != 0 && strcmp(lexer->current_token.value, "*=") != 0 && strcmp(lexer->current_token.value, "/=") != 0 && + strcmp(lexer->current_token.value, "<<") != 0 && strcmp(lexer->current_token.value, ">>") != 0 && + strcmp(lexer->current_token.value, "==") != 0 && strcmp(lexer->current_token.value, "!=") != 0 && + strcmp(lexer->current_token.value, "&&") != 0 && strcmp(lexer->current_token.value, "||") != 0 && + strcmp(lexer->current_token.value, ">") != 0 && strcmp(lexer->current_token.value, ">=") != 0 && strcmp(lexer->current_token.value, "<") != 0 && strcmp(lexer->current_token.value, "<=") != 0 && + strcmp(lexer->current_token.value, "++") != 0 && strcmp(lexer->current_token.value, "--") != 0 + ) + {} else - nextParseTree[nextSymbol_name][lexer->current_token.type] = lexer->current_token.value; - - if (strcmp(token_type, "PLUS2") == 0) { //debug for unary operators (wip) - // DEBUG_ARTI("%s\n", "array multiple 1 ", parseTree); - // DEBUG_ARTI("%s\n", "array multiple 2 ", expression); + { + if (nextParseTree.is()) + nextParseTree.as()[nextParseTree.size()][lexer->current_token.type] = lexer->current_token.value; //add in last element of array + else + nextParseTree[nextNode_name][lexer->current_token.type] = lexer->current_token.value; } lexer->eat(token_type); - if (strcmp(lexer->current_token.type, "") != 0) - DEBUG_ARTI(" -> [%s %s]", lexer->current_token.type, lexer->current_token.value); + DEBUG_ARTI(" -> [%s %s] %d\n", lexer->current_token.type, lexer->current_token.value, depth); - DEBUG_ARTI(" %d\n", depth); resultChild = ResultContinue; } else //deadend - { resultChild = ResultFail; - } } // if token - else //arrayElement is not a symbol, not a token, not an array and not an object + else //expressionElement is not a node, not a token, not an array and not an object { - if (lexer->definitionJson[nextExpression.as()].isNull()) - ERROR_ARTI("%s Programming error: %s not a symbol, token, array or object in %s\n", spaces+50-depth, nextExpression.as().c_str(), stringOrEmpty(nextSymbol_name)); + if (lexer->definitionJson.containsKey(nextExpression.as())) + ERROR_ARTI("%s Programming error: %s not a node, token, array or object in %s\n", spaces+50-depth, nextExpression.as().c_str(), stringOrEmpty(nextNode_name)); else - ERROR_ARTI("%s Definition error: \"%s\": \"%s\" symbol should be embedded in array\n", spaces+50-depth, stringOrEmpty(nextSymbol_name), nextExpression.as().c_str()); + ERROR_ARTI("%s Definition error: \"%s\": \"%s\" node should be embedded in array\n", spaces+50-depth, stringOrEmpty(nextNode_name), nextExpression.as().c_str()); } //nextExpression is not a token - if (!symbolExpression.isNull()) //if symbol + if (!nodeExpression.isNull()) //if node { nextParseTree.remove("connect"); //remove connector if (resultChild == ResultFail) { //remove result of parse - nextParseTree.remove(nextSymbol_name); //remove the failed stuff + nextParseTree.remove(nextNode_name); //remove the failed stuff - // DEBUG_ARTI("%s fail %s\n", spaces+50-depth, nextSymbol_name); + // DEBUG_ARTI("%s fail %s\n", spaces+50-depth, nextNode_name); } else //success { - DEBUG_ARTI("%s found %s\n", spaces+50-depth, nextSymbol_name); - + DEBUG_ARTI("%s found %s\n", spaces+50-depth, nextNode_name);//, nextParseTree.as().c_str()); //parseTree optimization moved to optimize function - // if (parseTree.is() )//&& parseTree.size() == 1 + // if (nextParseTree.is()) // { - // JsonObject nextParseTreeObject = parseTree.as(); - // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, symbol_name, parseTree.as().c_str()); - // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, nextSymbol_name, nextParseTree.as().c_str()); + // // optimize(nextParseTree, depth); + + // JsonObject innerObject = nextParseTree[nextNode_name].as(); + + // JsonObject::iterator begin = innerObject.begin(); + // if (fnextParseTree.size() == 1 && nextParseTree[nextNode_name].size() == 1 && lexer->definitionJson.containsKey(nextNode_name) && lexer->definitionJson.containsKey(nextNode_name) && lexer->definitionJson.containsKey(begin->key())) + // { + // // JsonObject nextParseTreeObject = nextParseTree.as(); + + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); + // DEBUG_ARTI("%s found replace %s by %s %s\n", spaces+50-depth, nextNode_name, begin->key().c_str(), nextParseTree.as().c_str()); + // DEBUG_ARTI("%s expression %s\n", spaces+50-depth, expression.as().c_str()); + // DEBUG_ARTI("%s found %s\n", spaces+50-depth, nextParseTree[nextNode_name].as().c_str()); + + // nextParseTree.remove(nextNode_name); + // // char temp[charLength]; + // // strcpy(temp, nextNode_name); + // // strcat(temp, "-"); + // // strcat(temp, begin->key().c_str()); + // nextParseTree[begin->key()] = begin->value(); + + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); + // } // } + // else + // DEBUG_ARTI("%s no jsonobject??? %s\n", spaces+50-depth, parseTree.as().c_str()); } - } // if symbol + } // if node - //determine result of arrayElement + //determine result of expressionElement if (operatorx == '|') { if (resultChild == ResultFail) {//if fail, go back and try another // result = ResultContinue; @@ -1161,15 +1293,16 @@ class ARTI { if (result != ResultContinue) //if no reason to continue then stop break; - } //for arrayelement + } //for expressionElement - if (operatorx == '|') { + if (operatorx == '|') + { if (result != ResultStop) //still looking but nothing to look for result = ResultFail; } } else { //should never happen - ERROR_ARTI("%s Programming error: no array %s %c %s\n", spaces+50-depth, stringOrEmpty(symbol_name), operatorx, expression.as().c_str()); + ERROR_ARTI("%s Programming error: no array %s %c %s\n", spaces+50-depth, stringOrEmpty(node_name), operatorx, expression.as().c_str()); } return result; @@ -1196,13 +1329,62 @@ class ARTI { { bool visitedAlready = false; - if (!definitionJson[key].isNull()) //if key is symbol_name + if (strcmp(key, "*") == 0 ) // multiple { - uint8_t construct = stringToConstruct(key); + } + else if (strcmp(key, "token") == 0) // do nothing with added tokens + visitedAlready = true; + else if (this->definitionJson["TOKENS"].containsKey(key)) // if token + { + const char * valueStr = value; + + if (strcmp(key, "INTEGER_CONST") == 0) + parseTree["token"] = F_integerConstant; + else if (strcmp(key, "REAL_CONST") == 0) + parseTree["token"] = F_realConstant; + else if (strcmp(valueStr, "+") == 0) + parseTree["token"] = F_plus; + else if (strcmp(valueStr, "-") == 0) + parseTree["token"] = F_minus; + else if (strcmp(valueStr, "*") == 0) + parseTree["token"] = F_multiplication; + else if (strcmp(valueStr, "/") == 0) + parseTree["token"] = F_division; + else if (strcmp(valueStr, "%") == 0) + parseTree["token"] = F_modulo; + else if (strcmp(valueStr, "<<") == 0) + parseTree["token"] = F_bitShiftLeft; + else if (strcmp(valueStr, ">>") == 0) + parseTree["token"] = F_bitShiftRight; + else if (strcmp(valueStr, "==") == 0) + parseTree["token"] = F_equal; + else if (strcmp(valueStr, "!=") == 0) + parseTree["token"] = F_notEqual; + else if (strcmp(valueStr, ">") == 0) + parseTree["token"] = F_greaterThen; + else if (strcmp(valueStr, ">=") == 0) + parseTree["token"] = F_greaterThenOrEqual; + else if (strcmp(valueStr, "<") == 0) + parseTree["token"] = F_lessThen; + else if (strcmp(valueStr, "<=") == 0) + parseTree["token"] = F_lessThenOrEqual; + else if (strcmp(valueStr, "&&") == 0) + parseTree["token"] = F_and; + else if (strcmp(valueStr, "||") == 0) + parseTree["token"] = F_or; + else + ERROR_ARTI("%s Programming error: token not defined as operator %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + + visitedAlready = true; + } + else //if key is node_name + { + uint8_t node = stringToNode(key); - switch (construct) + switch (node) { - case F_Program: { + case F_Program: + { const char * program_name = value["ID"]; global_scope = new ScopedSymbolTable(program_name, 1, nullptr); //current_scope @@ -1223,18 +1405,18 @@ class ARTI { #ifdef ARTI_DEBUG for (uint8_t i=0; isymbolsIndex; i++) { Symbol* symbol = global_scope->symbols[i]; - ANDBG_ARTI("%s %u %s %s.%s of %u (%u)\n", spaces+50-depth, i, constructToString(symbol->symbol_type), global_scope->scope_name, symbol->name, symbol->type, symbol->scope_level); + ANDBG_ARTI("%s %u %s %s.%s of %u (%u)\n", spaces+50-depth, i, nodeToString(symbol->symbol_type), global_scope->scope_name, symbol->name, symbol->type, symbol->scope_level); } #endif visitedAlready = true; break; } - case F_Function: { - + case F_Function: + { //find the function name (so we must know this is a function...) const char * function_name = value["ID"]; - Symbol* function_symbol = new Symbol(construct, function_name); + Symbol* function_symbol = new Symbol(node, function_name); current_scope->insert(function_symbol); ANDBG_ARTI("%s Function %s.%s\n", spaces+50-depth, current_scope->scope_name, function_name); @@ -1245,7 +1427,7 @@ class ARTI { ERROR_ARTI("ScopedSymbolTable %s childs full (%d)", current_scope->scope_name, nrOfChildScope); function_symbol->function_scope = function_scope; - if (!value["formals"].isNull()) + if (value.containsKey("formals")) analyze(value["formals"], nullptr, function_scope, depth + 1); if (value["block"].isNull()) @@ -1256,82 +1438,88 @@ class ARTI { #ifdef ARTI_DEBUG for (uint8_t i=0; isymbolsIndex; i++) { Symbol* symbol = function_scope->symbols[i]; - ANDBG_ARTI("%s %u %s %s.%s of %u (%u)\n", spaces+50-depth, i, constructToString(symbol->symbol_type), function_scope->scope_name, symbol->name, symbol->type, symbol->scope_level); + ANDBG_ARTI("%s %u %s %s.%s of %u (%u)\n", spaces+50-depth, i, nodeToString(symbol->symbol_type), function_scope->scope_name, symbol->name, symbol->type, symbol->scope_level); } #endif visitedAlready = true; break; } - case F_Var: + case F_VarDef: case F_Formal: case F_Assign: - case F_VarRef: { - const char * variable_name = value["ID"]; - if (construct == F_Assign) - variable_name = value["varref"]["ID"]; - - char param_type[charLength]; - if (!value["type"].isNull()) - serializeJson(value["type"], param_type); //current_scope.lookup(param.type_node.value); //need string, lookup also used to find types... + case F_VarRef: + { + JsonObject variable_value; + if (node == F_Assign) + variable_value = value["varref"]; else - strcpy(param_type, "notype"); + variable_value = value; + + const char * variable_name; + variable_name = variable_value["ID"]; + + if (variable_value.containsKey("indices")) + analyze(variable_value, "indices", current_scope, depth + 1); //check if external variable bool externalFound = false; uint8_t index = 0; for (JsonPair externalsPair: definitionJson["EXTERNALS"].as()) { if (strcmp(variable_name, externalsPair.key().c_str()) == 0) { - if (construct == F_Assign) - value["varref"]["external"] = index; //add external index to parseTree - else - value["external"] = index; //add external index to parseTree + variable_value["external"] = index; //add external index to parseTree ANDBG_ARTI("%s Ext Variable found %s (%u) %s\n", spaces+50-depth, variable_name, depth, key); externalFound = true; } index++; } - if (!externalFound) { + if (!externalFound) + { Symbol* var_symbol = current_scope->lookup(variable_name); //lookup here and parent scopes - if (construct == F_VarRef) + if (node == F_VarRef) { if (var_symbol == nullptr) ERROR_ARTI("%s VarRef %s ID not found in scope of %s\n", spaces+50-depth, variable_name, current_scope->scope_name); else - { - value["level"] = var_symbol->scope_level; - value["index"] = var_symbol->scope_index; ANDBG_ARTI("%s VarRef found %s.%s (%u)\n", spaces+50-depth, var_symbol->scope->scope_name, variable_name, depth); - } } else //assign and var/formal { //if variable not already defined, then add - if (construct != F_Assign || var_symbol == nullptr) { //only assign needs to check if not exists - var_symbol = new Symbol(construct, variable_name, 9); // no type support yet - if (construct == F_Assign) + if (node != F_Assign || var_symbol == nullptr) //only assign needs to check if not exists + { + char param_type[charLength]; + if (variable_value.containsKey("type")) + serializeJson(variable_value["type"], param_type); //current_scope.lookup(param.type_node.value); //need string, lookup also used to find types... + else + strcpy(param_type, "notype"); + + var_symbol = new Symbol(node, variable_name, 9); // no type support yet + if (node == F_Assign) global_scope->insert(var_symbol); // assigned variables are global scope else current_scope->insert(var_symbol); + ANDBG_ARTI("%s %s %s.%s of %s\n", spaces+50-depth, key, var_symbol->scope->scope_name, variable_name, param_type); } - else if (construct != F_Assign && var_symbol != nullptr) + else if (node != F_Assign && var_symbol != nullptr) ERROR_ARTI("%s %s Duplicate ID %s.%s\n", spaces+50-depth, key, var_symbol->scope->scope_name, variable_name); - if (construct == F_Assign) - { - value["varref"]["level"] = var_symbol->scope_level; - value["varref"]["index"] = var_symbol->scope_index;; - } + } + + if (var_symbol != nullptr) + { + variable_value["level"] = var_symbol->scope_level; + variable_value["index"] = var_symbol->scope_index;; } } - if (construct == F_Assign) + if (node == F_Assign) { ANDBG_ARTI("%s %s %s = (%u)\n", spaces+50-depth, key, variable_name, depth); - if (!value["assignoperator"].isNull()) + if (value.containsKey("assignoperator")) { JsonObject asop = value["assignoperator"]; JsonObject::iterator asopBegin = asop.begin(); @@ -1350,21 +1538,20 @@ class ARTI { value["assignoperator"] = F_minmin; } - if (!value["expr"].isNull()) + if (value.containsKey("expr")) analyze(value, "expr", current_scope, depth + 1); else if (value["assignoperator"].as() != F_plusplus && value["assignoperator"].as() != F_minmin) + { ERROR_ARTI("%s %s %s: Assign without expression\n", spaces+50-depth, key, variable_name); - - } - - if (!value["indices"].isNull()) { - analyze(value, "indices", current_scope, depth + 1); + errorOccurred = true; + } } visitedAlready = true; break; } - case F_Call: { + case F_Call: + { const char * function_name = value["ID"]; //check if external function @@ -1376,8 +1563,6 @@ class ARTI { { ANDBG_ARTI("%s Ext Function found %s (%u)\n", spaces+50-depth, function_name, depth); value["external"] = index; //add external index to parseTree - if (!value["actuals"].isNull()) - analyze(value["actuals"], nullptr, current_scope, depth + 1); externalFound = true; } index++; @@ -1387,79 +1572,32 @@ class ARTI { { Symbol* function_symbol = current_scope->lookup(function_name); //lookup here and parent scopes if (function_symbol != nullptr) - { - if (!value["actuals"].isNull()) - analyze(value["actuals"], nullptr, current_scope, depth + 1); - analyze(function_symbol->block, nullptr, current_scope, depth + 1); - } //function_symbol != nullptr else - { ERROR_ARTI("%s Function %s not found in scope of %s\n", spaces+50-depth, function_name, current_scope->scope_name); - } } //external functions + if (value.containsKey("actuals")) + analyze(value["actuals"], nullptr, current_scope, depth + 1); + visitedAlready = true; + break; } // case - break; + default: //visitedalready false => recursive call + break; } //switch - - } // is symbol_name - else if (!this->definitionJson["TOKENS"][key].isNull()) // if token - { - const char * valueStr = value; - - if (strcmp(key, "INTEGER_CONST") == 0) - parseTree["operator"] = F_integerConstant; - else if (strcmp(key, "REAL_CONST") == 0) - parseTree["operator"] = F_realConstant; - else if (strcmp(valueStr, "+") == 0) - parseTree["operator"] = F_plus; - else if (strcmp(valueStr, "-") == 0) - parseTree["operator"] = F_minus; - else if (strcmp(valueStr, "*") == 0) - parseTree["operator"] = F_multiplication; - else if (strcmp(valueStr, "/") == 0) - parseTree["operator"] = F_division; - else if (strcmp(valueStr, "%") == 0) - parseTree["operator"] = F_modulo; - else if (strcmp(valueStr, "<<") == 0) - parseTree["operator"] = F_bitShiftLeft; - else if (strcmp(valueStr, ">>") == 0) - parseTree["operator"] = F_bitShiftRight; - else if (strcmp(valueStr, "==") == 0) - parseTree["operator"] = F_equal; - else if (strcmp(valueStr, "!=") == 0) - parseTree["operator"] = F_notEqual; - else if (strcmp(valueStr, ">") == 0) - parseTree["operator"] = F_greaterThen; - else if (strcmp(valueStr, ">=") == 0) - parseTree["operator"] = F_greaterThenOrEqual; - else if (strcmp(valueStr, "<") == 0) - parseTree["operator"] = F_lessThen; - else if (strcmp(valueStr, "<=") == 0) - parseTree["operator"] = F_lessThenOrEqual; - else if (strcmp(valueStr, "&&") == 0) - parseTree["operator"] = F_and; - else if (strcmp(valueStr, "||") == 0) - parseTree["operator"] = F_or; - - visitedAlready = true; - } + } // is node_name if (!visitedAlready && value.size() > 0) // if size == 0 then injected key/value like operator analyze(value, nullptr, current_scope, depth + 1); } // key values } ///for elements in object - } else if (parseTree.is()) { for (JsonVariant newParseTree: parseTree.as()) - { analyze(newParseTree, nullptr, current_scope, depth + 1); - } } else //not array { @@ -1474,6 +1612,8 @@ class ARTI { //https://dev.to/lefebvre/compilers-106---optimizer--ig8 bool optimize(JsonVariant parseTree, uint8_t depth = 0) { + // DEBUG_ARTI("%s optimize %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + if (parseTree.is()) { //make the parsetree as small as possible to let the interpreter run as fast as possible: @@ -1486,31 +1626,47 @@ class ARTI { bool visitedAlready = false; // object: - // if key is symbol and value is object then optimize object after that if object empty then remove key + // if key is node and value is object then optimize object after that if object empty then remove key // else key is !ID and value is string then remove key // else key is * and value is array then optimize array after that if array empty then remove key * // array // if element is multiple then remove - if (!definitionJson[key].isNull() && value.is()) //if key is symbol_name + if (strcmp(key, "*") == 0 ) // multiple + { + optimize(value, depth + 1); + + if (value.size() == 0) + { + parseTree.remove(key); + // DEBUG_ARTI("%s optimize: remove empty %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + } + + visitedAlready = true; + } + else if (this->definitionJson["TOKENS"].containsKey(key)) // if key is token (moved to parse) + { + visitedAlready = true; + } + else if (value.is()) //if key is node_name { optimize(value, depth + 1); if (value.size() == 0) { - // DEBUG_ARTI("%s remove key without value %s (%u)\n", spaces+50-depth, key, depth); + // DEBUG_ARTI("%s optimize: remove key %s with empty object (%u)\n", spaces+50-depth, key, depth); parseTree.remove(key); } - else if (value.size() == 1) //try to shrink + else if (value.size() == 1) //try to shrink, moved to below { - //- check if a symbol is not used in analyzer / interpreter and has only one element: go to the parent and replace itself with its child (shrink) + //- check if a node is not used in analyzer / interpreter and has only one element: go to the parent and replace itself with its child (shrink) - // DEBUG_ARTI("%s symbol try to shrink %s : %s (%u)\n", spaces+50-depth, key, value.as().c_str(), value.size()); + // DEBUG_ARTI("%s node try to shrink %s : %s (%u)\n", spaces+50-depth, key, value.as().c_str(), value.size()); JsonObject::iterator objectIterator = value.as().begin(); - if (!definitionJson[objectIterator->key().c_str()].isNull()) // if value key is a symbol + if (definitionJson.containsKey(objectIterator->key().c_str())) // if value key is a node { // if (objectIterator->value().is() && objectIterator->value().size() == 1) // // { @@ -1518,26 +1674,26 @@ class ARTI { // // if (objectIterator2->value().is() ) //&& objectIterator.size() == 1 // { - // DEBUG_ARTI("%s symbol to shrink %s : %s from %s\n", spaces+50-depth, key, value.as().c_str(), parseTree.as().c_str()); - // DEBUG_ARTI("%s symbol to shrink %s : %s\n", spaces+50-depth, objectIterator->key().c_str(), objectIterator->value().as().c_str()); - // // DEBUG_ARTI("%s symbol to shrink %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), objectIterator2->value().as().c_str()); - // DEBUG_ARTI("%s symbol to shrink replace %s\n", spaces+50-depth, parseTree[key].as().c_str()); - // DEBUG_ARTI("%s symbol to shrink by %s\n", spaces+50-depth, objectIterator->value().as().c_str()); + // DEBUG_ARTI("%s node to shrink %s : %s from %s\n", spaces+50-depth, key, value.as().c_str(), parseTree.as().c_str()); + // DEBUG_ARTI("%s node to shrink %s : %s\n", spaces+50-depth, objectIterator->key().c_str(), objectIterator->value().as().c_str()); + // // DEBUG_ARTI("%s node to shrink %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), objectIterator2->value().as().c_str()); + // DEBUG_ARTI("%s node to shrink replace %s\n", spaces+50-depth, parseTree[key].as().c_str()); + // DEBUG_ARTI("%s node to shrink by %s\n", spaces+50-depth, objectIterator->value().as().c_str()); // // parseTree[key][objectIterator->key().c_str()] = objectIterator2->value(); // parseTree[key] = objectIterator->value(); // } // // else // // { - // // DEBUG_ARTI("%s symbol to shrink2 %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), objectIterator2->value().as().c_str()); + // // DEBUG_ARTI("%s node to shrink2 %s : %s\n", spaces+50-depth, objectIterator2->key().c_str(), objectIterator2->value().as().c_str()); // // } // } // else // DEBUG_ARTI("%s value should be an object %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), value.as().c_str()); - if (stringToConstruct(objectIterator->key().c_str()) == 255) // if key not a construct + if (stringToNode(objectIterator->key().c_str()) == F_NoNode) // if key not a node // if (objectIterator->value().size() == 1) { - DEBUG_ARTI("%s symbol to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, value.as().c_str(), parseTree.as().c_str()); - // DEBUG_ARTI("%s symbol to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), parseTree[key].as().c_str()); + DEBUG_ARTI("%s node to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, value.as().c_str(), parseTree.as().c_str()); + // DEBUG_ARTI("%s node to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), parseTree[key].as().c_str()); parseTree[key] = objectIterator->value(); // parseTree[key]["old"] = objectIterator->key(); @@ -1553,44 +1709,76 @@ class ARTI { visitedAlready = true; } - else if (!this->definitionJson["TOKENS"][key].isNull()) // if key is token + else + ERROR_ARTI("%s Programming Error: key no node and no token %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + + if (!visitedAlready && value.size() > 0) // if size == 0 then injected key/value like operator + optimize(value, depth + 1); + + } ///for elements in object + + //shrink + for (JsonPair parseTreePair : parseTree.as()) + { + const char * key = parseTreePair.key().c_str(); + JsonVariant value = parseTreePair.value(); + + if (false && value.is() && parseTree.size() == 1 && value.size() == 1 && definitionJson.containsKey(key)) //if key is node_name { - const char * valueStr = value; - //if key not one of below tokens then remove - if (strcmp(key, "ID") != 0 && strcmp(key, "INTEGER_CONST") != 0 && strcmp(key, "REAL_CONST") != 0 && - strcmp(key, "INTEGER") != 0 && strcmp(key, "REAL") != 0 && - strcmp(valueStr, "+") != 0 && strcmp(valueStr, "-") != 0 && strcmp(valueStr, "*") != 0 && strcmp(valueStr, "/") != 0 && strcmp(valueStr, "%") != 0 && - strcmp(valueStr, "+=") != 0 && strcmp(valueStr, "-=") != 0 && - strcmp(valueStr, "*=") != 0 && strcmp(valueStr, "/=") != 0 && - strcmp(valueStr, "<<") != 0 && strcmp(valueStr, ">>") != 0 && - strcmp(valueStr, "==") != 0 && strcmp(valueStr, "!=") != 0 && - strcmp(valueStr, "&&") != 0 && strcmp(valueStr, "||") != 0 && - strcmp(valueStr, ">") != 0 && strcmp(valueStr, ">=") != 0 && strcmp(valueStr, "<") != 0 && strcmp(valueStr, "<=") != 0 && - strcmp(valueStr, "++") != 0 && strcmp(valueStr, "--") != 0 - ) + JsonObject::iterator objectIterator = value.as().begin(); + + // DEBUG_ARTI("%s try replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), parseTree.as().c_str()); + + if (strcmp(objectIterator->key().c_str(), "ID") != 0) //&& definitionJson.containsKey(objectIterator->key())??? { - // DEBUG_ARTI("%s remove key/value %s %s (%u)\n", spaces+50-depth, key, valueStr, depth); - parseTree.remove(key); - } + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); + // DEBUG_ARTI("%s found replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), parseTree.as().c_str()); + // DEBUG_ARTI("%s found %s\n", spaces+50-depth, value.as().c_str()); - visitedAlready = true; - } - else if (strcmp(key, "*") == 0 ) // multiple - { - optimize(value, depth + 1); + // DEBUG_ARTI("%s found %s\n", spaces+50-depth, parseTree.as().c_str()); + DEBUG_ARTI("%s replace %s by %s %s\n", spaces+50-depth, key, objectIterator->key().c_str(), parseTree.as().c_str()); - if (value.size() == 0) parseTree.remove(key); + // parseTree[key] = value; + parseTree[objectIterator->key()] = objectIterator->value(); - visitedAlready = true; - } - else - DEBUG_ARTI("%s unknown status for %s %s (%u)\n", spaces+50-depth, key, value.as().c_str(), depth); + // DEBUG_ARTI("%s found %s:%s\n", spaces+50-depth, node_name, parseTree.as().c_str()); - if (!visitedAlready && value.size() > 0) // if size == 0 then injected key/value like operator - optimize(value, depth + 1); + } + // else + // { + // DEBUG_ARTI("%s not shrinkable %s %s\n", spaces+50-depth, key, value.as().c_str()); + // if (depth > 12) { + // // parseTree.remove(key); + // char temp[charLength]; + // strcpy(temp, key); + // strcat(temp, "-"); + // // strcat(temp, objectIterator->key().c_str()); + // // parseTree[temp] = value; + // } + // } + + if (false && definitionJson.containsKey(objectIterator->key().c_str())) // if value key is a node + { + if (stringToNode(objectIterator->key().c_str()) == F_NoNode) // if key not a node + // if (objectIterator->value().size() == 1) + { + DEBUG_ARTI("%s node to shrink %s in %s : %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, value.as().c_str(), parseTree.as().c_str()); + // DEBUG_ARTI("%s node to shrink %s in %s = %s from %s\n", spaces+50-depth, objectIterator->key().c_str(), key, objectIterator->value().as().c_str(), parseTree[key].as().c_str()); + parseTree[key] = objectIterator->value(); - } ///for elements in object + // parseTree[key]["old"] = objectIterator->key(); + + // parseTreePair.key() = objectIterator->key(); + // parseTreePair._key = objectIterator->key(); + // parseTree[objectIterator->key()] = objectIterator->value(); + // parseTree.remove(key); + // parseTree[key][objectIterator2->key().c_str()] = objectIterator2->value(); + } + } + } //value is jsonObject + + } // for } else if (parseTree.is()) { @@ -1603,12 +1791,12 @@ class ARTI { if (element == "multiple") { - // DEBUG_ARTI("%s remove array element 'multiple' of %s array (%u)\n", spaces+50-depth, element.as().c_str(), arrayIndex); + // DEBUG_ARTI("%s optimize: remove array element 'multiple' of array (%u)\n", spaces+50-depth, arrayIndex); parseTreeArray.remove(it); } else if (it->size() == 0) //remove {} elements (added by * arrays, don't know where added) { - // DEBUG_ARTI("%s remove array element {} of %s array (%u)\n", spaces+50-depth, element.as().c_str(), arrayIndex); + // DEBUG_ARTI("%s optimize: remove array element {} of %s array (%u)\n", spaces+50-depth, element.as().c_str(), arrayIndex); parseTreeArray.remove(it); } else @@ -1624,11 +1812,12 @@ class ARTI { ERROR_ARTI("%s Error: parseTree should be array or object %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); } + // DEBUG_ARTI("%s optimized %s (%u)\n", spaces+50-depth, parseTree.as().c_str(), depth); + return !errorOccurred; } //optimize - // ["PLUS2", "factor"], - // [ "MINUS2", "factor"], + // bool visit_ID(JsonVariant parseTree, const char * treeElement = nullptr, ScopedSymbolTable* current_scope = nullptr, uint8_t depth = 0) bool interpret(JsonVariant parseTree, const char * treeElement = nullptr, ScopedSymbolTable* current_scope = nullptr, uint8_t depth = 0) { @@ -1649,16 +1838,46 @@ class ARTI { JsonVariant value = parseTreePair.value(); if (treeElement == nullptr || strcmp(treeElement, key) == 0) { - // RUNLOG_ARTI("%s Interpret object element %s\n", spaces+50-depth, key); //, value.as().c_str() + // RUNLOG_ARTI("%s Interpret object element %s %s\n", spaces+50-depth, key, value.as().c_str()); bool visitedAlready = false; - if (!this->definitionJson[key].isNull()) { //if key is symbol_name - uint8_t construct = stringToConstruct(key); + if (strcmp(key, "*") == 0) + { + // do the recursive call below + } + else if (strcmp(key, "token") == 0 || strcmp(key, "variable") == 0) //variable decls done in analyze (see pas) + visitedAlready = true; + else if (parseTree.containsKey("token")) //key is token + { + // RUNLOG_ARTI("%s Token %s %s %s\n", spaces+50-depth, key, valueStr, parseTree.as().c_str()); + + const char * valueStr = value; + + switch (parseTree["token"].as()) + { + case F_integerConstant: + case F_realConstant: + valueStack->push(atof(valueStr)); //push value + #if ARTI_PLATFORM != ARTI_ARDUINO //for some weird reason this causes a crash on esp32 + RUNLOG_ARTI("%s %s %s (Push %u)\n", spaces+50-depth, key, valueStr, valueStack->stack_index); + #endif + break; + default: + valueStack->push(parseTree["token"].as()); // push Operator index + #if ARTI_PLATFORM != ARTI_ARDUINO //for some weird reason this causes a crash on esp32 + RUNLOG_ARTI("%s %s %s (Push %u)\n", spaces+50-depth, key, valueStr, valueStack->stack_index); + #endif + } + visitedAlready = true; + } + else //if key is node_name + { + uint8_t node = stringToNode(key); - // RUNLOG_ARTI("%s Symbol %s\n", spaces+50-depth, symbol_name); + // RUNLOG_ARTI("%s Node %s\n", spaces+50-depth, key); - switch (construct) + switch (node) { case F_Program: { @@ -1696,10 +1915,10 @@ class ARTI { const char * function_name = value["ID"]; //check if external function - if (!value["external"].isNull()) { + if (value.containsKey("external")) { uint8_t oldIndex = valueStack->stack_index; - if (!value["actuals"].isNull()) + if (value.containsKey("actuals")) interpret(value["actuals"], nullptr, current_scope, depth + 1); float returnValue = floatNull; @@ -1743,7 +1962,7 @@ class ARTI { uint8_t oldIndex = valueStack->stack_index; uint8_t lastIndex = valueStack->stack_index; - if (!value["actuals"].isNull()) + if (value.containsKey("actuals")) interpret(value["actuals"], nullptr, current_scope, depth + 1); for (uint8_t i=0; ifunction_scope->symbolsIndex; i++) //backwards because popped in reversed order @@ -1783,20 +2002,45 @@ class ARTI { case F_VarRef: case F_Assign: //get or set a variable { - const char * variable_name = value["ID"]; - uint8_t variable_level = value["level"]; - uint8_t variable_index = value["index"]; - uint8_t variable_external = value["external"]; + const char * variable_name; + uint8_t variable_level; + uint8_t variable_index; + uint8_t variable_external; + JsonObject variable_indices; + JsonObject variable_value; + + float resultValue = floatNull; + + if (node == F_Assign) + { + variable_value = value["varref"]; + + if (value.containsKey("expr")) //value assignment + { + interpret(value, "expr", current_scope, depth + 1); //value pushed + + resultValue = valueStack->popFloat(); //result of interpret expr (but not for -- and ++ !!!!) + } + } + else + variable_value = value; + + variable_name = variable_value["ID"]; + variable_level = variable_value["level"]; + variable_index = variable_value["index"]; + variable_external = variable_value["external"]; + variable_indices = variable_value["indices"]; uint8_t oldIndex = valueStack->stack_index; //array indices char indices[charLength]; //used in RUNLOG_ARTI only strcpy(indices, ""); - if (!value["indices"].isNull()) { + if (!variable_indices.isNull()) + { strcat(indices, "["); - interpret(value, "indices", current_scope, depth + 1); //values of indices pushed + interpret(variable_value, "indices", current_scope, depth + 1); //values of indices pushed char sep[3] = ""; for (uint8_t i = oldIndex; i< valueStack->stack_index; i++) { @@ -1811,44 +2055,29 @@ class ARTI { strcat(indices, "]"); } - if (construct == F_Assign) - { - variable_name = value["varref"]["ID"]; - variable_level = value["varref"]["level"]; - variable_index = value["varref"]["index"]; - variable_external = value["varref"]["external"]; - - if (!value["expr"].isNull()) //value assignment - interpret(value, "expr", current_scope, depth + 1); //value pushed - } - //check if external variable - if (!value["external"].isNull() || !value["varref"]["external"].isNull()) //added by Analyze + if (variable_value.containsKey("external")) //added by Analyze { - float returnValue = floatNull; - - if (construct == F_VarRef) { //get the value - returnValue = arti_get_external_variable(variable_external, (valueStack->stack_index - oldIndex>0)?valueStack->floatStack[oldIndex]:floatNull, (valueStack->stack_index - oldIndex>1)?valueStack->floatStack[oldIndex+1]:floatNull); + if (node == F_VarRef) { //get the value - valueStack->stack_index = oldIndex; + resultValue = arti_get_external_variable(variable_external, (valueStack->stack_index - oldIndex>0)?valueStack->floatStack[oldIndex]:floatNull, (valueStack->stack_index - oldIndex>1)?valueStack->floatStack[oldIndex+1]:floatNull); + valueStack->stack_index = oldIndex; - if (returnValue != floatNull) + if (resultValue != floatNull) { - valueStack->push(returnValue); - RUNLOG_ARTI("%s %s ext.%s = %f (push %u)\n", spaces+50-depth, key, variable_name, returnValue, valueStack->stack_index); //key is variable_declaration name is ID + valueStack->push(resultValue); + RUNLOG_ARTI("%s %s ext.%s = %f (push %u)\n", spaces+50-depth, key, variable_name, resultValue, valueStack->stack_index); //key is variable_declaration name is ID } else ERROR_ARTI("%s Error: %s ext.%s no value\n", spaces+50-depth, key, variable_name); } else //assign: set the external value... { - returnValue = valueStack->popFloat(); //result of interpret value - - arti_set_external_variable(returnValue, variable_external, (valueStack->stack_index - oldIndex>0)?valueStack->floatStack[oldIndex]:floatNull, (valueStack->stack_index - oldIndex>1)?valueStack->floatStack[oldIndex+1]:floatNull); + arti_set_external_variable(resultValue, variable_external, (valueStack->stack_index - oldIndex>0)?valueStack->floatStack[oldIndex]:floatNull, (valueStack->stack_index - oldIndex>1)?valueStack->floatStack[oldIndex+1]:floatNull); + valueStack->stack_index = oldIndex; - RUNLOG_ARTI("%s %s set ext.%s%s = %f (Pop %u)\n", spaces+50-depth, key, variable_name, indices, returnValue, oldIndex); - valueStack->stack_index = oldIndex; + RUNLOG_ARTI("%s %s set ext.%s%s = %f (Pop %u)\n", spaces+50-depth, key, variable_name, indices, resultValue, oldIndex); } } else //not external, get er set the variable @@ -1865,12 +2094,13 @@ class ARTI { // RUNLOG_ARTI("%s %s %s.%s = %s (push) %s %d-%d = %d (%d)\n", spaces+50-depth, key, ar->name, variable_name, varValue, variable_symbol->name, this->callStack->peek()->nesting_level,variable_symbol->scope_level, index, this->callStack->recordsCounter); //key is variable_declaration name is ID ar = this->callStack->records[index]; } - else { //var created here + else //var created here ar = this->callStack->peek(); - } - if (ar != nullptr) { - if (construct == F_VarRef) { //get the value + if (ar != nullptr) // variable found + { + if (node == F_VarRef) //get the value + { //determine type, for now assume float float varValue = ar->getFloat(variable_index); @@ -1881,27 +2111,27 @@ class ARTI { } else { //assign: set the value - if (!value["assignoperator"].isNull()) + if (value.containsKey("assignoperator")) { switch (value["assignoperator"].as()) { case F_plus: - ar->set(variable_index, ar->getFloat(variable_index) + valueStack->popFloat()); + ar->set(variable_index, ar->getFloat(variable_index) + resultValue); break; case F_minus: - ar->set(variable_index, ar->getFloat(variable_index) - valueStack->popFloat()); + ar->set(variable_index, ar->getFloat(variable_index) - resultValue); break; case F_multiplication: - ar->set(variable_index, ar->getFloat(variable_index) * valueStack->popFloat()); + ar->set(variable_index, ar->getFloat(variable_index) * resultValue); break; - case F_division: { - float divisor = valueStack->popFloat(); - if (divisor == 0) + case F_division: + { + if (resultValue == 0) // divisor { - divisor = 1; + resultValue = 1; ERROR_ARTI("%s /= division by 0 not possible, divisor ignored for %f\n", spaces+50-depth, ar->getFloat(variable_index)); } - ar->set(variable_index, ar->getFloat(variable_index) / divisor); + ar->set(variable_index, ar->getFloat(variable_index) / resultValue); break; } case F_plusplus: @@ -1912,16 +2142,16 @@ class ARTI { break; } - RUNLOG_ARTI("%s %s.%s%s %s= %f (pop %u) %u-%u\n", spaces+50-depth, ar->name, variable_name, indices, operatorToString(value["assignoperator"]), ar->getFloat(variable_index), valueStack->stack_index, variable_level, variable_index); + RUNLOG_ARTI("%s %s.%s%s %s= %f (pop %u) %u-%u\n", spaces+50-depth, ar->name, variable_name, indices, tokenToString(value["assignoperator"]), ar->getFloat(variable_index), valueStack->stack_index, variable_level, variable_index); } else { - ar->set(variable_index, valueStack->popFloat()); + ar->set(variable_index, resultValue); RUNLOG_ARTI("%s %s.%s%s := %f (pop %u) %u-%u\n", spaces+50-depth, ar->name, variable_name, indices, ar->getFloat(variable_index), valueStack->stack_index, variable_level, variable_index); } valueStack->stack_index = oldIndex; } - } + } //ar != nullptr else { //unknown variable ERROR_ARTI("%s %s %s unknown\n", spaces+50-depth, key, variable_name); valueStack->push(floatNull); @@ -1935,6 +2165,7 @@ class ARTI { { uint8_t oldIndex = valueStack->stack_index; + // RUNLOG_ARTI("%s before expr term interpret %s %s\n", spaces+50-depth, key, value.as().c_str()); interpret(value, nullptr, current_scope, depth + 1); //pushes results // RUNLOG_ARTI("%s %s interpret > (%u - %u = %u)\n", spaces+50-depth, key, valueStack->stack_index, oldIndex, valueStack->stack_index - oldIndex); @@ -2012,7 +2243,7 @@ class ARTI { ERROR_ARTI("%s Programming error: unknown operator %u\n", spaces+50-depth, operatorx); } - RUNLOG_ARTI("%s %f %s %f = %f (pop %u, push %u)\n", spaces+50-depth, left, operatorToString(operatorx), right, evaluation, valueStack->stack_index - i, valueStack->stack_index - i + 1); + RUNLOG_ARTI("%s %f %s %f = %f (pop %u, push %u)\n", spaces+50-depth, left, tokenToString(operatorx), right, evaluation, valueStack->stack_index - i, valueStack->stack_index - i + 1); left = evaluation; } @@ -2031,7 +2262,7 @@ class ARTI { RUNLOG_ARTI("%s unary - %f (push %u)\n", spaces+50-depth, valueStack->floatStack[oldIndex + 1], valueStack->stack_index ); } else - RUNLOG_ARTI("%s unary operator not supported %u %s\n", spaces+50-depth, operatorx, operatorToString(operatorx)); + RUNLOG_ARTI("%s unary operator not supported %u %s\n", spaces+50-depth, operatorx, tokenToString(operatorx)); } visitedAlready = true; @@ -2108,7 +2339,10 @@ class ARTI { RUNLOG_ARTI("%s If (stack %u)\n", spaces+50-depth, valueStack->stack_index); RUNLOG_ARTI("%s condition\n", spaces+50-depth); - interpret(value, "expr", current_scope, depth + 1); + if (value.containsKey("expr")) + interpret(value, "expr", current_scope, depth + 1); + // else if (value.containsKey("varref")) + // interpret(value, "varref", current_scope, depth + 1); float conditionResult = valueStack->popFloat(); @@ -2141,41 +2375,15 @@ class ARTI { visitedAlready = true; break; } // case + default: //visitedalready false => recursive call + break; } - - } // is key is symbol_name - - else if (!definitionJson["TOKENS"][key].isNull()) //if key is token - { - // RUNLOG_ARTI("%s Token %s %s %s\n", spaces+50-depth, key, valueStr, parseTree.as().c_str()); - - if (!parseTree["operator"].isNull()) - { - const char * valueStr = value; - - switch (parseTree["operator"].as()) - { - case F_integerConstant: - case F_realConstant: - valueStack->push(atof(valueStr)); //push value - #if ARTI_PLATFORM != ARTI_ARDUINO //for some weird reason this causes a crash on esp32 - RUNLOG_ARTI("%s %s %s (Push %u)\n", spaces+50-depth, key, valueStr, valueStack->stack_index); - #endif - break; - default: - valueStack->push(parseTree["operator"].as()); // push Operator index - #if ARTI_PLATFORM != ARTI_ARDUINO //for some weird reason this causes a crash on esp32 - RUNLOG_ARTI("%s %s %s (Push %u)\n", spaces+50-depth, key, valueStr, valueStack->stack_index); - #endif - } - } - - visitedAlready = true; - } + } // is key is node_name if (!visitedAlready && value.size() > 0) // if size == 0 then injected key/value like operator interpret(value, nullptr, current_scope, depth + 1); } // if treeelement + // RUNLOG_ARTI("%s before end for %u\n", spaces+50-depth, depth); } // for (JsonPair) } else if (parseTree.is()) @@ -2271,14 +2479,14 @@ class ARTI { JsonObject::iterator objectIterator = definitionJson.begin(); JsonObject metaData = objectIterator->value(); const char * version = metaData["version"]; - if (strcmp(version, "0.2.2") < 0) { - ERROR_ARTI("Version of definition.json file (%s) should be 0.2.2 or higher. Press Download wled.json\n", version); + if (strcmp(version, "0.2.3") != 0) { + ERROR_ARTI("Version of definition.json file (%s) should be 0.2.3. Press Download wled.json\n", version); closeLog(); return false; } - const char * startSymbol = metaData["start"]; - if (startSymbol == nullptr) { - ERROR_ARTI("Setup Error: No start symbol found in definition file\n"); + const char * startNode = metaData["start"]; + if (startNode == nullptr) { + ERROR_ARTI("Setup Error: No start node found in definition file\n"); closeLog(); return false; } @@ -2320,9 +2528,9 @@ class ARTI { // strcpy(parseTreeName, "Gen"); strcat(parseTreeName, ".json"); #if ARTI_PLATFORM == ARTI_ARDUINO - parseTreeJsonDoc = new DynamicJsonDocument(32768); // current largest (Subpixel) 5624 //strlen(programText) * 50); //less memory on arduino: 32 vs 64 bit? + parseTreeJsonDoc = new DynamicJsonDocument(32768); //less memory on arduino: 32 vs 64 bit? #else - parseTreeJsonDoc = new DynamicJsonDocument(65536); // current largest (Subpixel) 7926 //strlen(programText) * 100 + parseTreeJsonDoc = new DynamicJsonDocument(65536); #endif MEMORY_ARTI("parseTree %u => %u ✓\n", (unsigned int)parseTreeJsonDoc->capacity(), FREE_SIZE); @@ -2341,7 +2549,8 @@ class ARTI { if (stages < 1) {closeLog(); close(); return true;} - if (!loadParseTreeFile) { + if (!loadParseTreeFile) + { parseTreeJson = parseTreeJsonDoc->as(); lexer = new Lexer(programText, definitionJson); @@ -2349,27 +2558,30 @@ class ARTI { if (stages < 2) {closeLog(); close(); return true;} - uint8_t result = parse(parseTreeJson, startSymbol, '&', lexer->definitionJson[startSymbol], 0); + uint8_t result = parse(parseTreeJson, startNode, '&', lexer->definitionJson[startNode], 0); - if (this->lexer->pos != strlen(this->lexer->text)) { - ERROR_ARTI("Symbol %s Program not entirely parsed (%u,%u) %u of %u\n", startSymbol, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); + if (this->lexer->pos != strlen(this->lexer->text)) + { + ERROR_ARTI("Node %s Program not entirely parsed (%u,%u) %u of %u\n", startNode, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); closeLog(); return false; } else if (result == ResultFail) { - ERROR_ARTI("Symbol %s Program parsing failed (%u,%u) %u of %u\n", startSymbol, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); + ERROR_ARTI("Node %s Program parsing failed (%u,%u) %u of %u\n", startNode, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); closeLog(); return false; } else - DEBUG_ARTI("Symbol %s Parsed until (%u,%u) %u of %u\n", startSymbol, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); + { + DEBUG_ARTI("Node %s Parsed until (%u,%u) %u of %u\n", startNode, this->lexer->lineno, this->lexer->column, this->lexer->pos, (unsigned int)strlen(this->lexer->text)); + MEMORY_ARTI("parse %u ✓\n", FREE_SIZE); + } MEMORY_ARTI("definitionTree %u / %u%% (%u %u %u)\n", (unsigned int)definitionJsonDoc->memoryUsage(), 100 * definitionJsonDoc->memoryUsage() / definitionJsonDoc->capacity(), (unsigned int)definitionJsonDoc->size(), definitionJsonDoc->overflowed(), (unsigned int)definitionJsonDoc->nesting()); MEMORY_ARTI("parseTree %u / %u%% (%u %u %u)\n", (unsigned int)parseTreeJsonDoc->memoryUsage(), 100 * parseTreeJsonDoc->memoryUsage() / parseTreeJsonDoc->capacity(), (unsigned int)parseTreeJsonDoc->size(), parseTreeJsonDoc->overflowed(), (unsigned int)parseTreeJsonDoc->nesting()); + size_t memBefore = parseTreeJsonDoc->memoryUsage(); parseTreeJsonDoc->garbageCollect(); - MEMORY_ARTI("garbageCollect %u / %u%%\n", (unsigned int)parseTreeJsonDoc->memoryUsage(), 100 * parseTreeJsonDoc->memoryUsage() / parseTreeJsonDoc->capacity()); - //199 -> 6905 (34.69) - //469 -> 15923 (33.95) + MEMORY_ARTI("garbageCollect %u / %u%% -> %u / %u%%\n", (unsigned int)memBefore, 100 * memBefore / parseTreeJsonDoc->capacity(), (unsigned int)parseTreeJsonDoc->memoryUsage(), 100 * parseTreeJsonDoc->memoryUsage() / parseTreeJsonDoc->capacity()); delete lexer; lexer = nullptr; } @@ -2389,8 +2601,6 @@ class ARTI { free(programText); #endif - MEMORY_ARTI("parse %u ✓\n", FREE_SIZE); - if (stages >= 3) { DEBUG_ARTI("\nOptimizer\n"); @@ -2400,8 +2610,12 @@ class ARTI { closeLog(); return false; } + else + MEMORY_ARTI("optimize %u ✓\n", FREE_SIZE); - MEMORY_ARTI("optimize %u ✓\n", FREE_SIZE); + size_t memBefore = parseTreeJsonDoc->memoryUsage(); + parseTreeJsonDoc->garbageCollect(); + MEMORY_ARTI("garbageCollect %u / %u%% -> %u / %u%%\n", (unsigned int)memBefore, 100 * memBefore / parseTreeJsonDoc->capacity(), (unsigned int)parseTreeJsonDoc->memoryUsage(), 100 * parseTreeJsonDoc->memoryUsage() / parseTreeJsonDoc->capacity()); if (stages >= 4) { @@ -2409,21 +2623,24 @@ class ARTI { if (!analyze(parseTreeJson)) { ERROR_ARTI("Analyze failed\n"); - closeLog(); - return false; + errorOccurred = true; } - - MEMORY_ARTI("analyze %u ✓\n", FREE_SIZE); + else + MEMORY_ARTI("analyze %u ✓\n", FREE_SIZE); } } + size_t memBefore = parseTreeJsonDoc->memoryUsage(); + parseTreeJsonDoc->garbageCollect(); + MEMORY_ARTI("garbageCollect %u / %u%% -> %u / %u%%\n", (unsigned int)memBefore, 100 * memBefore / parseTreeJsonDoc->capacity(), (unsigned int)parseTreeJsonDoc->memoryUsage(), 100 * parseTreeJsonDoc->memoryUsage() / parseTreeJsonDoc->capacity()); + #ifdef ARTI_DEBUG // only write parseTree file if debug is on if (!loadParseTreeFile) serializeJsonPretty(*parseTreeJsonDoc, parseTreeFile); parseTreeFile.close(); #endif - if (stages < 5) {closeLog(); close(); return true;} + if (stages < 5 || errorOccurred) {closeLog(); close(); return true;} //interpret main callStack = new CallStack(); diff --git a/wled00/src/dependencies/arti/arti_wled.h b/wled00/src/dependencies/arti/arti_wled.h index 2f52158a75..5bf7efa915 100644 --- a/wled00/src/dependencies/arti/arti_wled.h +++ b/wled00/src/dependencies/arti/arti_wled.h @@ -1,8 +1,8 @@ /* @title Arduino Real Time Interpreter (ARTI) @file arti_wled_plugin.h - @version 0.2.2 - @date 20211216 + @version 0.2.3 + @date 20220103 @author Ewoud Wijma @repo https://github.com/ewoudwijma/ARTI */ @@ -531,7 +531,8 @@ bool ARTI::loop() // else // Serial.println("not ledsSet"); - if (!foundRenderFunction) { + if (!foundRenderFunction) + { ERROR_ARTI("%s renderFrame or renderLed not found\n", spaces+50-depth); errorOccurred = true; return false; @@ -556,8 +557,8 @@ ARTI * arti; // ARTI * arti; // } artiWrapper; -uint16_t WS2812FX::mode_customEffect(void) { - +uint16_t WS2812FX::mode_customEffect(void) +{ // //brightpulse // uint8_t lum = constrain(sampleAvg * 256.0 / (256.0 - SEGMENT.speed), 0, 255); // fill(color_blend(SEGCOLOR(1), SEGCOLOR(0), lum)); @@ -624,11 +625,13 @@ uint16_t WS2812FX::mode_customEffect(void) { char currentEffect[charLength]; strcpy(currentEffect, (SEGMENT.name != nullptr)?SEGMENT.name:"default"); //note: switching preset with segment name to preset without does not clear the SEGMENT.name variable, but not gonna solve here ;-) - if (strcmp(previousEffect, currentEffect) != 0) { + if (strcmp(previousEffect, currentEffect) != 0) + { strcpy(previousEffect, currentEffect); // if (artiWrapper != nullptr && artiWrapper->arti != nullptr) { - if (arti != nullptr) { + if (arti != nullptr) + { arti->close(); delete arti; arti = nullptr; } @@ -644,19 +647,21 @@ uint16_t WS2812FX::mode_customEffect(void) { succesful = arti->setup("/wled.json", programFileName); - if (!succesful) { + if (!succesful) ERROR_ARTI("Setup not succesful\n"); - } } - else { - - if (succesful) {// && SEGENV.call < 250 for each frame - if (esp_get_free_heap_size() <= 20000) { + else + { + if (succesful) // && SEGENV.call < 250 for each frame + { + if (esp_get_free_heap_size() <= 20000) + { ERROR_ARTI("Not enough free heap (%u <= 30000)\n", esp_get_free_heap_size()); notEnoughHeap = true; succesful = false; } - else { + else + { // static int previousMillis; // static int previousCall; // if (millis() - previousMillis > 5000) { //tried SEGENV.aux0 but that looks to be overwritten!!! (dangling pointer???) @@ -676,9 +681,8 @@ uint16_t WS2812FX::mode_customEffect(void) { notEnoughHeap = false; strcpy(previousEffect, ""); // force new create } - else { + else return mode_blink(); - } } }