Files
IoTManager/src/classes/IoTScenario.cpp
biver 90e8d4eaf4 Делаем доступным исходное значение числа
в виде строки для лучшей визуализаци
2022-09-05 16:26:27 +03:00

1020 lines
36 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "Global.h"
#include "classes/IoTItem.h"
#include "classes/IoTScenario.h"
#include "utils/FileUtils.h"
#include "NTP.h"
bool isIotScenException; // признак исключения и попытки прекратить выполнение сценария заранее
// Лексический анализатор возвращает токены [0-255], если это неизвестны,
// иначе одну из известных единиц кода
enum Token {
tok_eof = -1,
// операнды (первичные выражения: идентификаторы, числа)
tok_identifier = -4,
tok_number = -5,
tok_string = -3,
// двухсимвольные операторы бинарных операций
tok_equal = -2,
tok_notequal = -9,
tok_lesseq = -10,
tok_greateq = -11,
// управление
tok_if = -6,
tok_then = -7,
tok_else = -8
};
//===----------------------------------------------------------------------===//
// Abstract Syntax Tree (Абстрактное Синтаксическое Дерево или Дерево Парсинга)
//===----------------------------------------------------------------------===//
/// ExprAST - Базовый класс для всех узлов выражений.
ExprAST::~ExprAST() {}
IoTValue *ExprAST::exec() { return nullptr; }
int ExprAST::setValue(IoTValue *val) { return 0; } // 0 - установка значения не поддерживается наследником
bool ExprAST::hasEventIdName(String eventIdName) { return false; } // по умолчанию все узлы не связаны с ИД события, для которого выполняется сценарий
// struct IoTValue zeroIotVal;
/// NumberExprAST - Класс узла выражения для числовых литералов (Например, "1.0").
class NumberExprAST : public ExprAST {
IoTValue Val;
public:
NumberExprAST(String val) {
Val.valD = strtod(val.c_str(), 0);
Val.valS = val;
}
IoTValue *exec() {
if (isIotScenException) return nullptr;
// Serial.printf("Call from NumberExprAST: %f\n", Val.valD);
return &Val;
}
};
/// StringExprAST - Класс узла выражения для строковых литералов (Например, "Example with spaces and quotes").
class StringExprAST : public ExprAST {
IoTValue Val;
public:
StringExprAST(String val) {
Val.isDecimal = false;
Val.valS = val;
}
IoTValue *exec() {
if (isIotScenException) return nullptr;
// Serial.printf("Call from StringExprAST: %s\n", Val.valS.c_str());
return &Val;
}
};
/// VariableExprAST - Класс узла выражения для переменных (например, "a").
class VariableExprAST : public ExprAST {
String Name;
IoTItem *Item; // ссылка на объект модуля (прямой доступ к идентификатору указанному в сценарии), если получилось найти модуль по ID
bool ItemIsLocal = false;
public:
VariableExprAST(const String &name, IoTItem *item) : Name(name), Item(item) {
if (item) ItemIsLocal = item->iAmLocal;
}
int setValue(IoTValue *val) {
if (!ItemIsLocal) Item = findIoTItem(Name);
if (Item)
Item->setValue(*val);
else
return 0;
return 1;
}
IoTValue *exec() {
if (isIotScenException) return nullptr;
if (!ItemIsLocal) Item = findIoTItem(Name);
if (Item) {
// if (Item->value.isDecimal)
// Serial.printf("Call from VariableExprAST: %s = %f\n", Name.c_str(), Item->value.valD);
// else Serial.printf("Call from VariableExprAST: %s = %s\n", Name.c_str(), Item->value.valS.c_str());
return &(Item->value);
}
return nullptr; // Item не найден.
}
};
/// BinaryExprAST - Класс узла выражения для бинарных операторов.
class BinaryExprAST : public ExprAST {
signed char Op;
ExprAST *LHS, *RHS;
IoTValue val;
String lhsStr, rhsStr;
public:
BinaryExprAST(signed char op, ExprAST *lhs, ExprAST *rhs)
: Op(op), LHS(lhs), RHS(rhs) {}
~BinaryExprAST() {
if (LHS) delete LHS;
if (RHS) delete RHS;
// Serial.printf("Call from BinaryExprAST delete\n");
}
IoTValue *exec() {
if (isIotScenException) return nullptr;
// String printStr = "";
// if (Op == tok_equal)
// printStr = "==";
// else if (Op == tok_notequal)
// printStr = "!=";
// else if (Op == tok_lesseq)
// printStr = "<=";
// else if (Op == tok_greateq)
// printStr = ">=";
// else
// printStr = printStr + (char)Op;
// Serial.printf("Call from BinaryExprAST: %s\n", printStr.c_str());
if (RHS == nullptr || LHS == nullptr) return nullptr;
IoTValue *rhs = RHS->exec(); // получаем значение правого операнда для возможного использования в операции присваивания
if (Op == '=' && LHS->setValue(rhs)) { // если установка значения не поддерживается, т.е. слева не переменная, то работаем по другим комбинациям далее
return rhs; // иначе возвращаем присвоенное значение справа
}
IoTValue *lhs = LHS->exec(); // если присваивания не произошло, значит операция иная и необходимо значение левого операнда
if (lhs != nullptr && rhs != nullptr) {
if (lhs->isDecimal && rhs->isDecimal) {
switch (Op) {
case '>':
val.valD = lhs->valD > rhs->valD;
break;
case '<':
val.valD = lhs->valD < rhs->valD;
break;
case tok_lesseq:
val.valD = lhs->valD <= rhs->valD;
break;
case tok_greateq:
val.valD = lhs->valD >= rhs->valD;
break;
case tok_equal:
val.valD = lhs->valD == rhs->valD;
break;
case tok_notequal:
val.valD = lhs->valD != rhs->valD;
break;
case '+':
val.valD = lhs->valD + rhs->valD;
break;
case '-':
val.valD = lhs->valD - rhs->valD;
break;
case '*':
val.valD = lhs->valD * rhs->valD;
break;
case '/':
if (rhs->valD != 0)
val.valD = lhs->valD / rhs->valD;
else
val.valD = 3.4E+38;
break;
case '|':
val.valD = lhs->valD || rhs->valD;
break;
case '&':
val.valD = lhs->valD && rhs->valD;
break;
default:
break;
}
return &val;
}
if (!lhs->isDecimal || !rhs->isDecimal) {
if (lhs->isDecimal)
lhsStr = lhs->valD;
else
lhsStr = lhs->valS;
if (rhs->isDecimal)
rhsStr = rhs->valD;
else
rhsStr = rhs->valS;
switch (Op) {
case tok_equal:
val.valD = compStr(lhsStr, rhsStr);
break;
case '+':
val.valS = lhsStr + rhsStr;
val.valD = 1;
val.isDecimal = false;
break;
default:
break;
}
return &val;
}
}
return &val;
}
bool compStr(String str1, String str2) {
if (str1.length() != str2.length()) return false;
for (int i = 0; i < str1.length(); i++) {
if (str1[i] == '*' || str2[i] == '*') continue; //считаем, что если есть подстановочная звезда, то символы равны
if (str1[i] != str2[i]) return false;
}
return true; // str1 == str2;
}
};
/// CallExprAST - Класс узла выражения для вызова команды.
class CallExprAST : public ExprAST {
String Callee;
String Cmd;
std::vector<ExprAST *> Args;
IoTItem *Item; // ссылка на объект модуля (прямой доступ к идентификатору указанному в сценарии), если получилось найти модуль по ID
IoTValue ret; // хранение возвращаемого значения, т.к. возврат по ссылке осуществляется
bool ItemIsLocal = false;
public:
CallExprAST(const String &callee, String &cmd, std::vector<ExprAST *> &args, IoTItem *item)
: Callee(callee), Cmd(cmd), Args(args), Item(item) {
if (item) ItemIsLocal = item->iAmLocal;
}
IoTValue *exec() {
if (isIotScenException) return nullptr; // если прерывание, то сразу выходим
if (Cmd == "exit" || Callee == "exit") { // если системная команда, то выполняем и выходим
IoTValue *tmp;
if (Args.size() > 0 && Args[0])
tmp = Args[0]->exec();
else
SerialPrint("i", "SysExt", "Exit");
if (tmp) SerialPrint("i", "SysExt", "Exit = '" + tmp->valS + "'");
isIotScenException = true;
return nullptr;
}
if (!ItemIsLocal) Item = findIoTItem(Callee); // пробуем найти переменную если она не локальная (могла придти по сети в процессе)
if (!Item) return nullptr; // ret = zeroIotVal; // если все же не пришла, то либо опечатка, либо уже стерлась - выходим
// если все же все ок, то готовим параметры для передачи в модуль
std::vector<IoTValue> ArgsAsIoTValue;
for (unsigned int i = 0; i < Args.size(); i++) {
if (Args[i] == nullptr) return nullptr;
IoTValue *tmp = Args[i]->exec();
if (tmp != nullptr)
ArgsAsIoTValue.push_back(*tmp);
else
return nullptr; // ArgsAsIoTValue.push_back(zeroIotVal);
}
ret = Item->execute(Cmd, ArgsAsIoTValue); // вызываем команду из модуля напрямую с передачей всех аргументов
// if (ret.isDecimal) Serial.printf("Call from CallExprAST ID = %s, Command = %s, exec result = %f\n", Callee.c_str(), Cmd.c_str(), ret.valD);
// else Serial.printf("Call from CallExprAST ID = %s, Command = %s, exec result = %s\n", Callee.c_str(), Cmd.c_str(), ret.valS.c_str());
return &ret;
}
~CallExprAST() {
for (unsigned int i = 0; i < Args.size(); i++) {
if (Args[i]) delete Args[i];
}
Args.clear();
// Serial.printf("Call from CallExprAST delete\n");
}
};
// Для сокращения количества преобразований используем числовые коды для фиксации названия системной функции,
// которые поддерживает прошивка
enum SysOp {
sysop_notfound = 0,
sysop_reboot = 1,
sysop_digitalRead,
sysop_analogRead, //
sysop_digitalWrite, //
sysop_digitalInvert, //
sysop_deepSleep, //
sysop_getHours, //
sysop_getMinutes, //
sysop_getSeconds, //
sysop_getMonth, //
sysop_getDay,
sysop_gethhmm,
sysop_gethhmmss,
sysop_getTime,
sysop_getIP,
sysop_mqttPub
};
IoTValue sysExecute(SysOp command, std::vector<IoTValue> &param) {
IoTValue value = {};
if (_time_isTrust)
switch (command) {
case sysop_getHours:
value.valD = _time_local.hour;
break;
case sysop_getMinutes:
value.valD = _time_local.minute;
break;
case sysop_getSeconds:
value.valD = _time_local.second;
break;
case sysop_getMonth:
value.valD = _time_local.month;
break;
case sysop_getDay:
value.valD = _time_local.day_of_month;
break;
case sysop_gethhmm:
value.isDecimal = false;
value.valS = getTimeLocal_hhmm();
break;
case sysop_gethhmmss:
value.isDecimal = false;
value.valS = getTimeLocal_hhmmss();
break;
case sysop_getTime:
value.isDecimal = false;
value.valS = getDateTimeDotFormated();
break;
}
else {
value.isDecimal = false;
value.valS = "none";
}
switch (command) {
case sysop_reboot:
ESP.restart();
break;
case sysop_digitalRead:
if (param.size()) {
IoTgpio.pinMode(param[0].valD, INPUT);
value.valD = IoTgpio.digitalRead(param[0].valD);
}
break;
case sysop_analogRead:
if (param.size()) {
IoTgpio.pinMode(param[0].valD, INPUT);
value.valD = IoTgpio.analogRead(param[0].valD);
}
break;
case sysop_digitalWrite:
if (param.size() == 2) {
IoTgpio.pinMode(param[0].valD, OUTPUT);
IoTgpio.digitalWrite(param[0].valD, param[1].valD);
}
break;
case sysop_digitalInvert:
if (param.size()) {
IoTgpio.pinMode(param[0].valD, OUTPUT);
IoTgpio.digitalInvert(param[0].valD);
}
break;
case sysop_deepSleep:
if (param.size()) {
Serial.printf("Ушел спать на %d сек...", (int)param[0].valD);
#ifdef ESP32
esp_sleep_enable_timer_wakeup(param[0].valD * 1000000);
delay(1000);
esp_deep_sleep_start();
#else
ESP.deepSleep(param[0].valD * 1000000);
#endif
}
break;
case sysop_getIP:
value.valS = jsonReadStr(settingsFlashJson, F("ip"));
value.isDecimal = false;
break;
case sysop_mqttPub:
if (param.size() == 2) {
// Serial.printf("Call from sysExecute %s %s\n", param[0].valS.c_str(), param[1].valS.c_str());
value.valD = mqtt.publish(param[0].valS.c_str(), param[1].valS.c_str(), false);
}
break;
}
return value;
}
/// SysCallExprAST - Класс узла выражения для вызова системных команд.
class SysCallExprAST : public ExprAST {
String Callee;
std::vector<ExprAST *> Args;
IoTValue ret; // хранение возвращаемого значения, т.к. возврат по ссылке осуществляется
// bool ItemIsLocal = false;
SysOp operation;
public:
SysCallExprAST(const String &callee, std::vector<ExprAST *> &args)
: Callee(callee), Args(args) {
if (Callee == "reboot")
operation = sysop_reboot;
else if (Callee == "digitalRead")
operation = sysop_digitalRead;
else if (Callee == "analogRead")
operation = sysop_analogRead;
else if (Callee == "digitalWrite")
operation = sysop_digitalWrite;
else if (Callee == "digitalInvert")
operation = sysop_digitalInvert;
else if (Callee == "deepSleep")
operation = sysop_deepSleep;
else if (Callee == "getTime")
operation = sysop_getTime;
else if (Callee == "getHours")
operation = sysop_getHours;
else if (Callee == "getMinutes")
operation = sysop_getMinutes;
else if (Callee == "getSeconds")
operation = sysop_getSeconds;
else if (Callee == "getMonth")
operation = sysop_getMonth;
else if (Callee == "getDay")
operation = sysop_getDay;
else if (Callee == "getIP")
operation = sysop_getIP;
else if (Callee == "mqttPub")
operation = sysop_mqttPub;
else if (Callee == "gethhmm")
operation = sysop_gethhmm;
else if (Callee == "gethhmmss")
operation = sysop_gethhmmss;
else if (Callee == "getTime")
operation = sysop_getTime;
else
operation = sysop_notfound;
}
IoTValue *exec() {
//Serial.printf("Call from SysCallExprAST exec %d\n", operation);
if (isIotScenException) return nullptr; // если прерывание, то сразу выходим
// готовим параметры для передачи в в функцию интерпретатор действий
std::vector<IoTValue> ArgsAsIoTValue;
for (unsigned int i = 0; i < Args.size(); i++) {
if (Args[i] == nullptr) return nullptr;
IoTValue *tmp = Args[i]->exec();
if (tmp != nullptr)
ArgsAsIoTValue.push_back(*tmp);
else
return nullptr;
}
ret = sysExecute(operation, ArgsAsIoTValue); // вызываем функцию интерпретатор с передачей всех аргументов
return &ret;
}
~SysCallExprAST() {
for (unsigned int i = 0; i < Args.size(); i++) {
if (Args[i]) delete Args[i];
}
Args.clear();
// Serial.printf("Call from CallExprAST delete\n");
}
};
/// IfExprAST - Класс узла выражения для if/then/else.
class IfExprAST : public ExprAST {
ExprAST *Cond, *Then, *Else;
String _IDNames;
public:
IfExprAST(ExprAST *cond, ExprAST *then, ExprAST *_else, String *IDNames)
: Cond(cond), Then(then), Else(_else) {
if (IDNames) {
_IDNames = *IDNames;
} else
_IDNames = "";
}
bool hasEventIdName(String eventIdName) {
// Serial.printf("Call from BinaryExprAST _IDNames:%s\n", _IDNames.c_str());
return _IDNames.indexOf(" " + eventIdName + " ") >= 0; // определяем встречался ли ИД, для которого исполняем сценарий в выражении IF
}
IoTValue *exec() {
if (isIotScenException) return nullptr;
IoTValue *res_ret = nullptr;
IoTValue *cond_ret = nullptr;
if (Cond) cond_ret = Cond->exec();
if (!cond_ret) {
// Serial.printf("Call from IfExprAST: Skip If\n");
return nullptr; //&zeroIotVal;
}
if (cond_ret->isDecimal && cond_ret->valD) {
if (Then == nullptr) return nullptr;
res_ret = Then->exec();
} else {
if (Else == nullptr) return nullptr;
res_ret = Else->exec();
}
// if (!res_ret) Serial.printf("Call from IfExprAST: Cond result = %f, no body result\n", cond_ret->valD);
// else if (res_ret->isDecimal) Serial.printf("Call from IfExprAST: Cond result = %f, result = %f\n", cond_ret->valD, res_ret->valD);
// else Serial.printf("Call from IfExprAST: Cond result = %f, result = %s\n", cond_ret->valD, res_ret->valS.c_str());
Serial.printf("\n");
return cond_ret;
}
~IfExprAST() {
if (Cond) delete Cond;
if (Then) delete Then;
if (Else) delete Else;
// Serial.printf("Call from IfExprAST delete\n");
}
};
/// BracketsExprAST - Класс узла блока кода {}.
class BracketsExprAST : public ExprAST {
std::vector<ExprAST *> BracketsList;
public:
BracketsExprAST(std::vector<ExprAST *> &bracketsList)
: BracketsList(bracketsList) {}
IoTValue *exec() {
if (isIotScenException) return nullptr;
// Serial.printf("Call from BracketsExprAST OperCount = %d \n", BracketsList.size());
IoTValue *lastExecValue = nullptr;
for (unsigned int i = 0; i < BracketsList.size(); i++) {
if (BracketsList[i] == nullptr) return nullptr;
lastExecValue = BracketsList[i]->exec();
}
return lastExecValue;
}
~BracketsExprAST() {
for (unsigned int i = 0; i < BracketsList.size(); i++) {
if (BracketsList[i]) delete BracketsList[i];
}
BracketsList.clear();
// Serial.printf("Call from BracketsExprAST delete\n");
}
};
//===----------------------------------------------------------------------===//
// Lexer (Лексический анализатор)
//===----------------------------------------------------------------------===//
int IoTScenario::getLastChar() {
if (mode == 0) {
if (file) {
LastChar = file.read();
if (LastChar == 10) curLine++;
return LastChar;
} 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 - Возвращает следующий токен из стандартного потока ввода.
int IoTScenario::gettok() {
// Пропускаем пробелы.
while (isspace(LastChar))
LastChar = getLastChar();
if (isalpha(LastChar) || LastChar == '_') { // идентификатор: [a-zA-Z][a-zA-Z0-9]*
IdentifierStr = (char)LastChar;
while (isalnum((LastChar = getLastChar())) || LastChar == '_') {
IdentifierStr += (char)LastChar;
}
// Serial.printf("%s ", IdentifierStr.c_str());
if (IdentifierStr == "if") return tok_if;
if (IdentifierStr == "then") return tok_then;
if (IdentifierStr == "else") return tok_else;
return tok_identifier;
}
NumStr="";
if (LastChar == '-') {
LastChar = getLastChar();
if (isdigit(LastChar)) NumStr = "-";
else return '-';
}
if (isdigit(LastChar)) { // Число: [0-9.]+
do {
NumStr += (char)LastChar;
LastChar = getLastChar();
} while (isdigit(LastChar) || LastChar == '.');
return tok_number;
}
if (LastChar == '#') {
// Комментарий до конца строки
do LastChar = getLastChar();
while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');
if (LastChar != EOF)
return gettok();
}
if (LastChar == '"') { // "строка"
IdentifierStr = "";
LastChar = getLastChar();
while (LastChar != '"' && LastChar != EOF) {
IdentifierStr += (char)LastChar;
LastChar = getLastChar();
}
LastChar = getLastChar();
return tok_string;
}
// Проверка конца файла.
if (LastChar == EOF)
return tok_eof;
if (LastChar == '=') {
LastChar = getLastChar();
if (LastChar == '=') {
LastChar = getLastChar();
return tok_equal;
} else
return '=';
}
if (LastChar == '!') {
LastChar = getLastChar();
if (LastChar == '=') {
LastChar = getLastChar();
return tok_notequal;
} else
return '!';
}
if (LastChar == '<') {
LastChar = getLastChar();
if (LastChar == '=') {
LastChar = getLastChar();
return tok_lesseq;
} else
return '<';
}
if (LastChar == '>') {
LastChar = getLastChar();
if (LastChar == '=') {
LastChar = getLastChar();
return tok_greateq;
} else
return '>';
}
// В противном случае просто возвращаем символ как значение ASCII
int ThisChar = LastChar;
LastChar = getLastChar();
return ThisChar;
}
//===----------------------------------------------------------------------===//
// Parser (Парсер или Синтаксический Анализатор)
//===----------------------------------------------------------------------===//
/// CurTok/getNextToken - Предоставляет простой буфер токенов. CurTok - это текущий
/// токен, просматриваемый парсером. getNextToken получает следующий токен от
/// лексического анализатора и обновляет CurTok.
int IoTScenario::getNextToken() {
return CurTok = gettok();
}
/// GetTokPrecedence - Возвращает приоритет текущего бинарного оператора.
int IoTScenario::GetTokPrecedence() {
if (!(isascii(CurTok) || BinopPrecedence.count(CurTok)))
return -1;
// Удостоверимся, что это объявленный бинарный оператор.
int TokPrec = BinopPrecedence[CurTok];
if (TokPrec <= 0) return -1;
return TokPrec;
}
/// Error* - Это небольшие вспомогательные функции для обработки ошибок.
ExprAST *IoTScenario::Error(const char *Str) {
Serial.printf("Scenario error in line %d: %s\n", curLine-1, Str);
return nullptr;
}
/// identifierexpr
/// ::= identifier
/// ::= identifier '(' expression* ')'
ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames, bool callFromCondition) {
String IdName = IdentifierStr;
String Cmd = "";
IoTItem *tmpItem = findIoTItem(IdName);
getNextToken(); // получаем идентификатор.
if (CurTok == '.') {
getNextToken();
Cmd = IdentifierStr;
getNextToken();
}
if (CurTok != '(') { // Обычная переменная.
// if (tmpItem) return new VariableExprAST(IdName, tmpItem);
// else return new StringExprAST("id " + IdName + " not_found");
// создаем экземпляр переменной в любом случае, даж если не нашли (tmpItem = nulptr), т.к. переменная может придти из сети позже
return new VariableExprAST(IdName, tmpItem);
}
// Вызов функции.
getNextToken(); // получаем (
std::vector<ExprAST *> Args;
if (CurTok != ')') {
while (1) {
ExprAST *Arg = ParseExpression(IDNames, callFromCondition);
if (!Arg) return nullptr;
Args.push_back(Arg);
if (CurTok == ')') break;
if (CurTok != ',')
return Error("Expected ')' or ',' in argument list");
getNextToken();
}
}
// Получаем ';'.
getNextToken();
if (Cmd == "")
return new SysCallExprAST(IdName, Args); // создаем объект запуска системной функции
else
return new CallExprAST(IdName, Cmd, Args, tmpItem); // создаем объект запуска функции в любом случае даж если не нашли Item
}
/// numberexpr ::= number
ExprAST *IoTScenario::ParseNumberExpr() {
ExprAST *Result = new NumberExprAST(NumStr);
getNextToken(); // получаем число
return Result;
}
/// parenexpr ::= '(' expression ')'
ExprAST *IoTScenario::ParseParenExpr(String *IDNames, bool callFromCondition) {
getNextToken(); // получаем (.
ExprAST *V = ParseExpression(IDNames, callFromCondition);
if (!V) return nullptr;
if (CurTok != ')')
return Error("expected ')'");
//getNextToken(); // получаем ).
return V;
}
/// bracketsexpr ::= '{' expression '}'
ExprAST *IoTScenario::ParseBracketsExpr(String *IDNames, bool callFromCondition) {
getNextToken(); // получаем {.
std::vector<ExprAST *> bracketsList;
while (CurTok != '}') {
ExprAST *Expr = ParseExpression(IDNames, callFromCondition);
if (!Expr) return nullptr;
bracketsList.push_back(Expr);
//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(); // получаем }.
return new BracketsExprAST(bracketsList);
}
/// quotesexpr ::= '"' expression '"'
ExprAST *IoTScenario::ParseQuotesExpr() {
String StringCont = IdentifierStr;
ExprAST *Result = new StringExprAST(StringCont);
getNextToken(); // получаем число
return Result;
}
/// ifexpr ::= 'if' expression 'then' expression 'else' expression
ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
getNextToken(); // Получаем if.
// условие.
ExprAST *Cond = ParseExpression(IDNames, true);
if (!Cond) return nullptr;
if (CurTok != tok_then)
return Error("expected then");
getNextToken(); // Получаем then
ExprAST *Then = ParseExpression(IDNames, false);
if (!Then) return nullptr;
// if (CurTok != tok_else)
// return Error("expected else");
ExprAST *Else = nullptr;
if (CurTok == tok_else) {
getNextToken();
Else = ParseExpression(IDNames, false);
if (!Else) return nullptr;
}
return new IfExprAST(Cond, Then, Else, IDNames);
}
/// primary
/// ::= identifierexpr
/// ::= numberexpr
/// ::= parenexpr
ExprAST *IoTScenario::ParsePrimary(String *IDNames, bool callFromCondition) {
switch (CurTok) {
default:
Serial.println(CurTok);
return Error("unknown token when expecting an expression");
case tok_identifier: {
if (callFromCondition && IDNames) {
String tmpstr = *IDNames;
*IDNames = tmpstr + " " + IdentifierStr + " ";
}
return ParseIdentifierExpr(IDNames, callFromCondition);
}
case tok_number:
return ParseNumberExpr();
case '(':
return ParseParenExpr(IDNames, callFromCondition);
case '{':
return ParseBracketsExpr(IDNames, callFromCondition);
case tok_string:
return ParseQuotesExpr();
case tok_if:
return ParseIfExpr(IDNames);
}
}
/// binoprhs
/// ::= ('+' primary)*
ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition) {
// Если это бинарный оператор, получаем его приоритет
while (1) {
int TokPrec = GetTokPrecedence();
// Если этот бинарный оператор связывает выражения по крайней мере так же,
// как текущий, то используем его
if (TokPrec < ExprPrec)
return LHS;
// Отлично, мы знаем, что это бинарный оператор.
int BinOp = CurTok;
getNextToken(); // eat binop
// Разобрать первичное выражение после бинарного оператора
ExprAST *RHS = ParsePrimary(IDNames, callFromCondition);
if (!RHS) return nullptr;
// Если BinOp связан с RHS меньшим приоритетом, чем оператор после RHS,
// то берём часть вместе с RHS как LHS.
int NextPrec = GetTokPrecedence();
if (TokPrec < NextPrec) {
RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames, callFromCondition);
if (RHS == nullptr) return nullptr;
}
// Собираем LHS/RHS.
LHS = new BinaryExprAST(BinOp, LHS, RHS);
}
}
/// expression
/// ::= primary binoprhs
///
ExprAST *IoTScenario::ParseExpression(String *IDNames, bool callFromCondition) {
ExprAST *LHS = ParsePrimary(IDNames, callFromCondition);
if (!LHS) return nullptr;
return ParseBinOpRHS(0, LHS, IDNames, callFromCondition);
}
void IoTScenario::loadScenario(String fileName) { // подготавливаем контекст для чтения и интерпретации файла
if (mode == 0) {
if (file) file.close();
file = FileFS.open(fileName.c_str(), "r");
if (!file) {
Error("Open file scenario error");
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;
CurTok = 0;
curLine = 1;
charCount = 0;
if (mode == 0) file.seek(0);
if (mode < 2) {
while (CurTok != EOF) {
switch (CurTok) {
case tok_if: {
IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии
ExprAST *tmpAST = ParseIfExpr(&IDNames);
if (!tmpAST) {
Error("IF Expr wrong.");
break;
}
if (tmpAST->hasEventIdName(eventIdName)) {
tmpAST->exec();
}
delete tmpAST;
break;}
default: getNextToken(); break;
}
}
}
}
IoTScenario::IoTScenario() {
// Задаём стандартные бинарные операторы.
// 1 - наименьший приоритет.
BinopPrecedence['='] = 1;
BinopPrecedence['|'] = 5;
BinopPrecedence['&'] = 6;
BinopPrecedence[tok_equal] = 10; // ==
BinopPrecedence[tok_notequal] = 11; // !=
BinopPrecedence[tok_lesseq] = 15; // <=
BinopPrecedence[tok_greateq] = 16; // >=
BinopPrecedence['<'] = 20;
BinopPrecedence['>'] = 21;
BinopPrecedence['+'] = 25;
BinopPrecedence['-'] = 26;
BinopPrecedence['/'] = 27;
BinopPrecedence['*'] = 28; // highest.
}
IoTScenario::~IoTScenario() {
if (file) file.close();
}