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
tasmota*.bin.gz tasmota*.bin.gz
tasmota*.map tasmota*.map
tasmota*.map.gz
platformio_override.ini platformio_override.ini
platformio_tasmota_cenv.ini platformio_tasmota_cenv.ini

View File

@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Version bump to monitor possible HTTP issues releated to ``SetOption128`` - 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 ## [9.5.0.5] 20210815
### Added ### Added
- Inital support for Wi-Fi extender (#12784) - 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(debug);
be_extern_native_module(gc); be_extern_native_module(gc);
be_extern_native_module(solidify); be_extern_native_module(solidify);
be_extern_native_module(strict);
be_extern_native_module(introspect); be_extern_native_module(introspect);
/* Tasmota specific */ /* Tasmota specific */
@ -72,6 +73,9 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
#endif #endif
#if BE_USE_INTROSPECT_MODULE #if BE_USE_INTROSPECT_MODULE
&be_native_module(introspect), &be_native_module(introspect),
#endif
#if BE_USE_STRICT_MODULE
&be_native_module(strict),
#endif #endif
/* user-defined modules register start */ /* 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_tasmota_ntvlib(bvm *vm);
extern void be_load_wirelib(bvm *vm); extern void be_load_wirelib(bvm *vm);
extern void be_load_Driver_class(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_driver_i2c_lib(bvm *vm);
extern void be_load_md5_lib(bvm *vm); extern void be_load_md5_lib(bvm *vm);
extern void be_load_aes_gcm_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 #if !BE_USE_PRECOMPILED_OBJECT
/* be_load_xxxlib(vm); */ /* be_load_xxxlib(vm); */
#endif #endif
be_load_Timer_class(vm);
be_load_tasmota_ntvlib(vm); be_load_tasmota_ntvlib(vm);
be_load_Driver_class(vm); be_load_Driver_class(vm);
be_load_md5_lib(vm); be_load_md5_lib(vm);

View File

@ -925,162 +925,156 @@ 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 ** Solidified function: set_timer
********************************************************************/ ********************************************************************/
be_local_closure(set_timer, /* name */ be_local_closure(set_timer, /* name */
be_nested_proto( be_nested_proto(
9, /* nstack */ 10, /* nstack */
3, /* argc */ 4, /* argc */
0, /* has upvals */ 0, /* has upvals */
NULL, /* no upvals */ NULL, /* no upvals */
0, /* has sup protos */ 0, /* has sup protos */
NULL, /* no sub protos */ NULL, /* no sub protos */
1, /* has constants */ 1, /* has constants */
( &(const bvalue[ 3]) { /* upvals */ ( &(const bvalue[ 4]) { /* constants */
{ { .s=be_nested_const_str("_timers", -1694866380, 7) }, BE_STRING}, be_nested_string("_timers", -1694866380, 7), /* R256 - K0 */
{ { .s=be_nested_const_str("push", -2022703139, 4) }, BE_STRING}, be_nested_string("push", -2022703139, 4), /* R257 - K1 */
{ { .s=be_nested_const_str("millis", 1214679063, 6) }, BE_STRING}, 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("set_timer", 2135414533, 9)),
(be_nested_const_str("string", 398550328, 6)), (be_nested_const_str("input", -103256197, 5)),
( &(const binstruction[16]) { /* code */ ( &(const binstruction[16]) { /* code */
0x880C0100, // 0000 GETMBR R3 R0 R256 0x88100100, // 0000 GETMBR R4 R0 R256
0x740E0002, // 0001 JMPT R3 #0005 0x74120002, // 0001 JMPT R4 #0005
0x600C000A, // 0002 GETGBL R3 G10 0x6010000A, // 0002 GETGBL R4 G10
0x7C0C0000, // 0003 CALL R3 0 0x7C100000, // 0003 CALL R4 0
0x90020003, // 0004 SETMBR R0 R256 R3 0x90020004, // 0004 SETMBR R0 R256 R4
0x880C0100, // 0005 GETMBR R3 R0 R256 0x88100100, // 0005 GETMBR R4 R0 R256
0x8C0C0701, // 0006 GETMET R3 R3 R257 0x8C100901, // 0006 GETMET R4 R4 R257
0x6014000A, // 0007 GETGBL R5 G10 0xB81A0400, // 0007 GETNGBL R6 R258
0x7C140000, // 0008 CALL R5 0 0x8C1C0103, // 0008 GETMET R7 R0 R259
0x8C180102, // 0009 GETMET R6 R0 R258 0x5C240200, // 0009 MOVE R9 R1
0x5C200200, // 000A MOVE R8 R1 0x7C1C0400, // 000A CALL R7 2
0x7C180400, // 000B CALL R6 2 0x5C200400, // 000B MOVE R8 R2
0x40180A06, // 000C CONNECT R6 R5 R6 0x5C240600, // 000C MOVE R9 R3
0x40180A02, // 000D CONNECT R6 R5 R2 0x7C180600, // 000D CALL R6 3
0x7C0C0400, // 000E CALL R3 2 0x7C100400, // 000E CALL R4 2
0x80000000, // 000F RET 0 R0 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 ** Solidified function: run_deferred
********************************************************************/ ********************************************************************/
be_local_closure(run_deferred, /* name */
be_define_local_const_str(run_deferred_str_name, "run_deferred", 371594696, 12); be_nested_proto(
be_define_local_const_str(run_deferred_str_source, "string", 398550328, 6); 6, /* nstack */
be_define_local_const_str(run_deferred_str_0, "_timers", -1694866380, 7); 1, /* argc */
be_define_local_const_str(run_deferred_str_2, "size", 597743964, 4); 0, /* has upvals */
be_define_local_const_str(run_deferred_str_3, "time_reached", 2075136773, 12); NULL, /* no upvals */
be_define_local_const_str(run_deferred_str_5, "remove", -611183107, 6); 0, /* has sup protos */
NULL, /* no sub protos */
static const bvalue run_deferred_ktab[6] = { 1, /* has constants */
{ { .s=be_local_const_str(run_deferred_str_0) }, BE_STRING}, ( &(const bvalue[ 8]) { /* constants */
{ { .i=0 }, BE_INT}, be_nested_string("_timers", -1694866380, 7), /* R256 - K0 */
{ { .s=be_local_const_str(run_deferred_str_2) }, BE_STRING}, be_const_int(0), /* R257 - K1 */
{ { .s=be_local_const_str(run_deferred_str_3) }, BE_STRING}, be_nested_string("size", 597743964, 4), /* R258 - K2 */
{ { .i=1 }, BE_INT}, be_nested_string("time_reached", 2075136773, 12), /* R259 - K3 */
{ { .s=be_local_const_str(run_deferred_str_5) }, BE_STRING}, be_nested_string("due", -399437003, 3), /* R260 - K4 */
}; be_nested_string("f", -485742695, 1), /* R261 - K5 */
be_nested_string("remove", -611183107, 6), /* R262 - K6 */
static const uint32_t run_deferred_code[27] = { be_const_int(1), /* R263 - K7 */
0x88040100, // 0000 GETMBR R1 R0 R256 }),
0x78060017, // 0001 JMPF R1 #001A (be_nested_const_str("run_deferred", 371594696, 12)),
0x58040001, // 0002 LDCONST R1 K1 (be_nested_const_str("input", -103256197, 5)),
0x88080100, // 0003 GETMBR R2 R0 R256 ( &(const binstruction[27]) { /* code */
0x8C080502, // 0004 GETMET R2 R2 R258 0x88040100, // 0000 GETMBR R1 R0 R256
0x7C080200, // 0005 CALL R2 1 0x78060017, // 0001 JMPF R1 #001A
0x14080202, // 0006 LT R2 R1 R2 0x58040001, // 0002 LDCONST R1 K1
0x780A0011, // 0007 JMPF R2 #001A 0x88080100, // 0003 GETMBR R2 R0 R256
0x8C080103, // 0008 GETMET R2 R0 R259 0x8C080502, // 0004 GETMET R2 R2 R258
0x88100100, // 0009 GETMBR R4 R0 R256 0x7C080200, // 0005 CALL R2 1
0x94100801, // 000A GETIDX R4 R4 R1 0x14080202, // 0006 LT R2 R1 R2
0x94100901, // 000B GETIDX R4 R4 R257 0x780A0011, // 0007 JMPF R2 #001A
0x7C080400, // 000C CALL R2 2 0x8C080103, // 0008 GETMET R2 R0 R259
0x780A0009, // 000D JMPF R2 #0018 0x88100100, // 0009 GETMBR R4 R0 R256
0x88080100, // 000E GETMBR R2 R0 R256 0x94100801, // 000A GETIDX R4 R4 R1
0x94080401, // 000F GETIDX R2 R2 R1 0x88100904, // 000B GETMBR R4 R4 R260
0x94080504, // 0010 GETIDX R2 R2 R260 0x7C080400, // 000C CALL R2 2
0x880C0100, // 0011 GETMBR R3 R0 R256 0x780A0009, // 000D JMPF R2 #0018
0x8C0C0705, // 0012 GETMET R3 R3 R261 0x88080100, // 000E GETMBR R2 R0 R256
0x5C140200, // 0013 MOVE R5 R1 0x94080401, // 000F GETIDX R2 R2 R1
0x7C0C0400, // 0014 CALL R3 2 0x88080505, // 0010 GETMBR R2 R2 R261
0x5C0C0400, // 0015 MOVE R3 R2 0x880C0100, // 0011 GETMBR R3 R0 R256
0x7C0C0000, // 0016 CALL R3 0 0x8C0C0706, // 0012 GETMET R3 R3 R262
0x70020000, // 0017 JMP #0019 0x5C140200, // 0013 MOVE R5 R1
0x40304, // 0018 ADD R1 R1 R260 0x7C0C0400, // 0014 CALL R3 2
0x7001FFE8, // 0019 JMP #0003 0x5C0C0400, // 0015 MOVE R3 R2
0x80000000, // 001A RET 0 R0 0x7C0C0000, // 0016 CALL R3 0
}; 0x70020000, // 0017 JMP #0019
0x00040307, // 0018 ADD R1 R1 R263
static const bproto run_deferred_proto = { 0x7001FFE8, // 0019 JMP #0003
NULL, // bgcobject *next 0x80000000, // 001A RET 0 R0
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 // Add command to list
@ -1212,113 +1206,60 @@ 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 ** Solidified function: exec_cmd
********************************************************************/ ********************************************************************/
be_local_closure(exec_cmd, /* name */
be_define_local_const_str(exec_cmd_str_name, "exec_cmd", 493567399, 8); be_nested_proto(
be_define_local_const_str(exec_cmd_str_source, "string", 398550328, 6); 12, /* nstack */
be_define_local_const_str(exec_cmd_str_0, "_ccmd", -2131545883, 5); 4, /* argc */
be_define_local_const_str(exec_cmd_str_1, "json", 916562499, 4); 0, /* has upvals */
be_define_local_const_str(exec_cmd_str_2, "load", -435725847, 4); NULL, /* no upvals */
be_define_local_const_str(exec_cmd_str_3, "find_key_i", 850136726, 10); 0, /* has sup protos */
be_define_local_const_str(exec_cmd_str_4, "resolvecmnd", 993361485, 11); NULL, /* no sub protos */
1, /* has constants */
static const bvalue exec_cmd_ktab[5] = { ( &(const bvalue[ 5]) { /* constants */
{ { .s=be_local_const_str(exec_cmd_str_0) }, BE_STRING}, be_nested_string("_ccmd", -2131545883, 5), /* R256 - K0 */
{ { .s=be_local_const_str(exec_cmd_str_1) }, BE_STRING}, be_nested_string("json", 916562499, 4), /* R257 - K1 */
{ { .s=be_local_const_str(exec_cmd_str_2) }, BE_STRING}, be_nested_string("load", -435725847, 4), /* R258 - K2 */
{ { .s=be_local_const_str(exec_cmd_str_3) }, BE_STRING}, be_nested_string("find_key_i", 850136726, 10), /* R259 - K3 */
{ { .s=be_local_const_str(exec_cmd_str_4) }, BE_STRING}, be_nested_string("resolvecmnd", 993361485, 11), /* R260 - K4 */
}; }),
(be_nested_const_str("exec_cmd", 493567399, 8)),
static const uint32_t exec_cmd_code[27] = { (be_nested_const_str("string", 398550328, 6)),
0x88100100, // 0000 GETMBR R4 R0 R256 ( &(const binstruction[27]) { /* code */
0x78120016, // 0001 JMPF R4 #0019 0x88100100, // 0000 GETMBR R4 R0 R256
0xA4120200, // 0002 IMPORT R4 R257 0x78120016, // 0001 JMPF R4 #0019
0x8C140902, // 0003 GETMET R5 R4 R258 0xA4120200, // 0002 IMPORT R4 R257
0x5C1C0600, // 0004 MOVE R7 R3 0x8C140902, // 0003 GETMET R5 R4 R258
0x7C140400, // 0005 CALL R5 2 0x5C1C0600, // 0004 MOVE R7 R3
0x8C180103, // 0006 GETMET R6 R0 R259 0x7C140400, // 0005 CALL R5 2
0x88200100, // 0007 GETMBR R8 R0 R256 0x8C180103, // 0006 GETMET R6 R0 R259
0x5C240200, // 0008 MOVE R9 R1 0x88200100, // 0007 GETMBR R8 R0 R256
0x7C180600, // 0009 CALL R6 3 0x5C240200, // 0008 MOVE R9 R1
0x4C1C0000, // 000A LDNIL 7 0x7C180600, // 0009 CALL R6 3
0x201C0C07, // 000B NE R7 R6 R7 0x4C1C0000, // 000A LDNIL 7
0x781E000B, // 000C JMPF R7 #0019 0x201C0C07, // 000B NE R7 R6 R7
0x8C1C0104, // 000D GETMET R7 R0 R260 0x781E000B, // 000C JMPF R7 #0019
0x5C240C00, // 000E MOVE R9 R6 0x8C1C0104, // 000D GETMET R7 R0 R260
0x7C1C0400, // 000F CALL R7 2 0x5C240C00, // 000E MOVE R9 R6
0x881C0100, // 0010 GETMBR R7 R0 R256 0x7C1C0400, // 000F CALL R7 2
0x941C0E06, // 0011 GETIDX R7 R7 R6 0x881C0100, // 0010 GETMBR R7 R0 R256
0x5C200C00, // 0012 MOVE R8 R6 0x941C0E06, // 0011 GETIDX R7 R7 R6
0x5C240400, // 0013 MOVE R9 R2 0x5C200C00, // 0012 MOVE R8 R6
0x5C280600, // 0014 MOVE R10 R3 0x5C240400, // 0013 MOVE R9 R2
0x5C2C0A00, // 0015 MOVE R11 R5 0x5C280600, // 0014 MOVE R10 R3
0x7C1C0800, // 0016 CALL R7 4 0x5C2C0A00, // 0015 MOVE R11 R5
0x501C0200, // 0017 LDBOOL R7 1 0 0x7C1C0800, // 0016 CALL R7 4
0x80040E00, // 0018 RET 1 R7 0x501C0200, // 0017 LDBOOL R7 1 0
0x50100000, // 0019 LDBOOL R4 0 0 0x80040E00, // 0018 RET 1 R7
0x80040800, // 001A RET 1 R4 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 // Force gc and return allocated memory
"def gc() " "def gc() "
@ -1478,70 +1419,37 @@ be_local_closure(event, /* name */
/******************************************************************** /********************************************************************
** Solidified function: add_driver ** Solidified function: add_driver
********************************************************************/ ********************************************************************/
be_local_closure(add_driver, /* name */
be_define_local_const_str(add_driver_str_name, "add_driver", 1654458371, 10); be_nested_proto(
be_define_local_const_str(add_driver_str_source, "string", 398550328, 6); 5, /* nstack */
be_define_local_const_str(add_driver_str_0, "_drivers", -1034638311, 8); 2, /* argc */
be_define_local_const_str(add_driver_str_1, "push", -2022703139, 4); 0, /* has upvals */
NULL, /* no upvals */
static const bvalue add_driver_ktab[2] = { 0, /* has sup protos */
{ { .s=be_local_const_str(add_driver_str_0) }, BE_STRING}, NULL, /* no sub protos */
{ { .s=be_local_const_str(add_driver_str_1) }, BE_STRING}, 1, /* has constants */
}; ( &(const bvalue[ 2]) { /* constants */
be_nested_string("_drivers", -1034638311, 8), /* R256 - K0 */
static const uint32_t add_driver_code[12] = { be_nested_string("push", -2022703139, 4), /* R257 - K1 */
0x88080100, // 0000 GETMBR R2 R0 R256 }),
0x780A0004, // 0001 JMPF R2 #0007 (be_nested_const_str("add_driver", 1654458371, 10)),
0x88080100, // 0002 GETMBR R2 R0 R256 (be_nested_const_str("string", 398550328, 6)),
0x8C080501, // 0003 GETMET R2 R2 R257 ( &(const binstruction[12]) { /* code */
0x5C100200, // 0004 MOVE R4 R1 0x88080100, // 0000 GETMBR R2 R0 R256
0x7C080400, // 0005 CALL R2 2 0x780A0004, // 0001 JMPF R2 #0007
0x70020003, // 0006 JMP #000B 0x88080100, // 0002 GETMBR R2 R0 R256
0x6008000A, // 0007 GETGBL R2 G10 0x8C080501, // 0003 GETMET R2 R2 R257
0x7C080000, // 0008 CALL R2 0 0x5C100200, // 0004 MOVE R4 R1
0x400C0401, // 0009 CONNECT R3 R2 R1 0x7C080400, // 0005 CALL R2 2
0x90020002, // 000A SETMBR R0 R256 R2 0x70020003, // 0006 JMP #000B
0x80000000, // 000B RET 0 R0 0x6008000A, // 0007 GETGBL R2 G10
}; 0x7C080000, // 0008 CALL R2 0
0x400C0401, // 0009 CONNECT R3 R2 R1
static const bproto add_driver_proto = { 0x90020002, // 000A SETMBR R0 R256 R2
NULL, // bgcobject *next 0x80000000, // 000B RET 0 R0
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 }, { "exec_rules", (bntvfunc) &exec_rules_closure },
{ "set_timer", (bntvfunc) &set_timer_closure }, { "set_timer", (bntvfunc) &set_timer_closure },
{ "run_deferred", (bntvfunc) &run_deferred_closure }, { "run_deferred", (bntvfunc) &run_deferred_closure },
{ "remove_timer", (bntvfunc) &remove_timer_closure },
{ "add_cmd", (bntvfunc) &add_cmd_closure }, { "add_cmd", (bntvfunc) &add_cmd_closure },
{ "remove_cmd", (bntvfunc) &remove_cmd_closure }, { "remove_cmd", (bntvfunc) &remove_cmd_closure },
{ "exec_cmd", (bntvfunc) &exec_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) exec_rules, closure(exec_rules_closure)
set_timer, closure(set_timer_closure) set_timer, closure(set_timer_closure)
run_deferred, closure(run_deferred_closure) run_deferred, closure(run_deferred_closure)
remove_timer, closure(remove_timer_closure)
add_cmd, closure(add_cmd_closure) add_cmd, closure(add_cmd_closure)
remove_cmd, closure(remove_cmd_closure) remove_cmd, closure(remove_cmd_closure)
exec_cmd, closure(exec_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 * Use precompiled objects to avoid creating these objects at
* runtime. Enable this macro can greatly optimize RAM usage. * runtime. Enable this macro can greatly optimize RAM usage.
* Default: 1 * Default: 1
// **/ **/
#define BE_USE_PRECOMPILED_OBJECT 1 #define BE_USE_PRECOMPILED_OBJECT 1
/* Macro: BE_DEBUG_RUNTIME_INFO /* Macro: BE_DEBUG_RUNTIME_INFO
@ -142,6 +142,14 @@
**/ **/
#define BE_USE_DEBUG_HOOK 0 #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 /* Macro: BE_USE_XXX_MODULE
* These macros control whether the related module is compiled. * These macros control whether the related module is compiled.
* When they are true, they will enable related modules. At this * When they are true, they will enable related modules. At this
@ -159,6 +167,7 @@
#define BE_USE_GC_MODULE 1 #define BE_USE_GC_MODULE 1
#define BE_USE_SOLIDIFY_MODULE 1 #define BE_USE_SOLIDIFY_MODULE 1
#define BE_USE_INTROSPECT_MODULE 1 #define BE_USE_INTROSPECT_MODULE 1
#define BE_USE_STRICT_MODULE 1
/* Macro: BE_EXPLICIT_XXX /* Macro: BE_EXPLICIT_XXX
* If these macros are defined, the corresponding function will * If these macros are defined, the corresponding function will

View File

@ -1,6 +1,21 @@
#- Native code used for testing and code solidification -# #- Native code used for testing and code solidification -#
#- Do not use it -# #- 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 class Tasmota
# add `chars_in_string(s:string,c:string) -> int`` # add `chars_in_string(s:string,c:string) -> int``
@ -79,7 +94,7 @@ class Tasmota
var sub_event = event var sub_event = event
var rl = string.split(rl_list[0],'#') var rl = string.split(rl_list[0],'#')
for it:rl 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 if found == nil return false end
sub_event = sub_event[found] sub_event = sub_event[found]
end end
@ -127,9 +142,9 @@ class Tasmota
return false return false
end end
def set_timer(delay,f) def set_timer(delay,f,id)
if !self._timers self._timers=[] end if !self._timers self._timers=[] end
self._timers.push([self.millis(delay),f]) self._timers.push(Timer(self.millis(delay),f,id))
end end
# run every 50ms tick # run every 50ms tick
@ -137,8 +152,8 @@ class Tasmota
if self._timers if self._timers
var i=0 var i=0
while i<self._timers.size() while i<self._timers.size()
if self.time_reached(self._timers[i][0]) if self.time_reached(self._timers[i].due)
f=self._timers[i][1] var f=self._timers[i].f
self._timers.remove(i) self._timers.remove(i)
f() f()
else else
@ -148,6 +163,20 @@ class Tasmota
end end
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 # Add command to list
def add_cmd(c,f) def add_cmd(c,f)
if !self._ccmd if !self._ccmd
@ -265,6 +294,47 @@ class Tasmota
end end
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 -# #- dispatch callback number n, with parameters v0,v1,v2,v3 -#
def cb_dispatch(n,v0,v1,v2,v3) def cb_dispatch(n,v0,v1,v2,v3)
if self._cb == nil return 0 end 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" #include "be_constobj.h"
static be_define_const_map_slots(be_class_bytes_map) { static be_define_const_map_slots(be_class_bytes_map) {
{ be_const_key(resize, 10), be_const_func(m_resize) }, { be_const_key(dot_p, -1), be_const_index(0) },
{ be_const_key(asstring, 11), be_const_func(m_asstring) }, { be_const_key(seti, 6), be_const_func(m_set) },
{ be_const_key(init, -1), be_const_func(m_init) }, { be_const_key(get, 7), be_const_func(m_getu) },
{ be_const_key(seti, -1), be_const_func(m_set) }, { be_const_key(size, 10), be_const_func(m_size) },
{ be_const_key(setitem, -1), be_const_func(m_setitem) }, { be_const_key(resize, 1), be_const_func(m_resize) },
{ be_const_key(item, 18), be_const_func(m_item) }, { be_const_key(opt_add, 22), be_const_func(m_merge) },
{ 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(getbits, -1), be_const_closure(getbits_closure) }, { be_const_key(getbits, -1), be_const_closure(getbits_closure) },
{ be_const_key(fromstring, -1), be_const_func(m_fromstring) }, { be_const_key(geti, 16), be_const_func(m_geti) },
{ be_const_key(opt_add, -1), be_const_func(m_merge) }, { be_const_key(setitem, -1), be_const_func(m_setitem) },
{ 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(add, -1), be_const_func(m_add) }, { 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( static be_define_const_map(
be_class_bytes_map, be_class_bytes_map,
23 25
); );
BE_EXPORT_VARIABLE be_define_const_class( BE_EXPORT_VARIABLE be_define_const_class(

View File

@ -1,71 +1,72 @@
#include "be_constobj.h" #include "be_constobj.h"
static be_define_const_map_slots(be_class_tasmota_map) { 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(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(publish, -1), be_const_func(l_publish) },
{ be_const_key(resolvecmnd, -1), be_const_func(l_resolveCmnd) }, { be_const_key(resp_cmnd_error, -1), be_const_func(l_respCmndError) },
{ be_const_key(_ccmd, -1), be_const_index(4) }, { be_const_key(exec_rules, -1), be_const_closure(exec_rules_closure) },
{ be_const_key(time_dump, -1), be_const_func(l_time_dump) }, { be_const_key(_cmd, -1), be_const_func(l_cmd) },
{ be_const_key(scale_uint, -1), be_const_func(l_scaleuint) }, { be_const_key(gc, -1), be_const_closure(gc_closure) },
{ be_const_key(load, -1), be_const_closure(load_closure) }, { be_const_key(remove_rule, -1), be_const_closure(remove_rule_closure) },
{ be_const_key(exec_rules, 37), be_const_closure(exec_rules_closure) }, { be_const_key(event, -1), be_const_closure(event_closure) },
{ be_const_key(gen_cb, 36), be_const_closure(gen_cb_closure) }, { be_const_key(log, 25), be_const_func(l_logInfo) },
{ be_const_key(cb_dispatch, -1), be_const_closure(cb_dispatch_closure) }, { 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(time_reached, -1), be_const_func(l_timereached) },
{ be_const_key(web_send_decimal, -1), be_const_func(l_webSendDecimal) }, { be_const_key(resp_cmnd_done, -1), be_const_func(l_respCmndDone) },
{ be_const_key(delay, 54), be_const_func(l_delay) }, { be_const_key(time_dump, -1), be_const_func(l_time_dump) },
{ be_const_key(run_deferred, 9), be_const_closure(run_deferred_closure) }, { be_const_key(resolvecmnd, -1), be_const_func(l_resolveCmnd) },
{ be_const_key(get_power, -1), be_const_func(l_getpower) }, { be_const_key(millis, -1), be_const_func(l_millis) },
{ be_const_key(wire2, -1), be_const_index(5) }, { be_const_key(get_option, 52), be_const_func(l_getoption) },
{ be_const_key(wire1, 52), be_const_index(6) }, { 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( static be_define_const_map(
be_class_tasmota_map, be_class_tasmota_map,
60 61
); );
BE_EXPORT_VARIABLE be_define_const_class( 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; 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; int type = BE_NIL;
bvalue *o = be_indexof(vm, index); 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) { if (type == BE_NONE) {
type = BE_NIL; 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; return type;
} }
BERRY_API bbool be_getmember(bvm *vm, int index, const char *k) 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) 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) 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 uint8_t buf[]; // the actual data
} buf_impl; } 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 ** 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) 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); buf_impl * buf = bytes_check_data(vm, 0);
size_t len = buf->len; 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); char * hex_out = be_pushbuffer(vm, hex_len);
size_t l = be_strlcpy(hex_out, "bytes('", 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); l += be_strlcpy(&hex_out[l], "')", hex_len - l);
be_pushnstring(vm, hex_out, l); /* make escape string from buffer */ 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); 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 * Advanced API
*/ */
@ -973,6 +1228,8 @@ void be_load_byteslib(bvm *vm)
{ "tostring", m_tostring }, { "tostring", m_tostring },
{ "asstring", m_asstring }, { "asstring", m_asstring },
{ "fromstring", m_fromstring }, { "fromstring", m_fromstring },
{ "tob64", m_tob64 },
{ "fromb64", m_fromb64 },
{ "add", m_add }, { "add", m_add },
{ "get", m_getu }, { "get", m_getu },
{ "geti", m_geti }, { "geti", m_geti },
@ -1006,6 +1263,8 @@ class be_class_bytes (scope: global, name: bytes) {
tostring, func(m_tostring) tostring, func(m_tostring)
asstring, func(m_asstring) asstring, func(m_asstring)
fromstring, func(m_fromstring) fromstring, func(m_fromstring)
tob64, func(m_tob64)
fromb64, func(m_fromb64)
add, func(m_add) add, func(m_add)
get, func(m_getu) get, func(m_getu)
geti, func(m_geti) 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) static binstance* newobjself(bvm *vm, bclass *c)
{ {
size_t size = sizeof(binstance) + sizeof(bvalue) * (c->nvar - 1); 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); binstance *obj = cast_instance(gco);
be_assert(obj != NULL); be_assert(obj != NULL);
if (obj) { /* initialize members */ if (obj) { /* initialize members */
bvalue *v = obj->members, *end = v + c->nvar; 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; } while (v < end) { var_setnil(v); ++v; } /* Initialize all instance variables to `nil` */
obj->_class = c; obj->_class = c; /* set its class object */
obj->super = NULL; obj->super = NULL; /* no super class instance for now */
obj->sub = NULL; obj->sub = NULL; /* no subclass instance for now */
} }
return obj; 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) static binstance* newobject(bvm *vm, bclass *c)
{ {
binstance *obj, *prev; binstance *obj, *prev;
@ -201,23 +204,26 @@ static binstance* newobject(bvm *vm, bclass *c)
obj = prev = newobjself(vm, c); obj = prev = newobjself(vm, c);
var_setinstance(vm->top, obj); var_setinstance(vm->top, obj);
be_incrtop(vm); /* protect new objects from GC */ 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 = newobjself(vm, c);
prev->super->sub = prev; prev->super->sub = prev; /* link the super/sub classes instances */
prev = prev->super; prev = prev->super;
} }
be_stackpop(vm, 1); be_stackpop(vm, 1);
return obj; 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) bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
{ {
bvalue init; bvalue init;
size_t pos = reg - vm->reg; size_t pos = reg - vm->reg;
binstance *obj = newobject(vm, c); binstance *obj = newobject(vm, c); /* create empty object hierarchy from class hierarchy */
reg = vm->reg + pos - mode; /* the stack may have changed */ 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, obj);
var_setinstance(reg + mode, obj); var_setinstance(reg + mode, obj); /* copy to reg and reg+1 if mode==1 */
/* find constructor */ /* find constructor */
obj = instance_member(vm, obj, str_literal(vm, "init"), &init); obj = instance_member(vm, obj, str_literal(vm, "init"), &init);
if (obj && var_type(&init) != MT_VARIABLE) { 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; 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 be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst)
{ {
int type; int type;

View File

@ -23,8 +23,8 @@
#define min(a, b) ((a) < (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b))
#define notexpr(e) isset((e)->not, NOT_EXPR) #define notexpr(e) isset((e)->not, NOT_EXPR)
#define notmask(e) isset((e)->not, NOT_MASK) #define notmask(e) isset((e)->not, NOT_MASK)
#define exp2anyreg(f, e) exp2reg(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, (f)->freereg) #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 hasjump(e) ((e)->t != (e)->f || notexpr(e))
#define code_bool(f, r, b, j) codeABC(f, OP_LDBOOL, r, b, j) #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) #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) #define codelineinfo(finfo)
#endif #endif
/* Add new instruction in the code vector */
static int codeinst(bfuncinfo *finfo, binstruction ins) static int codeinst(bfuncinfo *finfo, binstruction ins)
{ {
/* put new instruction in code array */ /* 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)); 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) static void code_move(bfuncinfo *finfo, int a, int b)
{ {
if (finfo->pc) { if (finfo->pc) { /* If not the first instruction of the function */
binstruction *i = be_vector_end(&finfo->code); binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */
bopcode op = IGET_OP(*i); bopcode op = IGET_OP(*i);
if (op <= OP_LDNIL) { /* binop or unop */ if (op <= OP_LDNIL) { /* binop or unop */
/* remove redundant MOVE instruction */ /* 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) static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
{ {
/* release temporary register */ /* 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) static void allocstack(bfuncinfo *finfo, int count)
{ {
int nstack = finfo->freereg + 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 be_code_allocregs(bfuncinfo *finfo, int count)
{ {
int base = finfo->freereg; int base = finfo->freereg;
@ -227,6 +236,8 @@ void be_code_patchjump(bfuncinfo *finfo, int jmp)
patchlistaux(finfo, jmp, finfo->pc, finfo->pc); 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) static int newconst(bfuncinfo *finfo, bvalue *k)
{ {
int idx = be_vector_count(&finfo->kvec); int idx = be_vector_count(&finfo->kvec);
@ -239,6 +250,8 @@ static int newconst(bfuncinfo *finfo, bvalue *k)
return idx; 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) static int findconst(bfuncinfo *finfo, bexpdesc *e)
{ {
int i, count = be_vector_count(&finfo->kvec); int i, count = be_vector_count(&finfo->kvec);
@ -273,10 +286,11 @@ static int findconst(bfuncinfo *finfo, bexpdesc *e)
return -1; return -1;
} }
/* convert expdesc to constant and return kreg index (either constant kindex or register number) */
static int exp2const(bfuncinfo *finfo, bexpdesc *e) static int exp2const(bfuncinfo *finfo, bexpdesc *e)
{ {
int idx = findconst(finfo, e); int idx = findconst(finfo, e); /* does the constant already exist? */
if (idx == -1) { if (idx == -1) { /* if not add it */
bvalue k; bvalue k;
switch (e->type) { switch (e->type) {
case ETINT: case ETINT:
@ -291,16 +305,16 @@ static int exp2const(bfuncinfo *finfo, bexpdesc *e)
k.type = BE_STRING; k.type = BE_STRING;
k.v.s = e->v.s; k.v.s = e->v.s;
break; break;
default: default: /* all other values are filled later */
break; break;
} }
idx = newconst(finfo, &k); idx = newconst(finfo, &k); /* create new constant */
} }
if (idx < 256) { if (idx < 256) { /* if constant number fits in KB or KC */
e->type = ETCONST; e->type = ETCONST; /* new type is constant by index */
e->v.idx = setK(idx); e->v.idx = setK(idx);
} else { /* index value is too large */ } 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); e->v.idx = be_code_allocregs(finfo, 1);
codeABx(finfo, OP_LDCONST, e->v.idx, idx); 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) 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) { if (dst > finfo->freereg) {
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 */ 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) static bbool constint(bfuncinfo *finfo, bint i)
{ {
/* cache common numbers */ /* cache common numbers */
@ -349,8 +395,14 @@ static bbool constint(bfuncinfo *finfo, bint i)
return bfalse; 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) 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); be_assert(e != NULL);
switch (e->type) { switch (e->type) {
case ETINT: case ETINT:
@ -403,7 +455,7 @@ static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst) static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{ {
int reg = var2reg(finfo, e, 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 pcf = NO_JUMP; /* position of an eventual LOAD false */
int pct = NO_JUMP; /* position of an eventual LOAD true */ int pct = NO_JUMP; /* position of an eventual LOAD true */
int jpt = appendjump(finfo, jumpboolop(e, 1), e); int jpt = appendjump(finfo, jumpboolop(e, 1), e);
@ -420,31 +472,47 @@ static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
return reg; 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) { 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); be_code_freeregs(finfo, 1);
} else if (con1) { } else if (con1) {
dst = e1->v.idx; cand_dst = e1->v.idx;
} else if (con2) { } else if (con2) {
dst = e2->v.idx; cand_dst = e2->v.idx;
} else { } 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;
} }
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 src2 = exp2anyreg(finfo, e2);
int dst = codedestreg(finfo, e1, e2); dst = codedestreg(finfo, e1, e2, dst);
codeABC(finfo, op, dst, src1, src2); codeABC(finfo, op, dst, src1, src2);
e1->type = ETREG; 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) 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) { switch (op) {
case OptAnd: 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 OptNE: case OptGT: case OptGE: case OptConnect:
case OptBitAnd: case OptBitOr: case OptBitXor: case OptBitAnd: case OptBitOr: case OptBitXor:
case OptShiftL: case OptShiftR: case OptShiftL: case OptShiftR:
binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2); binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2, dst);
break; break;
default: 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) static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
{ {
int src = exp2anyreg(finfo, e); int src = exp2anyreg(finfo, e);
@ -495,6 +566,9 @@ static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
e->v.idx = dst; 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) static void code_not(bexpdesc *e)
{ {
switch (e->type) { switch (e->type) {
@ -514,6 +588,7 @@ static void code_not(bexpdesc *e)
e->type = ETBOOL; e->type = ETBOOL;
} }
/* Negative value of literal or emit NEG opcode */
static int code_neg(bfuncinfo *finfo, bexpdesc *e) static int code_neg(bfuncinfo *finfo, bexpdesc *e)
{ {
switch (e->type) { switch (e->type) {
@ -527,6 +602,7 @@ static int code_neg(bfuncinfo *finfo, bexpdesc *e)
return 0; return 0;
} }
/* Bit flip of literal or emit FLIP opcode */
static int code_flip(bfuncinfo *finfo, bexpdesc *e) static int code_flip(bfuncinfo *finfo, bexpdesc *e)
{ {
switch (e->type) { switch (e->type) {
@ -539,6 +615,7 @@ static int code_flip(bfuncinfo *finfo, bexpdesc *e)
return 0; return 0;
} }
/* Apply unary operator: not, neg or bitflip */
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e) int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e)
{ {
switch (op) { 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); 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 be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{ {
int src = exp2reg(finfo, 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) { 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) { switch (e1->type) {
case ETLOCAL: /* It can't be ETREG. */ case ETLOCAL: /* It can't be ETREG. */
if (e1->v.idx != src) { 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; 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); setsupvar(finfo, OP_SETGBL, e1, src);
break; 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); setbgblvar(finfo, OP_SETNGBL, e1, src);
break; break;
case ETUPVAL: case ETUPVAL:
@ -618,6 +699,9 @@ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
return 0; 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 be_code_nextreg(bfuncinfo *finfo, bexpdesc *e)
{ {
int dst = finfo->freereg; int dst = finfo->freereg;
@ -641,6 +725,9 @@ int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e)
return dst; 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) void be_code_call(bfuncinfo *finfo, int base, int argc)
{ {
codeABC(finfo, OP_CALL, base, argc, 0); 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) static void package_suffix(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{ {
int key = exp2anyreg(finfo, k); int key = exp2anyreg(finfo, k);
@ -730,12 +819,14 @@ int be_code_nglobal(bfuncinfo *finfo, bexpdesc *k)
return exp2anyreg(finfo, 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) void be_code_member(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{ {
package_suffix(finfo, c, k); package_suffix(finfo, c, k);
c->type = ETMEMBER; c->type = ETMEMBER;
} }
/* Package a INDEX suffix object from `c` with key `k` */
void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k) void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{ {
package_suffix(finfo, c, k); package_suffix(finfo, c, k);
@ -746,15 +837,15 @@ void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c)
{ {
int src; int src;
bvalue var; bvalue var;
var_setclass(&var, c); var_setclass(&var, c); /* new var of CLASS type */
src = newconst(finfo, &var); src = newconst(finfo, &var); /* allocate a new constant and return kreg */
if (dst->type == ETLOCAL) { if (dst->type == ETLOCAL) { /* if target is a local variable, just assign */
codeABx(finfo, OP_LDCONST, dst->v.idx, src); 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_LDCONST, finfo->freereg, src);
codeABx(finfo, OP_SETGBL, finfo->freereg, dst->v.idx); 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) 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); 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) void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v)
{ {
int dst, src = exp2anyreg(finfo, m); 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) void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{ {
if (e1) { if (e1) {
@ -806,7 +907,7 @@ void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
int src2 = e2 ? exp2anyreg(finfo, e2) : 0; int src2 = e2 ? exp2anyreg(finfo, e2) : 0;
codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2); codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2);
} else { } 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 */ /* release the register occupied by the expression */
free_expreg(finfo, e1); free_expreg(finfo, e1);

View File

@ -14,7 +14,7 @@
int be_code_allocregs(bfuncinfo *finfo, int count); int be_code_allocregs(bfuncinfo *finfo, int count);
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e); 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_unop(bfuncinfo *finfo, int op, bexpdesc *e);
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2); int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2);
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e); 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) BERRY_API void be_exit(bvm *vm, int status)
{ {
if (vm->errjmp) { if (vm->errjmp) {
@ -99,6 +101,8 @@ void be_throw_message(bvm *vm, int errorcode, const char *msg)
be_throw(vm, errorcode); 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) int be_execprotected(bvm *vm, bpfunc f, void *data)
{ {
struct blongjmp jmp; struct blongjmp jmp;
@ -292,6 +296,7 @@ static void m_pcall(bvm *vm, void *data)
be_dofunc(vm, p->v, p->argc); 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 be_protectedcall(bvm *vm, bvalue *v, int argc)
{ {
int res; int res;
@ -308,7 +313,8 @@ int be_protectedcall(bvm *vm, bvalue *v, int argc)
} }
#if BE_DEBUG && defined(be_assert) #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* be_incrtop(bvm *vm)
{ {
bvalue *top = vm->top++; bvalue *top = vm->top++;
@ -317,6 +323,7 @@ bvalue* be_incrtop(bvm *vm)
} }
#endif #endif
/* TODO what is the difference with be_stack_push? */
void be_stackpush(bvm *vm) void be_stackpush(bvm *vm)
{ {
/* make sure there is enough stack space */ /* make sure there is enough stack space */
@ -324,6 +331,7 @@ void be_stackpush(bvm *vm)
be_incrtop(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) void be_stack_require(bvm *vm, int count)
{ {
if (vm->top + count >= vm->stacktop) { 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) static void update_callstack(bvm *vm, intptr_t offset)
{ {
bcallframe *cf = be_stack_top(&vm->callstack); 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) static void stack_resize(bvm *vm, size_t size)
{ {
intptr_t offset; intptr_t offset;
bvalue *old = vm->stack; bvalue *old = vm->stack; /* save original pointer of stack before resize */
size_t os = (vm->stacktop - old) * sizeof(bvalue); 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); vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size); /* reallocate with the new size */
vm->stacktop = vm->stack + size; vm->stacktop = vm->stack + size; /* compute new stacktop */
offset = ptr_offset(vm->stack, old); offset = ptr_offset(vm->stack, old); /* compute the address difference between old and ne stack addresses */
/* update callframes */ /* update callframes */
update_callstack(vm, offset); update_callstack(vm, offset);
/* update open upvalues */ /* update open upvalues */
update_upvalues(vm, offset); 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) void be_stack_expansion(bvm *vm, int n)
{ {
size_t size = vm->stacktop - vm->stack; 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" #include "../generate/be_fixed_global.h"
#endif #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]; *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 */ /* load module to vm->top */
int be_module_load(bvm *vm, bstring *path) 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); res = load_native(vm, path);
if (res == BE_IO_ERROR) if (res == BE_IO_ERROR)
res = load_package(vm, path); res = load_package(vm, path);
if (res == BE_OK) if (res == BE_OK) {
cache_module(vm, path); cache_module(vm, path);
/* on first load of the module, try running the '()' function */
module_init(vm);
}
} }
return res; 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(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(EXBLK), /* A, Bx | ... */
OPCODE(CATCH), /* A, B, C | ... */ 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(CLASS), /* Bx | init class in K[Bx] */
OPCODE(GETNGBL), /* A, B | R(A) <- GLOBAL[B] by name */ OPCODE(GETNGBL), /* A, B | R(A) <- GLOBAL[B] by name */
OPCODE(SETNGBL) /* 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 */ 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) static void match_notoken(bparser *parser, btokentype type)
{ {
if (next_type(parser) == type) { /* error when token is match */ 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) static void check_symbol(bparser *parser, bexpdesc *e)
{ {
if (e->type == ETVOID && e->v.s == NULL) { /* error when token is not a symbol */ 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) static void check_var(bparser *parser, bexpdesc *e)
{ {
check_symbol(parser, e); /* check the token is a symbol */ check_symbol(parser, e); /* check the token is a symbol */
@ -172,14 +176,15 @@ void end_varinfo(bparser *parser, int beginpc)
#endif #endif
/* Initialize bblockinfo structure */
static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type) static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type)
{ {
binfo->prev = finfo->binfo; binfo->prev = finfo->binfo; /* save previous block */
finfo->binfo = binfo; finfo->binfo = binfo; /* tell parser this is the current block */
binfo->type = (bbyte)type; binfo->type = (bbyte)type;
binfo->hasupval = 0; binfo->hasupval = 0;
binfo->beginpc = finfo->pc; binfo->beginpc = finfo->pc; /* set starting pc for this block */
binfo->nactlocals = (bbyte)be_list_count(finfo->local); binfo->nactlocals = (bbyte)be_list_count(finfo->local); /* count number of local variables in previous block */
if (type & BLOCK_LOOP) { if (type & BLOCK_LOOP) {
binfo->breaklist = NO_JUMP; binfo->breaklist = NO_JUMP;
binfo->continuelist = 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); be_code_patchlist(finfo, binfo->continuelist, binfo->beginpc);
} }
end_varinfo(parser, beginpc); end_varinfo(parser, beginpc);
be_list_resize(parser->vm, finfo->local, binfo->nactlocals); 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; finfo->freereg = binfo->nactlocals; /* adjust first free register accordingly */
finfo->binfo = binfo->prev; finfo->binfo = binfo->prev; /* restore previous block */
} }
static void end_block(bparser *parser) static void end_block(bparser *parser)
@ -207,6 +212,8 @@ static void end_block(bparser *parser)
end_block_ex(parser, -1); 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) static bstring* parser_source(bparser *parser)
{ {
if (parser->finfo) { if (parser->finfo) {
@ -215,29 +222,30 @@ static bstring* parser_source(bparser *parser)
return be_newstr(parser->vm, parser->lexer.fname); 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) static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
{ {
bvm *vm = parser->vm; bvm *vm = parser->vm;
bproto *proto = be_newproto(vm); bproto *proto = be_newproto(vm);
var_setproto(vm->top, proto); var_setproto(vm->top, proto);
be_stackpush(vm); 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->code = be_vector_data(&finfo->code);
proto->codesize = be_vector_capacity(&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->ktab = be_vector_data(&finfo->kvec);
proto->nconst = be_vector_capacity(&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->ptab = be_vector_data(&finfo->pvec);
proto->nproto = be_vector_capacity(&finfo->pvec); proto->nproto = be_vector_capacity(&finfo->pvec);
proto->source = parser_source(parser); proto->source = parser_source(parser); /* keep a copy of source for function */
finfo->local = be_list_new(vm); finfo->local = be_list_new(vm); /* list for local variables */
var_setlist(vm->top, finfo->local); var_setlist(vm->top, finfo->local); /* push list of local variables on the stack (avoid gc) */
be_stackpush(vm); 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); var_setmap(vm->top, finfo->upval);
be_stackpush(vm); be_stackpush(vm);
finfo->prev = parser->finfo; finfo->prev = parser->finfo; /* init finfo */
finfo->lexer = &parser->lexer; finfo->lexer = &parser->lexer;
finfo->proto = proto; finfo->proto = proto;
finfo->freereg = 0; finfo->freereg = 0;
@ -258,6 +266,7 @@ static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
begin_block(finfo, binfo, 0); begin_block(finfo, binfo, 0);
} }
/* compute the upval structure */
static void setupvals(bfuncinfo *finfo) static void setupvals(bfuncinfo *finfo)
{ {
bproto *proto = finfo->proto; bproto *proto = finfo->proto;
@ -282,6 +291,7 @@ static void setupvals(bfuncinfo *finfo)
} }
} }
/* Function is complete, finalize bproto */
static void end_func(bparser *parser) static void end_func(bparser *parser)
{ {
bvm *vm = parser->vm; bvm *vm = parser->vm;
@ -289,9 +299,9 @@ static void end_func(bparser *parser)
bproto *proto = finfo->proto; bproto *proto = finfo->proto;
be_code_ret(finfo, NULL); /* append a return to last code */ be_code_ret(finfo, NULL); /* append a return to last code */
end_block(parser); end_block(parser); /* close block */
setupvals(finfo); setupvals(finfo); /* close upvals */
proto->code = be_vector_release(vm, &finfo->code); proto->code = be_vector_release(vm, &finfo->code); /* compact all vectors and return NULL if empty */
proto->codesize = finfo->pc; proto->codesize = finfo->pc;
proto->ktab = be_vector_release(vm, &finfo->kvec); proto->ktab = be_vector_release(vm, &finfo->kvec);
proto->nconst = be_vector_count(&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->varinfo = be_vector_release(vm, &finfo->varvec);
proto->nvarinfo = be_vector_count(&finfo->varvec); proto->nvarinfo = be_vector_count(&finfo->varvec);
#endif #endif
parser->finfo = parser->finfo->prev; parser->finfo = parser->finfo->prev; /* restore previous `finfo` */
be_stackpop(vm, 2); /* pop upval and local */ 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) static btokentype get_binop(bparser *parser)
{ {
btokentype op = next_type(parser); btokentype op = next_type(parser);
@ -318,6 +329,8 @@ static btokentype get_binop(bparser *parser)
return OP_NOT_BINARY; 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) static btokentype get_unary_op(bparser *parser)
{ {
btokentype op = next_type(parser); btokentype op = next_type(parser);
@ -327,6 +340,8 @@ static btokentype get_unary_op(bparser *parser)
return OP_NOT_UNARY; 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) static btokentype get_assign_op(bparser *parser)
{ {
btokentype op = next_type(parser); btokentype op = next_type(parser);
@ -336,6 +351,7 @@ static btokentype get_assign_op(bparser *parser)
return OP_NOT_ASSIGN; 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) static void init_exp(bexpdesc *e, exptype_t type, bint i)
{ {
e->type = (bbyte)type; e->type = (bbyte)type;
@ -346,6 +362,8 @@ static void init_exp(bexpdesc *e, exptype_t type, bint i)
e->v.i = 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) static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
{ {
int i, count = be_list_count(finfo->local); 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 */ 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) static int new_localvar(bparser *parser, bstring *name)
{ {
bfuncinfo *finfo = parser->finfo; 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) { if (reg == -1) {
bvalue *var; 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 */ reg = be_list_count(finfo->local); /* new local index */
var = be_list_push(parser->vm, finfo->local, NULL); var = be_list_push(parser->vm, finfo->local, NULL);
var_setstr(var, name); var_setstr(var, name);
@ -371,10 +399,13 @@ static int new_localvar(bparser *parser, bstring *name)
be_code_allocregs(finfo, 1); /* use a register */ be_code_allocregs(finfo, 1); /* use a register */
} }
begin_varinfo(parser, name); begin_varinfo(parser, name);
} else {
push_error(parser, "redefinition of '%s'", str(name));
} }
return reg; return reg;
} }
/* Find upval by name, if found return its index number, or -1 */
static int find_upval(bfuncinfo *finfo, bstring *s) static int find_upval(bfuncinfo *finfo, bstring *s)
{ {
bvm *vm = finfo->lexer->vm; bvm *vm = finfo->lexer->vm;
@ -385,6 +416,8 @@ static int find_upval(bfuncinfo *finfo, bstring *s)
return -1; 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) static void mark_upval(bfuncinfo *finfo, int level)
{ {
bblockinfo *binfo = finfo->prev->binfo; bblockinfo *binfo = finfo->prev->binfo;
@ -413,12 +446,14 @@ static int new_upval(bvm *vm, bfuncinfo *finfo, bstring *name, bexpdesc *var)
return index; 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) static void new_var(bparser *parser, bstring *name, bexpdesc *var)
{ {
bfuncinfo *finfo = parser->finfo; bfuncinfo *finfo = parser->finfo;
if (finfo->prev || finfo->binfo->prev || parser->islocal) { if (finfo->prev || finfo->binfo->prev || parser->islocal) {
init_exp(var, ETLOCAL, 0); 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 { } else {
init_exp(var, ETGLOBAL, 0); init_exp(var, ETGLOBAL, 0);
var->v.idx = be_global_new(parser->vm, name); 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) static void singlevar(bparser *parser, bexpdesc *var)
{ {
bexpdesc key; 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) static void func_varlist(bparser *parser)
{ {
bexpdesc v; bexpdesc v;
@ -502,36 +544,36 @@ static void func_varlist(bparser *parser)
str = next_token(parser).u.s; str = next_token(parser).u.s;
match_token(parser, TokenId); /* match and skip ID */ match_token(parser, TokenId); /* match and skip ID */
/* new local variable */ /* new local variable */
if (find_localvar(parser->finfo, str, 0) == -1) { new_var(parser, str, &v);
new_var(parser, str, &v);
} else {
push_error(parser, "redefinition of '%s'", str(str));
}
} }
} }
match_token(parser, OptRBK); /* skip ')' */ match_token(parser, OptRBK); /* skip ')' */
parser->finfo->proto->argc = parser->finfo->freereg; 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) static bproto* funcbody(bparser *parser, bstring *name, int type)
{ {
bfuncinfo finfo; bfuncinfo finfo;
bblockinfo binfo; bblockinfo binfo;
/* '(' varlist ')' block 'end' */ /* '(' varlist ')' block 'end' */
begin_func(parser, &finfo, &binfo); begin_func(parser, &finfo, &binfo); /* init new function context */
finfo.proto->name = name; 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")); new_localvar(parser, parser_newstr(parser, "self"));
} }
func_varlist(parser); func_varlist(parser); /* parse arg list */
stmtlist(parser); stmtlist(parser); /* parse statement without final `end` */
end_func(parser); end_func(parser); /* close function context */
match_token(parser, KeyEnd); /* skip 'end' */ 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) static void anon_func(bparser *parser, bexpdesc *e)
{ {
bproto *proto; bproto *proto;
@ -559,11 +601,7 @@ static void lambda_varlist(bparser *parser)
str = next_token(parser).u.s; str = next_token(parser).u.s;
match_token(parser, TokenId); /* match and skip ID */ match_token(parser, TokenId); /* match and skip ID */
/* new local variable */ /* new local variable */
if (find_localvar(parser->finfo, str, 0) == -1) { new_var(parser, str, &v);
new_var(parser, str, &v);
} else {
push_error(parser, "redefinition of '%s'", str(str));
}
} }
} }
match_token(parser, OptArrow); /* skip '->' */ match_token(parser, OptArrow); /* skip '->' */
@ -590,6 +628,9 @@ static void lambda_expr(bparser *parser, bexpdesc *e)
be_stackpop(parser->vm, 1); 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) static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
{ {
int idx; int idx;
@ -601,17 +642,19 @@ static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
init_exp(e, ETGLOBAL, idx); init_exp(e, ETGLOBAL, idx);
idx = be_code_nextreg(finfo, e); idx = be_code_nextreg(finfo, e);
be_code_call(finfo, idx, 0); 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) static void list_nextmember(bparser *parser, bexpdesc *l)
{ {
bexpdesc e, v = *l; bexpdesc e, v = *l;
bfuncinfo *finfo = parser->finfo; bfuncinfo *finfo = parser->finfo;
expr(parser, &e); /* value */ expr(parser, &e); /* value */
check_var(parser, &e); check_var(parser, &e); /* check that we don´t have an unknown symbol */
be_code_binop(finfo, OptConnect, &v, &e); be_code_binop(finfo, OptConnect, &v, &e, -1); /* add it to list with CONNECT */
be_code_freeregs(finfo, 1); be_code_freeregs(finfo, 1); /* since left arg is LOCAL, an ETREG was allocated. Free it */
} }
static void map_nextmember(bparser *parser, bexpdesc *l) static void map_nextmember(bparser *parser, bexpdesc *l)
@ -619,25 +662,25 @@ static void map_nextmember(bparser *parser, bexpdesc *l)
bexpdesc e, v = *l; bexpdesc e, v = *l;
bfuncinfo *finfo = parser->finfo; bfuncinfo *finfo = parser->finfo;
expr(parser, &e); /* key */ expr(parser, &e); /* key */
check_var(parser, &e); check_var(parser, &e); /* check if value is valid */
be_code_index(finfo, &v, &e); be_code_index(finfo, &v, &e); /* package as `v` as INDEX suffix for value `e` */
match_token(parser, OptColon); /* ':' */ match_token(parser, OptColon); /* ':' */
expr(parser, &e); /* value */ expr(parser, &e); /* value in `e` */
check_var(parser, &e); check_var(parser, &e); /* check if value is correct */
be_code_setvar(finfo, &v, &e); be_code_setvar(finfo, &v, &e); /* set suffi INDEX value to e */
} }
static void list_expr(bparser *parser, bexpdesc *e) static void list_expr(bparser *parser, bexpdesc *e)
{ {
/* '[' {expr ','} [expr] ']' */ /* '[' {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) { while (next_type(parser) != OptRSB) {
list_nextmember(parser, e); list_nextmember(parser, e);
if (!match_skip(parser, OptComma)) { /* ',' */ if (!match_skip(parser, OptComma)) { /* ',' */
break; break;
} }
} }
e->type = ETREG; e->type = ETREG; /* then turned to REG */
match_token(parser, OptRSB); /* skip ']' */ match_token(parser, OptRSB); /* skip ']' */
} }
@ -655,14 +698,16 @@ static void map_expr(bparser *parser, bexpdesc *e)
match_token(parser, OptRBR); /* skip '}' */ 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) static int exprlist(bparser *parser, bexpdesc *e)
{ {
bfuncinfo *finfo = parser->finfo; bfuncinfo *finfo = parser->finfo;
int n = 1; int n = 1;
/* expr { ',' expr } */ /* expr { ',' expr } */
expr(parser, e); expr(parser, e); /* parse expr */
check_var(parser, e); check_var(parser, e); /* check if valid */
be_code_nextreg(finfo, e); be_code_nextreg(finfo, e); /* move result to next reg */
while (match_skip(parser, OptComma)) { /* ',' */ while (match_skip(parser, OptComma)) { /* ',' */
expr(parser, e); expr(parser, e);
check_var(parser, e); check_var(parser, e);
@ -672,6 +717,9 @@ static int exprlist(bparser *parser, bexpdesc *e)
return n; 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) static void call_expr(bparser *parser, bexpdesc *e)
{ {
bexpdesc args; bexpdesc args;
@ -685,14 +733,15 @@ static void call_expr(bparser *parser, bexpdesc *e)
if (ismember) { if (ismember) {
base = be_code_getmethod(finfo, e); base = be_code_getmethod(finfo, e);
} else { } 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 '(' */ scan_next_token(parser); /* skip '(' */
if (next_type(parser) != OptRBK) { if (next_type(parser) != OptRBK) { /* if arg list is not empty */
argc = exprlist(parser, &args); argc = exprlist(parser, &args); /* push each argument as new reg and return number of args */
} }
match_token(parser, OptRBK); /* skip ')' */ match_token(parser, OptRBK); /* skip ')' */
argc += ismember; argc += ismember; /* if method there is an additional implicit arg */
be_code_call(finfo, base, argc); be_code_call(finfo, base, argc);
if (e->type != ETREG) { if (e->type != ETREG) {
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) static void member_expr(bparser *parser, bexpdesc *e)
{ {
bstring *str; bstring *str;
@ -823,9 +874,11 @@ static void suffix_alloc_reg(bparser *parser, bexpdesc *l)
/* compound assignment */ /* compound assignment */
static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r) 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 */ if (op != OptAssign) { /* check left variable */
check_var(parser, l); check_var(parser, l);
/* cache the register of the object when continuously assigning */ /* cache the register of the object when continuously assigning */
dst = parser->finfo->freereg;
suffix_alloc_reg(parser, l); suffix_alloc_reg(parser, l);
} }
expr(parser, r); /* right expression */ expr(parser, r); /* right expression */
@ -834,20 +887,37 @@ static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r)
bexpdesc e = *l; bexpdesc e = *l;
op = op < OptAndAssign ? op - OptAddAssign + OptAdd op = op < OptAndAssign ? op - OptAddAssign + OptAdd
: op - OptAndAssign + OptBitAnd; : 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; *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) static int check_newvar(bparser *parser, bexpdesc *e)
{ {
if (e->type == ETGLOBAL) { if (e->type == ETGLOBAL) {
if (e->v.idx < be_builtin_count(parser->vm)) { if (e->v.idx < be_builtin_count(parser->vm)) {
e->v.s = be_builtin_name(parser->vm, e->v.idx); 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 btrue;
} }
return bfalse; 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; 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) static void sub_expr(bparser *parser, bexpdesc *e, int prio)
{ {
bfuncinfo *finfo = parser->finfo; bfuncinfo *finfo = parser->finfo;
btokentype op = get_unary_op(parser); btokentype op = get_unary_op(parser); /* check if first token in unary op */
if (op != OP_NOT_UNARY) { if (op != OP_NOT_UNARY) { /* unary op found */
int line, res; int line, res;
scan_next_token(parser); scan_next_token(parser); /* move to next token */
line = parser->lexer.linenumber; line = parser->lexer.linenumber; /* remember line number for error reporting */
sub_expr(parser, e, UNARY_OP_PRIO); sub_expr(parser, e, UNARY_OP_PRIO); /* parse subexpr with new prio */
check_var(parser, e); check_var(parser, e); /* check that the value is ok */
res = be_code_unop(finfo, op, e); res = be_code_unop(finfo, op, e); /* apply unary op with optimizations if the token is a value */
if (res) { /* encode unary op */ if (res) { /* encode unary op */
parser->lexer.linenumber = line; parser->lexer.linenumber = line;
push_error(parser, "wrong type argument to unary '%s'", push_error(parser, "wrong type argument to unary '%s'",
res == 1 ? "negative" : "bit-flip"); res == 1 ? "negative" : "bit-flip");
} }
} else { } else {
suffix_expr(parser, e); suffix_expr(parser, e); /* parse left part of binop */
} }
op = get_binop(parser); op = get_binop(parser); /* check if binop */
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) { while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) { /* is binop applicable */
bexpdesc e2; bexpdesc e2;
check_var(parser, e); check_var(parser, e); /* check that left part is valid */
scan_next_token(parser); scan_next_token(parser); /* move to next token */
be_code_prebinop(finfo, op, e); /* and or */ be_code_prebinop(finfo, op, e); /* and or */
init_exp(&e2, ETVOID, 0); init_exp(&e2, ETVOID, 0);
sub_expr(parser, &e2, binary_op_prio(op)); sub_expr(parser, &e2, binary_op_prio(op)); /* parse right side */
check_var(parser, &e2); check_var(parser, &e2); /* check if valid */
be_code_binop(finfo, op, e, &e2); /* encode binary op */ be_code_binop(finfo, op, e, &e2, -1); /* encode binary op */
op = get_binop(parser); op = get_binop(parser); /* is there a following binop? */
} }
if (prio == ASSIGN_OP_PRIO) { if (prio == ASSIGN_OP_PRIO) {
cond_expr(parser, e); 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) static void expr(bparser *parser, bexpdesc *e)
{ {
init_exp(e, ETVOID, 0); init_exp(e, ETVOID, 0);

View File

@ -12,7 +12,7 @@
#include "be_string.h" #include "be_string.h"
typedef enum { typedef enum {
ETVOID, ETVOID, /* unknown (new variable or error) */
ETNIL, ETNIL,
ETBOOL, ETBOOL,
ETREAL, ETREAL,
@ -20,13 +20,13 @@ typedef enum {
ETSTRING, ETSTRING,
ETPROTO, ETPROTO,
ETCONST, ETCONST,
ETLOCAL, ETLOCAL, /* local variable, allocated until end of scope */
ETGLOBAL, ETGLOBAL, /* global by index number */
ETUPVAL, ETUPVAL,
ETMEMBER, ETMEMBER, /* member accessor (by name) */
ETINDEX, ETINDEX, /* index accessor (ex array index) */
ETREG, ETREG, /* temporary register, can be freed if top of stack */
ETNGLOBAL ETNGLOBAL /* named global */
} exptype_t; } exptype_t;
typedef struct { 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, ...) \ #define vm_error(vm, except, ...) \
be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__)) be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__))
#define RA() (reg + IGET_RA(ins)) #define RA() (reg + IGET_RA(ins)) /* Get value of register A */
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins))) #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))) #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 var2cl(_v) cast(bclosure*, var_toobj(_v)) /* cast var to closure */
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i) #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) #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) #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 #if BE_USE_DEBUG_HOOK
#define 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)); "'%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) static void check_bool(bvm *vm, binstance *obj, const char *method)
{ {
if (!var_isbool(vm->top)) { if (!var_isbool(vm->top)) {
@ -182,25 +184,29 @@ static void do_linehook(bvm *vm)
} }
#endif #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) static void precall(bvm *vm, bvalue *func, int nstack, int mode)
{ {
bcallframe *cf; bcallframe *cf;
int expan = nstack + BE_STACK_FREE_MIN; int expan = nstack + BE_STACK_FREE_MIN; /* `expan` is the minimum required space on the stack */
if (vm->stacktop < func + expan) { if (vm->stacktop < func + expan) { /* do we have too little space left on the stack? */
size_t fpos = func - vm->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); be_stack_expansion(vm, expan); /* expand stack (vector object), warning stack address changes */
func = vm->stack + fpos; func = vm->stack + fpos; /* recompute `func` address with new stack address */
} }
be_stack_push(vm, &vm->callstack, NULL); be_stack_push(vm, &vm->callstack, NULL); /* push a NULL value on callstack */
cf = be_stack_top(&vm->callstack); cf = be_stack_top(&vm->callstack); /* get address of new callframe at top of callstack */
cf->func = func - mode; cf->func = func - mode;
cf->top = vm->top; cf->top = vm->top; /* save previous stack top */
cf->reg = vm->reg; cf->reg = vm->reg; /* save previous stack base */
vm->reg = func + 1; vm->reg = func + 1; /* new stack base is right after function */
vm->top = vm->reg + nstack; 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; 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) static void push_closure(bvm *vm, bvalue *func, int nstack, int mode)
{ {
bclosure *cl = var_toobj(func); bclosure *cl = var_toobj(func);
@ -469,9 +475,9 @@ static void vm_exec(bvm *vm)
vm->cf->status |= BASE_FRAME; vm->cf->status |= BASE_FRAME;
newframe: /* a new call frame */ newframe: /* a new call frame */
be_assert(var_isclosure(vm->cf->func)); be_assert(var_isclosure(vm->cf->func));
clos = var_toobj(vm->cf->func); clos = var_toobj(vm->cf->func); /* `clos` is the current function/closure */
ktab = clos->proto->ktab; ktab = clos->proto->ktab; /* `ktab` is the current constant table */
reg = vm->reg; reg = vm->reg; /* `reg` is the current stack base for the callframe */
vm_exec_loop() { vm_exec_loop() {
opcase(LDNIL): { opcase(LDNIL): {
var_setnil(RA()); var_setnil(RA());
@ -1019,7 +1025,7 @@ newframe: /* a new call frame */
dispatch(); dispatch();
} }
opcase(RAISE): { 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; bvalue *top = vm->top;
top[0] = *RKB(); /* push the exception value to top */ top[0] = *RKB(); /* push the exception value to top */
if (IGET_RA(ins)) { /* has exception argument? */ if (IGET_RA(ins)) { /* has exception argument? */
@ -1046,8 +1052,8 @@ newframe: /* a new call frame */
dispatch(); dispatch();
} }
opcase(CALL): { opcase(CALL): {
bvalue *var = RA(); bvalue *var = RA(); /* `var` is the register for the call followed by arguments */
int mode = 0, argc = IGET_RKB(ins); int mode = 0, argc = IGET_RKB(ins); /* B contains number of arguments pushed on stack */
recall: /* goto: instantiation class and call constructor */ recall: /* goto: instantiation class and call constructor */
switch (var_type(var)) { switch (var_type(var)) {
case NOT_METHOD: case NOT_METHOD:
@ -1055,8 +1061,8 @@ newframe: /* a new call frame */
++var, --argc, mode = 1; ++var, --argc, mode = 1;
goto recall; goto recall;
case BE_CLASS: case BE_CLASS:
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) { if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) { /* instanciate object and find constructor */
reg = vm->reg + mode; reg = vm->reg + mode; /* constructor found */
mode = 0; mode = 0;
var = RA() + 1; /* to next register */ var = RA() + 1; /* to next register */
goto recall; /* call constructor */ goto recall; /* call constructor */
@ -1072,15 +1078,15 @@ newframe: /* a new call frame */
} }
case BE_CLOSURE: { case BE_CLOSURE: {
bvalue *v, *end; bvalue *v, *end;
bproto *proto = var2cl(var)->proto; bproto *proto = var2cl(var)->proto; /* get proto for closure */
push_closure(vm, var, proto->nstack, mode); push_closure(vm, var, proto->nstack, mode); /* prepare stack for closure */
reg = vm->reg; reg = vm->reg; /* `reg` has changed, now new base register */
v = reg + argc; v = reg + argc; /* end of provided arguments */
end = reg + proto->argc; end = reg + proto->argc; /* end of expected arguments */
for (; v < end; ++v) { for (; v < end; ++v) { /* set all not provided arguments to nil */
var_setnil(v); var_setnil(v);
} }
goto newframe; goto newframe; /* continue execution of the closure */
} }
case BE_NTVCLOS: { case BE_NTVCLOS: {
bntvclos *f = var_toobj(var); 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_set_named_gbl(vm) ((vm)->compopt |= (1<<COMP_NAMED_GBL))
#define comp_clear_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 */ /* Compilation options */
typedef enum { typedef enum {
COMP_NAMED_GBL = 0x00, /* compile with named globals */ COMP_NAMED_GBL = 0x00, /* compile with named globals */
COMP_STRICT = 0x01, /* compile with named globals */
} compoptmask; } compoptmask;
typedef struct { typedef struct {

View File

@ -352,6 +352,17 @@ typedef struct bntvmodule {
PROTO_VAR_INFO_BLOCK \ 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 */ /* new version for more compact literals */
#define be_local_closure(_name, _proto) \ #define be_local_closure(_name, _proto) \
static const bclosure _name##_closure = { \ static const bclosure _name##_closure = { \

View File

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

View File

@ -1,60 +1,70 @@
Import('env') Import("env")
import os import os
import shutil import shutil
import gzip import gzip
import pathlib
platform = env.PioPlatform() import tasmotapiolib
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def map_gzip(source, target, env): 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 # 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): if map_file.is_file():
gzip_file = "{}map{}{}.map.gz".format(OUTPUT_DIR, os.path.sep, variant) gzip_file = map_file.with_suffix(".map.gz")
# check if new target map files exist and remove if necessary # 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 # 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: with gzip.open(gzip_file, "wb", compresslevel=9) as f:
shutil.copyfileobj(fp, f) shutil.copyfileobj(fp, f)
# remove map file # remove map file
if os.path.isfile(bin_file): os.remove(bin_file) if map_file.is_file():
map_file.unlink()
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [map_gzip])
if not tasmotapiolib.is_env_set(tasmotapiolib.DISABLE_MAP_GZ, env):
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [map_gzip])
# gzip only for ESP8266 # gzip only for ESP8266
if env["PIOPLATFORM"] != "espressif32": if env["PIOPLATFORM"] != "espressif32":
def bin_gzip(source, target, env): 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 # create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) bin_file = tasmotapiolib.get_final_bin_path(env)
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant) gzip_file = bin_file.with_suffix(".bin.gz")
# check if new target files exist and remove if necessary # 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 # write gzip firmware file
with open(bin_file,"rb") as fp: with open(bin_file, "rb") as fp:
with gzip.open(gzip_file, "wb", compresslevel = 9) as f: with gzip.open(gzip_file, "wb", compresslevel=9) as f:
shutil.copyfileobj(fp, f) shutil.copyfileobj(fp, f)
ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size ORG_FIRMWARE_SIZE = bin_file.stat().st_size
GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size GZ_FIRMWARE_SIZE = gzip_file.stat().st_size
if ORG_FIRMWARE_SIZE > 995326: 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: 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,
)
)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip]) 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("env")
Import('projenv') Import("projenv")
import os import os
import shutil import shutil
import pathlib
import tasmotapiolib
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_map_copy(source, target, env): 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 # get locations and file names based on variant
if not os.path.isdir(OUTPUT_DIR): map_file = tasmotapiolib.get_final_map_path(env)
os.mkdir(OUTPUT_DIR) bin_file = tasmotapiolib.get_final_bin_path(env)
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)
# check if new target files exist and remove if necessary # check if new target files exist and remove if necessary
for f in [map_file, bin_file]: for f in [map_file, bin_file]:
if os.path.isfile(f): if f.is_file():
os.remove(f) f.unlink()
# copy firmware.bin to firmware/<variant>.bin # copy firmware.bin and map to final destination
shutil.copy(str(target[0]), bin_file) 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" env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_map_copy)
# 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])

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 -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
-Wno-switch-unreachable -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] [env:tasmota-rangeextender]
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-D FIRMWARE_RANGE_EXTENDER -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 prev_day = -1
def set_watch() def set_watch()
now = tasmota.rtc() var now = tasmota.rtc()
time_raw = now['local'] + now['timezone'] * 60 var time_raw = now['local'] + now['timezone'] * 60
time = tasmota.time_dump(time_raw) var time = tasmota.time_dump(time_raw)
# set second # set second
ren_sec.set_angle(60 * time['sec']) ren_sec.set_angle(60 * time['sec'])
# set minutes # set minutes

View File

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

View File

@ -316,7 +316,7 @@ float CharToFloat(const char *str)
char *pt = strbuf; char *pt = strbuf;
if (*pt == '\0') { return 0.0; } 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; signed char sign = 1;
if (*pt == '-') { sign = -1; } if (*pt == '-') { sign = -1; }
@ -524,17 +524,18 @@ bool StrCaseStr_P(const char* source, const char* search) {
} }
bool IsNumeric(const char* value) { bool IsNumeric(const char* value) {
// Test for characters '-.0123456789'
char *digit = (char*)value; char *digit = (char*)value;
while (isdigit(*digit) || *digit == '.' || *digit == '-') { digit++; } while (isdigit(*digit) || *digit == '.' || *digit == '-') { digit++; }
return (*digit == '\0'); 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') { 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; 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++;
*q = '\0'; *q = '\0';
} }

View File

@ -666,7 +666,7 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true)
return 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; return false;
} else { } else {
return true; return true;

View File

@ -197,8 +197,8 @@ void MqttInit(void) {
Settings->mqtt_port = 8883; Settings->mqtt_port = 8883;
#endif //USE_MQTT_AZURE_IOT #endif //USE_MQTT_AZURE_IOT
#ifdef USE_MQTT_TLS #ifdef USE_MQTT_TLS
if ((8883 == Settings->mqtt_port) || (8884 == Settings->mqtt_port)) { if ((8883 == Settings->mqtt_port) || (8884 == Settings->mqtt_port) || (443 == Settings->mqtt_port)) {
// Turn on TLS for port 8883 (TLS) and 8884 (TLS, client certificate) // Turn on TLS for port 8883 (TLS), 8884 (TLS, client certificate), 443 (TLS, user/password)
Settings->flag4.mqtt_tls = true; 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) 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) 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; } if (Rules.busy) { return false; }
Rules.busy = true; Rules.busy = true;
@ -858,7 +863,11 @@ void RulesInit(void)
void RulesEvery50ms(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 if (Settings->rule_enabled && !Rules.busy) { // Any rule enabled
#endif
char json_event[120]; char json_event[120];
if (-1 == Rules.new_power) { Rules.new_power = TasmotaGlobal.power; } 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 // // 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; } if (berry.rules_busy) { return false; }
berry.rules_busy = true; berry.rules_busy = true;
char * json_event = XdrvMailbox.data; char * json_event = XdrvMailbox.data;
bool serviced = false; 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; berry.rules_busy = false;
return serviced; // TODO event not handled return serviced; // TODO event not handled
} }
@ -277,7 +278,8 @@ void BerryInit(void) {
do { do {
berry.vm = be_vm_new(); /* create a virtual machine instance */ berry.vm = be_vm_new(); /* create a virtual machine instance */
be_set_obs_hook(berry.vm, &BerryObservability); 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); be_load_custom_libs(berry.vm);
// Register functions // Register functions
@ -731,7 +733,7 @@ bool Xdrv52(uint8_t function)
// Berry wide commands and events // Berry wide commands and events
case FUNC_RULES_PROCESS: case FUNC_RULES_PROCESS:
result = callBerryRule(); result = callBerryRule(nullptr);
break; break;
case FUNC_MQTT_DATA: case FUNC_MQTT_DATA:
result = callBerryEventDispatcher(PSTR("mqtt_data"), XdrvMailbox.topic, 0, XdrvMailbox.data, XdrvMailbox.data_len); 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 _serverUrl; // Connection info
String _writeUrl; // Cached full write url String _writeUrl; // Cached full write url
String _lastErrorResponse; // Server reponse or library error message for last failed request 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 interval = 0;
int _lastStatusCode = 0; // HTTP status code of last request to server int _lastStatusCode = 0; // HTTP status code of last request to server
int _lastRetryAfter = 0; // Store retry timeout suggested by server after last request 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 _connectionReuse; // true if HTTP connection should be kept open. Usable for frequent writes. Default false
bool init = false; bool init = false;
} IFDB; } IFDB;
@ -161,7 +162,7 @@ void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) {
IFDB._lastRequestTime = millis(); IFDB._lastRequestTime = millis();
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: HTTP status code %d"), IFDB._lastStatusCode); // AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: HTTP status code %d"), IFDB._lastStatusCode);
IFDB._lastRetryAfter = 0; IFDB._lastRetryAfter = 0;
if (IFDB._lastStatusCode >= 429) { //retryable server errors if (IFDB._lastStatusCode >= 429) { // Retryable server errors
if (IFDBhttpClient->hasHeader(RetryAfter)) { if (IFDBhttpClient->hasHeader(RetryAfter)) {
IFDB._lastRetryAfter = IFDBhttpClient->header(RetryAfter).toInt(); IFDB._lastRetryAfter = IFDBhttpClient->header(RetryAfter).toInt();
AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Reply after %d"), IFDB._lastRetryAfter); AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Reply after %d"), IFDB._lastRetryAfter);
@ -171,12 +172,12 @@ void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) {
IFDB._lastErrorResponse = ""; IFDB._lastErrorResponse = "";
if (IFDB._lastStatusCode != expectedStatusCode) { if (IFDB._lastStatusCode != expectedStatusCode) {
if (IFDB._lastStatusCode > 0) { if (IFDB._lastStatusCode > 0) {
IFDB._lastErrorResponse = IFDBhttpClient->getString(); IFDB._lastErrorResponse = IFDBhttpClient->getString(); // {"error":"database not found: \"db\""}\n
AddLog(LOG_LEVEL_INFO, PSTR("IFX: %s"), IFDB._lastErrorResponse.c_str()); // {"error":"database not found: \"db\""}
} else { } else {
IFDB._lastErrorResponse = IFDBhttpClient->errorToString(IFDB._lastStatusCode); 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) { if (1 == Settings->influxdb_version) {
url += InfluxDbAuth(); 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()); AddLog(LOG_LEVEL_INFO, PSTR("IFX: Validating connection to %s"), url.c_str());
if (!IFDBhttpClient->begin(*IFDBwifiClient, url)) { if (!IFDBhttpClient->begin(*IFDBwifiClient, url)) {
@ -221,7 +220,8 @@ int InfluxDbPostData(const char *data) {
return false; 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")); IFDBhttpClient->addHeader(F("Content-Type"), F("text/plain"));
InfluxDbBeforeRequest(); InfluxDbBeforeRequest();
IFDB._lastStatusCode = IFDBhttpClient->POST((uint8_t*)data, strlen(data)); IFDB._lastStatusCode = IFDBhttpClient->POST((uint8_t*)data, strlen(data));
@ -235,19 +235,22 @@ int InfluxDbPostData(const char *data) {
* Data preparation * Data preparation
\*********************************************************************************************/ \*********************************************************************************************/
char* InfluxDbNumber(char* alternative, const char* source) { char* InfluxDbNumber(char* alternative, JsonParserToken value) {
// Test for valid numeric data ('-.0123456789') or ON, OFF etc. as defined in kOptions if (value.isValid()) {
if (source != nullptr) { char* source = (char*)value.getStr();
char* out = (char*)source; // Test for valid numeric data ('-.0123456789') or ON, OFF etc. as defined in kOptions
// Convert special text as found in kOptions to a number if (source != nullptr) {
// Like "OFF" -> 0, "ON" -> 1, "TOGGLE" -> 2 char* out = source;
int number = GetStateNumber(source); // Convert special text as found in kOptions to a number
if (number >= 0) { // Like "OFF" -> 0, "ON" -> 1, "TOGGLE" -> 2
itoa(number, alternative, 10); int number = GetStateNumber(source);
out = alternative; if (number >= 0) {
} itoa(number, alternative, 10);
if (IsNumeric(out)) { out = alternative;
return out; }
if (IsNumeric(out)) {
return out;
}
} }
} }
return nullptr; return nullptr;
@ -256,10 +259,12 @@ char* InfluxDbNumber(char* alternative, const char* source) {
void InfluxDbProcessJson(void) { void InfluxDbProcessJson(void) {
if (!IFDB.init) { return; } 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(); JsonParserObject root = parser.getRootObject();
if (root) { if (root) {
char number[12]; // '1' to '255' char number[12]; // '1' to '255'
@ -280,8 +285,8 @@ void InfluxDbProcessJson(void) {
if (value2.isObject()) { if (value2.isObject()) {
JsonParserObject Object3 = value2.getObject(); JsonParserObject Object3 = value2.getObject();
for (auto key3 : Object3) { for (auto key3 : Object3) {
const char* value = InfluxDbNumber(number, key3.getValue().getStr()); char* value = InfluxDbNumber(number, key3.getValue());
if (value != nullptr) { if ((value != nullptr) && key2.isValid() && key3.isValid()) {
// Level 3 // Level 3
LowerCase(sensor, key2.getStr()); LowerCase(sensor, key2.getStr());
LowerCase(type, key3.getStr()); LowerCase(type, key3.getStr());
@ -294,44 +299,47 @@ void InfluxDbProcessJson(void) {
} else { } else {
// Level 2 // 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}, ... } // { ... "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(); if (!key1.isValid() || !value2.isValid()) { continue; }
const char* value = InfluxDbNumber(number, (isarray) ? (value2.getArray())[0].getStr() : value2.getStr()); LowerCase(type, key2.getStr());
if (value != nullptr) { 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()); LowerCase(sensor, key1.getStr());
LowerCase(type, key2.getStr());
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX2: sensor %s (%s), type %s (%s)"), key1.getStr(), sensor, key2.getStr(), type); // 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 if (is_array) {
snprintf_P(sensor_id, sizeof(sensor_id), PSTR(",id=%s"), value); JsonParserArray arr = value2.getArray();
} else { uint32_t i = 0;
if (isarray) { for (auto val : arr) {
JsonParserArray arr = value2.getArray(); i++;
uint32_t i = 0; // power1,device=shelly25,sensor=energy value=0.00
for (auto val : arr) { // power2,device=shelly25,sensor=energy value=4.12
i++; snprintf_P(linebuf, sizeof(linebuf), PSTR("%s%d,device=%s,sensor=%s%s value=%s\n"),
// power1,device=shelly25,sensor=energy value=0.00 type, i, TasmotaGlobal.mqtt_topic, sensor, sensor_id, val.getStr());
// power2,device=shelly25,sensor=energy value=4.12
snprintf_P(linebuf, sizeof(linebuf), PSTR("%s%d,device=%s,sensor=%s%s value=%s\n"),
type, i, TasmotaGlobal.mqtt_topic, sensor, sensor_id, val.getStr());
data += linebuf;
}
} else {
// temperature,device=demo,sensor=ds18b20,id=01144A0CB2AA value=22.63
snprintf_P(linebuf, sizeof(linebuf), PSTR("%s,device=%s,sensor=%s%s value=%s\n"),
type, TasmotaGlobal.mqtt_topic, sensor, sensor_id, value);
data += linebuf; data += linebuf;
} }
sensor_id[0] = '\0'; } else {
// temperature,device=demo,sensor=ds18b20,id=01144A0CB2AA value=22.63
snprintf_P(linebuf, sizeof(linebuf), PSTR("%s,device=%s,sensor=%s%s value=%s\n"),
type, TasmotaGlobal.mqtt_topic, sensor, sensor_id, value);
data += linebuf;
} }
sensor_id[0] = '\0';
} }
} }
} }
} else { } else {
// Level 1 // Level 1
// {"Time":"2021-08-13T14:15:56","Switch1":"ON","Switch2":"OFF", ... "TempUnit":"C"} // {"Time":"2021-08-13T14:15:56","Switch1":"ON","Switch2":"OFF", ... "TempUnit":"C"}
const char* value = InfluxDbNumber(number, value1.getStr()); char* value = InfluxDbNumber(number, value1);
if (value != nullptr) { if ((value != nullptr) && key1.isValid()) {
LowerCase(type, key1.getStr()); LowerCase(type, key1.getStr());
// switch1,device=demo,sensor=device value=0 // switch1,device=demo,sensor=device value=0
// power1,device=demo,sensor=device value=1 // power1,device=demo,sensor=device value=1
@ -391,6 +399,7 @@ void InfluxDbLoop(void) {
\*********************************************************************************************/ \*********************************************************************************************/
#define D_PRFX_INFLUXDB "Ifx" #define D_PRFX_INFLUXDB "Ifx"
#define D_CMND_INFLUXDBLOG "Log"
#define D_CMND_INFLUXDBHOST "Host" #define D_CMND_INFLUXDBHOST "Host"
#define D_CMND_INFLUXDBPORT "Port" #define D_CMND_INFLUXDBPORT "Port"
#define D_CMND_INFLUXDBUSER "User" #define D_CMND_INFLUXDBUSER "User"
@ -401,14 +410,14 @@ void InfluxDbLoop(void) {
#define D_CMND_INFLUXDBBUCKET "Bucket" #define D_CMND_INFLUXDBBUCKET "Bucket"
const char kInfluxDbCommands[] PROGMEM = D_PRFX_INFLUXDB "|" // Prefix const char kInfluxDbCommands[] PROGMEM = D_PRFX_INFLUXDB "|" // Prefix
"|" "|" D_CMND_INFLUXDBLOG "|"
D_CMND_INFLUXDBHOST "|" D_CMND_INFLUXDBPORT "|" D_CMND_INFLUXDBHOST "|" D_CMND_INFLUXDBPORT "|"
D_CMND_INFLUXDBUSER "|" D_CMND_INFLUXDBORG "|" D_CMND_INFLUXDBUSER "|" D_CMND_INFLUXDBORG "|"
D_CMND_INFLUXDBPASSWORD "|" D_CMND_INFLUXDBTOKEN "|" D_CMND_INFLUXDBPASSWORD "|" D_CMND_INFLUXDBTOKEN "|"
D_CMND_INFLUXDBDATABASE "|" D_CMND_INFLUXDBBUCKET; D_CMND_INFLUXDBDATABASE "|" D_CMND_INFLUXDBBUCKET;
void (* const InfluxCommand[])(void) PROGMEM = { void (* const InfluxCommand[])(void) PROGMEM = {
&CmndInfluxDbState, &CmndInfluxDbState, &CmndInfluxDbLog,
&CmndInfluxDbHost, &CmndInfluxDbPort, &CmndInfluxDbHost, &CmndInfluxDbPort,
&CmndInfluxDbUser, &CmndInfluxDbUser, &CmndInfluxDbUser, &CmndInfluxDbUser,
&CmndInfluxDbPassword, &CmndInfluxDbPassword, &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) { void CmndInfluxDbHost(void) {
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
SettingsUpdateText(SET_INFLUXDB_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_HOST) : XdrvMailbox.data); 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; char* data_save = XdrvMailbox.data;
XdrvMailbox.data = (char*)event; XdrvMailbox.data = (char*)event;
bool rule_handled = XdrvCallDriver(10, (teleperiod) ? FUNC_TELEPERIOD_RULES_PROCESS : FUNC_RULES_PROCESS); bool rule_handled = XdrvCallDriver(10, (teleperiod) ? FUNC_TELEPERIOD_RULES_PROCESS : FUNC_RULES_PROCESS);
#ifdef USE_BERRY #if defined(USE_BERRY) && !defined(USE_RULES)
// events are passed to both Rules engine AND Berry engine // 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); bool berry_handled = XdrvCallDriver(52, FUNC_RULES_PROCESS);
rule_handled |= berry_handled; rule_handled |= berry_handled;
#endif #endif

View File

@ -1198,7 +1198,7 @@ double CharToDouble(const char *str)
strlcpy(strbuf, str, sizeof(strbuf)); strlcpy(strbuf, str, sizeof(strbuf));
char *pt = 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; signed char sign = 1;
if (*pt == '-') { sign = -1; } if (*pt == '-') { sign = -1; }