Merge branch 'arendst:development' into feature/can-bus

This commit is contained in:
Marius Bezuidenhout 2021-08-20 09:18:02 +02:00 committed by GitHub
commit 7dca876f02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 3118 additions and 2510 deletions

1
.gitignore vendored
View File

@ -26,6 +26,7 @@ tasmota/tasmota.ino.cpp
tasmota*.bin
tasmota*.bin.gz
tasmota*.map
tasmota*.map.gz
platformio_override.ini
platformio_tasmota_cenv.ini

View File

@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file.
### Added
- Version bump to monitor possible HTTP issues releated to ``SetOption128``
### Changed
- Berry now compiling in ``strict`` mode to catch more bugs
### Fixed
- Fixed PWM5 on ESP32C3
## [9.5.0.5] 20210815
### Added
- Inital support for Wi-Fi extender (#12784)

View File

@ -20,6 +20,7 @@ be_extern_native_module(sys);
be_extern_native_module(debug);
be_extern_native_module(gc);
be_extern_native_module(solidify);
be_extern_native_module(strict);
be_extern_native_module(introspect);
/* Tasmota specific */
@ -72,6 +73,9 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
#endif
#if BE_USE_INTROSPECT_MODULE
&be_native_module(introspect),
#endif
#if BE_USE_STRICT_MODULE
&be_native_module(strict),
#endif
/* user-defined modules register start */
@ -101,6 +105,7 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
extern void be_load_tasmota_ntvlib(bvm *vm);
extern void be_load_wirelib(bvm *vm);
extern void be_load_Driver_class(bvm *vm);
extern void be_load_Timer_class(bvm *vm);
extern void be_load_driver_i2c_lib(bvm *vm);
extern void be_load_md5_lib(bvm *vm);
extern void be_load_aes_gcm_lib(bvm *vm);
@ -131,6 +136,7 @@ BERRY_API void be_load_custom_libs(bvm *vm)
#if !BE_USE_PRECOMPILED_OBJECT
/* be_load_xxxlib(vm); */
#endif
be_load_Timer_class(vm);
be_load_tasmota_ntvlib(vm);
be_load_Driver_class(vm);
be_load_md5_lib(vm);

View File

@ -925,93 +925,73 @@ const bclosure exec_rules_closure = {
/*******************************************************************/
/********************************************************************
"def set_timer(delay,f) "
"if !self._timers self._timers=[] end "
"self._timers.push([self.millis(delay),f]) "
"end "
********************************************************************/
/********************************************************************
** Solidified function: set_timer
********************************************************************/
be_local_closure(set_timer, /* name */
be_nested_proto(
9, /* nstack */
3, /* argc */
10, /* nstack */
4, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 3]) { /* upvals */
{ { .s=be_nested_const_str("_timers", -1694866380, 7) }, BE_STRING},
{ { .s=be_nested_const_str("push", -2022703139, 4) }, BE_STRING},
{ { .s=be_nested_const_str("millis", 1214679063, 6) }, BE_STRING},
( &(const bvalue[ 4]) { /* constants */
be_nested_string("_timers", -1694866380, 7), /* R256 - K0 */
be_nested_string("push", -2022703139, 4), /* R257 - K1 */
be_nested_string("Timer", -346839614, 5), /* R258 - K2 */
be_nested_string("millis", 1214679063, 6), /* R259 - K3 */
}),
(be_nested_const_str("set_timer", 2135414533, 9)),
(be_nested_const_str("string", 398550328, 6)),
(be_nested_const_str("input", -103256197, 5)),
( &(const binstruction[16]) { /* code */
0x880C0100, // 0000 GETMBR R3 R0 R256
0x740E0002, // 0001 JMPT R3 #0005
0x600C000A, // 0002 GETGBL R3 G10
0x7C0C0000, // 0003 CALL R3 0
0x90020003, // 0004 SETMBR R0 R256 R3
0x880C0100, // 0005 GETMBR R3 R0 R256
0x8C0C0701, // 0006 GETMET R3 R3 R257
0x6014000A, // 0007 GETGBL R5 G10
0x7C140000, // 0008 CALL R5 0
0x8C180102, // 0009 GETMET R6 R0 R258
0x5C200200, // 000A MOVE R8 R1
0x7C180400, // 000B CALL R6 2
0x40180A06, // 000C CONNECT R6 R5 R6
0x40180A02, // 000D CONNECT R6 R5 R2
0x7C0C0400, // 000E CALL R3 2
0x88100100, // 0000 GETMBR R4 R0 R256
0x74120002, // 0001 JMPT R4 #0005
0x6010000A, // 0002 GETGBL R4 G10
0x7C100000, // 0003 CALL R4 0
0x90020004, // 0004 SETMBR R0 R256 R4
0x88100100, // 0005 GETMBR R4 R0 R256
0x8C100901, // 0006 GETMET R4 R4 R257
0xB81A0400, // 0007 GETNGBL R6 R258
0x8C1C0103, // 0008 GETMET R7 R0 R259
0x5C240200, // 0009 MOVE R9 R1
0x7C1C0400, // 000A CALL R7 2
0x5C200400, // 000B MOVE R8 R2
0x5C240600, // 000C MOVE R9 R3
0x7C180600, // 000D CALL R6 3
0x7C100400, // 000E CALL R4 2
0x80000000, // 000F RET 0 R0
})
)
);
/*******************************************************************/
/********************************************************************
// run every 50ms tick
"def run_deferred() "
"if self._timers "
"var i=0 "
"while i<self._timers.size() "
"if self.time_reached(self._timers[i][0]) "
"f=self._timers[i][1] "
"self._timers.remove(i) "
"f() "
"else "
"i=i+1 "
"end "
"end "
"end "
"end "
********************************************************************/
/********************************************************************
** Solidified function: run_deferred
********************************************************************/
be_define_local_const_str(run_deferred_str_name, "run_deferred", 371594696, 12);
be_define_local_const_str(run_deferred_str_source, "string", 398550328, 6);
be_define_local_const_str(run_deferred_str_0, "_timers", -1694866380, 7);
be_define_local_const_str(run_deferred_str_2, "size", 597743964, 4);
be_define_local_const_str(run_deferred_str_3, "time_reached", 2075136773, 12);
be_define_local_const_str(run_deferred_str_5, "remove", -611183107, 6);
static const bvalue run_deferred_ktab[6] = {
{ { .s=be_local_const_str(run_deferred_str_0) }, BE_STRING},
{ { .i=0 }, BE_INT},
{ { .s=be_local_const_str(run_deferred_str_2) }, BE_STRING},
{ { .s=be_local_const_str(run_deferred_str_3) }, BE_STRING},
{ { .i=1 }, BE_INT},
{ { .s=be_local_const_str(run_deferred_str_5) }, BE_STRING},
};
static const uint32_t run_deferred_code[27] = {
be_local_closure(run_deferred, /* name */
be_nested_proto(
6, /* nstack */
1, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 8]) { /* constants */
be_nested_string("_timers", -1694866380, 7), /* R256 - K0 */
be_const_int(0), /* R257 - K1 */
be_nested_string("size", 597743964, 4), /* R258 - K2 */
be_nested_string("time_reached", 2075136773, 12), /* R259 - K3 */
be_nested_string("due", -399437003, 3), /* R260 - K4 */
be_nested_string("f", -485742695, 1), /* R261 - K5 */
be_nested_string("remove", -611183107, 6), /* R262 - K6 */
be_const_int(1), /* R263 - K7 */
}),
(be_nested_const_str("run_deferred", 371594696, 12)),
(be_nested_const_str("input", -103256197, 5)),
( &(const binstruction[27]) { /* code */
0x88040100, // 0000 GETMBR R1 R0 R256
0x78060017, // 0001 JMPF R1 #001A
0x58040001, // 0002 LDCONST R1 K1
@ -1023,64 +1003,78 @@ static const uint32_t run_deferred_code[27] = {
0x8C080103, // 0008 GETMET R2 R0 R259
0x88100100, // 0009 GETMBR R4 R0 R256
0x94100801, // 000A GETIDX R4 R4 R1
0x94100901, // 000B GETIDX R4 R4 R257
0x88100904, // 000B GETMBR R4 R4 R260
0x7C080400, // 000C CALL R2 2
0x780A0009, // 000D JMPF R2 #0018
0x88080100, // 000E GETMBR R2 R0 R256
0x94080401, // 000F GETIDX R2 R2 R1
0x94080504, // 0010 GETIDX R2 R2 R260
0x88080505, // 0010 GETMBR R2 R2 R261
0x880C0100, // 0011 GETMBR R3 R0 R256
0x8C0C0705, // 0012 GETMET R3 R3 R261
0x8C0C0706, // 0012 GETMET R3 R3 R262
0x5C140200, // 0013 MOVE R5 R1
0x7C0C0400, // 0014 CALL R3 2
0x5C0C0400, // 0015 MOVE R3 R2
0x7C0C0000, // 0016 CALL R3 0
0x70020000, // 0017 JMP #0019
0x40304, // 0018 ADD R1 R1 R260
0x00040307, // 0018 ADD R1 R1 R263
0x7001FFE8, // 0019 JMP #0003
0x80000000, // 001A RET 0 R0
};
static const bproto run_deferred_proto = {
NULL, // bgcobject *next
8, // type
0x08, // marked
6, // nstack
0, // nupvals
1, // argc
0, // varg
NULL, // bgcobject *gray
NULL, // bupvaldesc *upvals
(bvalue*) &run_deferred_ktab, // ktab
NULL, // bproto **ptab
(binstruction*) &run_deferred_code, // code
be_local_const_str(run_deferred_str_name), // name
27, // codesize
6, // nconst
0, // nproto
be_local_const_str(run_deferred_str_source), // source
#if BE_DEBUG_RUNTIME_INFO /* debug information */
NULL, // lineinfo
0, // nlineinfo
#endif
#if BE_DEBUG_VAR_INFO
NULL, // varinfo
0, // nvarinfo
#endif
};
const bclosure run_deferred_closure = {
NULL, // bgcobject *next
36, // type
0x08, // marked
0, // nupvals
NULL, // bgcobject *gray
(bproto*) &run_deferred_proto, // proto
{ NULL } // upvals
};
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: remove_timer
********************************************************************/
be_local_closure(remove_timer, /* name */
be_nested_proto(
6, /* nstack */
2, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 7]) { /* constants */
be_nested_string("tasmota", 424643812, 7), /* R256 - K0 */
be_nested_string("_timers", -1694866380, 7), /* R257 - K1 */
be_const_int(0), /* R258 - K2 */
be_nested_string("size", 597743964, 4), /* R259 - K3 */
be_nested_string("id", 926444256, 2), /* R260 - K4 */
be_nested_string("remove", -611183107, 6), /* R261 - K5 */
be_const_int(1), /* R262 - K6 */
}),
(be_nested_const_str("remove_timer", -153495081, 12)),
(be_nested_const_str("input", -103256197, 5)),
( &(const binstruction[23]) { /* code */
0xB80A0000, // 0000 GETNGBL R2 R256
0x88080501, // 0001 GETMBR R2 R2 R257
0x780A0012, // 0002 JMPF R2 #0016
0x58080002, // 0003 LDCONST R2 K2
0xB80E0000, // 0004 GETNGBL R3 R256
0x880C0701, // 0005 GETMBR R3 R3 R257
0x8C0C0703, // 0006 GETMET R3 R3 R259
0x7C0C0200, // 0007 CALL R3 1
0x140C0403, // 0008 LT R3 R2 R3
0x780E000B, // 0009 JMPF R3 #0016
0x880C0101, // 000A GETMBR R3 R0 R257
0x940C0602, // 000B GETIDX R3 R3 R2
0x880C0704, // 000C GETMBR R3 R3 R260
0x1C0C0601, // 000D EQ R3 R3 R1
0x780E0004, // 000E JMPF R3 #0014
0x880C0101, // 000F GETMBR R3 R0 R257
0x8C0C0705, // 0010 GETMET R3 R3 R261
0x5C140400, // 0011 MOVE R5 R2
0x7C0C0400, // 0012 CALL R3 2
0x70020000, // 0013 JMP #0015
0x00080506, // 0014 ADD R2 R2 R262
0x7001FFED, // 0015 JMP #0004
0x80000000, // 0016 RET 0 R0
})
)
);
/*******************************************************************/
/********************************************************************
// Add command to list
@ -1212,43 +1206,28 @@ be_local_closure(remove_cmd, /* name */
);
/*******************************************************************/
/********************************************************************
// Execute custom command
"def exec_cmd(cmd, idx, payload) "
"if self._ccmd "
"import json "
"var payload_json = json.load(payload) "
"var cmd_found = self.find_key_i(self._ccmd, cmd) "
"if cmd_found != nil "
"self.resolvecmnd(cmd_found) " // set the command name in XdrvMailbox.command
"self._ccmd[cmd_found](cmd_found, idx, payload, payload_json) "
"return true "
"end "
"end "
"return false "
"end "
********************************************************************/
/********************************************************************
** Solidified function: exec_cmd
********************************************************************/
be_define_local_const_str(exec_cmd_str_name, "exec_cmd", 493567399, 8);
be_define_local_const_str(exec_cmd_str_source, "string", 398550328, 6);
be_define_local_const_str(exec_cmd_str_0, "_ccmd", -2131545883, 5);
be_define_local_const_str(exec_cmd_str_1, "json", 916562499, 4);
be_define_local_const_str(exec_cmd_str_2, "load", -435725847, 4);
be_define_local_const_str(exec_cmd_str_3, "find_key_i", 850136726, 10);
be_define_local_const_str(exec_cmd_str_4, "resolvecmnd", 993361485, 11);
static const bvalue exec_cmd_ktab[5] = {
{ { .s=be_local_const_str(exec_cmd_str_0) }, BE_STRING},
{ { .s=be_local_const_str(exec_cmd_str_1) }, BE_STRING},
{ { .s=be_local_const_str(exec_cmd_str_2) }, BE_STRING},
{ { .s=be_local_const_str(exec_cmd_str_3) }, BE_STRING},
{ { .s=be_local_const_str(exec_cmd_str_4) }, BE_STRING},
};
static const uint32_t exec_cmd_code[27] = {
be_local_closure(exec_cmd, /* name */
be_nested_proto(
12, /* nstack */
4, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 5]) { /* constants */
be_nested_string("_ccmd", -2131545883, 5), /* R256 - K0 */
be_nested_string("json", 916562499, 4), /* R257 - K1 */
be_nested_string("load", -435725847, 4), /* R258 - K2 */
be_nested_string("find_key_i", 850136726, 10), /* R259 - K3 */
be_nested_string("resolvecmnd", 993361485, 11), /* R260 - K4 */
}),
(be_nested_const_str("exec_cmd", 493567399, 8)),
(be_nested_const_str("string", 398550328, 6)),
( &(const binstruction[27]) { /* code */
0x88100100, // 0000 GETMBR R4 R0 R256
0x78120016, // 0001 JMPF R4 #0019
0xA4120200, // 0002 IMPORT R4 R257
@ -1276,49 +1255,11 @@ static const uint32_t exec_cmd_code[27] = {
0x80040E00, // 0018 RET 1 R7
0x50100000, // 0019 LDBOOL R4 0 0
0x80040800, // 001A RET 1 R4
};
static const bproto exec_cmd_proto = {
NULL, // bgcobject *next
8, // type
0x08, // marked
12, // nstack
0, // nupvals
4, // argc
0, // varg
NULL, // bgcobject *gray
NULL, // bupvaldesc *upvals
(bvalue*) &exec_cmd_ktab, // ktab
NULL, // bproto **ptab
(binstruction*) &exec_cmd_code, // code
be_local_const_str(exec_cmd_str_name), // name
27, // codesize
5, // nconst
0, // nproto
be_local_const_str(exec_cmd_str_source), // source
#if BE_DEBUG_RUNTIME_INFO /* debug information */
NULL, // lineinfo
0, // nlineinfo
#endif
#if BE_DEBUG_VAR_INFO
NULL, // varinfo
0, // nvarinfo
#endif
};
static const bclosure exec_cmd_closure = {
NULL, // bgcobject *next
36, // type
0x08, // marked
0, // nupvals
NULL, // bgcobject *gray
(bproto*) &exec_cmd_proto, // proto
{ NULL } // upvals
};
})
)
);
/*******************************************************************/
/********************************************************************
// Force gc and return allocated memory
"def gc() "
@ -1478,18 +1419,22 @@ be_local_closure(event, /* name */
/********************************************************************
** Solidified function: add_driver
********************************************************************/
be_define_local_const_str(add_driver_str_name, "add_driver", 1654458371, 10);
be_define_local_const_str(add_driver_str_source, "string", 398550328, 6);
be_define_local_const_str(add_driver_str_0, "_drivers", -1034638311, 8);
be_define_local_const_str(add_driver_str_1, "push", -2022703139, 4);
static const bvalue add_driver_ktab[2] = {
{ { .s=be_local_const_str(add_driver_str_0) }, BE_STRING},
{ { .s=be_local_const_str(add_driver_str_1) }, BE_STRING},
};
static const uint32_t add_driver_code[12] = {
be_local_closure(add_driver, /* name */
be_nested_proto(
5, /* nstack */
2, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 2]) { /* constants */
be_nested_string("_drivers", -1034638311, 8), /* R256 - K0 */
be_nested_string("push", -2022703139, 4), /* R257 - K1 */
}),
(be_nested_const_str("add_driver", 1654458371, 10)),
(be_nested_const_str("string", 398550328, 6)),
( &(const binstruction[12]) { /* code */
0x88080100, // 0000 GETMBR R2 R0 R256
0x780A0004, // 0001 JMPF R2 #0007
0x88080100, // 0002 GETMBR R2 R0 R256
@ -1502,46 +1447,9 @@ static const uint32_t add_driver_code[12] = {
0x400C0401, // 0009 CONNECT R3 R2 R1
0x90020002, // 000A SETMBR R0 R256 R2
0x80000000, // 000B RET 0 R0
};
static const bproto add_driver_proto = {
NULL, // bgcobject *next
8, // type
0x08, // marked
5, // nstack
0, // nupvals
2, // argc
0, // varg
NULL, // bgcobject *gray
NULL, // bupvaldesc *upvals
(bvalue*) &add_driver_ktab, // ktab
NULL, // bproto **ptab
(binstruction*) &add_driver_code, // code
be_local_const_str(add_driver_str_name), // name
12, // codesize
2, // nconst
0, // nproto
be_local_const_str(add_driver_str_source), // source
#if BE_DEBUG_RUNTIME_INFO /* debug information */
NULL, // lineinfo
0, // nlineinfo
#endif
#if BE_DEBUG_VAR_INFO
NULL, // varinfo
0, // nvarinfo
#endif
};
const bclosure add_driver_closure = {
NULL, // bgcobject *next
36, // type
0x08, // marked
0, // nupvals
NULL, // bgcobject *gray
(bproto*) &add_driver_proto, // proto
{ NULL } // upvals
};
})
)
);
/*******************************************************************/
/********************************************************************
@ -2246,6 +2154,7 @@ void be_load_tasmota_ntvlib(bvm *vm)
{ "exec_rules", (bntvfunc) &exec_rules_closure },
{ "set_timer", (bntvfunc) &set_timer_closure },
{ "run_deferred", (bntvfunc) &run_deferred_closure },
{ "remove_timer", (bntvfunc) &remove_timer_closure },
{ "add_cmd", (bntvfunc) &add_cmd_closure },
{ "remove_cmd", (bntvfunc) &remove_cmd_closure },
{ "exec_cmd", (bntvfunc) &exec_cmd_closure },
@ -2329,6 +2238,7 @@ class be_class_tasmota (scope: global, name: Tasmota) {
exec_rules, closure(exec_rules_closure)
set_timer, closure(set_timer_closure)
run_deferred, closure(run_deferred_closure)
remove_timer, closure(remove_timer_closure)
add_cmd, closure(add_cmd_closure)
remove_cmd, closure(remove_cmd_closure)
exec_cmd, closure(exec_cmd_closure)

View File

@ -0,0 +1,108 @@
/********************************************************************
* Tasmota lib
*
* class Timer
*******************************************************************/
#include "be_constobj.h"
/********************************************************************
** Solidified function: tostring
********************************************************************/
be_local_closure(tostring, /* name */
be_nested_proto(
10, /* nstack */
1, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 6]) { /* constants */
be_nested_string("string", 398550328, 6), /* R256 - K0 */
be_nested_string("format", -1180859054, 6), /* R257 - K1 */
be_nested_string("<instance: %s(%s, %s, %s)", 257363333, 25), /* R258 - K2 */
be_nested_string("due", -399437003, 3), /* R259 - K3 */
be_nested_string("f", -485742695, 1), /* R260 - K4 */
be_nested_string("id", 926444256, 2), /* R261 - K5 */
}),
(be_nested_const_str("tostring", -1995258651, 8)),
(be_nested_const_str("input", -103256197, 5)),
( &(const binstruction[19]) { /* code */
0xA4060000, // 0000 IMPORT R1 R256
0x8C080301, // 0001 GETMET R2 R1 R257
0x58100002, // 0002 LDCONST R4 K2
0x60140013, // 0003 GETGBL R5 G19
0x60180004, // 0004 GETGBL R6 G4
0x5C1C0000, // 0005 MOVE R7 R0
0x7C180200, // 0006 CALL R6 1
0x7C140200, // 0007 CALL R5 1
0x60180013, // 0008 GETGBL R6 G19
0x881C0103, // 0009 GETMBR R7 R0 R259
0x7C180200, // 000A CALL R6 1
0x601C0013, // 000B GETGBL R7 G19
0x88200104, // 000C GETMBR R8 R0 R260
0x7C1C0200, // 000D CALL R7 1
0x60200013, // 000E GETGBL R8 G19
0x88240105, // 000F GETMBR R9 R0 R261
0x7C200200, // 0010 CALL R8 1
0x7C080C00, // 0011 CALL R2 6
0x80040400, // 0012 RET 1 R2
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified function: init
********************************************************************/
be_local_closure(init, /* name */
be_nested_proto(
4, /* nstack */
4, /* argc */
0, /* has upvals */
NULL, /* no upvals */
0, /* has sup protos */
NULL, /* no sub protos */
1, /* has constants */
( &(const bvalue[ 3]) { /* constants */
be_nested_string("due", -399437003, 3), /* R256 - K0 */
be_nested_string("f", -485742695, 1), /* R257 - K1 */
be_nested_string("id", 926444256, 2), /* R258 - K2 */
}),
(be_nested_const_str("init", 380752755, 4)),
(be_nested_const_str("input", -103256197, 5)),
( &(const binstruction[ 4]) { /* code */
0x90020001, // 0000 SETMBR R0 R256 R1
0x90020202, // 0001 SETMBR R0 R257 R2
0x90020403, // 0002 SETMBR R0 R258 R3
0x80000000, // 0003 RET 0 R0
})
)
);
/*******************************************************************/
/********************************************************************
** Solidified class: Timer
********************************************************************/
be_local_class(Timer,
3,
NULL,
be_nested_map(5,
( (struct bmapnode*) &(const bmapnode[]) {
{ be_nested_key("tostring", -1995258651, 8, 4), be_const_closure(tostring_closure) },
{ be_nested_key("id", 926444256, 2, 2), be_const_index(2) },
{ be_nested_key("f", -485742695, 1, -1), be_const_index(1) },
{ be_nested_key("due", -399437003, 3, -1), be_const_index(0) },
{ be_nested_key("init", 380752755, 4, -1), be_const_closure(init_closure) },
})),
(be_nested_const_str("Timer", -346839614, 5))
);
/*******************************************************************/
void be_load_Timer_class(bvm *vm) {
be_pushntvclass(vm, &be_class_Timer);
be_setglobal(vm, "Timer");
be_pop(vm, 1);
}

View File

@ -45,7 +45,7 @@
* Use precompiled objects to avoid creating these objects at
* runtime. Enable this macro can greatly optimize RAM usage.
* Default: 1
// **/
**/
#define BE_USE_PRECOMPILED_OBJECT 1
/* Macro: BE_DEBUG_RUNTIME_INFO
@ -142,6 +142,14 @@
**/
#define BE_USE_DEBUG_HOOK 0
/* Macro: BE_USE_DEBUG_GC
* Enable GC debug mode. This causes an actual gc after each
* allocation. It's much slower and should not be used
* in production code.
* Default: 0
**/
#define BE_USE_DEBUG_GC 0
/* Macro: BE_USE_XXX_MODULE
* These macros control whether the related module is compiled.
* When they are true, they will enable related modules. At this
@ -159,6 +167,7 @@
#define BE_USE_GC_MODULE 1
#define BE_USE_SOLIDIFY_MODULE 1
#define BE_USE_INTROSPECT_MODULE 1
#define BE_USE_STRICT_MODULE 1
/* Macro: BE_EXPLICIT_XXX
* If these macros are defined, the corresponding function will

View File

@ -1,6 +1,21 @@
#- Native code used for testing and code solidification -#
#- Do not use it -#
class Timer
var due, f, id
def init(due, f, id)
self.due = due
self.f = f
self.id = id
end
def tostring()
import string
return string.format("<instance: %s(%s, %s, %s)", str(classof(self)),
str(self.due), str(self.f), str(self.id))
end
end
tasmota = nil
class Tasmota
# add `chars_in_string(s:string,c:string) -> int``
@ -79,7 +94,7 @@ class Tasmota
var sub_event = event
var rl = string.split(rl_list[0],'#')
for it:rl
found=self.find_key_i(sub_event,it)
var found=self.find_key_i(sub_event,it)
if found == nil return false end
sub_event = sub_event[found]
end
@ -127,9 +142,9 @@ class Tasmota
return false
end
def set_timer(delay,f)
def set_timer(delay,f,id)
if !self._timers self._timers=[] end
self._timers.push([self.millis(delay),f])
self._timers.push(Timer(self.millis(delay),f,id))
end
# run every 50ms tick
@ -137,8 +152,8 @@ class Tasmota
if self._timers
var i=0
while i<self._timers.size()
if self.time_reached(self._timers[i][0])
f=self._timers[i][1]
if self.time_reached(self._timers[i].due)
var f=self._timers[i].f
self._timers.remove(i)
f()
else
@ -148,6 +163,20 @@ class Tasmota
end
end
# remove timers by id
def remove_timer(id)
if tasmota._timers
var i=0
while i<tasmota._timers.size()
if self._timers[i].id == id
self._timers.remove(i)
else
i=i+1
end
end
end
end
# Add command to list
def add_cmd(c,f)
if !self._ccmd
@ -265,6 +294,47 @@ class Tasmota
end
end
def add_driver(d)
if self._drivers
self._drivers.push(d)
else
self._drivers = [d]
end
end
# cmd high-level function
def cmd(command)
import json
var ret = self._cmd(command)
var j = json.load(ret)
if type(j) == 'instance'
return j
else
return {'response':j}
end
end
# set_light and get_light deprecetaion
def get_light(l)
print('tasmota.get_light() is deprecated, use light.get()')
import light
if l != nil
return light.get(l)
else
return light.get()
end
end
def set_light(v,l)
print('tasmota.set_light() is deprecated, use light.set()')
import light
if l != nil
return light.set(v,l)
else
return light.set(v)
end
end
#- dispatch callback number n, with parameters v0,v1,v2,v3 -#
def cb_dispatch(n,v0,v1,v2,v3)
if self._cb == nil return 0 end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,34 +1,36 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_bytes_map) {
{ be_const_key(resize, 10), be_const_func(m_resize) },
{ be_const_key(asstring, 11), be_const_func(m_asstring) },
{ be_const_key(init, -1), be_const_func(m_init) },
{ be_const_key(seti, -1), be_const_func(m_set) },
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
{ be_const_key(item, 18), be_const_func(m_item) },
{ be_const_key(dot_p, 3), be_const_index(0) },
{ be_const_key(geti, -1), be_const_func(m_geti) },
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
{ be_const_key(tostring, -1), be_const_func(m_tostring) },
{ be_const_key(size, -1), be_const_func(m_size) },
{ be_const_key(dot_p, -1), be_const_index(0) },
{ be_const_key(seti, 6), be_const_func(m_set) },
{ be_const_key(get, 7), be_const_func(m_getu) },
{ be_const_key(size, 10), be_const_func(m_size) },
{ be_const_key(resize, 1), be_const_func(m_resize) },
{ be_const_key(opt_add, 22), be_const_func(m_merge) },
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
{ be_const_key(fromstring, -1), be_const_func(m_fromstring) },
{ be_const_key(opt_add, -1), be_const_func(m_merge) },
{ be_const_key(_buffer, 2), be_const_func(m_buffer) },
{ be_const_key(copy, 8), be_const_func(m_copy) },
{ be_const_key(get, -1), be_const_func(m_getu) },
{ be_const_key(set, -1), be_const_func(m_set) },
{ be_const_key(opt_eq, -1), be_const_func(m_equal) },
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
{ be_const_key(clear, 16), be_const_func(m_clear) },
{ be_const_key(setbits, -1), be_const_closure(setbits_closure) },
{ be_const_key(geti, 16), be_const_func(m_geti) },
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
{ be_const_key(add, -1), be_const_func(m_add) },
{ be_const_key(fromb64, -1), be_const_func(m_fromb64) },
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
{ be_const_key(set, -1), be_const_func(m_set) },
{ be_const_key(asstring, -1), be_const_func(m_asstring) },
{ be_const_key(copy, 3), be_const_func(m_copy) },
{ be_const_key(opt_eq, 2), be_const_func(m_equal) },
{ be_const_key(tob64, -1), be_const_func(m_tob64) },
{ be_const_key(setbits, 12), be_const_closure(setbits_closure) },
{ be_const_key(_buffer, -1), be_const_func(m_buffer) },
{ be_const_key(fromstring, 0), be_const_func(m_fromstring) },
{ be_const_key(tostring, 9), be_const_func(m_tostring) },
{ be_const_key(item, 8), be_const_func(m_item) },
{ be_const_key(init, 23), be_const_func(m_init) },
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
{ be_const_key(clear, -1), be_const_func(m_clear) },
};
static be_define_const_map(
be_class_bytes_map,
23
25
);
BE_EXPORT_VARIABLE be_define_const_class(

View File

@ -1,71 +1,72 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_tasmota_map) {
{ be_const_key(gc, -1), be_const_closure(gc_closure) },
{ be_const_key(_cmd, -1), be_const_func(l_cmd) },
{ be_const_key(event, -1), be_const_closure(event_closure) },
{ be_const_key(millis, -1), be_const_func(l_millis) },
{ be_const_key(eth, 23), be_const_func(l_eth) },
{ be_const_key(try_rule, -1), be_const_closure(try_rule_closure) },
{ be_const_key(resp_cmnd, -1), be_const_func(l_respCmnd) },
{ be_const_key(set_light, -1), be_const_closure(set_light_closure) },
{ be_const_key(remove_rule, -1), be_const_closure(remove_rule_closure) },
{ be_const_key(get_light, -1), be_const_closure(get_light_closure) },
{ be_const_key(_rules, -1), be_const_index(0) },
{ be_const_key(add_driver, 0), be_const_closure(add_driver_closure) },
{ be_const_key(save, 38), be_const_func(l_save) },
{ be_const_key(set_timer, -1), be_const_closure(set_timer_closure) },
{ be_const_key(yield, 48), be_const_func(l_yield) },
{ be_const_key(resp_cmnd_done, -1), be_const_func(l_respCmndDone) },
{ be_const_key(find_op, 4), be_const_closure(find_op_closure) },
{ be_const_key(get_free_heap, -1), be_const_func(l_getFreeHeap) },
{ be_const_key(remove_cmd, -1), be_const_closure(remove_cmd_closure) },
{ be_const_key(exec_cmd, 20), be_const_closure(exec_cmd_closure) },
{ be_const_key(add_cmd, -1), be_const_closure(add_cmd_closure) },
{ be_const_key(log, -1), be_const_func(l_logInfo) },
{ be_const_key(_get_cb, 18), be_const_func(l_get_cb) },
{ be_const_key(_timers, 35), be_const_index(1) },
{ be_const_key(wifi, 55), be_const_func(l_wifi) },
{ be_const_key(_drivers, 10), be_const_index(2) },
{ be_const_key(find_key_i, -1), be_const_closure(find_key_i_closure) },
{ be_const_key(_cb, -1), be_const_index(3) },
{ be_const_key(web_send, -1), be_const_func(l_webSend) },
{ be_const_key(resp_cmnd_error, -1), be_const_func(l_respCmndError) },
{ be_const_key(memory, 31), be_const_func(l_memory) },
{ be_const_key(resp_cmnd_str, -1), be_const_func(l_respCmndStr) },
{ be_const_key(time_str, 7), be_const_closure(time_str_closure) },
{ be_const_key(set_power, 34), be_const_func(l_setpower) },
{ be_const_key(get_option, 46), be_const_func(l_getoption) },
{ be_const_key(rtc, -1), be_const_func(l_rtc) },
{ be_const_key(response_append, -1), be_const_func(l_respAppend) },
{ be_const_key(publish_result, -1), be_const_func(l_publish_result) },
{ be_const_key(chars_in_string, 50), be_const_closure(chars_in_string_closure) },
{ be_const_key(cmd, 2), be_const_closure(cmd_closure) },
{ be_const_key(wire_scan, -1), be_const_closure(wire_scan_closure) },
{ be_const_key(i2c_enabled, -1), be_const_func(l_i2cenabled) },
{ be_const_key(resp_cmnd_failed, 1), be_const_func(l_respCmndFailed) },
{ be_const_key(add_rule, 29), be_const_closure(add_rule_closure) },
{ be_const_key(publish, -1), be_const_func(l_publish) },
{ be_const_key(resolvecmnd, -1), be_const_func(l_resolveCmnd) },
{ be_const_key(_ccmd, -1), be_const_index(4) },
{ be_const_key(time_dump, -1), be_const_func(l_time_dump) },
{ be_const_key(scale_uint, -1), be_const_func(l_scaleuint) },
{ be_const_key(load, -1), be_const_closure(load_closure) },
{ be_const_key(exec_rules, 37), be_const_closure(exec_rules_closure) },
{ be_const_key(gen_cb, 36), be_const_closure(gen_cb_closure) },
{ be_const_key(cb_dispatch, -1), be_const_closure(cb_dispatch_closure) },
{ be_const_key(resp_cmnd_error, -1), be_const_func(l_respCmndError) },
{ be_const_key(exec_rules, -1), be_const_closure(exec_rules_closure) },
{ be_const_key(_cmd, -1), be_const_func(l_cmd) },
{ be_const_key(gc, -1), be_const_closure(gc_closure) },
{ be_const_key(remove_rule, -1), be_const_closure(remove_rule_closure) },
{ be_const_key(event, -1), be_const_closure(event_closure) },
{ be_const_key(log, 25), be_const_func(l_logInfo) },
{ be_const_key(_drivers, -1), be_const_index(0) },
{ be_const_key(set_light, -1), be_const_closure(set_light_closure) },
{ be_const_key(wire2, 26), be_const_index(1) },
{ be_const_key(get_free_heap, -1), be_const_func(l_getFreeHeap) },
{ be_const_key(resp_cmnd_failed, -1), be_const_func(l_respCmndFailed) },
{ be_const_key(publish_result, -1), be_const_func(l_publish_result) },
{ be_const_key(wire1, -1), be_const_index(2) },
{ be_const_key(set_power, 46), be_const_func(l_setpower) },
{ be_const_key(cmd, -1), be_const_closure(cmd_closure) },
{ be_const_key(get_light, 42), be_const_closure(get_light_closure) },
{ be_const_key(_rules, 54), be_const_index(3) },
{ be_const_key(run_deferred, 19), be_const_closure(run_deferred_closure) },
{ be_const_key(try_rule, 18), be_const_closure(try_rule_closure) },
{ be_const_key(remove_cmd, 41), be_const_closure(remove_cmd_closure) },
{ be_const_key(add_driver, 0), be_const_closure(add_driver_closure) },
{ be_const_key(get_power, 36), be_const_func(l_getpower) },
{ be_const_key(remove_timer, -1), be_const_closure(remove_timer_closure) },
{ be_const_key(_timers, -1), be_const_index(4) },
{ be_const_key(add_rule, 33), be_const_closure(add_rule_closure) },
{ be_const_key(time_reached, -1), be_const_func(l_timereached) },
{ be_const_key(web_send_decimal, -1), be_const_func(l_webSendDecimal) },
{ be_const_key(delay, 54), be_const_func(l_delay) },
{ be_const_key(run_deferred, 9), be_const_closure(run_deferred_closure) },
{ be_const_key(get_power, -1), be_const_func(l_getpower) },
{ be_const_key(wire2, -1), be_const_index(5) },
{ be_const_key(wire1, 52), be_const_index(6) },
{ be_const_key(resp_cmnd_done, -1), be_const_func(l_respCmndDone) },
{ be_const_key(time_dump, -1), be_const_func(l_time_dump) },
{ be_const_key(resolvecmnd, -1), be_const_func(l_resolveCmnd) },
{ be_const_key(millis, -1), be_const_func(l_millis) },
{ be_const_key(get_option, 52), be_const_func(l_getoption) },
{ be_const_key(eth, -1), be_const_func(l_eth) },
{ be_const_key(wifi, 22), be_const_func(l_wifi) },
{ be_const_key(response_append, 43), be_const_func(l_respAppend) },
{ be_const_key(find_op, -1), be_const_closure(find_op_closure) },
{ be_const_key(set_timer, 28), be_const_closure(set_timer_closure) },
{ be_const_key(find_key_i, -1), be_const_closure(find_key_i_closure) },
{ be_const_key(gen_cb, -1), be_const_closure(gen_cb_closure) },
{ be_const_key(memory, 47), be_const_func(l_memory) },
{ be_const_key(resp_cmnd_str, -1), be_const_func(l_respCmndStr) },
{ be_const_key(chars_in_string, -1), be_const_closure(chars_in_string_closure) },
{ be_const_key(resp_cmnd, -1), be_const_func(l_respCmnd) },
{ be_const_key(web_send_decimal, 44), be_const_func(l_webSendDecimal) },
{ be_const_key(load, -1), be_const_closure(load_closure) },
{ be_const_key(web_send, 56), be_const_func(l_webSend) },
{ be_const_key(_ccmd, -1), be_const_index(5) },
{ be_const_key(i2c_enabled, 48), be_const_func(l_i2cenabled) },
{ be_const_key(save, -1), be_const_func(l_save) },
{ be_const_key(exec_cmd, 2), be_const_closure(exec_cmd_closure) },
{ be_const_key(cb_dispatch, -1), be_const_closure(cb_dispatch_closure) },
{ be_const_key(wire_scan, -1), be_const_closure(wire_scan_closure) },
{ be_const_key(scale_uint, -1), be_const_func(l_scaleuint) },
{ be_const_key(delay, -1), be_const_func(l_delay) },
{ be_const_key(_cb, -1), be_const_index(6) },
{ be_const_key(add_cmd, -1), be_const_closure(add_cmd_closure) },
{ be_const_key(_get_cb, -1), be_const_func(l_get_cb) },
{ be_const_key(yield, -1), be_const_func(l_yield) },
{ be_const_key(time_str, -1), be_const_closure(time_str_closure) },
};
static be_define_const_map(
be_class_tasmota_map,
60
61
);
BE_EXPORT_VARIABLE be_define_const_class(

View File

@ -0,0 +1,17 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libstrict_map) {
{ be_const_key(init, -1), be_const_func(m_init) },
};
static be_define_const_map(
m_libstrict_map,
1
);
static be_define_const_module(
m_libstrict,
"strict"
);
BE_EXPORT_VARIABLE be_define_const_native_module(strict, NULL);

View File

@ -640,7 +640,8 @@ BERRY_API bbool be_copy(bvm *vm, int index)
return bfalse;
}
static int ins_member(bvm *vm, int index, const char *k)
/* `onlyins` limits the search to instance, and discards module. Makes sure getmethod does not return anything for module. */
static int ins_member(bvm *vm, int index, const char *k, bbool onlyins)
{
int type = BE_NIL;
bvalue *o = be_indexof(vm, index);
@ -652,18 +653,25 @@ static int ins_member(bvm *vm, int index, const char *k)
if (type == BE_NONE) {
type = BE_NIL;
}
} else if (var_ismodule(o) && !onlyins) {
bmodule *module = var_toobj(o);
bvalue *v = be_module_attr(vm, module, be_newstr(vm, k));
if (v != NULL) {
*top = *v;
type = v->type;
}
}
return type;
}
BERRY_API bbool be_getmember(bvm *vm, int index, const char *k)
{
return ins_member(vm, index, k) != BE_NIL;
return ins_member(vm, index, k, bfalse) != BE_NIL;
}
BERRY_API bbool be_getmethod(bvm *vm, int index, const char *k)
{
return basetype(ins_member(vm, index, k)) == BE_FUNCTION;
return basetype(ins_member(vm, index, k, btrue)) == BE_FUNCTION;
}
BERRY_API bbool be_getindex(bvm *vm, int index)

View File

@ -28,6 +28,198 @@ typedef struct buf_impl {
uint8_t buf[]; // the actual data
} buf_impl;
/********************************************************************
** Base64 lib from https://github.com/Densaugeo/base64_arduino
**
********************************************************************/
/* binary_to_base64:
* Description:
* Converts a single byte from a binary value to the corresponding base64 character
* Parameters:
* v - Byte to convert
* Returns:
* ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character
* and 255 is returned
*/
static unsigned char binary_to_base64(unsigned char v);
/* base64_to_binary:
* Description:
* Converts a single byte from a base64 character to the corresponding binary value
* Parameters:
* c - Base64 character (as ascii code)
* Returns:
* 6-bit binary value
*/
static unsigned char base64_to_binary(unsigned char c);
/* encode_base64_length:
* Description:
* Calculates length of base64 string needed for a given number of binary bytes
* Parameters:
* input_length - Amount of binary data in bytes
* Returns:
* Number of base64 characters needed to encode input_length bytes of binary data
*/
static unsigned int encode_base64_length(unsigned int input_length);
/* decode_base64_length:
* Description:
* Calculates number of bytes of binary data in a base64 string
* Parameters:
* input - Base64-encoded null-terminated string
* Returns:
* Number of bytes of binary data in input
*/
static unsigned int decode_base64_length(unsigned char input[]);
/* encode_base64:
* Description:
* Converts an array of bytes to a base64 null-terminated string
* Parameters:
* input - Pointer to input data
* input_length - Number of bytes to read from input pointer
* output - Pointer to output string. Null terminator will be added automatically
* Returns:
* Length of encoded string in bytes (not including null terminator)
*/
static unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]);
/* decode_base64:
* Description:
* Converts a base64 null-terminated string to an array of bytes
* Parameters:
* input - Pointer to input string
* output - Pointer to output array
* Returns:
* Number of bytes in the decoded binary
*/
static unsigned int decode_base64(unsigned char input[], unsigned char output[]);
static unsigned char binary_to_base64(unsigned char v) {
// Capital letters - 'A' is ascii 65 and base64 0
if(v < 26) return v + 'A';
// Lowercase letters - 'a' is ascii 97 and base64 26
if(v < 52) return v + 71;
// Digits - '0' is ascii 48 and base64 52
if(v < 62) return v - 4;
// '+' is ascii 43 and base64 62
if(v == 62) return '+';
// '/' is ascii 47 and base64 63
if(v == 63) return '/';
return 64;
}
static unsigned char base64_to_binary(unsigned char c) {
// Capital letters - 'A' is ascii 65 and base64 0
if('A' <= c && c <= 'Z') return c - 'A';
// Lowercase letters - 'a' is ascii 97 and base64 26
if('a' <= c && c <= 'z') return c - 71;
// Digits - '0' is ascii 48 and base64 52
if('0' <= c && c <= '9') return c + 4;
// '+' is ascii 43 and base64 62
if(c == '+') return 62;
// '/' is ascii 47 and base64 63
if(c == '/') return 63;
return 255;
}
static unsigned int encode_base64_length(unsigned int input_length) {
return (input_length + 2)/3*4;
}
static unsigned int decode_base64_length(unsigned char input[]) {
unsigned char *start = input;
while(base64_to_binary(input[0]) < 64) {
++input;
}
unsigned int input_length = input - start;
unsigned int output_length = input_length/4*3;
switch(input_length % 4) {
default: return output_length;
case 2: return output_length + 1;
case 3: return output_length + 2;
}
}
static unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
unsigned int full_sets = input_length/3;
// While there are still full sets of 24 bits...
for(unsigned int i = 0; i < full_sets; ++i) {
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
output[3] = binary_to_base64( input[2] & 0x3F);
input += 3;
output += 4;
}
switch(input_length % 3) {
case 0:
output[0] = '\0';
break;
case 1:
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4);
output[2] = '=';
output[3] = '=';
output[4] = '\0';
break;
case 2:
output[0] = binary_to_base64( input[0] >> 2);
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
output[2] = binary_to_base64((input[1] & 0x0F) << 2);
output[3] = '=';
output[4] = '\0';
break;
}
return encode_base64_length(input_length);
}
static unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
unsigned int output_length = decode_base64_length(input);
// While there are still full sets of 24 bits...
for(unsigned int i = 2; i < output_length; i += 3) {
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);
input += 4;
output += 3;
}
switch(output_length % 3) {
case 1:
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
break;
case 2:
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
break;
}
return output_length;
}
/********************************************************************
** Buffer low-level implementation
**
@ -343,13 +535,26 @@ static size_t tohex(char * out, size_t outsz, const uint8_t * in, size_t insz) {
static int m_tostring(bvm *vm)
{
int argc = be_top(vm);
int max_len = 32; /* limit to 32 bytes by default */
int truncated = 0;
if (argc > 1 && be_isint(vm, 2)) {
max_len = be_toint(vm, 2); /* you can specify the len as second argument, or 0 for unlimited */
}
buf_impl * buf = bytes_check_data(vm, 0);
size_t len = buf->len;
size_t hex_len = len * 2 + 5 + 2 + 2 + 1; /* reserve size for `bytes("")\0` - 9 chars */
if (max_len > 0 && len > max_len) {
len = max_len; /* limit output size */
truncated = 1;
}
size_t hex_len = len * 2 + 5 + 2 + 2 + 1 + truncated * 3; /* reserve size for `bytes("")\0` - 9 chars */
char * hex_out = be_pushbuffer(vm, hex_len);
size_t l = be_strlcpy(hex_out, "bytes('", hex_len);
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), buf->len);
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), len);
if (truncated) {
l += be_strlcpy(&hex_out[l], "...", hex_len - l);
}
l += be_strlcpy(&hex_out[l], "')", hex_len - l);
be_pushnstring(vm, hex_out, l); /* make escape string from buffer */
@ -699,6 +904,56 @@ static int m_nequal(bvm *vm)
return bytes_equal(vm, bfalse);
}
/*
* Converts bytes() to a base64 string
*
* Note: there are no line breaks inserted
*
* `b.tob64() -> string`
*/
static int m_tob64(bvm *vm)
{
buf_impl * buf = bytes_check_data(vm, 0);
size_t len = buf->len;
size_t b64_len = encode_base64_length(len) + 1; /* size of base64 encoded string for this binary length, add NULL terminator */
char * b64_out = be_pushbuffer(vm, b64_len);
size_t converted = encode_base64(buf_get_buf(buf), len, b64_out);
be_pushnstring(vm, b64_out, converted); /* make string from buffer */
be_remove(vm, -2); /* remove buffer */
be_return(vm);
}
/*
* Converts base63 to bytes()
*
* `bytes().fromb64() -> bytes()`
*/
static int m_fromb64(bvm *vm)
{
int argc = be_top(vm);
if (argc >= 2 && be_isstring(vm, 2)) {
const char *s = be_tostring(vm, 2);
size_t len = be_strlen(vm, 2);
size_t bin_len = decode_base64_length(s); /* do a first pass to calculate the buffer size */
buf_impl * buf = bytes_check_data(vm, 0);
buf = bytes_resize(vm, buf, bin_len); /* resize if needed */
if (bin_len > buf->size) { /* avoid overflow */
be_raise(vm, "memory_error", "cannot allocate buffer");
}
size_t bin_len_final = decode_base64(s, buf_get_buf(buf)); /* decode */
buf->len = bin_len_final;
be_pop(vm, 1); /* remove arg to leave instance */
be_return(vm);
}
be_raise(vm, "type_error", "operand must be a string");
be_return_nil(vm);
}
/*
* Advanced API
*/
@ -973,6 +1228,8 @@ void be_load_byteslib(bvm *vm)
{ "tostring", m_tostring },
{ "asstring", m_asstring },
{ "fromstring", m_fromstring },
{ "tob64", m_tob64 },
{ "fromb64", m_fromb64 },
{ "add", m_add },
{ "get", m_getu },
{ "geti", m_geti },
@ -1006,6 +1263,8 @@ class be_class_bytes (scope: global, name: bytes) {
tostring, func(m_tostring)
asstring, func(m_asstring)
fromstring, func(m_fromstring)
tob64, func(m_tob64)
fromb64, func(m_fromb64)
add, func(m_add)
get, func(m_getu)
geti, func(m_geti)

View File

@ -178,6 +178,7 @@ void be_class_upvalue_init(bvm *vm, bclass *c)
}
}
/* (internal) Instanciate an instance for a single class and initialize variables to nil */
static binstance* newobjself(bvm *vm, bclass *c)
{
size_t size = sizeof(binstance) + sizeof(bvalue) * (c->nvar - 1);
@ -185,15 +186,17 @@ static binstance* newobjself(bvm *vm, bclass *c)
binstance *obj = cast_instance(gco);
be_assert(obj != NULL);
if (obj) { /* initialize members */
bvalue *v = obj->members, *end = v + c->nvar;
while (v < end) { var_setnil(v); ++v; }
obj->_class = c;
obj->super = NULL;
obj->sub = NULL;
bvalue *v = obj->members, *end = v + c->nvar; /* instance variables is a simple array of pointers at obj->members of size c->nvar */
while (v < end) { var_setnil(v); ++v; } /* Initialize all instance variables to `nil` */
obj->_class = c; /* set its class object */
obj->super = NULL; /* no super class instance for now */
obj->sub = NULL; /* no subclass instance for now */
}
return obj;
}
/* (internal) Instanciate the whole chain of instances when there is a class hierarchy */
/* All variables set to nil, constructors are not called here */
static binstance* newobject(bvm *vm, bclass *c)
{
binstance *obj, *prev;
@ -201,23 +204,26 @@ static binstance* newobject(bvm *vm, bclass *c)
obj = prev = newobjself(vm, c);
var_setinstance(vm->top, obj);
be_incrtop(vm); /* protect new objects from GC */
for (c = c->super; c; c = c->super) {
for (c = c->super; c; c = c->super) { /* initialize one instance object per class and per superclass */
prev->super = newobjself(vm, c);
prev->super->sub = prev;
prev->super->sub = prev; /* link the super/sub classes instances */
prev = prev->super;
}
be_stackpop(vm, 1);
return obj;
}
/* Instanciate new instance from stack with argc parameters */
/* Pushes the constructor on the stack to be executed if a construtor is found */
/* Returns true if a constructor is found */
bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
{
bvalue init;
size_t pos = reg - vm->reg;
binstance *obj = newobject(vm, c);
reg = vm->reg + pos - mode; /* the stack may have changed */
binstance *obj = newobject(vm, c); /* create empty object hierarchy from class hierarchy */
reg = vm->reg + pos - mode; /* the stack may have changed, mode=1 when class is instanciated from module #104 */
var_setinstance(reg, obj);
var_setinstance(reg + mode, obj);
var_setinstance(reg + mode, obj); /* copy to reg and reg+1 if mode==1 */
/* find constructor */
obj = instance_member(vm, obj, str_literal(vm, "init"), &init);
if (obj && var_type(&init) != MT_VARIABLE) {
@ -231,6 +237,10 @@ bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
return bfalse;
}
/* Find instance member by name and copy value to `dst` */
/* Input: none of `obj`, `name` and `dst` may not be NULL */
/* Returns the type of the member or BE_NONE if member not found */
/* TODO need to support synthetic members */
int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst)
{
int type;

View File

@ -23,8 +23,8 @@
#define min(a, b) ((a) < (b) ? (a) : (b))
#define notexpr(e) isset((e)->not, NOT_EXPR)
#define notmask(e) isset((e)->not, NOT_MASK)
#define exp2anyreg(f, e) exp2reg(f, e, (f)->freereg)
#define var2anyreg(f, e) var2reg(f, e, (f)->freereg)
#define exp2anyreg(f, e) exp2reg(f, e, -1) /* -1 means allocate a new register if needed */
#define var2anyreg(f, e) var2reg(f, e, -1) /* -1 means allocate a new register if needed */
#define hasjump(e) ((e)->t != (e)->f || notexpr(e))
#define code_bool(f, r, b, j) codeABC(f, OP_LDBOOL, r, b, j)
#define code_call(f, a, b) codeABC(f, OP_CALL, a, b, 0)
@ -56,6 +56,7 @@ static void codelineinfo(bfuncinfo *finfo)
#define codelineinfo(finfo)
#endif
/* Add new instruction in the code vector */
static int codeinst(bfuncinfo *finfo, binstruction ins)
{
/* put new instruction in code array */
@ -77,10 +78,13 @@ static int codeABx(bfuncinfo *finfo, bopcode op, int a, int bx)
return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx));
}
/* Move value from register b to register a */
/* Check the previous instruction to compact both instruction as one if possible */
/* If b is a constant, add LDCONST or add MOVE otherwise */
static void code_move(bfuncinfo *finfo, int a, int b)
{
if (finfo->pc) {
binstruction *i = be_vector_end(&finfo->code);
if (finfo->pc) { /* If not the first instruction of the function */
binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */
bopcode op = IGET_OP(*i);
if (op <= OP_LDNIL) { /* binop or unop */
/* remove redundant MOVE instruction */
@ -98,6 +102,8 @@ static void code_move(bfuncinfo *finfo, int a, int b)
}
}
/* Free register at top (checks that it´s a register) */
/* Warning: the register must be at top of stack */
static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
{
/* release temporary register */
@ -106,6 +112,8 @@ static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
}
}
/* Privat. Allocate `count` new registers on the stack and uptade proto´s max nstack accordingly */
/* Note: deallocate is simpler and handled by a macro */
static void allocstack(bfuncinfo *finfo, int count)
{
int nstack = finfo->freereg + count;
@ -117,6 +125,7 @@ static void allocstack(bfuncinfo *finfo, int count)
}
}
/* Allocate `count` registers at top of stack, update stack accordingly */
int be_code_allocregs(bfuncinfo *finfo, int count)
{
int base = finfo->freereg;
@ -227,6 +236,8 @@ void be_code_patchjump(bfuncinfo *finfo, int jmp)
patchlistaux(finfo, jmp, finfo->pc, finfo->pc);
}
/* Allocate new constant for value k */
/* If k is NULL then push `nil` value */
static int newconst(bfuncinfo *finfo, bvalue *k)
{
int idx = be_vector_count(&finfo->kvec);
@ -239,6 +250,8 @@ static int newconst(bfuncinfo *finfo, bvalue *k)
return idx;
}
/* Find constant by value and return constant number, or -1 if constant does not exist */
/* The search is linear and lilited to 50 elements for performance reasons */
static int findconst(bfuncinfo *finfo, bexpdesc *e)
{
int i, count = be_vector_count(&finfo->kvec);
@ -273,10 +286,11 @@ static int findconst(bfuncinfo *finfo, bexpdesc *e)
return -1;
}
/* convert expdesc to constant and return kreg index (either constant kindex or register number) */
static int exp2const(bfuncinfo *finfo, bexpdesc *e)
{
int idx = findconst(finfo, e);
if (idx == -1) {
int idx = findconst(finfo, e); /* does the constant already exist? */
if (idx == -1) { /* if not add it */
bvalue k;
switch (e->type) {
case ETINT:
@ -291,16 +305,16 @@ static int exp2const(bfuncinfo *finfo, bexpdesc *e)
k.type = BE_STRING;
k.v.s = e->v.s;
break;
default:
default: /* all other values are filled later */
break;
}
idx = newconst(finfo, &k);
idx = newconst(finfo, &k); /* create new constant */
}
if (idx < 256) {
e->type = ETCONST;
if (idx < 256) { /* if constant number fits in KB or KC */
e->type = ETCONST; /* new type is constant by index */
e->v.idx = setK(idx);
} else { /* index value is too large */
e->type = ETREG;
e->type = ETREG; /* does not fit in compact mode, allocate an explicit register and emit LDCONTS */
e->v.idx = be_code_allocregs(finfo, 1);
codeABx(finfo, OP_LDCONST, e->v.idx, idx);
}
@ -321,9 +335,38 @@ static void free_suffix(bfuncinfo *finfo, bexpdesc *e)
}
}
static int suffix_destreg(bfuncinfo *finfo, bexpdesc *e1, int dst)
{
int cand_dst = dst; /* candidate for new dst */
int nlocal = be_list_count(finfo->local);
int reg1 = (e1->v.ss.tt == ETREG) ? e1->v.ss.obj : -1; /* check if obj is ETREG or -1 */
int reg2 = (!isK(e1->v.ss.idx) && e1->v.ss.idx >= nlocal) ? e1->v.ss.idx : -1; /* check if idx is ETREG or -1 */
if (reg1 >= 0 && reg2 >= 0) {
/* both are ETREG, we keep the lowest and discard the other */
if (reg1 != reg2) {
cand_dst = min(reg1, reg2);
be_code_freeregs(finfo, 1); /* and free the other one */
} else {
cand_dst = reg1; /* both ETREG are equal, we return its value */
}
} else if (reg1 >= 0) {
cand_dst = reg1;
} else if (reg2 >= 0) {
cand_dst = reg2;
} else {
// dst unchanged
}
if (dst >= finfo->freereg) {
dst = cand_dst; /* if dst was allocating a new register, use the more precise candidate */
}
return dst;
}
static int code_suffix(bfuncinfo *finfo, bopcode op, bexpdesc *e, int dst)
{
free_suffix(finfo, e); /* free temporary registers */
dst = suffix_destreg(finfo, e, dst);
if (dst > finfo->freereg) {
dst = finfo->freereg;
}
@ -339,6 +382,9 @@ static void code_closure(bfuncinfo *finfo, int idx, int dst)
codeABx(finfo, OP_CLOSURE, dst, idx); /* load closure to register */
}
/* Given an integer, check if we should create a constant */
/* True for values 0..3 and if there is room for kindex */
/* This optimization makes code more compact for commonly used ints */
static bbool constint(bfuncinfo *finfo, bint i)
{
/* cache common numbers */
@ -349,8 +395,14 @@ static bbool constint(bfuncinfo *finfo, bint i)
return bfalse;
}
/* Compute variable from an expdesc */
/* Return constant index, or existing register or fallback to dst */
/* At exit, If dst is `freereg`, the register is allocated */
static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{
if (dst < 0) { /* if unspecified, allocate a new register if needed */
dst = finfo->freereg;
}
be_assert(e != NULL);
switch (e->type) {
case ETINT:
@ -403,7 +455,7 @@ static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{
int reg = var2reg(finfo, e, dst);
if (hasjump(e)) {
if (hasjump(e)) { /* if conditional expression */
int pcf = NO_JUMP; /* position of an eventual LOAD false */
int pct = NO_JUMP; /* position of an eventual LOAD true */
int jpt = appendjump(finfo, jumpboolop(e, 1), e);
@ -420,31 +472,47 @@ static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
return reg;
}
static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
/* Select dest registers from both expressions */
/* If one of them is already a register, keep it */
/* If e1 or e2 are registers, we keep the lowest and free the highest (that must be at top) */
/* If none is a register, allocate a new one */
/* Returns the destination register, guaranteed to be ETREG */
static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, int dst)
{
int dst, con1 = e1->type == ETREG, con2 = e2->type == ETREG;
int cand_dst = dst;
int con1 = e1->type == ETREG, con2 = e2->type == ETREG;
if (con1 && con2) {
dst = min(e1->v.idx, e2->v.idx);
cand_dst = min(e1->v.idx, e2->v.idx);
be_code_freeregs(finfo, 1);
} else if (con1) {
dst = e1->v.idx;
cand_dst = e1->v.idx;
} else if (con2) {
dst = e2->v.idx;
cand_dst = e2->v.idx;
} else {
dst = be_code_allocregs(finfo, 1);
if (dst >= finfo->freereg) {
cand_dst = be_code_allocregs(finfo, 1);
return cand_dst;
}
}
if (dst >= finfo->freereg) {
return cand_dst;
} else {
return dst;
}
}
static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2)
/* compute binary expression and update e1 as result */
/* On exit, e1 is guaranteed to be ETREG, which may have been allocated */
static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2, int dst)
{
int src1 = exp2anyreg(finfo, e1);
if (dst < 0) { dst = finfo->freereg; }
int src1 = exp2reg(finfo, e1, dst); /* potentially force the target for src1 reg */
int src2 = exp2anyreg(finfo, e2);
int dst = codedestreg(finfo, e1, e2);
dst = codedestreg(finfo, e1, e2, dst);
codeABC(finfo, op, dst, src1, src2);
e1->type = ETREG;
e1->v.idx = dst;
e1->v.idx = dst; /* update register as output */
}
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
@ -462,7 +530,8 @@ void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
}
}
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2)
/* Apply binary operator `op` to e1 and e2, result in e1 */
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst)
{
switch (op) {
case OptAnd:
@ -480,12 +549,14 @@ void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2)
case OptNE: case OptGT: case OptGE: case OptConnect:
case OptBitAnd: case OptBitOr: case OptBitXor:
case OptShiftL: case OptShiftR:
binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2);
binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2, dst);
break;
default: break;
}
}
/* Apply unary operator and return register number */
/* If input is register, change in place or allocate new register */
static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
{
int src = exp2anyreg(finfo, e);
@ -495,6 +566,9 @@ static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
e->v.idx = dst;
}
/* Apply not to conditional expression */
/* If literal compute the value */
/* Or invert t/f subexpressions */
static void code_not(bexpdesc *e)
{
switch (e->type) {
@ -514,6 +588,7 @@ static void code_not(bexpdesc *e)
e->type = ETBOOL;
}
/* Negative value of literal or emit NEG opcode */
static int code_neg(bfuncinfo *finfo, bexpdesc *e)
{
switch (e->type) {
@ -527,6 +602,7 @@ static int code_neg(bfuncinfo *finfo, bexpdesc *e)
return 0;
}
/* Bit flip of literal or emit FLIP opcode */
static int code_flip(bfuncinfo *finfo, bexpdesc *e)
{
switch (e->type) {
@ -539,6 +615,7 @@ static int code_flip(bfuncinfo *finfo, bexpdesc *e)
return 0;
}
/* Apply unary operator: not, neg or bitflip */
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e)
{
switch (op) {
@ -583,24 +660,28 @@ static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src)
codeABC(finfo, op, obj, e1->v.ss.idx, src);
}
/* Assign expr e2 to e1 */
/* e1 must be in a register and have a valid idx */
/* return 1 if assignment was possible, 0 if type is not compatible */
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
int src = exp2reg(finfo, e2,
e1->type == ETLOCAL ? e1->v.idx : finfo->freereg);
e1->type == ETLOCAL ? e1->v.idx : -1); /* Convert e2 to kreg */
/* If e1 is a local variable, use the register */
if (e1->type != ETLOCAL || e1->v.idx != src) {
free_expreg(finfo, e2); /* free source (only ETREG) */
free_expreg(finfo, e2); /* free source (checks only ETREG) */ /* TODO e2 is at top */
}
switch (e1->type) {
case ETLOCAL: /* It can't be ETREG. */
if (e1->v.idx != src) {
code_move(finfo, e1->v.idx, src);
code_move(finfo, e1->v.idx, src); /* do explicit move only if needed */
}
break;
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) */
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) by global index */
setsupvar(finfo, OP_SETGBL, e1, src);
break;
case ETNGLOBAL: /* store to grobal R(A) -> G(Bx) */
case ETNGLOBAL: /* store to global R(A) -> G(Bx) by name */
setbgblvar(finfo, OP_SETNGBL, e1, src);
break;
case ETUPVAL:
@ -618,6 +699,9 @@ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
return 0;
}
/* Get the expdesc as a register */
/* if already in a register, use the existing register */
/* if local or const, allocate a new register and copy value */
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e)
{
int dst = finfo->freereg;
@ -641,6 +725,9 @@ int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e)
return dst;
}
/* Generate a CALL instruction at base register with argc consecutive values */
/* i.e. arg1 is base+1... */
/* Important: argc registers are freed upon call, which are supposed to be registers above base */
void be_code_call(bfuncinfo *finfo, int base, int argc)
{
codeABC(finfo, OP_CALL, base, argc, 0);
@ -717,6 +804,8 @@ void be_code_ret(bfuncinfo *finfo, bexpdesc *e)
}
}
/* Package a suffix object from `c` with key `k` */
/* Both expdesc are materialized in kregs */
static void package_suffix(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
int key = exp2anyreg(finfo, k);
@ -730,12 +819,14 @@ int be_code_nglobal(bfuncinfo *finfo, bexpdesc *k)
return exp2anyreg(finfo, k);
}
/* Package a MEMBER suffix object from `c` with key `k` */
void be_code_member(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
package_suffix(finfo, c, k);
c->type = ETMEMBER;
}
/* Package a INDEX suffix object from `c` with key `k` */
void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
package_suffix(finfo, c, k);
@ -746,15 +837,15 @@ void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c)
{
int src;
bvalue var;
var_setclass(&var, c);
src = newconst(finfo, &var);
if (dst->type == ETLOCAL) {
var_setclass(&var, c); /* new var of CLASS type */
src = newconst(finfo, &var); /* allocate a new constant and return kreg */
if (dst->type == ETLOCAL) { /* if target is a local variable, just assign */
codeABx(finfo, OP_LDCONST, dst->v.idx, src);
} else {
} else { /* otherwise set as global with same name as class name */
codeABx(finfo, OP_LDCONST, finfo->freereg, src);
codeABx(finfo, OP_SETGBL, finfo->freereg, dst->v.idx);
}
codeABx(finfo, OP_CLASS, 0, src);
codeABx(finfo, OP_CLASS, 0, src); /* emit CLASS opcode to register class */
}
void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
@ -766,6 +857,12 @@ void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
free_expreg(finfo, s);
}
/* Emit IMPORT opcode for import module */
/* `m` is module name, is copied to register if not already */
/* `v` is destination where the imported module is stored */
/* If destination is a local variable, it is the destination of the IMPORT opcode */
/* otherwise the value is copied to a temporary register and stored to the destination */
/* TODO is this optilization useful, isn´t it done anyways by be_code_move optim? */
void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v)
{
int dst, src = exp2anyreg(finfo, m);
@ -799,6 +896,10 @@ void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp)
}
}
/* Emit RAISE opcode */
/* e1 is the exception code */
/* e2 is the exception description */
/* both are materialized to a temp register (if not null) */
void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
if (e1) {
@ -806,7 +907,7 @@ void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
int src2 = e2 ? exp2anyreg(finfo, e2) : 0;
codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2);
} else {
codeABC(finfo, OP_RAISE, 2, 0, 0);
codeABC(finfo, OP_RAISE, 2, 0, 0); /* rethrow the current exception with parameters already on top of stack */
}
/* release the register occupied by the expression */
free_expreg(finfo, e1);

View File

@ -14,7 +14,7 @@
int be_code_allocregs(bfuncinfo *finfo, int count);
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e);
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2);
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst);
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e);
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2);
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e);

View File

@ -82,6 +82,8 @@ void be_throw(bvm *vm, int errorcode)
}
}
/* Fatal error Exit */
/* Raise a BE_EXIT exception if within a try/catch block, or exit VM */
BERRY_API void be_exit(bvm *vm, int status)
{
if (vm->errjmp) {
@ -99,6 +101,8 @@ void be_throw_message(bvm *vm, int errorcode, const char *msg)
be_throw(vm, errorcode);
}
/* Exec protected: exec function and capture any exception and contain it within call */
/* Exceptions or fatal errors are not propagated */
int be_execprotected(bvm *vm, bpfunc f, void *data)
{
struct blongjmp jmp;
@ -292,6 +296,7 @@ static void m_pcall(bvm *vm, void *data)
be_dofunc(vm, p->v, p->argc);
}
/* Protected call: contain any exception of fatal error and restore context if something went wrong */
int be_protectedcall(bvm *vm, bvalue *v, int argc)
{
int res;
@ -308,7 +313,8 @@ int be_protectedcall(bvm *vm, bvalue *v, int argc)
}
#if BE_DEBUG && defined(be_assert)
/* increase top register */
/* increase top register and return new top */
/* Does not expand the stack if there is not enough room, but may corrupt memory */
bvalue* be_incrtop(bvm *vm)
{
bvalue *top = vm->top++;
@ -317,6 +323,7 @@ bvalue* be_incrtop(bvm *vm)
}
#endif
/* TODO what is the difference with be_stack_push? */
void be_stackpush(bvm *vm)
{
/* make sure there is enough stack space */
@ -324,6 +331,7 @@ void be_stackpush(bvm *vm)
be_incrtop(vm);
}
/* check that the stack is able to store `count` items, and increase stack if needed */
void be_stack_require(bvm *vm, int count)
{
if (vm->top + count >= vm->stacktop) {
@ -331,6 +339,7 @@ void be_stack_require(bvm *vm, int count)
}
}
/* Scan the entire callstack and adjust all pointer by `offset` */
static void update_callstack(bvm *vm, intptr_t offset)
{
bcallframe *cf = be_stack_top(&vm->callstack);
@ -353,20 +362,25 @@ static void update_upvalues(bvm *vm, intptr_t offset)
}
}
/* Resize the stack to new `size` as number of elements */
/* Then update all pointers in callstack and upvalues with the new stack address */
static void stack_resize(bvm *vm, size_t size)
{
intptr_t offset;
bvalue *old = vm->stack;
size_t os = (vm->stacktop - old) * sizeof(bvalue);
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size);
vm->stacktop = vm->stack + size;
offset = ptr_offset(vm->stack, old);
bvalue *old = vm->stack; /* save original pointer of stack before resize */
size_t os = (vm->stacktop - old) * sizeof(bvalue); /* size of current stack allocated in bytes */
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size); /* reallocate with the new size */
vm->stacktop = vm->stack + size; /* compute new stacktop */
offset = ptr_offset(vm->stack, old); /* compute the address difference between old and ne stack addresses */
/* update callframes */
update_callstack(vm, offset);
/* update open upvalues */
update_upvalues(vm, offset);
}
/* Stack resize internal API */
/* Increases the stack by `n` elements, reallocate stack if needed and update all callstacks and upvals */
/* Check if we are above the max allowed stack */
void be_stack_expansion(bvm *vm, int n)
{
size_t size = vm->stacktop - vm->stack;

View File

@ -83,4 +83,4 @@ module global (scope: global, depend: BE_USE_GLOBAL_MODULE) {
#include "../generate/be_fixed_global.h"
#endif
#endif /* BE_USE_INTROSPECT_MODULE */
#endif /* BE_USE_GLOBAL_MODULE */

View File

@ -252,6 +252,16 @@ static void cache_module(bvm *vm, bstring *name)
*v = vm->top[-1];
}
/* Try to run '()' function of module. Module is already loaded. */
static void module_init(bvm *vm) {
if (be_getmember(vm, -1, "init")) {
/* found, call it with no parameter */
be_call(vm, 0);
/* we don't care about the result */
}
be_pop(vm, 1);
}
/* load module to vm->top */
int be_module_load(bvm *vm, bstring *path)
{
@ -260,8 +270,11 @@ int be_module_load(bvm *vm, bstring *path)
res = load_native(vm, path);
if (res == BE_IO_ERROR)
res = load_package(vm, path);
if (res == BE_OK)
if (res == BE_OK) {
cache_module(vm, path);
/* on first load of the module, try running the '()' function */
module_init(vm);
}
}
return res;
}

View File

@ -51,7 +51,7 @@ OPCODE(CLOSE), /* A | close upvalues */
OPCODE(IMPORT), /* A, B, C | IF (A == C) import module name as RK(B) to RK(A), ELSE from module RK(C) import name as RK(B) to RK(A) */
OPCODE(EXBLK), /* A, Bx | ... */
OPCODE(CATCH), /* A, B, C | ... */
OPCODE(RAISE), /* A, B, C | ... */
OPCODE(RAISE), /* A, B, C | RAISE(B,C) B is code, C is description. A==0 only B provided, A==1 B and C are provided, A==2 rethrow with both parameters already on stack */
OPCODE(CLASS), /* Bx | init class in K[Bx] */
OPCODE(GETNGBL), /* A, B | R(A) <- GLOBAL[B] by name */
OPCODE(SETNGBL) /* A, B | R(A) -> GLOBAL[B] by name */

View File

@ -87,6 +87,8 @@ static void match_token(bparser *parser, btokentype type)
scan_next_token(parser); /* skip this token */
}
/* Check that the next token is not of type `type` */
/* or raise an exception */
static void match_notoken(bparser *parser, btokentype type)
{
if (next_type(parser) == type) { /* error when token is match */
@ -95,6 +97,7 @@ static void match_notoken(bparser *parser, btokentype type)
}
}
/* check that if the expdesc is a symbol, it is avalid one or raise an exception */
static void check_symbol(bparser *parser, bexpdesc *e)
{
if (e->type == ETVOID && e->v.s == NULL) { /* error when token is not a symbol */
@ -103,6 +106,7 @@ static void check_symbol(bparser *parser, bexpdesc *e)
}
}
/* check that the value in `e` is valid for a variable, i.e. conatins a value or a valid symbol */
static void check_var(bparser *parser, bexpdesc *e)
{
check_symbol(parser, e); /* check the token is a symbol */
@ -172,14 +176,15 @@ void end_varinfo(bparser *parser, int beginpc)
#endif
/* Initialize bblockinfo structure */
static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type)
{
binfo->prev = finfo->binfo;
finfo->binfo = binfo;
binfo->prev = finfo->binfo; /* save previous block */
finfo->binfo = binfo; /* tell parser this is the current block */
binfo->type = (bbyte)type;
binfo->hasupval = 0;
binfo->beginpc = finfo->pc;
binfo->nactlocals = (bbyte)be_list_count(finfo->local);
binfo->beginpc = finfo->pc; /* set starting pc for this block */
binfo->nactlocals = (bbyte)be_list_count(finfo->local); /* count number of local variables in previous block */
if (type & BLOCK_LOOP) {
binfo->breaklist = NO_JUMP;
binfo->continuelist = NO_JUMP;
@ -197,9 +202,9 @@ static void end_block_ex(bparser *parser, int beginpc)
be_code_patchlist(finfo, binfo->continuelist, binfo->beginpc);
}
end_varinfo(parser, beginpc);
be_list_resize(parser->vm, finfo->local, binfo->nactlocals);
finfo->freereg = binfo->nactlocals;
finfo->binfo = binfo->prev;
be_list_resize(parser->vm, finfo->local, binfo->nactlocals); /* remove local variables from this block, they are now out of scope */
finfo->freereg = binfo->nactlocals; /* adjust first free register accordingly */
finfo->binfo = binfo->prev; /* restore previous block */
}
static void end_block(bparser *parser)
@ -207,6 +212,8 @@ static void end_block(bparser *parser)
end_block_ex(parser, -1);
}
/* Return the name of the source for this parser, could be `string`,
`stdin` or the name of the current function */
static bstring* parser_source(bparser *parser)
{
if (parser->finfo) {
@ -215,29 +222,30 @@ static bstring* parser_source(bparser *parser)
return be_newstr(parser->vm, parser->lexer.fname);
}
/* Initialize a function block and create a new `bprotoˋ */
static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
{
bvm *vm = parser->vm;
bproto *proto = be_newproto(vm);
var_setproto(vm->top, proto);
be_stackpush(vm);
be_vector_init(vm, &finfo->code, sizeof(binstruction));
be_vector_init(vm, &finfo->code, sizeof(binstruction)); /* vector for code, vectors are not gced */
proto->code = be_vector_data(&finfo->code);
proto->codesize = be_vector_capacity(&finfo->code);
be_vector_init(vm, &finfo->kvec, sizeof(bvalue));
be_vector_init(vm, &finfo->kvec, sizeof(bvalue)); /* vector for constants */
proto->ktab = be_vector_data(&finfo->kvec);
proto->nconst = be_vector_capacity(&finfo->kvec);
be_vector_init(vm, &finfo->pvec, sizeof(bproto*));
be_vector_init(vm, &finfo->pvec, sizeof(bproto*)); /* vector for subprotos */
proto->ptab = be_vector_data(&finfo->pvec);
proto->nproto = be_vector_capacity(&finfo->pvec);
proto->source = parser_source(parser);
finfo->local = be_list_new(vm);
var_setlist(vm->top, finfo->local);
proto->source = parser_source(parser); /* keep a copy of source for function */
finfo->local = be_list_new(vm); /* list for local variables */
var_setlist(vm->top, finfo->local); /* push list of local variables on the stack (avoid gc) */
be_stackpush(vm);
finfo->upval = be_map_new(vm);
finfo->upval = be_map_new(vm); /* push a map for upvals on stack */
var_setmap(vm->top, finfo->upval);
be_stackpush(vm);
finfo->prev = parser->finfo;
finfo->prev = parser->finfo; /* init finfo */
finfo->lexer = &parser->lexer;
finfo->proto = proto;
finfo->freereg = 0;
@ -258,6 +266,7 @@ static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
begin_block(finfo, binfo, 0);
}
/* compute the upval structure */
static void setupvals(bfuncinfo *finfo)
{
bproto *proto = finfo->proto;
@ -282,6 +291,7 @@ static void setupvals(bfuncinfo *finfo)
}
}
/* Function is complete, finalize bproto */
static void end_func(bparser *parser)
{
bvm *vm = parser->vm;
@ -289,9 +299,9 @@ static void end_func(bparser *parser)
bproto *proto = finfo->proto;
be_code_ret(finfo, NULL); /* append a return to last code */
end_block(parser);
setupvals(finfo);
proto->code = be_vector_release(vm, &finfo->code);
end_block(parser); /* close block */
setupvals(finfo); /* close upvals */
proto->code = be_vector_release(vm, &finfo->code); /* compact all vectors and return NULL if empty */
proto->codesize = finfo->pc;
proto->ktab = be_vector_release(vm, &finfo->kvec);
proto->nconst = be_vector_count(&finfo->kvec);
@ -305,10 +315,11 @@ static void end_func(bparser *parser)
proto->varinfo = be_vector_release(vm, &finfo->varvec);
proto->nvarinfo = be_vector_count(&finfo->varvec);
#endif
parser->finfo = parser->finfo->prev;
parser->finfo = parser->finfo->prev; /* restore previous `finfo` */
be_stackpop(vm, 2); /* pop upval and local */
}
/* is the next token a binary operator? If yes return the operator or `OP_NOT_BINARY` */
static btokentype get_binop(bparser *parser)
{
btokentype op = next_type(parser);
@ -318,6 +329,8 @@ static btokentype get_binop(bparser *parser)
return OP_NOT_BINARY;
}
/* is the next token a unary operator? If yes return the operator or `OP_NOT_BINARY` */
/* operator 'negative' 'not' and 'flip' */
static btokentype get_unary_op(bparser *parser)
{
btokentype op = next_type(parser);
@ -327,6 +340,8 @@ static btokentype get_unary_op(bparser *parser)
return OP_NOT_UNARY;
}
/* is the next token an assignment operator? If yes return the operator or `OP_NOT_BINARY` */
/* `=`, `+=`, ... `>>=` */
static btokentype get_assign_op(bparser *parser)
{
btokentype op = next_type(parser);
@ -336,6 +351,7 @@ static btokentype get_assign_op(bparser *parser)
return OP_NOT_ASSIGN;
}
/* Initialize bexpdesc structure with specific type and value as int */
static void init_exp(bexpdesc *e, exptype_t type, bint i)
{
e->type = (bbyte)type;
@ -346,6 +362,8 @@ static void init_exp(bexpdesc *e, exptype_t type, bint i)
e->v.i = i;
}
/* find local variable by name, starting at index `begin` */
/* linear search, returns -1 if not found */
static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
{
int i, count = be_list_count(finfo->local);
@ -358,12 +376,22 @@ static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
return -1; /* not found */
}
/* create a new local variable by name, or return the current register if already exists */
/* returns the Reg number for the variable */
/* If STRICT, we don't allow a var to hide a var from outer scope */
/* We don't allow the same var to be defined twice in same scope */
static int new_localvar(bparser *parser, bstring *name)
{
bfuncinfo *finfo = parser->finfo;
int reg = find_localvar(finfo, name, finfo->binfo->nactlocals);
int reg = find_localvar(finfo, name, finfo->binfo->nactlocals); /* look if already exists skipping the local variables from upper blocks */
/* 'strict': raise an exception if the local variable shadows another local variable */
if (reg == -1) {
bvalue *var;
if (comp_is_strict(parser->vm)) {
if (find_localvar(finfo, name, 0) >= 0 && str(name)[0] != '.') { /* we do accept nested redifinition of internal variables starting with ':' */
push_error(parser, "strict: redefinition of '%s' from outer scope", str(name));
}
}
reg = be_list_count(finfo->local); /* new local index */
var = be_list_push(parser->vm, finfo->local, NULL);
var_setstr(var, name);
@ -371,10 +399,13 @@ static int new_localvar(bparser *parser, bstring *name)
be_code_allocregs(finfo, 1); /* use a register */
}
begin_varinfo(parser, name);
} else {
push_error(parser, "redefinition of '%s'", str(name));
}
return reg;
}
/* Find upval by name, if found return its index number, or -1 */
static int find_upval(bfuncinfo *finfo, bstring *s)
{
bvm *vm = finfo->lexer->vm;
@ -385,6 +416,8 @@ static int find_upval(bfuncinfo *finfo, bstring *s)
return -1;
}
/* Recursively search for upper blocks that are referenced in upvals */
/* and mark them with `hasupval` */
static void mark_upval(bfuncinfo *finfo, int level)
{
bblockinfo *binfo = finfo->prev->binfo;
@ -413,12 +446,14 @@ static int new_upval(bvm *vm, bfuncinfo *finfo, bstring *name, bexpdesc *var)
return index;
}
/* create a new variable in currenr context as name, and create expdesc for it */
/* If within a block, create as local, otherwise as global */
static void new_var(bparser *parser, bstring *name, bexpdesc *var)
{
bfuncinfo *finfo = parser->finfo;
if (finfo->prev || finfo->binfo->prev || parser->islocal) {
init_exp(var, ETLOCAL, 0);
var->v.idx = new_localvar(parser, name);
var->v.idx = new_localvar(parser, name); /* if local, contains the index in current local var list */
} else {
init_exp(var, ETGLOBAL, 0);
var->v.idx = be_global_new(parser->vm, name);
@ -465,6 +500,9 @@ static int singlevaraux(bvm *vm, bfuncinfo *finfo, bstring *s, bexpdesc *var)
}
}
/* get variable from next toden as name */
/* and create an expdesc from it */
/* can be new, global, named global, upval */
static void singlevar(bparser *parser, bexpdesc *var)
{
bexpdesc key;
@ -490,6 +528,10 @@ static void singlevar(bparser *parser, bexpdesc *var)
}
}
/* Parse function or method definition variable list */
/* Create an implicit local variable for each argument starting at R0 */
/* Update function proto argc to the expected number or arguments */
/* Raise an exception if multiple arguments have the same name */
static void func_varlist(bparser *parser)
{
bexpdesc v;
@ -502,36 +544,36 @@ static void func_varlist(bparser *parser)
str = next_token(parser).u.s;
match_token(parser, TokenId); /* match and skip ID */
/* new local variable */
if (find_localvar(parser->finfo, str, 0) == -1) {
new_var(parser, str, &v);
} else {
push_error(parser, "redefinition of '%s'", str(str));
}
}
}
match_token(parser, OptRBK); /* skip ')' */
parser->finfo->proto->argc = parser->finfo->freereg;
}
/* Parse a function includind arg list and body */
/* Given name and type (function or method) */
/* Returns `bproto` object */
static bproto* funcbody(bparser *parser, bstring *name, int type)
{
bfuncinfo finfo;
bblockinfo binfo;
/* '(' varlist ')' block 'end' */
begin_func(parser, &finfo, &binfo);
begin_func(parser, &finfo, &binfo); /* init new function context */
finfo.proto->name = name;
if (type & FUNC_METHOD) {
if (type & FUNC_METHOD) { /* If method, add an implicit first argument `self` */
new_localvar(parser, parser_newstr(parser, "self"));
}
func_varlist(parser);
stmtlist(parser);
end_func(parser);
func_varlist(parser); /* parse arg list */
stmtlist(parser); /* parse statement without final `end` */
end_func(parser); /* close function context */
match_token(parser, KeyEnd); /* skip 'end' */
return finfo.proto;
return finfo.proto; /* return fully constructed `bproto` */
}
/* anonymous function */
/* anonymous function, build `bproto` object with name `<anonymous>` */
/* and build a expdesc for the bproto */
static void anon_func(bparser *parser, bexpdesc *e)
{
bproto *proto;
@ -559,11 +601,7 @@ static void lambda_varlist(bparser *parser)
str = next_token(parser).u.s;
match_token(parser, TokenId); /* match and skip ID */
/* new local variable */
if (find_localvar(parser->finfo, str, 0) == -1) {
new_var(parser, str, &v);
} else {
push_error(parser, "redefinition of '%s'", str(str));
}
}
}
match_token(parser, OptArrow); /* skip '->' */
@ -590,6 +628,9 @@ static void lambda_expr(bparser *parser, bexpdesc *e)
be_stackpop(parser->vm, 1);
}
/* Instanciate a builtin type by name */
/* Allocates a new register for the value, and call empty constructor */
/* Is allocated as LOCAL and must be changed to REG when completed */
static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
{
int idx;
@ -601,17 +642,19 @@ static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
init_exp(e, ETGLOBAL, idx);
idx = be_code_nextreg(finfo, e);
be_code_call(finfo, idx, 0);
e->type = ETLOCAL;
e->type = ETLOCAL; /* declare as local, will be changed to ETREG when completely initialized */
}
/* Parse next member within a list */
/* `l` contains the current list. The expr is evaluated and added to the list */
static void list_nextmember(bparser *parser, bexpdesc *l)
{
bexpdesc e, v = *l;
bfuncinfo *finfo = parser->finfo;
expr(parser, &e); /* value */
check_var(parser, &e);
be_code_binop(finfo, OptConnect, &v, &e);
be_code_freeregs(finfo, 1);
check_var(parser, &e); /* check that we don´t have an unknown symbol */
be_code_binop(finfo, OptConnect, &v, &e, -1); /* add it to list with CONNECT */
be_code_freeregs(finfo, 1); /* since left arg is LOCAL, an ETREG was allocated. Free it */
}
static void map_nextmember(bparser *parser, bexpdesc *l)
@ -619,25 +662,25 @@ static void map_nextmember(bparser *parser, bexpdesc *l)
bexpdesc e, v = *l;
bfuncinfo *finfo = parser->finfo;
expr(parser, &e); /* key */
check_var(parser, &e);
be_code_index(finfo, &v, &e);
check_var(parser, &e); /* check if value is valid */
be_code_index(finfo, &v, &e); /* package as `v` as INDEX suffix for value `e` */
match_token(parser, OptColon); /* ':' */
expr(parser, &e); /* value */
check_var(parser, &e);
be_code_setvar(finfo, &v, &e);
expr(parser, &e); /* value in `e` */
check_var(parser, &e); /* check if value is correct */
be_code_setvar(finfo, &v, &e); /* set suffi INDEX value to e */
}
static void list_expr(bparser *parser, bexpdesc *e)
{
/* '[' {expr ','} [expr] ']' */
new_primtype(parser, "list", e); /* new list */
new_primtype(parser, "list", e); /* new list, created as LOCAL first */
while (next_type(parser) != OptRSB) {
list_nextmember(parser, e);
if (!match_skip(parser, OptComma)) { /* ',' */
break;
}
}
e->type = ETREG;
e->type = ETREG; /* then turned to REG */
match_token(parser, OptRSB); /* skip ']' */
}
@ -655,14 +698,16 @@ static void map_expr(bparser *parser, bexpdesc *e)
match_token(parser, OptRBR); /* skip '}' */
}
/* push each argument as new reg and return number of args */
/* TODO `e` is ignored by caller */
static int exprlist(bparser *parser, bexpdesc *e)
{
bfuncinfo *finfo = parser->finfo;
int n = 1;
/* expr { ',' expr } */
expr(parser, e);
check_var(parser, e);
be_code_nextreg(finfo, e);
expr(parser, e); /* parse expr */
check_var(parser, e); /* check if valid */
be_code_nextreg(finfo, e); /* move result to next reg */
while (match_skip(parser, OptComma)) { /* ',' */
expr(parser, e);
check_var(parser, e);
@ -672,6 +717,9 @@ static int exprlist(bparser *parser, bexpdesc *e)
return n;
}
/* parse call to method or function */
/* `e` can be a member (method) or a register */
/* On return, `e` is ETREG to the result of the call */
static void call_expr(bparser *parser, bexpdesc *e)
{
bexpdesc args;
@ -685,14 +733,15 @@ static void call_expr(bparser *parser, bexpdesc *e)
if (ismember) {
base = be_code_getmethod(finfo, e);
} else {
base = be_code_nextreg(finfo, e);
base = be_code_nextreg(finfo, e); /* allocate a new base reg if not at top already */
}
/* base is always taken at top of freereg and allocates 1 reg for function and 2 regs for method */
scan_next_token(parser); /* skip '(' */
if (next_type(parser) != OptRBK) {
argc = exprlist(parser, &args);
if (next_type(parser) != OptRBK) { /* if arg list is not empty */
argc = exprlist(parser, &args); /* push each argument as new reg and return number of args */
}
match_token(parser, OptRBK); /* skip ')' */
argc += ismember;
argc += ismember; /* if method there is an additional implicit arg */
be_code_call(finfo, base, argc);
if (e->type != ETREG) {
e->type = ETREG;
@ -700,6 +749,8 @@ static void call_expr(bparser *parser, bexpdesc *e)
}
}
/* Parse member expression */
/* Generates an ETMEMBER object that is materialized later into GETMBR, GETMET or SETMBR */
static void member_expr(bparser *parser, bexpdesc *e)
{
bstring *str;
@ -823,9 +874,11 @@ static void suffix_alloc_reg(bparser *parser, bexpdesc *l)
/* compound assignment */
static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r)
{
int dst = -1; /* destination register in case of compound assignment */
if (op != OptAssign) { /* check left variable */
check_var(parser, l);
/* cache the register of the object when continuously assigning */
dst = parser->finfo->freereg;
suffix_alloc_reg(parser, l);
}
expr(parser, r); /* right expression */
@ -834,20 +887,37 @@ static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r)
bexpdesc e = *l;
op = op < OptAndAssign ? op - OptAddAssign + OptAdd
: op - OptAndAssign + OptBitAnd;
be_code_binop(parser->finfo, op, &e, r); /* coding operation */
be_code_binop(parser->finfo, op, &e, r, dst); /* coding operation */
*r = e;
}
}
/* check if we need to create a new local variable with this name to be assigned to */
/* Returns true if it´s a new local variable */
/* A new implicit local variable is created if no global has the same name (excluding builtins) */
/* This means that you can override a builtin silently */
/* This also means that a function cannot create a global, they must preexist or create with `global` module */
/* TODO add warning in strict mode */
static int check_newvar(bparser *parser, bexpdesc *e)
{
if (e->type == ETGLOBAL) {
if (e->v.idx < be_builtin_count(parser->vm)) {
e->v.s = be_builtin_name(parser->vm, e->v.idx);
if (comp_is_strict(parser->vm)) {
push_error(parser, "strict: redefinition of builtin '%s'",
str(e->v.s));
}
return btrue;
}
return bfalse;
}
if (comp_is_strict(parser->vm)) {
bfuncinfo *finfo = parser->finfo;
if ((e->type == ETVOID) && (finfo->prev || finfo->binfo->prev || parser->islocal)) {
push_error(parser, "strict: no global '%s', did you mean 'var %s'?",
str(e->v.s), str(e->v.s));
}
}
return e->type == ETVOID;
}
@ -913,39 +983,42 @@ static void cond_expr(bparser *parser, bexpdesc *e)
static void sub_expr(bparser *parser, bexpdesc *e, int prio)
{
bfuncinfo *finfo = parser->finfo;
btokentype op = get_unary_op(parser);
if (op != OP_NOT_UNARY) {
btokentype op = get_unary_op(parser); /* check if first token in unary op */
if (op != OP_NOT_UNARY) { /* unary op found */
int line, res;
scan_next_token(parser);
line = parser->lexer.linenumber;
sub_expr(parser, e, UNARY_OP_PRIO);
check_var(parser, e);
res = be_code_unop(finfo, op, e);
scan_next_token(parser); /* move to next token */
line = parser->lexer.linenumber; /* remember line number for error reporting */
sub_expr(parser, e, UNARY_OP_PRIO); /* parse subexpr with new prio */
check_var(parser, e); /* check that the value is ok */
res = be_code_unop(finfo, op, e); /* apply unary op with optimizations if the token is a value */
if (res) { /* encode unary op */
parser->lexer.linenumber = line;
push_error(parser, "wrong type argument to unary '%s'",
res == 1 ? "negative" : "bit-flip");
}
} else {
suffix_expr(parser, e);
suffix_expr(parser, e); /* parse left part of binop */
}
op = get_binop(parser);
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) {
op = get_binop(parser); /* check if binop */
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) { /* is binop applicable */
bexpdesc e2;
check_var(parser, e);
scan_next_token(parser);
check_var(parser, e); /* check that left part is valid */
scan_next_token(parser); /* move to next token */
be_code_prebinop(finfo, op, e); /* and or */
init_exp(&e2, ETVOID, 0);
sub_expr(parser, &e2, binary_op_prio(op));
check_var(parser, &e2);
be_code_binop(finfo, op, e, &e2); /* encode binary op */
op = get_binop(parser);
sub_expr(parser, &e2, binary_op_prio(op)); /* parse right side */
check_var(parser, &e2); /* check if valid */
be_code_binop(finfo, op, e, &e2, -1); /* encode binary op */
op = get_binop(parser); /* is there a following binop? */
}
if (prio == ASSIGN_OP_PRIO) {
cond_expr(parser, e);
}
}
/* Parse new expression and return value in `e` (overwritten) */
/* Initializes an empty expdes and parse subexpr */
/* Always allocates a new temp register at top of freereg */
static void expr(bparser *parser, bexpdesc *e)
{
init_exp(e, ETVOID, 0);

View File

@ -12,7 +12,7 @@
#include "be_string.h"
typedef enum {
ETVOID,
ETVOID, /* unknown (new variable or error) */
ETNIL,
ETBOOL,
ETREAL,
@ -20,13 +20,13 @@ typedef enum {
ETSTRING,
ETPROTO,
ETCONST,
ETLOCAL,
ETGLOBAL,
ETLOCAL, /* local variable, allocated until end of scope */
ETGLOBAL, /* global by index number */
ETUPVAL,
ETMEMBER,
ETINDEX,
ETREG,
ETNGLOBAL
ETMEMBER, /* member accessor (by name) */
ETINDEX, /* index accessor (ex array index) */
ETREG, /* temporary register, can be freed if top of stack */
ETNGLOBAL /* named global */
} exptype_t;
typedef struct {

View File

@ -0,0 +1,40 @@
/********************************************************************
** Copyright (c) 2018-2021 Guan Wenliang & Stephan Hadinger
** This file is part of the Berry default interpreter.
** skiars@qq.com, https://github.com/Skiars/berry
** See Copyright Notice in the LICENSE file or at
** https://github.com/Skiars/berry/blob/master/LICENSE
********************************************************************/
#include "be_object.h"
#include "be_module.h"
#include "be_string.h"
#include "be_vector.h"
#include "be_class.h"
#include "be_debug.h"
#include "be_map.h"
#include "be_vm.h"
#if BE_USE_STRICT_MODULE
static int m_init(bvm *vm)
{
comp_set_strict(vm); /* enable compiler strict mode */
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(strict) {
be_native_module_function("init", m_init),
};
be_define_native_module(strict, NULL);
#else
/* @const_object_info_begin
module strict (scope: strict, depend: BE_USE_STRICT_MODULE) {
init, func(m_init)
}
@const_object_info_end */
#include "../generate/be_fixed_strict.h"
#endif
#endif /* BE_USE_STRICT_MODULE */

View File

@ -28,14 +28,14 @@
#define vm_error(vm, except, ...) \
be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__))
#define RA() (reg + IGET_RA(ins))
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins)))
#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins)))
#define RA() (reg + IGET_RA(ins)) /* Get value of register A */
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins))) /* Get value of register or constant B */
#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins))) /* Get value of register or constant C */
#define var2cl(_v) cast(bclosure*, var_toobj(_v))
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i)
#define val2bool(v) ((v) ? btrue : bfalse)
#define ibinop(op, a, b) ((a)->v.i op (b)->v.i)
#define var2cl(_v) cast(bclosure*, var_toobj(_v)) /* cast var to closure */
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i) /* get var as real or convert to real if integer */
#define val2bool(v) ((v) ? btrue : bfalse) /* get var as bool (trur if non zero) */
#define ibinop(op, a, b) ((a)->v.i op (b)->v.i) /* apply binary operator to both arguments as integers */
#if BE_USE_DEBUG_HOOK
#define DEBUG_HOOK() \
@ -149,6 +149,8 @@ static void call_error(bvm *vm, bvalue *v)
"'%s' value is not callable", be_vtype2str(v));
}
/* Check that the return value is bool or raise an exception */
/* `obj` and `method` are only passed for error reporting */
static void check_bool(bvm *vm, binstance *obj, const char *method)
{
if (!var_isbool(vm->top)) {
@ -182,25 +184,29 @@ static void do_linehook(bvm *vm)
}
#endif
/* Prepare the stack for the function/method call */
/* `func` is a pointer to the function/method on the stack, it contains the closure before call and the result after the call */
/* `nstackˋ is the stack depth used by the function (determined by compiler), we add BE_STACK_FREE_MIN as a safety margin */
static void precall(bvm *vm, bvalue *func, int nstack, int mode)
{
bcallframe *cf;
int expan = nstack + BE_STACK_FREE_MIN;
if (vm->stacktop < func + expan) {
size_t fpos = func - vm->stack;
be_stack_expansion(vm, expan);
func = vm->stack + fpos;
int expan = nstack + BE_STACK_FREE_MIN; /* `expan` is the minimum required space on the stack */
if (vm->stacktop < func + expan) { /* do we have too little space left on the stack? */
size_t fpos = func - vm->stack; /* compute offset of `func` from base stack, in case stack is reallocated and base address changes */
be_stack_expansion(vm, expan); /* expand stack (vector object), warning stack address changes */
func = vm->stack + fpos; /* recompute `func` address with new stack address */
}
be_stack_push(vm, &vm->callstack, NULL);
cf = be_stack_top(&vm->callstack);
be_stack_push(vm, &vm->callstack, NULL); /* push a NULL value on callstack */
cf = be_stack_top(&vm->callstack); /* get address of new callframe at top of callstack */
cf->func = func - mode;
cf->top = vm->top;
cf->reg = vm->reg;
vm->reg = func + 1;
vm->top = vm->reg + nstack;
vm->cf = cf;
cf->top = vm->top; /* save previous stack top */
cf->reg = vm->reg; /* save previous stack base */
vm->reg = func + 1; /* new stack base is right after function */
vm->top = vm->reg + nstack; /* new stack top is above the registers used by the function, so we don´t mess with them */
vm->cf = cf; /* set new current callframe */
}
/* Prepare call of closure, setting the instruction pointer (ip) */
static void push_closure(bvm *vm, bvalue *func, int nstack, int mode)
{
bclosure *cl = var_toobj(func);
@ -469,9 +475,9 @@ static void vm_exec(bvm *vm)
vm->cf->status |= BASE_FRAME;
newframe: /* a new call frame */
be_assert(var_isclosure(vm->cf->func));
clos = var_toobj(vm->cf->func);
ktab = clos->proto->ktab;
reg = vm->reg;
clos = var_toobj(vm->cf->func); /* `clos` is the current function/closure */
ktab = clos->proto->ktab; /* `ktab` is the current constant table */
reg = vm->reg; /* `reg` is the current stack base for the callframe */
vm_exec_loop() {
opcase(LDNIL): {
var_setnil(RA());
@ -1019,7 +1025,7 @@ newframe: /* a new call frame */
dispatch();
}
opcase(RAISE): {
if (IGET_RA(ins) < 2) {
if (IGET_RA(ins) < 2) { /* A==2 means no arguments are passed to RAISE, i.e. rethrow with current exception */
bvalue *top = vm->top;
top[0] = *RKB(); /* push the exception value to top */
if (IGET_RA(ins)) { /* has exception argument? */
@ -1046,8 +1052,8 @@ newframe: /* a new call frame */
dispatch();
}
opcase(CALL): {
bvalue *var = RA();
int mode = 0, argc = IGET_RKB(ins);
bvalue *var = RA(); /* `var` is the register for the call followed by arguments */
int mode = 0, argc = IGET_RKB(ins); /* B contains number of arguments pushed on stack */
recall: /* goto: instantiation class and call constructor */
switch (var_type(var)) {
case NOT_METHOD:
@ -1055,8 +1061,8 @@ newframe: /* a new call frame */
++var, --argc, mode = 1;
goto recall;
case BE_CLASS:
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) {
reg = vm->reg + mode;
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) { /* instanciate object and find constructor */
reg = vm->reg + mode; /* constructor found */
mode = 0;
var = RA() + 1; /* to next register */
goto recall; /* call constructor */
@ -1072,15 +1078,15 @@ newframe: /* a new call frame */
}
case BE_CLOSURE: {
bvalue *v, *end;
bproto *proto = var2cl(var)->proto;
push_closure(vm, var, proto->nstack, mode);
reg = vm->reg;
v = reg + argc;
end = reg + proto->argc;
for (; v < end; ++v) {
bproto *proto = var2cl(var)->proto; /* get proto for closure */
push_closure(vm, var, proto->nstack, mode); /* prepare stack for closure */
reg = vm->reg; /* `reg` has changed, now new base register */
v = reg + argc; /* end of provided arguments */
end = reg + proto->argc; /* end of expected arguments */
for (; v < end; ++v) { /* set all not provided arguments to nil */
var_setnil(v);
}
goto newframe;
goto newframe; /* continue execution of the closure */
}
case BE_NTVCLOS: {
bntvclos *f = var_toobj(var);

View File

@ -14,9 +14,14 @@
#define comp_set_named_gbl(vm) ((vm)->compopt |= (1<<COMP_NAMED_GBL))
#define comp_clear_named_gbl(vm) ((vm)->compopt &= ~(1<<COMP_NAMED_GBL))
#define comp_is_strict(vm) ((vm)->compopt & (1<<COMP_STRICT))
#define comp_set_strict(vm) ((vm)->compopt |= (1<<COMP_STRICT))
#define comp_clear_strict(vm) ((vm)->compopt &= ~(1<<COMP_STRICT))
/* Compilation options */
typedef enum {
COMP_NAMED_GBL = 0x00, /* compile with named globals */
COMP_STRICT = 0x01, /* compile with named globals */
} compoptmask;
typedef struct {

View File

@ -352,6 +352,17 @@ typedef struct bntvmodule {
PROTO_VAR_INFO_BLOCK \
}
#define be_define_local_closure(_name) \
const bclosure _name##_closure = { \
NULL, /* bgcobject *next */ \
BE_CLOSURE, /* type BE_CLOSURE */ \
GC_CONST, /* marked GC_CONST */ \
0, /* nupvals */ \
NULL, /* bgcobject *gray */ \
(bproto*) &_name##_proto, /* proto */ \
{ NULL } /* upvals */ \
}
/* new version for more compact literals */
#define be_local_closure(_name, _proto) \
static const bclosure _name##_closure = { \

View File

@ -32,12 +32,21 @@
* ESP32 analogWrite emulation support
\*********************************************************************************************/
#if CONFIG_IDF_TARGET_ESP32C3
#define PWM_SUPPORTED_CHANNELS 6
#define PWM_CHANNEL_OFFSET 1 // Webcam uses channel 0, so we offset standard PWM
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#else // other ESP32
#define PWM_SUPPORTED_CHANNELS 8
#define PWM_CHANNEL_OFFSET 2 // Webcam uses channel 0, so we offset standard PWM
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
inline uint32_t _analog_pin2chan(uint32_t pin) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {

View File

@ -1,60 +1,70 @@
Import('env')
Import("env")
import os
import shutil
import gzip
import pathlib
platform = env.PioPlatform()
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
import tasmotapiolib
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def map_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
map_file = tasmotapiolib.get_final_map_path(env)
if os.path.isfile(bin_file):
gzip_file = "{}map{}{}.map.gz".format(OUTPUT_DIR, os.path.sep, variant)
if map_file.is_file():
gzip_file = map_file.with_suffix(".map.gz")
# check if new target map files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
if gzip_file.is_file():
gzip_file.unlink()
# write gzip map file
with open(bin_file,"rb") as fp:
with map_file.open("rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel=9) as f:
shutil.copyfileobj(fp, f)
# remove map file
if os.path.isfile(bin_file): os.remove(bin_file)
if map_file.is_file():
map_file.unlink()
if not tasmotapiolib.is_env_set(tasmotapiolib.DISABLE_MAP_GZ, env):
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [map_gzip])
# gzip only for ESP8266
if env["PIOPLATFORM"] != "espressif32":
def bin_gzip(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = tasmotapiolib.get_final_bin_path(env)
gzip_file = bin_file.with_suffix(".bin.gz")
# check if new target files exist and remove if necessary
if os.path.isfile(gzip_file): os.remove(gzip_file)
if os.path.isfile(gzip_file):
os.remove(gzip_file)
# write gzip firmware file
with open(bin_file, "rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel=9) as f:
shutil.copyfileobj(fp, f)
ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
ORG_FIRMWARE_SIZE = bin_file.stat().st_size
GZ_FIRMWARE_SIZE = gzip_file.stat().st_size
if ORG_FIRMWARE_SIZE > 995326:
print("\u001b[31;1m!!! Tasmota firmware size is too big with {} bytes. Max size is 995326 bytes !!! \u001b[0m".format(ORG_FIRMWARE_SIZE))
print(
"\u001b[31;1m!!! Tasmota firmware size is too big with {} bytes. Max size is 995326 bytes !!! \u001b[0m".format(
ORG_FIRMWARE_SIZE
)
)
else:
print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
print(
"Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format(
(GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100,
ORG_FIRMWARE_SIZE,
GZ_FIRMWARE_SIZE,
)
)
if not tasmotapiolib.is_env_set(tasmotapiolib.DISABLE_BIN_GZ, env):
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])

View File

@ -1,54 +1,27 @@
Import('env')
Import('projenv')
Import("env")
Import("projenv")
import os
import shutil
import pathlib
import tasmotapiolib
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_map_copy(source, target, env):
variant = str(target[0]).split(os.path.sep)[2]
firsttarget = pathlib.Path(target[0].path)
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
os.mkdir(OUTPUT_DIR)
for d in ['firmware', 'map']:
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
os.mkdir("{}{}".format(OUTPUT_DIR, d))
# create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
# get locations and file names based on variant
map_file = tasmotapiolib.get_final_map_path(env)
bin_file = tasmotapiolib.get_final_bin_path(env)
# check if new target files exist and remove if necessary
for f in [map_file, bin_file]:
if os.path.isfile(f):
os.remove(f)
if f.is_file():
f.unlink()
# copy firmware.bin to firmware/<variant>.bin
shutil.copy(str(target[0]), bin_file)
# copy firmware.bin and map to final destination
shutil.copy(firsttarget, bin_file)
shutil.move(tasmotapiolib.get_source_map_path(env), map_file)
# move firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file)
#map_new_loc = str(target[0]).split(os.path.sep)[0] + os.path.sep + str(target[0]).split(os.path.sep)[1] + os.path.sep + str(target[0]).split(os.path.sep)[2] + os.path.sep + "Tasmota.map"
# PIO env variables see: https://github.com/platformio/platformio-core/blob/develop/platformio/builder/main.py#L108:L128
proj_build_dir = env["PROJECT_BUILD_DIR"]
#build_dir = env["BUILD_DIR"]
pio_env = env["PIOENV"]
proj_dir = env["PROJECT_DIR"]
map_name = str(proj_dir).split(os.path.sep)[-1]
map_new_loc = proj_build_dir + os.path.sep + pio_env + os.path.sep + map_name + ".map"
#print("proj_build_dir: {}".format(proj_build_dir))
#print("pioenv: {}".format(pio_env))
#print("build_dir: {}".format(build_dir))
#print("map_name: {}".format(map_name))
#print("proj_dir: {}".format(proj_dir))
#print("map_new_loc: {}".format(map_new_loc))
# move Tasmota.map to map/<variant>.map
if os.path.isfile(map_new_loc):
shutil.move(map_new_loc, map_file)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_map_copy])
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_map_copy)

121
pio-tools/tasmotapiolib.py Normal file
View File

@ -0,0 +1,121 @@
"""Supporting library for pio-tools scripts
This also provides functions to allow overrides of some settings, see the available
overides below.
Overrides can be set using environment variables or .ini file settings for controlling
build output file locations and formats.
To set a value using an environment variable, prefix the value with "TASMOTA_" and
ensure the entire value is UPPER CASE, for example in bash, it would be:
export TASMOTA_DISABLE_MAP_GZ=1
To set a value in your .ini file, such as in platformio_override.ini, create a
[tasmota] section, and put the key ensuring it is all lower case, for example:
[tasmota]
disable_map_gz = 1
map_dir = /tmp/map_files/
Values in .ini files override environment variables
"""
import pathlib
import os
# === AVAILABLE OVERRIDES ===
# if set to 1, will not gzip esp8266 bin files
DISABLE_BIN_GZ = "disable_bin_gz"
# if set, an alternative ptah to put generated .bin files, relative to project directory
BIN_DIR = "bin_dir"
# if set to 1, will not gzip generated .map files
DISABLE_MAP_GZ = "disable_map_gz"
# if set, an alternative path to put generated .map files, relative to project directory
MAP_DIR = "map_dir"
# === END AVAILABLE OVERRIDES ===
# This is the default output directory
OUTPUT_DIR = pathlib.Path("build_output")
def get_variant(env) -> str:
"""Get the current build variant."""
return env["PIOENV"]
def get_final_bin_path(env) -> pathlib.Path:
"""Path to the final destination for the .bin
If the parent directory does not exist, it will be created"""
firmware_dir = get_override_path(BIN_DIR, env)
firmware_dir.mkdir(parents=True, exist_ok=True)
return firmware_dir / "{}.bin".format(get_variant(env))
def get_final_map_path(env) -> pathlib.Path:
"""Path to the final destination for the .map file
If the parent directory does not exist, it will be created"""
map_dir = get_override_path(MAP_DIR, env)
map_dir.mkdir(parents=True, exist_ok=True)
return map_dir / "{}.map".format(get_variant(env))
def get_source_map_path(env) -> pathlib.Path:
"""Path to the built .map file.
Tests potential locations, returning the first match.
Raises FileNotFoundError if no match found"""
fwmap_path = pathlib.Path("firmware.map")
if fwmap_path.is_file():
return fwmap_path
# firmware maybe in project build directory
# PIO env variables see: https://github.com/platformio/platformio-core/blob/develop/platformio/builder/main.py#L108:L128
proj_build_dir = pathlib.Path(env["PROJECT_BUILD_DIR"])
proj_dir = pathlib.Path(env["PROJECT_DIR"])
map_name = proj_dir.parts[-1] + ".map"
fwmap_path = proj_build_dir / get_variant(env) / map_name
if fwmap_path.is_file():
return fwmap_path
raise FileNotFoundError
def get_tasmota_override_option(name: str, env):
"""Gets a set override option from a .ini or env variable, None if no match"""
config = env.GetProjectConfig()
override = config.get("tasmota", name.lower(), None)
if override is not None:
return override
# Return env if available
return os.environ.get("TASMOTA_" + name.upper())
def get_override_path(pathtype: str, env) -> pathlib.Path:
"""
Returns a path to a givens override path if set, otherwise OUTPUT_DIR is used
pathtype must be either MAP_DIR or BIN_DIR.
"""
override = get_tasmota_override_option(pathtype, env)
if override:
return pathlib.Path(override)
if pathtype == BIN_DIR:
return OUTPUT_DIR / "firmware"
elif pathtype == MAP_DIR:
return OUTPUT_DIR / "map"
raise ValueError
def is_env_set(name: str, env):
"""True if the enviornment variable <name> is set to `1`"""
val = get_tasmota_override_option(name, env)
if val:
val = val.strip()
return val == "1"
return False

View File

@ -16,6 +16,17 @@ build_flags = ${env.build_flags}
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
-Wno-switch-unreachable
; Settings in [tasmota] do NOT affect firmware building
[tasmota]
; Uncomment next line if you do NOT want gzipped map file(s)
;disable_map_gz = 1
; Uncomment and specify a folder where to place the map file(s) (default set to folder build_output)
;map_dir = /tmp/map_files/
; Uncomment next line if you do NOT want additionally gzipped firmware file(s)
;disable_bin_gz = 1
; Uncomment and specify a folder where to place the firmware file(s) (default set to folder build_output)
;bin_dir = /tmp/bin_files/
[env:tasmota-rangeextender]
build_flags = ${env.build_flags}
-D FIRMWARE_RANGE_EXTENDER

View File

@ -1,219 +0,0 @@
#-
- Demo for M5Stack black edition, using ILI9341
-
-#
if gpio.pin_used(gpio.ILI9341_CS) && gpio.pin_used(gpio.ILI9341_DC) && gpio.pin_used(gpio.OLED_RESET)
lv.start(udisplay.ILI9341_M5Stack_Core)
hres = lv.get_hor_res()
vres = lv.get_ver_res()
scr = lv.scr_act()
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.RED))
scr.set_style_local_bg_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.OPA_COVER)
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFA000))
scr.set_style_local_bg_grad_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFF4000))
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x000080))
scr.set_style_local_bg_grad_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x000030))
scr.set_style_local_bg_grad_dir(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.GRAD_DIR_VER)
#- logo = lv.img_create(scr)
logo.set_src("A:/Sunrise320.bin")
logo.align(0,0,0,0) -#
btn = lv_btn(scr)
btn.set_pos(40,40)
btn.set_size(180, 45)
#btn.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x1fa3ec))
label = lv_label(btn)
label.set_text(lv.SYMBOL_HOME + "Tasmota")
#label.set_style_local_text_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFFFFF))
#f28 = lv.montserrat_font(28)
#if f28 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f28) end
btn.align(0, lv.ALIGN_CENTER, 0, 0)
log = lv_label(scr)
f20 = lv.montserrat_font(20)
if f20 != nil log.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f20) end
#- log.set_long_mode(lv.LABEL_LONG_DOT) -#
log.set_long_mode(lv.LABEL_LONG_SROLL)
log.set_width(hres)
log.set_align(lv.LABEL_ALIGN_LEFT)
log.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xD00000))
log.set_style_local_bg_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv.OPA_COVER)
log.set_style_local_text_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0xFFFFFF))
log.set_text("Welcome to Tasmota - long text rolling")
log.set_text("Welcome to Tasmota")
tastyle = lv_style()
tastyle.set_bg_color(lv.STATE_DEFAULT, lv_color(0x1fa3ec))
tastyle.set_text_color(lv.STATE_DEFAULT, lv_color(0xFFFFFF))
tastyle.set_radius(lv.STATE_DEFAULT, 10)
tastyle.set_bg_opa(lv.STATE_DEFAULT, lv.OPA_COVER)
f20 = lv.montserrat_font(20)
if f20 != nil tastyle.set_text_font(lv.STATE_DEFAULT, f20) end
btn.add_style(lv.OBJ_PART_MAIN, tastyle)
prev_btn = lv_btn(scr)
prev_btn.set_pos(20,vres-40)
prev_btn.set_size(80, 30)
prev_btn.add_style(lv.OBJ_PART_MAIN, tastyle)
prev_label = lv_label(prev_btn)
# prev_label.set_text("Prev")
prev_label.set_text("<")
next_btn = lv_btn(scr)
next_btn.set_pos(220,vres-40)
next_btn.set_size(80, 30)
next_btn.add_style(lv.OBJ_PART_MAIN, tastyle)
next_label = lv_label(next_btn)
#next_label.set_text("Next")
next_label.set_text(">")
home_btn = lv_btn(scr)
home_btn.set_pos(120,vres-40)
home_btn.set_size(80, 30)
home_btn.add_style(lv.OBJ_PART_MAIN, tastyle)
home_label = lv_label(home_btn)
home_label.set_text(lv.SYMBOL_OK)
btn.del()
logo.set_src("A:/Sunrise.bin")
logo.align(0,0,0,0)
btn.set_height(120)
label.del()
logo = lv.img_create(btn)
logo.set_tasmota_logo()
logo.set_zoom(384)
logo.set_angle(400)
logo.set_style_local_image_recolor_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, 255)
logo.set_style_local_image_recolor(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.BLUE))
#- logo on splash screen -#
btn.del()
logo = lv.img_create(lv.scr_act())
logo.set_tasmota_logo()
logo.set_style_local_image_recolor_opa(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, 255)
logo.set_style_local_image_recolor(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.WHITE))
logo.align(0,0,0,0)
logo.set_zoom(400)
#- anil -#
logo.set_zoom(384)
angle = 0
do_rotate = nil
do_rotate = def () angle += 100 logo.set_angle(angle) tasmota.timer(100, do_rotate) end
t48 = lv.tasmota_font(48)
label.set_text("A")
if t48 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, t48) end
btn.set_height(120)
f10 = lv.montserrat_font(10)
if f10 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f10) end
#- try zooming an image -#
img = lv.img_create(btn)
label.del()
img.set_src(lv.SYMBOL_OK)
#-
f8 = lv.montserrat_font(8)
if f8 != nil label.set_style_local_text_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f8) end
-#
#-
f14 = lv.montserrat_font(14)
f28 = lv.montserrat_font(28)
btn.set_height(80)
btn.set_style_local_bg_color(0, lv.STATE_DEFAULT, lv_color(0xFFEEFF))
label.set_style_local_text_font(0, lv.STATE_DEFAULT, f28)
scr = lv.scr_act()
scr.set_style_local_bg_color(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x400000))
scr.set_style_local_value_font(lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, f28)
label.set_style_local_value_font(lv.BTN_PART_MAIN, lv.STATE_DEFAULT, f28)
-#
#- lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); -#
#- lv.obj_set_style_local_bg_color(lv.scr_act(), lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(0x00FF00)) -#
#- lv_obj_set_style_local_bg_opa( lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER); -#
#-
lv.demo()
-#
else
print('ILI9341 pins are not configured')
end
#-
lv.obj_set_style_local_bg_color(lv.scr_act(), lv.OBJ_PART_MAIN, lv.STATE_DEFAULT, lv_color(lv.BLACK))
gauge = lv.gauge_create(lv.scr_act())
gauge.set_size(150,150)
co = lv.cpicker_create(lv.scr_act())
co.set_size(150,150)
co.set_pos(170,20)
k = lv.keyboard_create(lv.scr_act())
cal = lv.calendar_create(lv.scr_act())
cal.del()
c = lv.checkbox_create(lv.scr_act())
c.del()
c = lv.chart_create(lv.scr_act())
c.del()
co.del()
k = lv.keyboard_create(lv.scr_act())
k.del()
led = lv.led_create(lv.scr_act())
led.del()
m = lv.msgbox_create(lv.scr_act())
m.del()
# menu item
rol = lv.roller_create(lv.scr_act())
rol.del()
sl = lv.slider_create(lv.scr_act())
sl.del()
sp = lv.spinner_create(lv.scr_act())
sp.del()
w = lv.win_create(lv.scr_act())
w.del()
t = lv.textarea_create(lv.scr_act())
t.set_text("Tasmota")
t.del()
-#

View File

@ -38,9 +38,9 @@ ren_sec.set_pos(110,10)
prev_day = -1
def set_watch()
now = tasmota.rtc()
time_raw = now['local'] + now['timezone'] * 60
time = tasmota.time_dump(time_raw)
var now = tasmota.rtc()
var time_raw = now['local'] + now['timezone'] * 60
var time = tasmota.time_dump(time_raw)
# set second
ren_sec.set_angle(60 * time['sec'])
# set minutes

View File

@ -44,7 +44,7 @@ def crc32_create_table()
return a
end
crc32_table = crc32_create_table()
var crc32_table = crc32_create_table()
def crc32_update(buf, crc)
crc ^= 0xffffffff
@ -137,7 +137,6 @@ class Partition_info
# print("Segment count", seg_count)
var seg_offset = addr + 0x20 # sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) = 24 + 8
var seg_size = 0
for seg_num:0..seg_count-1
# print(string.format("Reading 0x%08X", seg_offset))
@ -375,7 +374,6 @@ class Partition
#- parse the raw bytes to a structured list of partition items -#
def parse()
var i
for i:0..94 # there are maximum 95 slots + md5 (0xC00)
var item_raw = self.raw[i*32..(i+1)*32-1]
var magic = item_raw.get(0,2)
@ -754,7 +752,7 @@ class Partition_manager : Driver
end
#- create and register driver in Tasmota -#
partition_manager = Partition_manager()
var partition_manager = Partition_manager()
tasmota.add_driver(partition_manager)
## can be removed if put in 'autoexec.bat'
partition_manager.web_add_handler()

View File

@ -316,7 +316,7 @@ float CharToFloat(const char *str)
char *pt = strbuf;
if (*pt == '\0') { return 0.0; }
while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces
while ((*pt != '\0') && isspace(*pt)) { pt++; } // Trim leading spaces
signed char sign = 1;
if (*pt == '-') { sign = -1; }
@ -524,17 +524,18 @@ bool StrCaseStr_P(const char* source, const char* search) {
}
bool IsNumeric(const char* value) {
// Test for characters '-.0123456789'
char *digit = (char*)value;
while (isdigit(*digit) || *digit == '.' || *digit == '-') { digit++; }
return (*digit == '\0');
}
char* Trim(char* p)
{
char* Trim(char* p) {
// Remove leading and trailing tab, \n, \v, \f, \r and space
if (*p != '\0') {
while ((*p != '\0') && isblank(*p)) { p++; } // Trim leading spaces
while ((*p != '\0') && isspace(*p)) { p++; } // Trim leading spaces
char* q = p + strlen(p) -1;
while ((q >= p) && isblank(*q)) { q--; } // Trim trailing spaces
while ((q >= p) && isspace(*q)) { q--; } // Trim trailing spaces
q++;
*q = '\0';
}

View File

@ -666,7 +666,7 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true)
return true;
}
}
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Referer denied"));
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP "Referer denied. Use 'SO128 1' for HTTP API commands. 'Webpassword' is recommended."));
return false;
} else {
return true;

View File

@ -197,8 +197,8 @@ void MqttInit(void) {
Settings->mqtt_port = 8883;
#endif //USE_MQTT_AZURE_IOT
#ifdef USE_MQTT_TLS
if ((8883 == Settings->mqtt_port) || (8884 == Settings->mqtt_port)) {
// Turn on TLS for port 8883 (TLS) and 8884 (TLS, client certificate)
if ((8883 == Settings->mqtt_port) || (8884 == Settings->mqtt_port) || (443 == Settings->mqtt_port)) {
// Turn on TLS for port 8883 (TLS), 8884 (TLS, client certificate), 443 (TLS, user/password)
Settings->flag4.mqtt_tls = true;
}
Mqtt.mqtt_tls = Settings->flag4.mqtt_tls; // this flag should not change even if we change the SetOption (until reboot)

View File

@ -801,6 +801,11 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
bool RulesProcessEvent(const char *json_event)
{
#ifdef USE_BERRY
// events are passed to Berry before Rules engine
callBerryRule(json_event);
#endif
if (Rules.busy) { return false; }
Rules.busy = true;
@ -858,7 +863,11 @@ void RulesInit(void)
void RulesEvery50ms(void)
{
#ifdef USE_BERRY
if (!Rules.busy) { // Emitting Rules events is always enabled with Berry
#else
if (Settings->rule_enabled && !Rules.busy) { // Any rule enabled
#endif
char json_event[120];
if (-1 == Rules.new_power) { Rules.new_power = TasmotaGlobal.power; }

View File

@ -93,12 +93,13 @@ extern "C" {
\*********************************************************************************************/
// // call a function (if exists) of type void -> void
bool callBerryRule(void) {
// If event == nullptr, then take XdrvMailbox.data
bool callBerryRule(const char *event) {
if (berry.rules_busy) { return false; }
berry.rules_busy = true;
char * json_event = XdrvMailbox.data;
bool serviced = false;
serviced = callBerryEventDispatcher(PSTR("rule"), nullptr, 0, XdrvMailbox.data);
serviced = callBerryEventDispatcher(PSTR("rule"), nullptr, 0, event ? event : XdrvMailbox.data);
berry.rules_busy = false;
return serviced; // TODO event not handled
}
@ -277,7 +278,8 @@ void BerryInit(void) {
do {
berry.vm = be_vm_new(); /* create a virtual machine instance */
be_set_obs_hook(berry.vm, &BerryObservability);
comp_set_named_gbl(berry.vm);
comp_set_named_gbl(berry.vm); /* Enable named globals in Berry compiler */
comp_set_strict(berry.vm); /* Enable strict mode in Berry compiler */
be_load_custom_libs(berry.vm);
// Register functions
@ -731,7 +733,7 @@ bool Xdrv52(uint8_t function)
// Berry wide commands and events
case FUNC_RULES_PROCESS:
result = callBerryRule();
result = callBerryRule(nullptr);
break;
case FUNC_MQTT_DATA:
result = callBerryEventDispatcher(PSTR("mqtt_data"), XdrvMailbox.topic, 0, XdrvMailbox.data, XdrvMailbox.data_len);

View File

@ -84,10 +84,11 @@ struct {
String _serverUrl; // Connection info
String _writeUrl; // Cached full write url
String _lastErrorResponse; // Server reponse or library error message for last failed request
uint32_t _lastRequestTime = 0; // Last time in ms we made are a request to server
uint32_t _lastRequestTime = 0; // Last time in ms we made a request to server
int interval = 0;
int _lastStatusCode = 0; // HTTP status code of last request to server
int _lastRetryAfter = 0; // Store retry timeout suggested by server after last request
uint8_t log_level = LOG_LEVEL_DEBUG_MORE;
bool _connectionReuse; // true if HTTP connection should be kept open. Usable for frequent writes. Default false
bool init = false;
} IFDB;
@ -161,7 +162,7 @@ void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) {
IFDB._lastRequestTime = millis();
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: HTTP status code %d"), IFDB._lastStatusCode);
IFDB._lastRetryAfter = 0;
if (IFDB._lastStatusCode >= 429) { //retryable server errors
if (IFDB._lastStatusCode >= 429) { // Retryable server errors
if (IFDBhttpClient->hasHeader(RetryAfter)) {
IFDB._lastRetryAfter = IFDBhttpClient->header(RetryAfter).toInt();
AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Reply after %d"), IFDB._lastRetryAfter);
@ -171,12 +172,12 @@ void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) {
IFDB._lastErrorResponse = "";
if (IFDB._lastStatusCode != expectedStatusCode) {
if (IFDB._lastStatusCode > 0) {
IFDB._lastErrorResponse = IFDBhttpClient->getString();
AddLog(LOG_LEVEL_INFO, PSTR("IFX: %s"), IFDB._lastErrorResponse.c_str()); // {"error":"database not found: \"db\""}
IFDB._lastErrorResponse = IFDBhttpClient->getString(); // {"error":"database not found: \"db\""}\n
} else {
IFDB._lastErrorResponse = IFDBhttpClient->errorToString(IFDB._lastStatusCode);
AddLog(LOG_LEVEL_INFO, PSTR("IFX: Error %s"), IFDB._lastErrorResponse.c_str());
}
IFDB._lastErrorResponse.trim(); // Remove trailing \n
AddLog(LOG_LEVEL_INFO, PSTR("IFX: Error %s"), IFDB._lastErrorResponse.c_str());
}
}
@ -191,8 +192,6 @@ bool InfluxDbValidateConnection(void) {
if (1 == Settings->influxdb_version) {
url += InfluxDbAuth();
}
// on version 1.8.9 /health works fine
// String url = IFDB._serverUrl + "/health";
AddLog(LOG_LEVEL_INFO, PSTR("IFX: Validating connection to %s"), url.c_str());
if (!IFDBhttpClient->begin(*IFDBwifiClient, url)) {
@ -221,7 +220,8 @@ int InfluxDbPostData(const char *data) {
return false;
}
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("IFX: Sending\n%s"), data);
Trim((char*)data); // Remove trailing \n
AddLog(IFDB.log_level, PSTR("IFX: Sending\n%s"), data);
IFDBhttpClient->addHeader(F("Content-Type"), F("text/plain"));
InfluxDbBeforeRequest();
IFDB._lastStatusCode = IFDBhttpClient->POST((uint8_t*)data, strlen(data));
@ -235,10 +235,12 @@ int InfluxDbPostData(const char *data) {
* Data preparation
\*********************************************************************************************/
char* InfluxDbNumber(char* alternative, const char* source) {
char* InfluxDbNumber(char* alternative, JsonParserToken value) {
if (value.isValid()) {
char* source = (char*)value.getStr();
// Test for valid numeric data ('-.0123456789') or ON, OFF etc. as defined in kOptions
if (source != nullptr) {
char* out = (char*)source;
char* out = source;
// Convert special text as found in kOptions to a number
// Like "OFF" -> 0, "ON" -> 1, "TOGGLE" -> 2
int number = GetStateNumber(source);
@ -250,16 +252,19 @@ char* InfluxDbNumber(char* alternative, const char* source) {
return out;
}
}
}
return nullptr;
}
void InfluxDbProcessJson(void) {
if (!IFDB.init) { return; }
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: JSON %s"), ResponseData());
AddLog(IFDB.log_level, PSTR("IFX: Process %s"), ResponseData());
// String jsonStr = ResponseData(); // Make a copy before use
// JsonParser parser((char *)jsonStr.c_str());
JsonParser parser((char *)ResponseData()); // Destroys ResponseData but saves heap space
String jsonStr = ResponseData();
JsonParser parser((char *)jsonStr.c_str());
JsonParserObject root = parser.getRootObject();
if (root) {
char number[12]; // '1' to '255'
@ -280,8 +285,8 @@ void InfluxDbProcessJson(void) {
if (value2.isObject()) {
JsonParserObject Object3 = value2.getObject();
for (auto key3 : Object3) {
const char* value = InfluxDbNumber(number, key3.getValue().getStr());
if (value != nullptr) {
char* value = InfluxDbNumber(number, key3.getValue());
if ((value != nullptr) && key2.isValid() && key3.isValid()) {
// Level 3
LowerCase(sensor, key2.getStr());
LowerCase(type, key3.getStr());
@ -294,18 +299,22 @@ void InfluxDbProcessJson(void) {
} else {
// Level 2
// { ... "ANALOG":{"Temperature":184.72},"DS18B20":{"Id":"01144A0CB2AA","Temperature":24.88},"HTU21":{"Temperature":25.32,"Humidity":49.2,"DewPoint":13.88},"Global":{"Temperature":24.88,"Humidity":49.2,"DewPoint":13.47}, ... }
bool isarray = value2.isArray();
const char* value = InfluxDbNumber(number, (isarray) ? (value2.getArray())[0].getStr() : value2.getStr());
if (value != nullptr) {
LowerCase(sensor, key1.getStr());
if (!key1.isValid() || !value2.isValid()) { continue; }
LowerCase(type, key2.getStr());
bool is_id = (!strcmp_P(type, PSTR("id"))); // Index for DS18B20
bool is_array = value2.isArray();
char* value = nullptr;
if (is_id && !is_array) {
snprintf_P(sensor_id, sizeof(sensor_id), PSTR(",id=%s"), value2.getStr());
} else {
value = InfluxDbNumber(number, (is_array) ? (value2.getArray())[0] : value2);
}
if ((value != nullptr) && key2.isValid()) {
LowerCase(sensor, key1.getStr());
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX2: sensor %s (%s), type %s (%s)"), key1.getStr(), sensor, key2.getStr(), type);
if (strcmp(type, "id") == 0) { // Index for DS18B20
snprintf_P(sensor_id, sizeof(sensor_id), PSTR(",id=%s"), value);
} else {
if (isarray) {
if (is_array) {
JsonParserArray arr = value2.getArray();
uint32_t i = 0;
for (auto val : arr) {
@ -326,12 +335,11 @@ void InfluxDbProcessJson(void) {
}
}
}
}
} else {
// Level 1
// {"Time":"2021-08-13T14:15:56","Switch1":"ON","Switch2":"OFF", ... "TempUnit":"C"}
const char* value = InfluxDbNumber(number, value1.getStr());
if (value != nullptr) {
char* value = InfluxDbNumber(number, value1);
if ((value != nullptr) && key1.isValid()) {
LowerCase(type, key1.getStr());
// switch1,device=demo,sensor=device value=0
// power1,device=demo,sensor=device value=1
@ -391,6 +399,7 @@ void InfluxDbLoop(void) {
\*********************************************************************************************/
#define D_PRFX_INFLUXDB "Ifx"
#define D_CMND_INFLUXDBLOG "Log"
#define D_CMND_INFLUXDBHOST "Host"
#define D_CMND_INFLUXDBPORT "Port"
#define D_CMND_INFLUXDBUSER "User"
@ -401,14 +410,14 @@ void InfluxDbLoop(void) {
#define D_CMND_INFLUXDBBUCKET "Bucket"
const char kInfluxDbCommands[] PROGMEM = D_PRFX_INFLUXDB "|" // Prefix
"|"
"|" D_CMND_INFLUXDBLOG "|"
D_CMND_INFLUXDBHOST "|" D_CMND_INFLUXDBPORT "|"
D_CMND_INFLUXDBUSER "|" D_CMND_INFLUXDBORG "|"
D_CMND_INFLUXDBPASSWORD "|" D_CMND_INFLUXDBTOKEN "|"
D_CMND_INFLUXDBDATABASE "|" D_CMND_INFLUXDBBUCKET;
void (* const InfluxCommand[])(void) PROGMEM = {
&CmndInfluxDbState,
&CmndInfluxDbState, &CmndInfluxDbLog,
&CmndInfluxDbHost, &CmndInfluxDbPort,
&CmndInfluxDbUser, &CmndInfluxDbUser,
&CmndInfluxDbPassword, &CmndInfluxDbPassword,
@ -437,6 +446,13 @@ void CmndInfluxDbState(void) {
}
}
void CmndInfluxDbLog(void) {
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
IFDB.log_level = XdrvMailbox.payload;
}
ResponseCmndNumber(IFDB.log_level);
}
void CmndInfluxDbHost(void) {
if (XdrvMailbox.data_len > 0) {
SettingsUpdateText(SET_INFLUXDB_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_HOST) : XdrvMailbox.data);

View File

@ -1083,8 +1083,8 @@ bool XdrvRulesProcess(bool teleperiod, const char* event) {
char* data_save = XdrvMailbox.data;
XdrvMailbox.data = (char*)event;
bool rule_handled = XdrvCallDriver(10, (teleperiod) ? FUNC_TELEPERIOD_RULES_PROCESS : FUNC_RULES_PROCESS);
#ifdef USE_BERRY
// events are passed to both Rules engine AND Berry engine
#if defined(USE_BERRY) && !defined(USE_RULES)
// events are sent to Berry in Rules driver, or here if USE_RULES is not defined (only on a subset)
bool berry_handled = XdrvCallDriver(52, FUNC_RULES_PROCESS);
rule_handled |= berry_handled;
#endif

View File

@ -1198,7 +1198,7 @@ double CharToDouble(const char *str)
strlcpy(strbuf, str, sizeof(strbuf));
char *pt = strbuf;
while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces
while ((*pt != '\0') && isspace(*pt)) { pt++; } // Trim leading spaces
signed char sign = 1;
if (*pt == '-') { sign = -1; }