Merge pull request #185 from biveraxe/ver4dev

Сценарии(вложенные ифы, режимы) и мелкие правки
This commit is contained in:
2022-09-04 23:39:26 +03:00
committed by GitHub
4 changed files with 133 additions and 67 deletions

View File

@@ -12,6 +12,15 @@ class ExprAST {
}; };
class IoTScenario { class IoTScenario {
int mode = 0; // режим работы:
// 0 - весь сценарий хранится в FS, читаем посимвольно из файла. Максимальная экономии памяти, но медленно
// 1 - весь сценарий подгружается в String, читаем посимвольно из строки
// 2 - сценарий конвертируется в структуру классов за один проход и выполняется только в памяти. Максимальная
// скорость, но на 8266 при средних сценариях не хватает памяти
String strFromFile;
int charCount;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Lexer (Лексический анализатор) // Lexer (Лексический анализатор)
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -47,16 +56,16 @@ class IoTScenario {
/// identifierexpr /// identifierexpr
/// ::= identifier /// ::= identifier
/// ::= identifier '(' expression* ')' /// ::= identifier '(' expression* ')'
ExprAST *ParseIdentifierExpr(String *IDNames); ExprAST *ParseIdentifierExpr(String *IDNames, bool callFromCondition);
/// numberexpr ::= number /// numberexpr ::= number
ExprAST *ParseNumberExpr(); ExprAST *ParseNumberExpr();
/// parenexpr ::= '(' expression ')' /// parenexpr ::= '(' expression ')'
ExprAST *ParseParenExpr(); ExprAST *ParseParenExpr(String *IDNames, bool callFromCondition);
/// bracketsexpr ::= '{' expression '}' /// bracketsexpr ::= '{' expression '}'
ExprAST *ParseBracketsExpr(); ExprAST *ParseBracketsExpr(String *IDNames, bool callFromCondition);
/// quotesexpr ::= '"' expression '"' /// quotesexpr ::= '"' expression '"'
ExprAST *ParseQuotesExpr(); ExprAST *ParseQuotesExpr();
@@ -68,16 +77,16 @@ class IoTScenario {
/// ::= identifierexpr /// ::= identifierexpr
/// ::= numberexpr /// ::= numberexpr
/// ::= parenexpr /// ::= parenexpr
ExprAST *ParsePrimary(String *IDNames); ExprAST *ParsePrimary(String *IDNames, bool callFromCondition);
/// binoprhs /// binoprhs
/// ::= ('+' primary)* /// ::= ('+' primary)*
ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames); ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition);
/// expression /// expression
/// ::= primary binoprhs /// ::= primary binoprhs
/// ///
ExprAST *ParseExpression(String *IDNames); ExprAST *ParseExpression(String *IDNames, bool callFromCondition);
int getLastChar(); int getLastChar();
fs::File file; fs::File file;

View File

@@ -98,6 +98,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
strFromFile.replace("{\"scen\":\"", ""); strFromFile.replace("{\"scen\":\"", "");
strFromFile.replace("\\n", "\n"); strFromFile.replace("\\n", "\n");
strFromFile.replace("\\\"", "\""); strFromFile.replace("\\\"", "\"");
strFromFile.replace(";", " ");
strFromFile.replace("\\t", " ");
strFromFile.remove(strFromFile.length() - 2, 2); strFromFile.remove(strFromFile.length() - 2, 2);
} }
myfile.close(); myfile.close();

View File

@@ -586,11 +586,22 @@ class BracketsExprAST : public ExprAST {
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
int IoTScenario::getLastChar() { int IoTScenario::getLastChar() {
if (mode == 0) {
if (file) { if (file) {
LastChar = file.read(); LastChar = file.read();
if (LastChar == 10) curLine++; if (LastChar == 10) curLine++;
return LastChar; return LastChar;
} else return EOF; } else return EOF;
} else if (mode == 1) {
if (charCount < strFromFile.length()) {
LastChar = strFromFile.charAt(charCount);
//Serial.printf("%d, ", LastChar);
if (LastChar == 10) curLine++;
charCount++;
return LastChar;
} else return EOF;
}
} }
/// gettok - Возвращает следующий токен из стандартного потока ввода. /// gettok - Возвращает следующий токен из стандартного потока ввода.
@@ -728,7 +739,7 @@ ExprAST *IoTScenario::Error(const char *Str) {
/// identifierexpr /// identifierexpr
/// ::= identifier /// ::= identifier
/// ::= identifier '(' expression* ')' /// ::= identifier '(' expression* ')'
ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames) { ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames, bool callFromCondition) {
String IdName = IdentifierStr; String IdName = IdentifierStr;
String Cmd = ""; String Cmd = "";
IoTItem *tmpItem = findIoTItem(IdName); IoTItem *tmpItem = findIoTItem(IdName);
@@ -754,7 +765,7 @@ ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames) {
std::vector<ExprAST *> Args; std::vector<ExprAST *> Args;
if (CurTok != ')') { if (CurTok != ')') {
while (1) { while (1) {
ExprAST *Arg = ParseExpression(IDNames); ExprAST *Arg = ParseExpression(IDNames, callFromCondition);
if (!Arg) return nullptr; if (!Arg) return nullptr;
Args.push_back(Arg); Args.push_back(Arg);
@@ -783,9 +794,9 @@ ExprAST *IoTScenario::ParseNumberExpr() {
} }
/// parenexpr ::= '(' expression ')' /// parenexpr ::= '(' expression ')'
ExprAST *IoTScenario::ParseParenExpr() { ExprAST *IoTScenario::ParseParenExpr(String *IDNames, bool callFromCondition) {
getNextToken(); // получаем (. getNextToken(); // получаем (.
ExprAST *V = ParseExpression(nullptr); ExprAST *V = ParseExpression(IDNames, callFromCondition);
if (!V) return nullptr; if (!V) return nullptr;
if (CurTok != ')') if (CurTok != ')')
@@ -795,28 +806,31 @@ ExprAST *IoTScenario::ParseParenExpr() {
} }
/// bracketsexpr ::= '{' expression '}' /// bracketsexpr ::= '{' expression '}'
ExprAST *IoTScenario::ParseBracketsExpr() { ExprAST *IoTScenario::ParseBracketsExpr(String *IDNames, bool callFromCondition) {
getNextToken(); // получаем {. getNextToken(); // получаем {.
std::vector<ExprAST *> bracketsList; std::vector<ExprAST *> bracketsList;
if (CurTok != '}') {
while (1) { while (CurTok != '}') {
ExprAST *Expr = ParseExpression(nullptr); ExprAST *Expr = ParseExpression(IDNames, callFromCondition);
if (!Expr) return nullptr; if (!Expr) return nullptr;
bracketsList.push_back(Expr); bracketsList.push_back(Expr);
if (CurTok != ';') //if (CurTok == '}') break;
return Error("Expected ';' in operation list");
int ttok = getNextToken();
if (!ttok) {
Error("Expected '}'");
break;
}
if (CurTok == '}') break; //Serial.printf("ParseBracketsExpr CurTok = %d \n", CurTok);
//if (CurTok != ';')
// return Error("Expected ';' in operation list");
//int ttok = getNextToken();
if (CurTok == tok_eof) {
return Error("Expected '}'");
} }
} }
//getNextToken(); // получаем }. getNextToken(); // получаем }.
return new BracketsExprAST(bracketsList); return new BracketsExprAST(bracketsList);
} }
@@ -833,14 +847,14 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
getNextToken(); // Получаем if. getNextToken(); // Получаем if.
// условие. // условие.
ExprAST *Cond = ParseExpression(IDNames); ExprAST *Cond = ParseExpression(IDNames, true);
if (!Cond) return nullptr; if (!Cond) return nullptr;
if (CurTok != tok_then) if (CurTok != tok_then)
return Error("expected then"); return Error("expected then");
getNextToken(); // Получаем then getNextToken(); // Получаем then
ExprAST *Then = ParseExpression(nullptr); ExprAST *Then = ParseExpression(IDNames, false);
if (!Then) return nullptr; if (!Then) return nullptr;
// if (CurTok != tok_else) // if (CurTok != tok_else)
@@ -848,7 +862,8 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
ExprAST *Else = nullptr; ExprAST *Else = nullptr;
if (CurTok == tok_else) { if (CurTok == tok_else) {
getNextToken(); getNextToken();
Else = ParseExpression(nullptr); Else = ParseExpression(IDNames, false);
if (!Else) return nullptr;
} }
return new IfExprAST(Cond, Then, Else, IDNames); return new IfExprAST(Cond, Then, Else, IDNames);
@@ -858,24 +873,24 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
/// ::= identifierexpr /// ::= identifierexpr
/// ::= numberexpr /// ::= numberexpr
/// ::= parenexpr /// ::= parenexpr
ExprAST *IoTScenario::ParsePrimary(String *IDNames) { ExprAST *IoTScenario::ParsePrimary(String *IDNames, bool callFromCondition) {
switch (CurTok) { switch (CurTok) {
default: default:
Serial.println(CurTok); Serial.println(CurTok);
return Error("unknown token when expecting an expression"); return Error("unknown token when expecting an expression");
case tok_identifier: { case tok_identifier: {
if (IDNames) { if (callFromCondition && IDNames) {
String tmpstr = *IDNames; String tmpstr = *IDNames;
*IDNames = tmpstr + " " + IdentifierStr + " "; *IDNames = tmpstr + " " + IdentifierStr + " ";
} }
return ParseIdentifierExpr(IDNames); return ParseIdentifierExpr(IDNames, callFromCondition);
} }
case tok_number: case tok_number:
return ParseNumberExpr(); return ParseNumberExpr();
case '(': case '(':
return ParseParenExpr(); return ParseParenExpr(IDNames, callFromCondition);
case '{': case '{':
return ParseBracketsExpr(); return ParseBracketsExpr(IDNames, callFromCondition);
case tok_string: case tok_string:
return ParseQuotesExpr(); return ParseQuotesExpr();
case tok_if: case tok_if:
@@ -885,7 +900,7 @@ ExprAST *IoTScenario::ParsePrimary(String *IDNames) {
/// binoprhs /// binoprhs
/// ::= ('+' primary)* /// ::= ('+' primary)*
ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames) { ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition) {
// Если это бинарный оператор, получаем его приоритет // Если это бинарный оператор, получаем его приоритет
while (1) { while (1) {
int TokPrec = GetTokPrecedence(); int TokPrec = GetTokPrecedence();
@@ -900,14 +915,14 @@ ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames)
getNextToken(); // eat binop getNextToken(); // eat binop
// Разобрать первичное выражение после бинарного оператора // Разобрать первичное выражение после бинарного оператора
ExprAST *RHS = ParsePrimary(IDNames); ExprAST *RHS = ParsePrimary(IDNames, callFromCondition);
if (!RHS) return nullptr; if (!RHS) return nullptr;
// Если BinOp связан с RHS меньшим приоритетом, чем оператор после RHS, // Если BinOp связан с RHS меньшим приоритетом, чем оператор после RHS,
// то берём часть вместе с RHS как LHS. // то берём часть вместе с RHS как LHS.
int NextPrec = GetTokPrecedence(); int NextPrec = GetTokPrecedence();
if (TokPrec < NextPrec) { if (TokPrec < NextPrec) {
RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames); RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames, callFromCondition);
if (RHS == nullptr) return nullptr; if (RHS == nullptr) return nullptr;
} }
@@ -919,29 +934,45 @@ ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames)
/// expression /// expression
/// ::= primary binoprhs /// ::= primary binoprhs
/// ///
ExprAST *IoTScenario::ParseExpression(String *IDNames) { ExprAST *IoTScenario::ParseExpression(String *IDNames, bool callFromCondition) {
ExprAST *LHS = ParsePrimary(IDNames); ExprAST *LHS = ParsePrimary(IDNames, callFromCondition);
if (!LHS) return nullptr; if (!LHS) return nullptr;
return ParseBinOpRHS(0, LHS, IDNames); return ParseBinOpRHS(0, LHS, IDNames, callFromCondition);
} }
void IoTScenario::loadScenario(String fileName) { // подготавливаем контекст для чтения и интерпретации файла void IoTScenario::loadScenario(String fileName) { // подготавливаем контекст для чтения и интерпретации файла
if (mode == 0) {
if (file) file.close(); if (file) file.close();
file = FileFS.open(fileName, "r"); file = FileFS.open(fileName.c_str(), "r");
}
void IoTScenario::exec(String eventIdName) { // посимвольно считываем и сразу интерпретируем сценарий в дерево AST
if (!file) { if (!file) {
Error("Open file scenario error"); Error("Open file scenario error");
return; return;
} }
} else if (mode == 1) {
file = FileFS.open(fileName.c_str(), "r");
if (!file) {
Error("Open file scenario error");
return;
}
strFromFile = file.readString();
Serial.printf("strFromFile: %s, %s\n", strFromFile.c_str(), fileName.c_str());
file.close();
}
}
void IoTScenario::exec(String eventIdName) { // посимвольно считываем и сразу интерпретируем сценарий в дерево AST
if (mode == 0 && !file) return;
LastChar = 0; LastChar = 0;
CurTok = 0; CurTok = 0;
file.seek(0);
curLine = 1; curLine = 1;
charCount = 0;
while ((getNextToken()) != EOF) { if (mode == 0) file.seek(0);
if (mode < 2) {
while (CurTok != EOF) {
switch (CurTok) { switch (CurTok) {
case tok_if: { case tok_if: {
IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии
@@ -956,6 +987,8 @@ void IoTScenario::exec(String eventIdName) { // посимвольно счит
} }
delete tmpAST; delete tmpAST;
break;} break;}
default: getNextToken(); break;
}
} }
} }
} }

View File

@@ -27,11 +27,33 @@
"moduleDesc": "Добавляет инструмент таймеров обратного отсчета для организации периодичных операций или логических конструкций. Часто используется как вспомогательный элемент для автоматизации.", "moduleDesc": "Добавляет инструмент таймеров обратного отсчета для организации периодичных операций или логических конструкций. Часто используется как вспомогательный элемент для автоматизации.",
"propInfo": { "propInfo": {
"int": "Задает размер в секундах одного шага(тика) таймера.", "int": "Задает размер в секундах одного шага(тика) таймера.",
"countDown": "Начальное значение таймера, с которого начинается обратный отсчет.", "countDown": "Начальное значение таймера, с которого начинается обратный отсчет. countDown=0 - бесконечный счет (имеет смысл при ticker=1, иначе таймер будет выключен), countDown=-1 - отключает таймер совсем (используется для запуска системы с выключенным таймером)",
"ticker": "Генерировать(1) или нет(0) события при каждом тике таймера.", "ticker": "Генерировать(1) или нет(0) события при каждом тике таймера.",
"repeat": "Сбрасывать(1) или нет(0) таймер в начальное состояние при достижении нуля.", "repeat": "Сбрасывать(1) или нет(0) таймер в начальное состояние при достижении нуля.",
"needSave": "Требуется сохранять(1) или нет(0) состояние в энерго независимую память. Функция находится в разработке." "needSave": "Требуется сохранять(1) или нет(0) состояние в энерго независимую память. Функция находится в разработке."
},
"funcInfo": [
{
"name": "stop",
"descr": "Поставить процесс на паузу, при этом не будет событий и не будет уменьшаться счетчик.",
"params": []
},
{
"name": "reset",
"descr": "Вернуть таймер к началу, установленному в countDown.",
"params": []
},
{
"name": "continue",
"descr": "Продолжить выполнение с момента остановки.",
"params": []
},
{
"name": "int",
"descr": "Можно изменить шаг тиков.",
"params": []
} }
]
}, },
"defActive": true, "defActive": true,
"devices": { "devices": {