/* xdrv_52_3_berry_embedded.ino - Berry scripting language, embedded code Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef USE_BERRY /*********************************************************************************************\ * Handlers for Berry calls and async * \*********************************************************************************************/ const char berry_prog[] = "" //"def func(x) for i:1..x print('a') end end " //"def testreal() return str(1.2+1) end " //"def noop() log('noop before'); yield(); log('middle after'); yield(); log('noop after'); end " //"log(\"foobar\") " // auto-import modules // // import alias "import wire " // Phase 1 // Prepare the super class that will be eventually in Flash "class Tasmota_ntv " "var _op, _operators, _rules, _timers, _cmd " // Map all native functions to methods // Again, this will be eventually pre-compiled "var getfreeheap, publish, cmd, getoption, millis, timereached, yield " "var respcmnd, respcmndstr, respcmnd_done, respcmnd_error, respcmnd_failed, resolvecmnd " "def init_ntv() " "import tasmota_ntv " "self.getfreeheap = tasmota_ntv.getfreeheap " "self.publish = tasmota_ntv.publish " "self.cmd = tasmota_ntv.cmd " "self.getoption = tasmota_ntv.getoption " "self.millis = tasmota_ntv.millis " "self.timereached = tasmota_ntv.timereached " "self.yield = tasmota_ntv.yield " "self._operators = tasmota_ntv._operators " "self.respcmnd = tasmota_ntv.respcmnd " "self.respcmndstr = tasmota_ntv.respcmndstr " "self.respcmnd_done = tasmota_ntv.respcmnd_done " "self.respcmnd_error = tasmota_ntv.respcmnd_error " "self.respcmnd_failed = tasmota_ntv.respcmnd_failed " "self.resolvecmnd = tasmota_ntv.resolvecmnd " "end " "def init() " "self._op = [ " "['==', /s1,s2-> str(s1) == str(s2)]," "['!==',/s1,s2-> str(s1) != str(s2)]," "['=', /f1,f2-> real(f1) == real(f2)]," "['!=', /f1,f2-> real(f1) != real(f2)]," "['>=', /f1,f2-> real(f1) >= real(f2)]," "['<=', /f1,f2-> real(f1) <= real(f2)]," "['>', /f1,f2-> real(f1) > real(f2)]," "['<', /f1,f2-> real(f1) < real(f2)]," "] " "self._rules = {} " "self._timers = [] " "self._cmd = {} " "self.init_ntv() " "end " "end " "class Tasmota: Tasmota_ntv " // add `charsinstring(s:string,c:string) -> int`` // looks for any char in c, and return the position of the first chat // or -1 if not found "def charsinstring(s,c) " "for i:0..size(s)-1 " "for j:0..size(c)-1 " "if s[i] == c[j] return i end " "end " "end " "return -1 " "end " // find a key in map, case insensitive, return actual key or nil if not found "def findkeyi(m,keyi) " "import string " "var keyu = string.toupper(keyi) " "if classof(m) == map " "for k:m.keys() " "if string.toupper(k)==keyu || keyi=='?' " "return k " "end " "end " "end " "end " // Rules "def addrule(pat,f) self._rules[pat] = f end " // # split the item when there is an operator, returns a list of (left,op,right) // # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"] "def find_op(item) " "import string " "var pos = self.charsinstring(item, self._operators) " "if pos>=0 " "var op_split = string.split(item,pos) " // #print(op_split) "var op_left = op_split[0] " "var op_rest = op_split[1] " // # iterate through operators "for op: self._op " "if string.find(op_rest,op[0]) == 0 " "var op_func = op[1] " "var op_right = string.split(op_rest,size(op[0]))[1] " "return [op_left,op_func,op_right] " "end " "end " "end " "return [item, nil, nil] " "end " // Rules trigger if match. return true if match, false if not // Note: condition is not yet managed "def try_rule(ev, rule, f) " "import string " "var rl_list = self.find_op(rule) " "var e=ev " "var rl=string.split(rl_list[0],'#') " "for it:rl " "found=self.findkeyi(e,it) " "if found == nil " "return false " "end " "e=e[found] " "end " // # check if condition is true "if rl_list[1] " // # did we find a function "if !rl_list[1](e,rl_list[2]) " // # condition is not met "return false " "end " "end " "f(e,ev) " "return true " "end " // Run rules, i.e. check each individual rule // Returns true if at least one rule matched, false if none "def exec_rules(ev_json) " "import json " "var ev = json.load(ev_json) " "var ret = false " "if ev == nil " "print('BRY: ERROR, bad json: '+ev_json, 3) " "else " "for r: self._rules.keys() " "ret = self.try_rule(ev,r,self._rules[r]) || ret " "end " "end " "return ret " "end " "def settimer(delay,f) self._timers.push([self.millis(delay),f]) end " "def run_deferred() " "var i=0 " "while i