mirror of https://github.com/arendst/Tasmota.git
Merge branch 'arendst:development' into feature/can-bus
This commit is contained in:
commit
7dca876f02
|
@ -26,6 +26,7 @@ tasmota/tasmota.ino.cpp
|
|||
tasmota*.bin
|
||||
tasmota*.bin.gz
|
||||
tasmota*.map
|
||||
tasmota*.map.gz
|
||||
platformio_override.ini
|
||||
platformio_tasmota_cenv.ini
|
||||
|
||||
|
|
|
@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file.
|
|||
### Added
|
||||
- Version bump to monitor possible HTTP issues releated to ``SetOption128``
|
||||
|
||||
### Changed
|
||||
- Berry now compiling in ``strict`` mode to catch more bugs
|
||||
|
||||
### Fixed
|
||||
- Fixed PWM5 on ESP32C3
|
||||
|
||||
## [9.5.0.5] 20210815
|
||||
### Added
|
||||
- Inital support for Wi-Fi extender (#12784)
|
||||
|
|
|
@ -20,6 +20,7 @@ be_extern_native_module(sys);
|
|||
be_extern_native_module(debug);
|
||||
be_extern_native_module(gc);
|
||||
be_extern_native_module(solidify);
|
||||
be_extern_native_module(strict);
|
||||
be_extern_native_module(introspect);
|
||||
|
||||
/* Tasmota specific */
|
||||
|
@ -72,6 +73,9 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
|
|||
#endif
|
||||
#if BE_USE_INTROSPECT_MODULE
|
||||
&be_native_module(introspect),
|
||||
#endif
|
||||
#if BE_USE_STRICT_MODULE
|
||||
&be_native_module(strict),
|
||||
#endif
|
||||
/* user-defined modules register start */
|
||||
|
||||
|
@ -101,6 +105,7 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
|
|||
extern void be_load_tasmota_ntvlib(bvm *vm);
|
||||
extern void be_load_wirelib(bvm *vm);
|
||||
extern void be_load_Driver_class(bvm *vm);
|
||||
extern void be_load_Timer_class(bvm *vm);
|
||||
extern void be_load_driver_i2c_lib(bvm *vm);
|
||||
extern void be_load_md5_lib(bvm *vm);
|
||||
extern void be_load_aes_gcm_lib(bvm *vm);
|
||||
|
@ -131,6 +136,7 @@ BERRY_API void be_load_custom_libs(bvm *vm)
|
|||
#if !BE_USE_PRECOMPILED_OBJECT
|
||||
/* be_load_xxxlib(vm); */
|
||||
#endif
|
||||
be_load_Timer_class(vm);
|
||||
be_load_tasmota_ntvlib(vm);
|
||||
be_load_Driver_class(vm);
|
||||
be_load_md5_lib(vm);
|
||||
|
|
|
@ -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
|
||||
********************************************************************/
|
||||
be_local_closure(set_timer, /* name */
|
||||
be_nested_proto(
|
||||
9, /* nstack */
|
||||
3, /* argc */
|
||||
10, /* nstack */
|
||||
4, /* argc */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
( &(const bvalue[ 3]) { /* upvals */
|
||||
{ { .s=be_nested_const_str("_timers", -1694866380, 7) }, BE_STRING},
|
||||
{ { .s=be_nested_const_str("push", -2022703139, 4) }, BE_STRING},
|
||||
{ { .s=be_nested_const_str("millis", 1214679063, 6) }, BE_STRING},
|
||||
( &(const bvalue[ 4]) { /* constants */
|
||||
be_nested_string("_timers", -1694866380, 7), /* R256 - K0 */
|
||||
be_nested_string("push", -2022703139, 4), /* R257 - K1 */
|
||||
be_nested_string("Timer", -346839614, 5), /* R258 - K2 */
|
||||
be_nested_string("millis", 1214679063, 6), /* R259 - K3 */
|
||||
}),
|
||||
(be_nested_const_str("set_timer", 2135414533, 9)),
|
||||
(be_nested_const_str("string", 398550328, 6)),
|
||||
(be_nested_const_str("input", -103256197, 5)),
|
||||
( &(const binstruction[16]) { /* code */
|
||||
0x880C0100, // 0000 GETMBR R3 R0 R256
|
||||
0x740E0002, // 0001 JMPT R3 #0005
|
||||
0x600C000A, // 0002 GETGBL R3 G10
|
||||
0x7C0C0000, // 0003 CALL R3 0
|
||||
0x90020003, // 0004 SETMBR R0 R256 R3
|
||||
0x880C0100, // 0005 GETMBR R3 R0 R256
|
||||
0x8C0C0701, // 0006 GETMET R3 R3 R257
|
||||
0x6014000A, // 0007 GETGBL R5 G10
|
||||
0x7C140000, // 0008 CALL R5 0
|
||||
0x8C180102, // 0009 GETMET R6 R0 R258
|
||||
0x5C200200, // 000A MOVE R8 R1
|
||||
0x7C180400, // 000B CALL R6 2
|
||||
0x40180A06, // 000C CONNECT R6 R5 R6
|
||||
0x40180A02, // 000D CONNECT R6 R5 R2
|
||||
0x7C0C0400, // 000E CALL R3 2
|
||||
0x88100100, // 0000 GETMBR R4 R0 R256
|
||||
0x74120002, // 0001 JMPT R4 #0005
|
||||
0x6010000A, // 0002 GETGBL R4 G10
|
||||
0x7C100000, // 0003 CALL R4 0
|
||||
0x90020004, // 0004 SETMBR R0 R256 R4
|
||||
0x88100100, // 0005 GETMBR R4 R0 R256
|
||||
0x8C100901, // 0006 GETMET R4 R4 R257
|
||||
0xB81A0400, // 0007 GETNGBL R6 R258
|
||||
0x8C1C0103, // 0008 GETMET R7 R0 R259
|
||||
0x5C240200, // 0009 MOVE R9 R1
|
||||
0x7C1C0400, // 000A CALL R7 2
|
||||
0x5C200400, // 000B MOVE R8 R2
|
||||
0x5C240600, // 000C MOVE R9 R3
|
||||
0x7C180600, // 000D CALL R6 3
|
||||
0x7C100400, // 000E CALL R4 2
|
||||
0x80000000, // 000F RET 0 R0
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
// run every 50ms tick
|
||||
"def run_deferred() "
|
||||
"if self._timers "
|
||||
"var i=0 "
|
||||
"while i<self._timers.size() "
|
||||
"if self.time_reached(self._timers[i][0]) "
|
||||
"f=self._timers[i][1] "
|
||||
"self._timers.remove(i) "
|
||||
"f() "
|
||||
"else "
|
||||
"i=i+1 "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
********************************************************************/
|
||||
/********************************************************************
|
||||
** Solidified function: run_deferred
|
||||
********************************************************************/
|
||||
|
||||
be_define_local_const_str(run_deferred_str_name, "run_deferred", 371594696, 12);
|
||||
be_define_local_const_str(run_deferred_str_source, "string", 398550328, 6);
|
||||
be_define_local_const_str(run_deferred_str_0, "_timers", -1694866380, 7);
|
||||
be_define_local_const_str(run_deferred_str_2, "size", 597743964, 4);
|
||||
be_define_local_const_str(run_deferred_str_3, "time_reached", 2075136773, 12);
|
||||
be_define_local_const_str(run_deferred_str_5, "remove", -611183107, 6);
|
||||
|
||||
static const bvalue run_deferred_ktab[6] = {
|
||||
{ { .s=be_local_const_str(run_deferred_str_0) }, BE_STRING},
|
||||
{ { .i=0 }, BE_INT},
|
||||
{ { .s=be_local_const_str(run_deferred_str_2) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(run_deferred_str_3) }, BE_STRING},
|
||||
{ { .i=1 }, BE_INT},
|
||||
{ { .s=be_local_const_str(run_deferred_str_5) }, BE_STRING},
|
||||
};
|
||||
|
||||
static const uint32_t run_deferred_code[27] = {
|
||||
0x88040100, // 0000 GETMBR R1 R0 R256
|
||||
0x78060017, // 0001 JMPF R1 #001A
|
||||
0x58040001, // 0002 LDCONST R1 K1
|
||||
0x88080100, // 0003 GETMBR R2 R0 R256
|
||||
0x8C080502, // 0004 GETMET R2 R2 R258
|
||||
0x7C080200, // 0005 CALL R2 1
|
||||
0x14080202, // 0006 LT R2 R1 R2
|
||||
0x780A0011, // 0007 JMPF R2 #001A
|
||||
0x8C080103, // 0008 GETMET R2 R0 R259
|
||||
0x88100100, // 0009 GETMBR R4 R0 R256
|
||||
0x94100801, // 000A GETIDX R4 R4 R1
|
||||
0x94100901, // 000B GETIDX R4 R4 R257
|
||||
0x7C080400, // 000C CALL R2 2
|
||||
0x780A0009, // 000D JMPF R2 #0018
|
||||
0x88080100, // 000E GETMBR R2 R0 R256
|
||||
0x94080401, // 000F GETIDX R2 R2 R1
|
||||
0x94080504, // 0010 GETIDX R2 R2 R260
|
||||
0x880C0100, // 0011 GETMBR R3 R0 R256
|
||||
0x8C0C0705, // 0012 GETMET R3 R3 R261
|
||||
0x5C140200, // 0013 MOVE R5 R1
|
||||
0x7C0C0400, // 0014 CALL R3 2
|
||||
0x5C0C0400, // 0015 MOVE R3 R2
|
||||
0x7C0C0000, // 0016 CALL R3 0
|
||||
0x70020000, // 0017 JMP #0019
|
||||
0x40304, // 0018 ADD R1 R1 R260
|
||||
0x7001FFE8, // 0019 JMP #0003
|
||||
0x80000000, // 001A RET 0 R0
|
||||
};
|
||||
|
||||
static const bproto run_deferred_proto = {
|
||||
NULL, // bgcobject *next
|
||||
8, // type
|
||||
0x08, // marked
|
||||
6, // nstack
|
||||
0, // nupvals
|
||||
1, // argc
|
||||
0, // varg
|
||||
NULL, // bgcobject *gray
|
||||
NULL, // bupvaldesc *upvals
|
||||
(bvalue*) &run_deferred_ktab, // ktab
|
||||
NULL, // bproto **ptab
|
||||
(binstruction*) &run_deferred_code, // code
|
||||
be_local_const_str(run_deferred_str_name), // name
|
||||
27, // codesize
|
||||
6, // nconst
|
||||
0, // nproto
|
||||
be_local_const_str(run_deferred_str_source), // source
|
||||
#if BE_DEBUG_RUNTIME_INFO /* debug information */
|
||||
NULL, // lineinfo
|
||||
0, // nlineinfo
|
||||
#endif
|
||||
#if BE_DEBUG_VAR_INFO
|
||||
NULL, // varinfo
|
||||
0, // nvarinfo
|
||||
#endif
|
||||
};
|
||||
|
||||
const bclosure run_deferred_closure = {
|
||||
NULL, // bgcobject *next
|
||||
36, // type
|
||||
0x08, // marked
|
||||
0, // nupvals
|
||||
NULL, // bgcobject *gray
|
||||
(bproto*) &run_deferred_proto, // proto
|
||||
{ NULL } // upvals
|
||||
};
|
||||
|
||||
be_local_closure(run_deferred, /* name */
|
||||
be_nested_proto(
|
||||
6, /* nstack */
|
||||
1, /* argc */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
( &(const bvalue[ 8]) { /* constants */
|
||||
be_nested_string("_timers", -1694866380, 7), /* R256 - K0 */
|
||||
be_const_int(0), /* R257 - K1 */
|
||||
be_nested_string("size", 597743964, 4), /* R258 - K2 */
|
||||
be_nested_string("time_reached", 2075136773, 12), /* R259 - K3 */
|
||||
be_nested_string("due", -399437003, 3), /* R260 - K4 */
|
||||
be_nested_string("f", -485742695, 1), /* R261 - K5 */
|
||||
be_nested_string("remove", -611183107, 6), /* R262 - K6 */
|
||||
be_const_int(1), /* R263 - K7 */
|
||||
}),
|
||||
(be_nested_const_str("run_deferred", 371594696, 12)),
|
||||
(be_nested_const_str("input", -103256197, 5)),
|
||||
( &(const binstruction[27]) { /* code */
|
||||
0x88040100, // 0000 GETMBR R1 R0 R256
|
||||
0x78060017, // 0001 JMPF R1 #001A
|
||||
0x58040001, // 0002 LDCONST R1 K1
|
||||
0x88080100, // 0003 GETMBR R2 R0 R256
|
||||
0x8C080502, // 0004 GETMET R2 R2 R258
|
||||
0x7C080200, // 0005 CALL R2 1
|
||||
0x14080202, // 0006 LT R2 R1 R2
|
||||
0x780A0011, // 0007 JMPF R2 #001A
|
||||
0x8C080103, // 0008 GETMET R2 R0 R259
|
||||
0x88100100, // 0009 GETMBR R4 R0 R256
|
||||
0x94100801, // 000A GETIDX R4 R4 R1
|
||||
0x88100904, // 000B GETMBR R4 R4 R260
|
||||
0x7C080400, // 000C CALL R2 2
|
||||
0x780A0009, // 000D JMPF R2 #0018
|
||||
0x88080100, // 000E GETMBR R2 R0 R256
|
||||
0x94080401, // 000F GETIDX R2 R2 R1
|
||||
0x88080505, // 0010 GETMBR R2 R2 R261
|
||||
0x880C0100, // 0011 GETMBR R3 R0 R256
|
||||
0x8C0C0706, // 0012 GETMET R3 R3 R262
|
||||
0x5C140200, // 0013 MOVE R5 R1
|
||||
0x7C0C0400, // 0014 CALL R3 2
|
||||
0x5C0C0400, // 0015 MOVE R3 R2
|
||||
0x7C0C0000, // 0016 CALL R3 0
|
||||
0x70020000, // 0017 JMP #0019
|
||||
0x00040307, // 0018 ADD R1 R1 R263
|
||||
0x7001FFE8, // 0019 JMP #0003
|
||||
0x80000000, // 001A RET 0 R0
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
/********************************************************************
|
||||
** Solidified function: remove_timer
|
||||
********************************************************************/
|
||||
be_local_closure(remove_timer, /* name */
|
||||
be_nested_proto(
|
||||
6, /* nstack */
|
||||
2, /* argc */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
( &(const bvalue[ 7]) { /* constants */
|
||||
be_nested_string("tasmota", 424643812, 7), /* R256 - K0 */
|
||||
be_nested_string("_timers", -1694866380, 7), /* R257 - K1 */
|
||||
be_const_int(0), /* R258 - K2 */
|
||||
be_nested_string("size", 597743964, 4), /* R259 - K3 */
|
||||
be_nested_string("id", 926444256, 2), /* R260 - K4 */
|
||||
be_nested_string("remove", -611183107, 6), /* R261 - K5 */
|
||||
be_const_int(1), /* R262 - K6 */
|
||||
}),
|
||||
(be_nested_const_str("remove_timer", -153495081, 12)),
|
||||
(be_nested_const_str("input", -103256197, 5)),
|
||||
( &(const binstruction[23]) { /* code */
|
||||
0xB80A0000, // 0000 GETNGBL R2 R256
|
||||
0x88080501, // 0001 GETMBR R2 R2 R257
|
||||
0x780A0012, // 0002 JMPF R2 #0016
|
||||
0x58080002, // 0003 LDCONST R2 K2
|
||||
0xB80E0000, // 0004 GETNGBL R3 R256
|
||||
0x880C0701, // 0005 GETMBR R3 R3 R257
|
||||
0x8C0C0703, // 0006 GETMET R3 R3 R259
|
||||
0x7C0C0200, // 0007 CALL R3 1
|
||||
0x140C0403, // 0008 LT R3 R2 R3
|
||||
0x780E000B, // 0009 JMPF R3 #0016
|
||||
0x880C0101, // 000A GETMBR R3 R0 R257
|
||||
0x940C0602, // 000B GETIDX R3 R3 R2
|
||||
0x880C0704, // 000C GETMBR R3 R3 R260
|
||||
0x1C0C0601, // 000D EQ R3 R3 R1
|
||||
0x780E0004, // 000E JMPF R3 #0014
|
||||
0x880C0101, // 000F GETMBR R3 R0 R257
|
||||
0x8C0C0705, // 0010 GETMET R3 R3 R261
|
||||
0x5C140400, // 0011 MOVE R5 R2
|
||||
0x7C0C0400, // 0012 CALL R3 2
|
||||
0x70020000, // 0013 JMP #0015
|
||||
0x00080506, // 0014 ADD R2 R2 R262
|
||||
0x7001FFED, // 0015 JMP #0004
|
||||
0x80000000, // 0016 RET 0 R0
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
/********************************************************************
|
||||
// Add command to list
|
||||
|
@ -1212,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
|
||||
********************************************************************/
|
||||
|
||||
be_define_local_const_str(exec_cmd_str_name, "exec_cmd", 493567399, 8);
|
||||
be_define_local_const_str(exec_cmd_str_source, "string", 398550328, 6);
|
||||
be_define_local_const_str(exec_cmd_str_0, "_ccmd", -2131545883, 5);
|
||||
be_define_local_const_str(exec_cmd_str_1, "json", 916562499, 4);
|
||||
be_define_local_const_str(exec_cmd_str_2, "load", -435725847, 4);
|
||||
be_define_local_const_str(exec_cmd_str_3, "find_key_i", 850136726, 10);
|
||||
be_define_local_const_str(exec_cmd_str_4, "resolvecmnd", 993361485, 11);
|
||||
|
||||
static const bvalue exec_cmd_ktab[5] = {
|
||||
{ { .s=be_local_const_str(exec_cmd_str_0) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(exec_cmd_str_1) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(exec_cmd_str_2) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(exec_cmd_str_3) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(exec_cmd_str_4) }, BE_STRING},
|
||||
};
|
||||
|
||||
static const uint32_t exec_cmd_code[27] = {
|
||||
0x88100100, // 0000 GETMBR R4 R0 R256
|
||||
0x78120016, // 0001 JMPF R4 #0019
|
||||
0xA4120200, // 0002 IMPORT R4 R257
|
||||
0x8C140902, // 0003 GETMET R5 R4 R258
|
||||
0x5C1C0600, // 0004 MOVE R7 R3
|
||||
0x7C140400, // 0005 CALL R5 2
|
||||
0x8C180103, // 0006 GETMET R6 R0 R259
|
||||
0x88200100, // 0007 GETMBR R8 R0 R256
|
||||
0x5C240200, // 0008 MOVE R9 R1
|
||||
0x7C180600, // 0009 CALL R6 3
|
||||
0x4C1C0000, // 000A LDNIL 7
|
||||
0x201C0C07, // 000B NE R7 R6 R7
|
||||
0x781E000B, // 000C JMPF R7 #0019
|
||||
0x8C1C0104, // 000D GETMET R7 R0 R260
|
||||
0x5C240C00, // 000E MOVE R9 R6
|
||||
0x7C1C0400, // 000F CALL R7 2
|
||||
0x881C0100, // 0010 GETMBR R7 R0 R256
|
||||
0x941C0E06, // 0011 GETIDX R7 R7 R6
|
||||
0x5C200C00, // 0012 MOVE R8 R6
|
||||
0x5C240400, // 0013 MOVE R9 R2
|
||||
0x5C280600, // 0014 MOVE R10 R3
|
||||
0x5C2C0A00, // 0015 MOVE R11 R5
|
||||
0x7C1C0800, // 0016 CALL R7 4
|
||||
0x501C0200, // 0017 LDBOOL R7 1 0
|
||||
0x80040E00, // 0018 RET 1 R7
|
||||
0x50100000, // 0019 LDBOOL R4 0 0
|
||||
0x80040800, // 001A RET 1 R4
|
||||
};
|
||||
|
||||
static const bproto exec_cmd_proto = {
|
||||
NULL, // bgcobject *next
|
||||
8, // type
|
||||
0x08, // marked
|
||||
12, // nstack
|
||||
0, // nupvals
|
||||
4, // argc
|
||||
0, // varg
|
||||
NULL, // bgcobject *gray
|
||||
NULL, // bupvaldesc *upvals
|
||||
(bvalue*) &exec_cmd_ktab, // ktab
|
||||
NULL, // bproto **ptab
|
||||
(binstruction*) &exec_cmd_code, // code
|
||||
be_local_const_str(exec_cmd_str_name), // name
|
||||
27, // codesize
|
||||
5, // nconst
|
||||
0, // nproto
|
||||
be_local_const_str(exec_cmd_str_source), // source
|
||||
#if BE_DEBUG_RUNTIME_INFO /* debug information */
|
||||
NULL, // lineinfo
|
||||
0, // nlineinfo
|
||||
#endif
|
||||
#if BE_DEBUG_VAR_INFO
|
||||
NULL, // varinfo
|
||||
0, // nvarinfo
|
||||
#endif
|
||||
};
|
||||
|
||||
static const bclosure exec_cmd_closure = {
|
||||
NULL, // bgcobject *next
|
||||
36, // type
|
||||
0x08, // marked
|
||||
0, // nupvals
|
||||
NULL, // bgcobject *gray
|
||||
(bproto*) &exec_cmd_proto, // proto
|
||||
{ NULL } // upvals
|
||||
};
|
||||
|
||||
be_local_closure(exec_cmd, /* name */
|
||||
be_nested_proto(
|
||||
12, /* nstack */
|
||||
4, /* argc */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
( &(const bvalue[ 5]) { /* constants */
|
||||
be_nested_string("_ccmd", -2131545883, 5), /* R256 - K0 */
|
||||
be_nested_string("json", 916562499, 4), /* R257 - K1 */
|
||||
be_nested_string("load", -435725847, 4), /* R258 - K2 */
|
||||
be_nested_string("find_key_i", 850136726, 10), /* R259 - K3 */
|
||||
be_nested_string("resolvecmnd", 993361485, 11), /* R260 - K4 */
|
||||
}),
|
||||
(be_nested_const_str("exec_cmd", 493567399, 8)),
|
||||
(be_nested_const_str("string", 398550328, 6)),
|
||||
( &(const binstruction[27]) { /* code */
|
||||
0x88100100, // 0000 GETMBR R4 R0 R256
|
||||
0x78120016, // 0001 JMPF R4 #0019
|
||||
0xA4120200, // 0002 IMPORT R4 R257
|
||||
0x8C140902, // 0003 GETMET R5 R4 R258
|
||||
0x5C1C0600, // 0004 MOVE R7 R3
|
||||
0x7C140400, // 0005 CALL R5 2
|
||||
0x8C180103, // 0006 GETMET R6 R0 R259
|
||||
0x88200100, // 0007 GETMBR R8 R0 R256
|
||||
0x5C240200, // 0008 MOVE R9 R1
|
||||
0x7C180600, // 0009 CALL R6 3
|
||||
0x4C1C0000, // 000A LDNIL 7
|
||||
0x201C0C07, // 000B NE R7 R6 R7
|
||||
0x781E000B, // 000C JMPF R7 #0019
|
||||
0x8C1C0104, // 000D GETMET R7 R0 R260
|
||||
0x5C240C00, // 000E MOVE R9 R6
|
||||
0x7C1C0400, // 000F CALL R7 2
|
||||
0x881C0100, // 0010 GETMBR R7 R0 R256
|
||||
0x941C0E06, // 0011 GETIDX R7 R7 R6
|
||||
0x5C200C00, // 0012 MOVE R8 R6
|
||||
0x5C240400, // 0013 MOVE R9 R2
|
||||
0x5C280600, // 0014 MOVE R10 R3
|
||||
0x5C2C0A00, // 0015 MOVE R11 R5
|
||||
0x7C1C0800, // 0016 CALL R7 4
|
||||
0x501C0200, // 0017 LDBOOL R7 1 0
|
||||
0x80040E00, // 0018 RET 1 R7
|
||||
0x50100000, // 0019 LDBOOL R4 0 0
|
||||
0x80040800, // 001A RET 1 R4
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
// Force gc and return allocated memory
|
||||
"def gc() "
|
||||
|
@ -1478,70 +1419,37 @@ be_local_closure(event, /* name */
|
|||
/********************************************************************
|
||||
** Solidified function: add_driver
|
||||
********************************************************************/
|
||||
|
||||
be_define_local_const_str(add_driver_str_name, "add_driver", 1654458371, 10);
|
||||
be_define_local_const_str(add_driver_str_source, "string", 398550328, 6);
|
||||
be_define_local_const_str(add_driver_str_0, "_drivers", -1034638311, 8);
|
||||
be_define_local_const_str(add_driver_str_1, "push", -2022703139, 4);
|
||||
|
||||
static const bvalue add_driver_ktab[2] = {
|
||||
{ { .s=be_local_const_str(add_driver_str_0) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(add_driver_str_1) }, BE_STRING},
|
||||
};
|
||||
|
||||
static const uint32_t add_driver_code[12] = {
|
||||
0x88080100, // 0000 GETMBR R2 R0 R256
|
||||
0x780A0004, // 0001 JMPF R2 #0007
|
||||
0x88080100, // 0002 GETMBR R2 R0 R256
|
||||
0x8C080501, // 0003 GETMET R2 R2 R257
|
||||
0x5C100200, // 0004 MOVE R4 R1
|
||||
0x7C080400, // 0005 CALL R2 2
|
||||
0x70020003, // 0006 JMP #000B
|
||||
0x6008000A, // 0007 GETGBL R2 G10
|
||||
0x7C080000, // 0008 CALL R2 0
|
||||
0x400C0401, // 0009 CONNECT R3 R2 R1
|
||||
0x90020002, // 000A SETMBR R0 R256 R2
|
||||
0x80000000, // 000B RET 0 R0
|
||||
};
|
||||
|
||||
static const bproto add_driver_proto = {
|
||||
NULL, // bgcobject *next
|
||||
8, // type
|
||||
0x08, // marked
|
||||
5, // nstack
|
||||
0, // nupvals
|
||||
2, // argc
|
||||
0, // varg
|
||||
NULL, // bgcobject *gray
|
||||
NULL, // bupvaldesc *upvals
|
||||
(bvalue*) &add_driver_ktab, // ktab
|
||||
NULL, // bproto **ptab
|
||||
(binstruction*) &add_driver_code, // code
|
||||
be_local_const_str(add_driver_str_name), // name
|
||||
12, // codesize
|
||||
2, // nconst
|
||||
0, // nproto
|
||||
be_local_const_str(add_driver_str_source), // source
|
||||
#if BE_DEBUG_RUNTIME_INFO /* debug information */
|
||||
NULL, // lineinfo
|
||||
0, // nlineinfo
|
||||
#endif
|
||||
#if BE_DEBUG_VAR_INFO
|
||||
NULL, // varinfo
|
||||
0, // nvarinfo
|
||||
#endif
|
||||
};
|
||||
|
||||
const bclosure add_driver_closure = {
|
||||
NULL, // bgcobject *next
|
||||
36, // type
|
||||
0x08, // marked
|
||||
0, // nupvals
|
||||
NULL, // bgcobject *gray
|
||||
(bproto*) &add_driver_proto, // proto
|
||||
{ NULL } // upvals
|
||||
};
|
||||
|
||||
be_local_closure(add_driver, /* name */
|
||||
be_nested_proto(
|
||||
5, /* nstack */
|
||||
2, /* argc */
|
||||
0, /* has upvals */
|
||||
NULL, /* no upvals */
|
||||
0, /* has sup protos */
|
||||
NULL, /* no sub protos */
|
||||
1, /* has constants */
|
||||
( &(const bvalue[ 2]) { /* constants */
|
||||
be_nested_string("_drivers", -1034638311, 8), /* R256 - K0 */
|
||||
be_nested_string("push", -2022703139, 4), /* R257 - K1 */
|
||||
}),
|
||||
(be_nested_const_str("add_driver", 1654458371, 10)),
|
||||
(be_nested_const_str("string", 398550328, 6)),
|
||||
( &(const binstruction[12]) { /* code */
|
||||
0x88080100, // 0000 GETMBR R2 R0 R256
|
||||
0x780A0004, // 0001 JMPF R2 #0007
|
||||
0x88080100, // 0002 GETMBR R2 R0 R256
|
||||
0x8C080501, // 0003 GETMET R2 R2 R257
|
||||
0x5C100200, // 0004 MOVE R4 R1
|
||||
0x7C080400, // 0005 CALL R2 2
|
||||
0x70020003, // 0006 JMP #000B
|
||||
0x6008000A, // 0007 GETGBL R2 G10
|
||||
0x7C080000, // 0008 CALL R2 0
|
||||
0x400C0401, // 0009 CONNECT R3 R2 R1
|
||||
0x90020002, // 000A SETMBR R0 R256 R2
|
||||
0x80000000, // 000B RET 0 R0
|
||||
})
|
||||
)
|
||||
);
|
||||
/*******************************************************************/
|
||||
|
||||
/********************************************************************
|
||||
|
@ -2246,6 +2154,7 @@ void be_load_tasmota_ntvlib(bvm *vm)
|
|||
{ "exec_rules", (bntvfunc) &exec_rules_closure },
|
||||
{ "set_timer", (bntvfunc) &set_timer_closure },
|
||||
{ "run_deferred", (bntvfunc) &run_deferred_closure },
|
||||
{ "remove_timer", (bntvfunc) &remove_timer_closure },
|
||||
{ "add_cmd", (bntvfunc) &add_cmd_closure },
|
||||
{ "remove_cmd", (bntvfunc) &remove_cmd_closure },
|
||||
{ "exec_cmd", (bntvfunc) &exec_cmd_closure },
|
||||
|
@ -2329,6 +2238,7 @@ class be_class_tasmota (scope: global, name: Tasmota) {
|
|||
exec_rules, closure(exec_rules_closure)
|
||||
set_timer, closure(set_timer_closure)
|
||||
run_deferred, closure(run_deferred_closure)
|
||||
remove_timer, closure(remove_timer_closure)
|
||||
add_cmd, closure(add_cmd_closure)
|
||||
remove_cmd, closure(remove_cmd_closure)
|
||||
exec_cmd, closure(exec_cmd_closure)
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -45,7 +45,7 @@
|
|||
* Use precompiled objects to avoid creating these objects at
|
||||
* runtime. Enable this macro can greatly optimize RAM usage.
|
||||
* Default: 1
|
||||
// **/
|
||||
**/
|
||||
#define BE_USE_PRECOMPILED_OBJECT 1
|
||||
|
||||
/* Macro: BE_DEBUG_RUNTIME_INFO
|
||||
|
@ -142,6 +142,14 @@
|
|||
**/
|
||||
#define BE_USE_DEBUG_HOOK 0
|
||||
|
||||
/* Macro: BE_USE_DEBUG_GC
|
||||
* Enable GC debug mode. This causes an actual gc after each
|
||||
* allocation. It's much slower and should not be used
|
||||
* in production code.
|
||||
* Default: 0
|
||||
**/
|
||||
#define BE_USE_DEBUG_GC 0
|
||||
|
||||
/* Macro: BE_USE_XXX_MODULE
|
||||
* These macros control whether the related module is compiled.
|
||||
* When they are true, they will enable related modules. At this
|
||||
|
@ -159,6 +167,7 @@
|
|||
#define BE_USE_GC_MODULE 1
|
||||
#define BE_USE_SOLIDIFY_MODULE 1
|
||||
#define BE_USE_INTROSPECT_MODULE 1
|
||||
#define BE_USE_STRICT_MODULE 1
|
||||
|
||||
/* Macro: BE_EXPLICIT_XXX
|
||||
* If these macros are defined, the corresponding function will
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
#- Native code used for testing and code solidification -#
|
||||
#- Do not use it -#
|
||||
|
||||
class Timer
|
||||
var due, f, id
|
||||
def init(due, f, id)
|
||||
self.due = due
|
||||
self.f = f
|
||||
self.id = id
|
||||
end
|
||||
def tostring()
|
||||
import string
|
||||
return string.format("<instance: %s(%s, %s, %s)", str(classof(self)),
|
||||
str(self.due), str(self.f), str(self.id))
|
||||
end
|
||||
end
|
||||
|
||||
tasmota = nil
|
||||
class Tasmota
|
||||
|
||||
# add `chars_in_string(s:string,c:string) -> int``
|
||||
|
@ -79,7 +94,7 @@ class Tasmota
|
|||
var sub_event = event
|
||||
var rl = string.split(rl_list[0],'#')
|
||||
for it:rl
|
||||
found=self.find_key_i(sub_event,it)
|
||||
var found=self.find_key_i(sub_event,it)
|
||||
if found == nil return false end
|
||||
sub_event = sub_event[found]
|
||||
end
|
||||
|
@ -127,9 +142,9 @@ class Tasmota
|
|||
return false
|
||||
end
|
||||
|
||||
def set_timer(delay,f)
|
||||
def set_timer(delay,f,id)
|
||||
if !self._timers self._timers=[] end
|
||||
self._timers.push([self.millis(delay),f])
|
||||
self._timers.push(Timer(self.millis(delay),f,id))
|
||||
end
|
||||
|
||||
# run every 50ms tick
|
||||
|
@ -137,8 +152,8 @@ class Tasmota
|
|||
if self._timers
|
||||
var i=0
|
||||
while i<self._timers.size()
|
||||
if self.time_reached(self._timers[i][0])
|
||||
f=self._timers[i][1]
|
||||
if self.time_reached(self._timers[i].due)
|
||||
var f=self._timers[i].f
|
||||
self._timers.remove(i)
|
||||
f()
|
||||
else
|
||||
|
@ -148,6 +163,20 @@ class Tasmota
|
|||
end
|
||||
end
|
||||
|
||||
# remove timers by id
|
||||
def remove_timer(id)
|
||||
if tasmota._timers
|
||||
var i=0
|
||||
while i<tasmota._timers.size()
|
||||
if self._timers[i].id == id
|
||||
self._timers.remove(i)
|
||||
else
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add command to list
|
||||
def add_cmd(c,f)
|
||||
if !self._ccmd
|
||||
|
@ -265,6 +294,47 @@ class Tasmota
|
|||
end
|
||||
end
|
||||
|
||||
def add_driver(d)
|
||||
if self._drivers
|
||||
self._drivers.push(d)
|
||||
else
|
||||
self._drivers = [d]
|
||||
end
|
||||
end
|
||||
|
||||
# cmd high-level function
|
||||
def cmd(command)
|
||||
import json
|
||||
var ret = self._cmd(command)
|
||||
var j = json.load(ret)
|
||||
if type(j) == 'instance'
|
||||
return j
|
||||
else
|
||||
return {'response':j}
|
||||
end
|
||||
end
|
||||
|
||||
# set_light and get_light deprecetaion
|
||||
def get_light(l)
|
||||
print('tasmota.get_light() is deprecated, use light.get()')
|
||||
import light
|
||||
if l != nil
|
||||
return light.get(l)
|
||||
else
|
||||
return light.get()
|
||||
end
|
||||
end
|
||||
def set_light(v,l)
|
||||
print('tasmota.set_light() is deprecated, use light.set()')
|
||||
import light
|
||||
if l != nil
|
||||
return light.set(v,l)
|
||||
else
|
||||
return light.set(v)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#- dispatch callback number n, with parameters v0,v1,v2,v3 -#
|
||||
def cb_dispatch(n,v0,v1,v2,v3)
|
||||
if self._cb == nil return 0 end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,34 +1,36 @@
|
|||
#include "be_constobj.h"
|
||||
|
||||
static be_define_const_map_slots(be_class_bytes_map) {
|
||||
{ be_const_key(resize, 10), be_const_func(m_resize) },
|
||||
{ be_const_key(asstring, 11), be_const_func(m_asstring) },
|
||||
{ be_const_key(init, -1), be_const_func(m_init) },
|
||||
{ be_const_key(seti, -1), be_const_func(m_set) },
|
||||
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
|
||||
{ be_const_key(item, 18), be_const_func(m_item) },
|
||||
{ be_const_key(dot_p, 3), be_const_index(0) },
|
||||
{ be_const_key(geti, -1), be_const_func(m_geti) },
|
||||
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
|
||||
{ be_const_key(tostring, -1), be_const_func(m_tostring) },
|
||||
{ be_const_key(size, -1), be_const_func(m_size) },
|
||||
{ be_const_key(dot_p, -1), be_const_index(0) },
|
||||
{ be_const_key(seti, 6), be_const_func(m_set) },
|
||||
{ be_const_key(get, 7), be_const_func(m_getu) },
|
||||
{ be_const_key(size, 10), be_const_func(m_size) },
|
||||
{ be_const_key(resize, 1), be_const_func(m_resize) },
|
||||
{ be_const_key(opt_add, 22), be_const_func(m_merge) },
|
||||
{ be_const_key(getbits, -1), be_const_closure(getbits_closure) },
|
||||
{ be_const_key(fromstring, -1), be_const_func(m_fromstring) },
|
||||
{ be_const_key(opt_add, -1), be_const_func(m_merge) },
|
||||
{ be_const_key(_buffer, 2), be_const_func(m_buffer) },
|
||||
{ be_const_key(copy, 8), be_const_func(m_copy) },
|
||||
{ be_const_key(get, -1), be_const_func(m_getu) },
|
||||
{ be_const_key(set, -1), be_const_func(m_set) },
|
||||
{ be_const_key(opt_eq, -1), be_const_func(m_equal) },
|
||||
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
|
||||
{ be_const_key(clear, 16), be_const_func(m_clear) },
|
||||
{ be_const_key(setbits, -1), be_const_closure(setbits_closure) },
|
||||
{ be_const_key(geti, 16), be_const_func(m_geti) },
|
||||
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
|
||||
{ be_const_key(add, -1), be_const_func(m_add) },
|
||||
{ be_const_key(fromb64, -1), be_const_func(m_fromb64) },
|
||||
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
|
||||
{ be_const_key(set, -1), be_const_func(m_set) },
|
||||
{ be_const_key(asstring, -1), be_const_func(m_asstring) },
|
||||
{ be_const_key(copy, 3), be_const_func(m_copy) },
|
||||
{ be_const_key(opt_eq, 2), be_const_func(m_equal) },
|
||||
{ be_const_key(tob64, -1), be_const_func(m_tob64) },
|
||||
{ be_const_key(setbits, 12), be_const_closure(setbits_closure) },
|
||||
{ be_const_key(_buffer, -1), be_const_func(m_buffer) },
|
||||
{ be_const_key(fromstring, 0), be_const_func(m_fromstring) },
|
||||
{ be_const_key(tostring, 9), be_const_func(m_tostring) },
|
||||
{ be_const_key(item, 8), be_const_func(m_item) },
|
||||
{ be_const_key(init, 23), be_const_func(m_init) },
|
||||
{ be_const_key(opt_connect, -1), be_const_func(m_connect) },
|
||||
{ be_const_key(clear, -1), be_const_func(m_clear) },
|
||||
};
|
||||
|
||||
static be_define_const_map(
|
||||
be_class_bytes_map,
|
||||
23
|
||||
25
|
||||
);
|
||||
|
||||
BE_EXPORT_VARIABLE be_define_const_class(
|
||||
|
|
|
@ -1,71 +1,72 @@
|
|||
#include "be_constobj.h"
|
||||
|
||||
static be_define_const_map_slots(be_class_tasmota_map) {
|
||||
{ be_const_key(gc, -1), be_const_closure(gc_closure) },
|
||||
{ be_const_key(_cmd, -1), be_const_func(l_cmd) },
|
||||
{ be_const_key(event, -1), be_const_closure(event_closure) },
|
||||
{ be_const_key(millis, -1), be_const_func(l_millis) },
|
||||
{ be_const_key(eth, 23), be_const_func(l_eth) },
|
||||
{ be_const_key(try_rule, -1), be_const_closure(try_rule_closure) },
|
||||
{ be_const_key(resp_cmnd, -1), be_const_func(l_respCmnd) },
|
||||
{ be_const_key(set_light, -1), be_const_closure(set_light_closure) },
|
||||
{ be_const_key(remove_rule, -1), be_const_closure(remove_rule_closure) },
|
||||
{ be_const_key(get_light, -1), be_const_closure(get_light_closure) },
|
||||
{ be_const_key(_rules, -1), be_const_index(0) },
|
||||
{ be_const_key(add_driver, 0), be_const_closure(add_driver_closure) },
|
||||
{ be_const_key(save, 38), be_const_func(l_save) },
|
||||
{ be_const_key(set_timer, -1), be_const_closure(set_timer_closure) },
|
||||
{ be_const_key(yield, 48), be_const_func(l_yield) },
|
||||
{ be_const_key(resp_cmnd_done, -1), be_const_func(l_respCmndDone) },
|
||||
{ be_const_key(find_op, 4), be_const_closure(find_op_closure) },
|
||||
{ be_const_key(get_free_heap, -1), be_const_func(l_getFreeHeap) },
|
||||
{ be_const_key(remove_cmd, -1), be_const_closure(remove_cmd_closure) },
|
||||
{ be_const_key(exec_cmd, 20), be_const_closure(exec_cmd_closure) },
|
||||
{ be_const_key(add_cmd, -1), be_const_closure(add_cmd_closure) },
|
||||
{ be_const_key(log, -1), be_const_func(l_logInfo) },
|
||||
{ be_const_key(_get_cb, 18), be_const_func(l_get_cb) },
|
||||
{ be_const_key(_timers, 35), be_const_index(1) },
|
||||
{ be_const_key(wifi, 55), be_const_func(l_wifi) },
|
||||
{ be_const_key(_drivers, 10), be_const_index(2) },
|
||||
{ be_const_key(find_key_i, -1), be_const_closure(find_key_i_closure) },
|
||||
{ be_const_key(_cb, -1), be_const_index(3) },
|
||||
{ be_const_key(web_send, -1), be_const_func(l_webSend) },
|
||||
{ be_const_key(resp_cmnd_error, -1), be_const_func(l_respCmndError) },
|
||||
{ be_const_key(memory, 31), be_const_func(l_memory) },
|
||||
{ be_const_key(resp_cmnd_str, -1), be_const_func(l_respCmndStr) },
|
||||
{ be_const_key(time_str, 7), be_const_closure(time_str_closure) },
|
||||
{ be_const_key(set_power, 34), be_const_func(l_setpower) },
|
||||
{ be_const_key(get_option, 46), be_const_func(l_getoption) },
|
||||
{ be_const_key(rtc, -1), be_const_func(l_rtc) },
|
||||
{ be_const_key(response_append, -1), be_const_func(l_respAppend) },
|
||||
{ be_const_key(publish_result, -1), be_const_func(l_publish_result) },
|
||||
{ be_const_key(chars_in_string, 50), be_const_closure(chars_in_string_closure) },
|
||||
{ be_const_key(cmd, 2), be_const_closure(cmd_closure) },
|
||||
{ be_const_key(wire_scan, -1), be_const_closure(wire_scan_closure) },
|
||||
{ be_const_key(i2c_enabled, -1), be_const_func(l_i2cenabled) },
|
||||
{ be_const_key(resp_cmnd_failed, 1), be_const_func(l_respCmndFailed) },
|
||||
{ be_const_key(add_rule, 29), be_const_closure(add_rule_closure) },
|
||||
{ be_const_key(publish, -1), be_const_func(l_publish) },
|
||||
{ be_const_key(resolvecmnd, -1), be_const_func(l_resolveCmnd) },
|
||||
{ be_const_key(_ccmd, -1), be_const_index(4) },
|
||||
{ be_const_key(time_dump, -1), be_const_func(l_time_dump) },
|
||||
{ be_const_key(scale_uint, -1), be_const_func(l_scaleuint) },
|
||||
{ be_const_key(load, -1), be_const_closure(load_closure) },
|
||||
{ be_const_key(exec_rules, 37), be_const_closure(exec_rules_closure) },
|
||||
{ be_const_key(gen_cb, 36), be_const_closure(gen_cb_closure) },
|
||||
{ be_const_key(cb_dispatch, -1), be_const_closure(cb_dispatch_closure) },
|
||||
{ be_const_key(resp_cmnd_error, -1), be_const_func(l_respCmndError) },
|
||||
{ be_const_key(exec_rules, -1), be_const_closure(exec_rules_closure) },
|
||||
{ be_const_key(_cmd, -1), be_const_func(l_cmd) },
|
||||
{ be_const_key(gc, -1), be_const_closure(gc_closure) },
|
||||
{ be_const_key(remove_rule, -1), be_const_closure(remove_rule_closure) },
|
||||
{ be_const_key(event, -1), be_const_closure(event_closure) },
|
||||
{ be_const_key(log, 25), be_const_func(l_logInfo) },
|
||||
{ be_const_key(_drivers, -1), be_const_index(0) },
|
||||
{ be_const_key(set_light, -1), be_const_closure(set_light_closure) },
|
||||
{ be_const_key(wire2, 26), be_const_index(1) },
|
||||
{ be_const_key(get_free_heap, -1), be_const_func(l_getFreeHeap) },
|
||||
{ be_const_key(resp_cmnd_failed, -1), be_const_func(l_respCmndFailed) },
|
||||
{ be_const_key(publish_result, -1), be_const_func(l_publish_result) },
|
||||
{ be_const_key(wire1, -1), be_const_index(2) },
|
||||
{ be_const_key(set_power, 46), be_const_func(l_setpower) },
|
||||
{ be_const_key(cmd, -1), be_const_closure(cmd_closure) },
|
||||
{ be_const_key(get_light, 42), be_const_closure(get_light_closure) },
|
||||
{ be_const_key(_rules, 54), be_const_index(3) },
|
||||
{ be_const_key(run_deferred, 19), be_const_closure(run_deferred_closure) },
|
||||
{ be_const_key(try_rule, 18), be_const_closure(try_rule_closure) },
|
||||
{ be_const_key(remove_cmd, 41), be_const_closure(remove_cmd_closure) },
|
||||
{ be_const_key(add_driver, 0), be_const_closure(add_driver_closure) },
|
||||
{ be_const_key(get_power, 36), be_const_func(l_getpower) },
|
||||
{ be_const_key(remove_timer, -1), be_const_closure(remove_timer_closure) },
|
||||
{ be_const_key(_timers, -1), be_const_index(4) },
|
||||
{ be_const_key(add_rule, 33), be_const_closure(add_rule_closure) },
|
||||
{ be_const_key(time_reached, -1), be_const_func(l_timereached) },
|
||||
{ be_const_key(web_send_decimal, -1), be_const_func(l_webSendDecimal) },
|
||||
{ be_const_key(delay, 54), be_const_func(l_delay) },
|
||||
{ be_const_key(run_deferred, 9), be_const_closure(run_deferred_closure) },
|
||||
{ be_const_key(get_power, -1), be_const_func(l_getpower) },
|
||||
{ be_const_key(wire2, -1), be_const_index(5) },
|
||||
{ be_const_key(wire1, 52), be_const_index(6) },
|
||||
{ be_const_key(resp_cmnd_done, -1), be_const_func(l_respCmndDone) },
|
||||
{ be_const_key(time_dump, -1), be_const_func(l_time_dump) },
|
||||
{ be_const_key(resolvecmnd, -1), be_const_func(l_resolveCmnd) },
|
||||
{ be_const_key(millis, -1), be_const_func(l_millis) },
|
||||
{ be_const_key(get_option, 52), be_const_func(l_getoption) },
|
||||
{ be_const_key(eth, -1), be_const_func(l_eth) },
|
||||
{ be_const_key(wifi, 22), be_const_func(l_wifi) },
|
||||
{ be_const_key(response_append, 43), be_const_func(l_respAppend) },
|
||||
{ be_const_key(find_op, -1), be_const_closure(find_op_closure) },
|
||||
{ be_const_key(set_timer, 28), be_const_closure(set_timer_closure) },
|
||||
{ be_const_key(find_key_i, -1), be_const_closure(find_key_i_closure) },
|
||||
{ be_const_key(gen_cb, -1), be_const_closure(gen_cb_closure) },
|
||||
{ be_const_key(memory, 47), be_const_func(l_memory) },
|
||||
{ be_const_key(resp_cmnd_str, -1), be_const_func(l_respCmndStr) },
|
||||
{ be_const_key(chars_in_string, -1), be_const_closure(chars_in_string_closure) },
|
||||
{ be_const_key(resp_cmnd, -1), be_const_func(l_respCmnd) },
|
||||
{ be_const_key(web_send_decimal, 44), be_const_func(l_webSendDecimal) },
|
||||
{ be_const_key(load, -1), be_const_closure(load_closure) },
|
||||
{ be_const_key(web_send, 56), be_const_func(l_webSend) },
|
||||
{ be_const_key(_ccmd, -1), be_const_index(5) },
|
||||
{ be_const_key(i2c_enabled, 48), be_const_func(l_i2cenabled) },
|
||||
{ be_const_key(save, -1), be_const_func(l_save) },
|
||||
{ be_const_key(exec_cmd, 2), be_const_closure(exec_cmd_closure) },
|
||||
{ be_const_key(cb_dispatch, -1), be_const_closure(cb_dispatch_closure) },
|
||||
{ be_const_key(wire_scan, -1), be_const_closure(wire_scan_closure) },
|
||||
{ be_const_key(scale_uint, -1), be_const_func(l_scaleuint) },
|
||||
{ be_const_key(delay, -1), be_const_func(l_delay) },
|
||||
{ be_const_key(_cb, -1), be_const_index(6) },
|
||||
{ be_const_key(add_cmd, -1), be_const_closure(add_cmd_closure) },
|
||||
{ be_const_key(_get_cb, -1), be_const_func(l_get_cb) },
|
||||
{ be_const_key(yield, -1), be_const_func(l_yield) },
|
||||
{ be_const_key(time_str, -1), be_const_closure(time_str_closure) },
|
||||
};
|
||||
|
||||
static be_define_const_map(
|
||||
be_class_tasmota_map,
|
||||
60
|
||||
61
|
||||
);
|
||||
|
||||
BE_EXPORT_VARIABLE be_define_const_class(
|
||||
|
|
|
@ -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);
|
|
@ -640,7 +640,8 @@ BERRY_API bbool be_copy(bvm *vm, int index)
|
|||
return bfalse;
|
||||
}
|
||||
|
||||
static int ins_member(bvm *vm, int index, const char *k)
|
||||
/* `onlyins` limits the search to instance, and discards module. Makes sure getmethod does not return anything for module. */
|
||||
static int ins_member(bvm *vm, int index, const char *k, bbool onlyins)
|
||||
{
|
||||
int type = BE_NIL;
|
||||
bvalue *o = be_indexof(vm, index);
|
||||
|
@ -652,18 +653,25 @@ static int ins_member(bvm *vm, int index, const char *k)
|
|||
if (type == BE_NONE) {
|
||||
type = BE_NIL;
|
||||
}
|
||||
} else if (var_ismodule(o) && !onlyins) {
|
||||
bmodule *module = var_toobj(o);
|
||||
bvalue *v = be_module_attr(vm, module, be_newstr(vm, k));
|
||||
if (v != NULL) {
|
||||
*top = *v;
|
||||
type = v->type;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
BERRY_API bbool be_getmember(bvm *vm, int index, const char *k)
|
||||
{
|
||||
return ins_member(vm, index, k) != BE_NIL;
|
||||
return ins_member(vm, index, k, bfalse) != BE_NIL;
|
||||
}
|
||||
|
||||
BERRY_API bbool be_getmethod(bvm *vm, int index, const char *k)
|
||||
{
|
||||
return basetype(ins_member(vm, index, k)) == BE_FUNCTION;
|
||||
return basetype(ins_member(vm, index, k, btrue)) == BE_FUNCTION;
|
||||
}
|
||||
|
||||
BERRY_API bbool be_getindex(bvm *vm, int index)
|
||||
|
|
|
@ -28,6 +28,198 @@ typedef struct buf_impl {
|
|||
uint8_t buf[]; // the actual data
|
||||
} buf_impl;
|
||||
|
||||
/********************************************************************
|
||||
** Base64 lib from https://github.com/Densaugeo/base64_arduino
|
||||
**
|
||||
********************************************************************/
|
||||
|
||||
/* binary_to_base64:
|
||||
* Description:
|
||||
* Converts a single byte from a binary value to the corresponding base64 character
|
||||
* Parameters:
|
||||
* v - Byte to convert
|
||||
* Returns:
|
||||
* ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character
|
||||
* and 255 is returned
|
||||
*/
|
||||
static unsigned char binary_to_base64(unsigned char v);
|
||||
|
||||
/* base64_to_binary:
|
||||
* Description:
|
||||
* Converts a single byte from a base64 character to the corresponding binary value
|
||||
* Parameters:
|
||||
* c - Base64 character (as ascii code)
|
||||
* Returns:
|
||||
* 6-bit binary value
|
||||
*/
|
||||
static unsigned char base64_to_binary(unsigned char c);
|
||||
|
||||
/* encode_base64_length:
|
||||
* Description:
|
||||
* Calculates length of base64 string needed for a given number of binary bytes
|
||||
* Parameters:
|
||||
* input_length - Amount of binary data in bytes
|
||||
* Returns:
|
||||
* Number of base64 characters needed to encode input_length bytes of binary data
|
||||
*/
|
||||
static unsigned int encode_base64_length(unsigned int input_length);
|
||||
|
||||
/* decode_base64_length:
|
||||
* Description:
|
||||
* Calculates number of bytes of binary data in a base64 string
|
||||
* Parameters:
|
||||
* input - Base64-encoded null-terminated string
|
||||
* Returns:
|
||||
* Number of bytes of binary data in input
|
||||
*/
|
||||
static unsigned int decode_base64_length(unsigned char input[]);
|
||||
|
||||
/* encode_base64:
|
||||
* Description:
|
||||
* Converts an array of bytes to a base64 null-terminated string
|
||||
* Parameters:
|
||||
* input - Pointer to input data
|
||||
* input_length - Number of bytes to read from input pointer
|
||||
* output - Pointer to output string. Null terminator will be added automatically
|
||||
* Returns:
|
||||
* Length of encoded string in bytes (not including null terminator)
|
||||
*/
|
||||
static unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]);
|
||||
|
||||
/* decode_base64:
|
||||
* Description:
|
||||
* Converts a base64 null-terminated string to an array of bytes
|
||||
* Parameters:
|
||||
* input - Pointer to input string
|
||||
* output - Pointer to output array
|
||||
* Returns:
|
||||
* Number of bytes in the decoded binary
|
||||
*/
|
||||
static unsigned int decode_base64(unsigned char input[], unsigned char output[]);
|
||||
|
||||
static unsigned char binary_to_base64(unsigned char v) {
|
||||
// Capital letters - 'A' is ascii 65 and base64 0
|
||||
if(v < 26) return v + 'A';
|
||||
|
||||
// Lowercase letters - 'a' is ascii 97 and base64 26
|
||||
if(v < 52) return v + 71;
|
||||
|
||||
// Digits - '0' is ascii 48 and base64 52
|
||||
if(v < 62) return v - 4;
|
||||
|
||||
// '+' is ascii 43 and base64 62
|
||||
if(v == 62) return '+';
|
||||
|
||||
// '/' is ascii 47 and base64 63
|
||||
if(v == 63) return '/';
|
||||
|
||||
return 64;
|
||||
}
|
||||
|
||||
static unsigned char base64_to_binary(unsigned char c) {
|
||||
// Capital letters - 'A' is ascii 65 and base64 0
|
||||
if('A' <= c && c <= 'Z') return c - 'A';
|
||||
|
||||
// Lowercase letters - 'a' is ascii 97 and base64 26
|
||||
if('a' <= c && c <= 'z') return c - 71;
|
||||
|
||||
// Digits - '0' is ascii 48 and base64 52
|
||||
if('0' <= c && c <= '9') return c + 4;
|
||||
|
||||
// '+' is ascii 43 and base64 62
|
||||
if(c == '+') return 62;
|
||||
|
||||
// '/' is ascii 47 and base64 63
|
||||
if(c == '/') return 63;
|
||||
|
||||
return 255;
|
||||
}
|
||||
|
||||
static unsigned int encode_base64_length(unsigned int input_length) {
|
||||
return (input_length + 2)/3*4;
|
||||
}
|
||||
|
||||
static unsigned int decode_base64_length(unsigned char input[]) {
|
||||
unsigned char *start = input;
|
||||
|
||||
while(base64_to_binary(input[0]) < 64) {
|
||||
++input;
|
||||
}
|
||||
|
||||
unsigned int input_length = input - start;
|
||||
|
||||
unsigned int output_length = input_length/4*3;
|
||||
|
||||
switch(input_length % 4) {
|
||||
default: return output_length;
|
||||
case 2: return output_length + 1;
|
||||
case 3: return output_length + 2;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
|
||||
unsigned int full_sets = input_length/3;
|
||||
|
||||
// While there are still full sets of 24 bits...
|
||||
for(unsigned int i = 0; i < full_sets; ++i) {
|
||||
output[0] = binary_to_base64( input[0] >> 2);
|
||||
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
|
||||
output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
|
||||
output[3] = binary_to_base64( input[2] & 0x3F);
|
||||
|
||||
input += 3;
|
||||
output += 4;
|
||||
}
|
||||
|
||||
switch(input_length % 3) {
|
||||
case 0:
|
||||
output[0] = '\0';
|
||||
break;
|
||||
case 1:
|
||||
output[0] = binary_to_base64( input[0] >> 2);
|
||||
output[1] = binary_to_base64((input[0] & 0x03) << 4);
|
||||
output[2] = '=';
|
||||
output[3] = '=';
|
||||
output[4] = '\0';
|
||||
break;
|
||||
case 2:
|
||||
output[0] = binary_to_base64( input[0] >> 2);
|
||||
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
|
||||
output[2] = binary_to_base64((input[1] & 0x0F) << 2);
|
||||
output[3] = '=';
|
||||
output[4] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
return encode_base64_length(input_length);
|
||||
}
|
||||
|
||||
static unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
|
||||
unsigned int output_length = decode_base64_length(input);
|
||||
|
||||
// While there are still full sets of 24 bits...
|
||||
for(unsigned int i = 2; i < output_length; i += 3) {
|
||||
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
|
||||
output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);
|
||||
|
||||
input += 4;
|
||||
output += 3;
|
||||
}
|
||||
|
||||
switch(output_length % 3) {
|
||||
case 1:
|
||||
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||
break;
|
||||
case 2:
|
||||
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return output_length;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
** Buffer low-level implementation
|
||||
**
|
||||
|
@ -343,13 +535,26 @@ static size_t tohex(char * out, size_t outsz, const uint8_t * in, size_t insz) {
|
|||
|
||||
static int m_tostring(bvm *vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
int max_len = 32; /* limit to 32 bytes by default */
|
||||
int truncated = 0;
|
||||
if (argc > 1 && be_isint(vm, 2)) {
|
||||
max_len = be_toint(vm, 2); /* you can specify the len as second argument, or 0 for unlimited */
|
||||
}
|
||||
buf_impl * buf = bytes_check_data(vm, 0);
|
||||
size_t len = buf->len;
|
||||
size_t hex_len = len * 2 + 5 + 2 + 2 + 1; /* reserve size for `bytes("")\0` - 9 chars */
|
||||
if (max_len > 0 && len > max_len) {
|
||||
len = max_len; /* limit output size */
|
||||
truncated = 1;
|
||||
}
|
||||
size_t hex_len = len * 2 + 5 + 2 + 2 + 1 + truncated * 3; /* reserve size for `bytes("")\0` - 9 chars */
|
||||
|
||||
char * hex_out = be_pushbuffer(vm, hex_len);
|
||||
size_t l = be_strlcpy(hex_out, "bytes('", hex_len);
|
||||
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), buf->len);
|
||||
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), len);
|
||||
if (truncated) {
|
||||
l += be_strlcpy(&hex_out[l], "...", hex_len - l);
|
||||
}
|
||||
l += be_strlcpy(&hex_out[l], "')", hex_len - l);
|
||||
|
||||
be_pushnstring(vm, hex_out, l); /* make escape string from buffer */
|
||||
|
@ -699,6 +904,56 @@ static int m_nequal(bvm *vm)
|
|||
return bytes_equal(vm, bfalse);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts bytes() to a base64 string
|
||||
*
|
||||
* Note: there are no line breaks inserted
|
||||
*
|
||||
* `b.tob64() -> string`
|
||||
*/
|
||||
static int m_tob64(bvm *vm)
|
||||
{
|
||||
buf_impl * buf = bytes_check_data(vm, 0);
|
||||
size_t len = buf->len;
|
||||
size_t b64_len = encode_base64_length(len) + 1; /* size of base64 encoded string for this binary length, add NULL terminator */
|
||||
|
||||
char * b64_out = be_pushbuffer(vm, b64_len);
|
||||
size_t converted = encode_base64(buf_get_buf(buf), len, b64_out);
|
||||
|
||||
be_pushnstring(vm, b64_out, converted); /* make string from buffer */
|
||||
be_remove(vm, -2); /* remove buffer */
|
||||
be_return(vm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts base63 to bytes()
|
||||
*
|
||||
* `bytes().fromb64() -> bytes()`
|
||||
*/
|
||||
static int m_fromb64(bvm *vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
if (argc >= 2 && be_isstring(vm, 2)) {
|
||||
const char *s = be_tostring(vm, 2);
|
||||
size_t len = be_strlen(vm, 2);
|
||||
size_t bin_len = decode_base64_length(s); /* do a first pass to calculate the buffer size */
|
||||
|
||||
buf_impl * buf = bytes_check_data(vm, 0);
|
||||
buf = bytes_resize(vm, buf, bin_len); /* resize if needed */
|
||||
if (bin_len > buf->size) { /* avoid overflow */
|
||||
be_raise(vm, "memory_error", "cannot allocate buffer");
|
||||
}
|
||||
|
||||
size_t bin_len_final = decode_base64(s, buf_get_buf(buf)); /* decode */
|
||||
buf->len = bin_len_final;
|
||||
be_pop(vm, 1); /* remove arg to leave instance */
|
||||
be_return(vm);
|
||||
}
|
||||
be_raise(vm, "type_error", "operand must be a string");
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Advanced API
|
||||
*/
|
||||
|
@ -973,6 +1228,8 @@ void be_load_byteslib(bvm *vm)
|
|||
{ "tostring", m_tostring },
|
||||
{ "asstring", m_asstring },
|
||||
{ "fromstring", m_fromstring },
|
||||
{ "tob64", m_tob64 },
|
||||
{ "fromb64", m_fromb64 },
|
||||
{ "add", m_add },
|
||||
{ "get", m_getu },
|
||||
{ "geti", m_geti },
|
||||
|
@ -1006,6 +1263,8 @@ class be_class_bytes (scope: global, name: bytes) {
|
|||
tostring, func(m_tostring)
|
||||
asstring, func(m_asstring)
|
||||
fromstring, func(m_fromstring)
|
||||
tob64, func(m_tob64)
|
||||
fromb64, func(m_fromb64)
|
||||
add, func(m_add)
|
||||
get, func(m_getu)
|
||||
geti, func(m_geti)
|
||||
|
|
|
@ -178,6 +178,7 @@ void be_class_upvalue_init(bvm *vm, bclass *c)
|
|||
}
|
||||
}
|
||||
|
||||
/* (internal) Instanciate an instance for a single class and initialize variables to nil */
|
||||
static binstance* newobjself(bvm *vm, bclass *c)
|
||||
{
|
||||
size_t size = sizeof(binstance) + sizeof(bvalue) * (c->nvar - 1);
|
||||
|
@ -185,15 +186,17 @@ static binstance* newobjself(bvm *vm, bclass *c)
|
|||
binstance *obj = cast_instance(gco);
|
||||
be_assert(obj != NULL);
|
||||
if (obj) { /* initialize members */
|
||||
bvalue *v = obj->members, *end = v + c->nvar;
|
||||
while (v < end) { var_setnil(v); ++v; }
|
||||
obj->_class = c;
|
||||
obj->super = NULL;
|
||||
obj->sub = NULL;
|
||||
bvalue *v = obj->members, *end = v + c->nvar; /* instance variables is a simple array of pointers at obj->members of size c->nvar */
|
||||
while (v < end) { var_setnil(v); ++v; } /* Initialize all instance variables to `nil` */
|
||||
obj->_class = c; /* set its class object */
|
||||
obj->super = NULL; /* no super class instance for now */
|
||||
obj->sub = NULL; /* no subclass instance for now */
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* (internal) Instanciate the whole chain of instances when there is a class hierarchy */
|
||||
/* All variables set to nil, constructors are not called here */
|
||||
static binstance* newobject(bvm *vm, bclass *c)
|
||||
{
|
||||
binstance *obj, *prev;
|
||||
|
@ -201,23 +204,26 @@ static binstance* newobject(bvm *vm, bclass *c)
|
|||
obj = prev = newobjself(vm, c);
|
||||
var_setinstance(vm->top, obj);
|
||||
be_incrtop(vm); /* protect new objects from GC */
|
||||
for (c = c->super; c; c = c->super) {
|
||||
for (c = c->super; c; c = c->super) { /* initialize one instance object per class and per superclass */
|
||||
prev->super = newobjself(vm, c);
|
||||
prev->super->sub = prev;
|
||||
prev->super->sub = prev; /* link the super/sub classes instances */
|
||||
prev = prev->super;
|
||||
}
|
||||
be_stackpop(vm, 1);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Instanciate new instance from stack with argc parameters */
|
||||
/* Pushes the constructor on the stack to be executed if a construtor is found */
|
||||
/* Returns true if a constructor is found */
|
||||
bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
|
||||
{
|
||||
bvalue init;
|
||||
size_t pos = reg - vm->reg;
|
||||
binstance *obj = newobject(vm, c);
|
||||
reg = vm->reg + pos - mode; /* the stack may have changed */
|
||||
binstance *obj = newobject(vm, c); /* create empty object hierarchy from class hierarchy */
|
||||
reg = vm->reg + pos - mode; /* the stack may have changed, mode=1 when class is instanciated from module #104 */
|
||||
var_setinstance(reg, obj);
|
||||
var_setinstance(reg + mode, obj);
|
||||
var_setinstance(reg + mode, obj); /* copy to reg and reg+1 if mode==1 */
|
||||
/* find constructor */
|
||||
obj = instance_member(vm, obj, str_literal(vm, "init"), &init);
|
||||
if (obj && var_type(&init) != MT_VARIABLE) {
|
||||
|
@ -231,6 +237,10 @@ bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc, int mode)
|
|||
return bfalse;
|
||||
}
|
||||
|
||||
/* Find instance member by name and copy value to `dst` */
|
||||
/* Input: none of `obj`, `name` and `dst` may not be NULL */
|
||||
/* Returns the type of the member or BE_NONE if member not found */
|
||||
/* TODO need to support synthetic members */
|
||||
int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst)
|
||||
{
|
||||
int type;
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define notexpr(e) isset((e)->not, NOT_EXPR)
|
||||
#define notmask(e) isset((e)->not, NOT_MASK)
|
||||
#define exp2anyreg(f, e) exp2reg(f, e, (f)->freereg)
|
||||
#define var2anyreg(f, e) var2reg(f, e, (f)->freereg)
|
||||
#define exp2anyreg(f, e) exp2reg(f, e, -1) /* -1 means allocate a new register if needed */
|
||||
#define var2anyreg(f, e) var2reg(f, e, -1) /* -1 means allocate a new register if needed */
|
||||
#define hasjump(e) ((e)->t != (e)->f || notexpr(e))
|
||||
#define code_bool(f, r, b, j) codeABC(f, OP_LDBOOL, r, b, j)
|
||||
#define code_call(f, a, b) codeABC(f, OP_CALL, a, b, 0)
|
||||
|
@ -56,6 +56,7 @@ static void codelineinfo(bfuncinfo *finfo)
|
|||
#define codelineinfo(finfo)
|
||||
#endif
|
||||
|
||||
/* Add new instruction in the code vector */
|
||||
static int codeinst(bfuncinfo *finfo, binstruction ins)
|
||||
{
|
||||
/* put new instruction in code array */
|
||||
|
@ -77,10 +78,13 @@ static int codeABx(bfuncinfo *finfo, bopcode op, int a, int bx)
|
|||
return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx));
|
||||
}
|
||||
|
||||
/* Move value from register b to register a */
|
||||
/* Check the previous instruction to compact both instruction as one if possible */
|
||||
/* If b is a constant, add LDCONST or add MOVE otherwise */
|
||||
static void code_move(bfuncinfo *finfo, int a, int b)
|
||||
{
|
||||
if (finfo->pc) {
|
||||
binstruction *i = be_vector_end(&finfo->code);
|
||||
if (finfo->pc) { /* If not the first instruction of the function */
|
||||
binstruction *i = be_vector_end(&finfo->code); /* get the last instruction */
|
||||
bopcode op = IGET_OP(*i);
|
||||
if (op <= OP_LDNIL) { /* binop or unop */
|
||||
/* remove redundant MOVE instruction */
|
||||
|
@ -98,6 +102,8 @@ static void code_move(bfuncinfo *finfo, int a, int b)
|
|||
}
|
||||
}
|
||||
|
||||
/* Free register at top (checks that it´s a register) */
|
||||
/* Warning: the register must be at top of stack */
|
||||
static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
|
||||
{
|
||||
/* release temporary register */
|
||||
|
@ -106,6 +112,8 @@ static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
|
|||
}
|
||||
}
|
||||
|
||||
/* Privat. Allocate `count` new registers on the stack and uptade proto´s max nstack accordingly */
|
||||
/* Note: deallocate is simpler and handled by a macro */
|
||||
static void allocstack(bfuncinfo *finfo, int count)
|
||||
{
|
||||
int nstack = finfo->freereg + count;
|
||||
|
@ -117,6 +125,7 @@ static void allocstack(bfuncinfo *finfo, int count)
|
|||
}
|
||||
}
|
||||
|
||||
/* Allocate `count` registers at top of stack, update stack accordingly */
|
||||
int be_code_allocregs(bfuncinfo *finfo, int count)
|
||||
{
|
||||
int base = finfo->freereg;
|
||||
|
@ -227,6 +236,8 @@ void be_code_patchjump(bfuncinfo *finfo, int jmp)
|
|||
patchlistaux(finfo, jmp, finfo->pc, finfo->pc);
|
||||
}
|
||||
|
||||
/* Allocate new constant for value k */
|
||||
/* If k is NULL then push `nil` value */
|
||||
static int newconst(bfuncinfo *finfo, bvalue *k)
|
||||
{
|
||||
int idx = be_vector_count(&finfo->kvec);
|
||||
|
@ -239,6 +250,8 @@ static int newconst(bfuncinfo *finfo, bvalue *k)
|
|||
return idx;
|
||||
}
|
||||
|
||||
/* Find constant by value and return constant number, or -1 if constant does not exist */
|
||||
/* The search is linear and lilited to 50 elements for performance reasons */
|
||||
static int findconst(bfuncinfo *finfo, bexpdesc *e)
|
||||
{
|
||||
int i, count = be_vector_count(&finfo->kvec);
|
||||
|
@ -273,10 +286,11 @@ static int findconst(bfuncinfo *finfo, bexpdesc *e)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* convert expdesc to constant and return kreg index (either constant kindex or register number) */
|
||||
static int exp2const(bfuncinfo *finfo, bexpdesc *e)
|
||||
{
|
||||
int idx = findconst(finfo, e);
|
||||
if (idx == -1) {
|
||||
int idx = findconst(finfo, e); /* does the constant already exist? */
|
||||
if (idx == -1) { /* if not add it */
|
||||
bvalue k;
|
||||
switch (e->type) {
|
||||
case ETINT:
|
||||
|
@ -291,16 +305,16 @@ static int exp2const(bfuncinfo *finfo, bexpdesc *e)
|
|||
k.type = BE_STRING;
|
||||
k.v.s = e->v.s;
|
||||
break;
|
||||
default:
|
||||
default: /* all other values are filled later */
|
||||
break;
|
||||
}
|
||||
idx = newconst(finfo, &k);
|
||||
idx = newconst(finfo, &k); /* create new constant */
|
||||
}
|
||||
if (idx < 256) {
|
||||
e->type = ETCONST;
|
||||
if (idx < 256) { /* if constant number fits in KB or KC */
|
||||
e->type = ETCONST; /* new type is constant by index */
|
||||
e->v.idx = setK(idx);
|
||||
} else { /* index value is too large */
|
||||
e->type = ETREG;
|
||||
e->type = ETREG; /* does not fit in compact mode, allocate an explicit register and emit LDCONTS */
|
||||
e->v.idx = be_code_allocregs(finfo, 1);
|
||||
codeABx(finfo, OP_LDCONST, e->v.idx, idx);
|
||||
}
|
||||
|
@ -321,9 +335,38 @@ static void free_suffix(bfuncinfo *finfo, bexpdesc *e)
|
|||
}
|
||||
}
|
||||
|
||||
static int suffix_destreg(bfuncinfo *finfo, bexpdesc *e1, int dst)
|
||||
{
|
||||
int cand_dst = dst; /* candidate for new dst */
|
||||
int nlocal = be_list_count(finfo->local);
|
||||
int reg1 = (e1->v.ss.tt == ETREG) ? e1->v.ss.obj : -1; /* check if obj is ETREG or -1 */
|
||||
int reg2 = (!isK(e1->v.ss.idx) && e1->v.ss.idx >= nlocal) ? e1->v.ss.idx : -1; /* check if idx is ETREG or -1 */
|
||||
|
||||
if (reg1 >= 0 && reg2 >= 0) {
|
||||
/* both are ETREG, we keep the lowest and discard the other */
|
||||
if (reg1 != reg2) {
|
||||
cand_dst = min(reg1, reg2);
|
||||
be_code_freeregs(finfo, 1); /* and free the other one */
|
||||
} else {
|
||||
cand_dst = reg1; /* both ETREG are equal, we return its value */
|
||||
}
|
||||
} else if (reg1 >= 0) {
|
||||
cand_dst = reg1;
|
||||
} else if (reg2 >= 0) {
|
||||
cand_dst = reg2;
|
||||
} else {
|
||||
// dst unchanged
|
||||
}
|
||||
|
||||
if (dst >= finfo->freereg) {
|
||||
dst = cand_dst; /* if dst was allocating a new register, use the more precise candidate */
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static int code_suffix(bfuncinfo *finfo, bopcode op, bexpdesc *e, int dst)
|
||||
{
|
||||
free_suffix(finfo, e); /* free temporary registers */
|
||||
dst = suffix_destreg(finfo, e, dst);
|
||||
if (dst > finfo->freereg) {
|
||||
dst = finfo->freereg;
|
||||
}
|
||||
|
@ -339,6 +382,9 @@ static void code_closure(bfuncinfo *finfo, int idx, int dst)
|
|||
codeABx(finfo, OP_CLOSURE, dst, idx); /* load closure to register */
|
||||
}
|
||||
|
||||
/* Given an integer, check if we should create a constant */
|
||||
/* True for values 0..3 and if there is room for kindex */
|
||||
/* This optimization makes code more compact for commonly used ints */
|
||||
static bbool constint(bfuncinfo *finfo, bint i)
|
||||
{
|
||||
/* cache common numbers */
|
||||
|
@ -349,8 +395,14 @@ static bbool constint(bfuncinfo *finfo, bint i)
|
|||
return bfalse;
|
||||
}
|
||||
|
||||
/* Compute variable from an expdesc */
|
||||
/* Return constant index, or existing register or fallback to dst */
|
||||
/* At exit, If dst is `freereg`, the register is allocated */
|
||||
static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
|
||||
{
|
||||
if (dst < 0) { /* if unspecified, allocate a new register if needed */
|
||||
dst = finfo->freereg;
|
||||
}
|
||||
be_assert(e != NULL);
|
||||
switch (e->type) {
|
||||
case ETINT:
|
||||
|
@ -403,7 +455,7 @@ static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
|
|||
static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
|
||||
{
|
||||
int reg = var2reg(finfo, e, dst);
|
||||
if (hasjump(e)) {
|
||||
if (hasjump(e)) { /* if conditional expression */
|
||||
int pcf = NO_JUMP; /* position of an eventual LOAD false */
|
||||
int pct = NO_JUMP; /* position of an eventual LOAD true */
|
||||
int jpt = appendjump(finfo, jumpboolop(e, 1), e);
|
||||
|
@ -420,31 +472,47 @@ static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
|
|||
return reg;
|
||||
}
|
||||
|
||||
static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
|
||||
/* Select dest registers from both expressions */
|
||||
/* If one of them is already a register, keep it */
|
||||
/* If e1 or e2 are registers, we keep the lowest and free the highest (that must be at top) */
|
||||
/* If none is a register, allocate a new one */
|
||||
/* Returns the destination register, guaranteed to be ETREG */
|
||||
static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, int dst)
|
||||
{
|
||||
int dst, con1 = e1->type == ETREG, con2 = e2->type == ETREG;
|
||||
int cand_dst = dst;
|
||||
int con1 = e1->type == ETREG, con2 = e2->type == ETREG;
|
||||
|
||||
if (con1 && con2) {
|
||||
dst = min(e1->v.idx, e2->v.idx);
|
||||
cand_dst = min(e1->v.idx, e2->v.idx);
|
||||
be_code_freeregs(finfo, 1);
|
||||
} else if (con1) {
|
||||
dst = e1->v.idx;
|
||||
cand_dst = e1->v.idx;
|
||||
} else if (con2) {
|
||||
dst = e2->v.idx;
|
||||
cand_dst = e2->v.idx;
|
||||
} else {
|
||||
dst = be_code_allocregs(finfo, 1);
|
||||
if (dst >= finfo->freereg) {
|
||||
cand_dst = be_code_allocregs(finfo, 1);
|
||||
return cand_dst;
|
||||
}
|
||||
}
|
||||
if (dst >= finfo->freereg) {
|
||||
return cand_dst;
|
||||
} else {
|
||||
return dst;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2)
|
||||
/* compute binary expression and update e1 as result */
|
||||
/* On exit, e1 is guaranteed to be ETREG, which may have been allocated */
|
||||
static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2, int dst)
|
||||
{
|
||||
int src1 = exp2anyreg(finfo, e1);
|
||||
if (dst < 0) { dst = finfo->freereg; }
|
||||
int src1 = exp2reg(finfo, e1, dst); /* potentially force the target for src1 reg */
|
||||
int src2 = exp2anyreg(finfo, e2);
|
||||
int dst = codedestreg(finfo, e1, e2);
|
||||
dst = codedestreg(finfo, e1, e2, dst);
|
||||
codeABC(finfo, op, dst, src1, src2);
|
||||
e1->type = ETREG;
|
||||
e1->v.idx = dst;
|
||||
e1->v.idx = dst; /* update register as output */
|
||||
}
|
||||
|
||||
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
|
||||
|
@ -462,7 +530,8 @@ void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
|
|||
}
|
||||
}
|
||||
|
||||
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2)
|
||||
/* Apply binary operator `op` to e1 and e2, result in e1 */
|
||||
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst)
|
||||
{
|
||||
switch (op) {
|
||||
case OptAnd:
|
||||
|
@ -480,12 +549,14 @@ void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2)
|
|||
case OptNE: case OptGT: case OptGE: case OptConnect:
|
||||
case OptBitAnd: case OptBitOr: case OptBitXor:
|
||||
case OptShiftL: case OptShiftR:
|
||||
binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2);
|
||||
binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2, dst);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply unary operator and return register number */
|
||||
/* If input is register, change in place or allocate new register */
|
||||
static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
|
||||
{
|
||||
int src = exp2anyreg(finfo, e);
|
||||
|
@ -495,6 +566,9 @@ static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
|
|||
e->v.idx = dst;
|
||||
}
|
||||
|
||||
/* Apply not to conditional expression */
|
||||
/* If literal compute the value */
|
||||
/* Or invert t/f subexpressions */
|
||||
static void code_not(bexpdesc *e)
|
||||
{
|
||||
switch (e->type) {
|
||||
|
@ -514,6 +588,7 @@ static void code_not(bexpdesc *e)
|
|||
e->type = ETBOOL;
|
||||
}
|
||||
|
||||
/* Negative value of literal or emit NEG opcode */
|
||||
static int code_neg(bfuncinfo *finfo, bexpdesc *e)
|
||||
{
|
||||
switch (e->type) {
|
||||
|
@ -527,6 +602,7 @@ static int code_neg(bfuncinfo *finfo, bexpdesc *e)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Bit flip of literal or emit FLIP opcode */
|
||||
static int code_flip(bfuncinfo *finfo, bexpdesc *e)
|
||||
{
|
||||
switch (e->type) {
|
||||
|
@ -539,6 +615,7 @@ static int code_flip(bfuncinfo *finfo, bexpdesc *e)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Apply unary operator: not, neg or bitflip */
|
||||
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e)
|
||||
{
|
||||
switch (op) {
|
||||
|
@ -583,24 +660,28 @@ static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src)
|
|||
codeABC(finfo, op, obj, e1->v.ss.idx, src);
|
||||
}
|
||||
|
||||
/* Assign expr e2 to e1 */
|
||||
/* e1 must be in a register and have a valid idx */
|
||||
/* return 1 if assignment was possible, 0 if type is not compatible */
|
||||
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
|
||||
{
|
||||
int src = exp2reg(finfo, e2,
|
||||
e1->type == ETLOCAL ? e1->v.idx : finfo->freereg);
|
||||
e1->type == ETLOCAL ? e1->v.idx : -1); /* Convert e2 to kreg */
|
||||
/* If e1 is a local variable, use the register */
|
||||
|
||||
if (e1->type != ETLOCAL || e1->v.idx != src) {
|
||||
free_expreg(finfo, e2); /* free source (only ETREG) */
|
||||
free_expreg(finfo, e2); /* free source (checks only ETREG) */ /* TODO e2 is at top */
|
||||
}
|
||||
switch (e1->type) {
|
||||
case ETLOCAL: /* It can't be ETREG. */
|
||||
if (e1->v.idx != src) {
|
||||
code_move(finfo, e1->v.idx, src);
|
||||
code_move(finfo, e1->v.idx, src); /* do explicit move only if needed */
|
||||
}
|
||||
break;
|
||||
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) */
|
||||
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) by global index */
|
||||
setsupvar(finfo, OP_SETGBL, e1, src);
|
||||
break;
|
||||
case ETNGLOBAL: /* store to grobal R(A) -> G(Bx) */
|
||||
case ETNGLOBAL: /* store to global R(A) -> G(Bx) by name */
|
||||
setbgblvar(finfo, OP_SETNGBL, e1, src);
|
||||
break;
|
||||
case ETUPVAL:
|
||||
|
@ -618,6 +699,9 @@ int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get the expdesc as a register */
|
||||
/* if already in a register, use the existing register */
|
||||
/* if local or const, allocate a new register and copy value */
|
||||
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e)
|
||||
{
|
||||
int dst = finfo->freereg;
|
||||
|
@ -641,6 +725,9 @@ int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e)
|
|||
return dst;
|
||||
}
|
||||
|
||||
/* Generate a CALL instruction at base register with argc consecutive values */
|
||||
/* i.e. arg1 is base+1... */
|
||||
/* Important: argc registers are freed upon call, which are supposed to be registers above base */
|
||||
void be_code_call(bfuncinfo *finfo, int base, int argc)
|
||||
{
|
||||
codeABC(finfo, OP_CALL, base, argc, 0);
|
||||
|
@ -717,6 +804,8 @@ void be_code_ret(bfuncinfo *finfo, bexpdesc *e)
|
|||
}
|
||||
}
|
||||
|
||||
/* Package a suffix object from `c` with key `k` */
|
||||
/* Both expdesc are materialized in kregs */
|
||||
static void package_suffix(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
|
||||
{
|
||||
int key = exp2anyreg(finfo, k);
|
||||
|
@ -730,12 +819,14 @@ int be_code_nglobal(bfuncinfo *finfo, bexpdesc *k)
|
|||
return exp2anyreg(finfo, k);
|
||||
}
|
||||
|
||||
/* Package a MEMBER suffix object from `c` with key `k` */
|
||||
void be_code_member(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
|
||||
{
|
||||
package_suffix(finfo, c, k);
|
||||
c->type = ETMEMBER;
|
||||
}
|
||||
|
||||
/* Package a INDEX suffix object from `c` with key `k` */
|
||||
void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
|
||||
{
|
||||
package_suffix(finfo, c, k);
|
||||
|
@ -746,15 +837,15 @@ void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c)
|
|||
{
|
||||
int src;
|
||||
bvalue var;
|
||||
var_setclass(&var, c);
|
||||
src = newconst(finfo, &var);
|
||||
if (dst->type == ETLOCAL) {
|
||||
var_setclass(&var, c); /* new var of CLASS type */
|
||||
src = newconst(finfo, &var); /* allocate a new constant and return kreg */
|
||||
if (dst->type == ETLOCAL) { /* if target is a local variable, just assign */
|
||||
codeABx(finfo, OP_LDCONST, dst->v.idx, src);
|
||||
} else {
|
||||
} else { /* otherwise set as global with same name as class name */
|
||||
codeABx(finfo, OP_LDCONST, finfo->freereg, src);
|
||||
codeABx(finfo, OP_SETGBL, finfo->freereg, dst->v.idx);
|
||||
}
|
||||
codeABx(finfo, OP_CLASS, 0, src);
|
||||
codeABx(finfo, OP_CLASS, 0, src); /* emit CLASS opcode to register class */
|
||||
}
|
||||
|
||||
void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
|
||||
|
@ -766,6 +857,12 @@ void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
|
|||
free_expreg(finfo, s);
|
||||
}
|
||||
|
||||
/* Emit IMPORT opcode for import module */
|
||||
/* `m` is module name, is copied to register if not already */
|
||||
/* `v` is destination where the imported module is stored */
|
||||
/* If destination is a local variable, it is the destination of the IMPORT opcode */
|
||||
/* otherwise the value is copied to a temporary register and stored to the destination */
|
||||
/* TODO is this optilization useful, isn´t it done anyways by be_code_move optim? */
|
||||
void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v)
|
||||
{
|
||||
int dst, src = exp2anyreg(finfo, m);
|
||||
|
@ -799,6 +896,10 @@ void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp)
|
|||
}
|
||||
}
|
||||
|
||||
/* Emit RAISE opcode */
|
||||
/* e1 is the exception code */
|
||||
/* e2 is the exception description */
|
||||
/* both are materialized to a temp register (if not null) */
|
||||
void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
|
||||
{
|
||||
if (e1) {
|
||||
|
@ -806,7 +907,7 @@ void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
|
|||
int src2 = e2 ? exp2anyreg(finfo, e2) : 0;
|
||||
codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2);
|
||||
} else {
|
||||
codeABC(finfo, OP_RAISE, 2, 0, 0);
|
||||
codeABC(finfo, OP_RAISE, 2, 0, 0); /* rethrow the current exception with parameters already on top of stack */
|
||||
}
|
||||
/* release the register occupied by the expression */
|
||||
free_expreg(finfo, e1);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
int be_code_allocregs(bfuncinfo *finfo, int count);
|
||||
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e);
|
||||
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2);
|
||||
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst);
|
||||
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e);
|
||||
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2);
|
||||
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e);
|
||||
|
|
|
@ -82,6 +82,8 @@ void be_throw(bvm *vm, int errorcode)
|
|||
}
|
||||
}
|
||||
|
||||
/* Fatal error Exit */
|
||||
/* Raise a BE_EXIT exception if within a try/catch block, or exit VM */
|
||||
BERRY_API void be_exit(bvm *vm, int status)
|
||||
{
|
||||
if (vm->errjmp) {
|
||||
|
@ -99,6 +101,8 @@ void be_throw_message(bvm *vm, int errorcode, const char *msg)
|
|||
be_throw(vm, errorcode);
|
||||
}
|
||||
|
||||
/* Exec protected: exec function and capture any exception and contain it within call */
|
||||
/* Exceptions or fatal errors are not propagated */
|
||||
int be_execprotected(bvm *vm, bpfunc f, void *data)
|
||||
{
|
||||
struct blongjmp jmp;
|
||||
|
@ -292,6 +296,7 @@ static void m_pcall(bvm *vm, void *data)
|
|||
be_dofunc(vm, p->v, p->argc);
|
||||
}
|
||||
|
||||
/* Protected call: contain any exception of fatal error and restore context if something went wrong */
|
||||
int be_protectedcall(bvm *vm, bvalue *v, int argc)
|
||||
{
|
||||
int res;
|
||||
|
@ -308,7 +313,8 @@ int be_protectedcall(bvm *vm, bvalue *v, int argc)
|
|||
}
|
||||
|
||||
#if BE_DEBUG && defined(be_assert)
|
||||
/* increase top register */
|
||||
/* increase top register and return new top */
|
||||
/* Does not expand the stack if there is not enough room, but may corrupt memory */
|
||||
bvalue* be_incrtop(bvm *vm)
|
||||
{
|
||||
bvalue *top = vm->top++;
|
||||
|
@ -317,6 +323,7 @@ bvalue* be_incrtop(bvm *vm)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* TODO what is the difference with be_stack_push? */
|
||||
void be_stackpush(bvm *vm)
|
||||
{
|
||||
/* make sure there is enough stack space */
|
||||
|
@ -324,6 +331,7 @@ void be_stackpush(bvm *vm)
|
|||
be_incrtop(vm);
|
||||
}
|
||||
|
||||
/* check that the stack is able to store `count` items, and increase stack if needed */
|
||||
void be_stack_require(bvm *vm, int count)
|
||||
{
|
||||
if (vm->top + count >= vm->stacktop) {
|
||||
|
@ -331,6 +339,7 @@ void be_stack_require(bvm *vm, int count)
|
|||
}
|
||||
}
|
||||
|
||||
/* Scan the entire callstack and adjust all pointer by `offset` */
|
||||
static void update_callstack(bvm *vm, intptr_t offset)
|
||||
{
|
||||
bcallframe *cf = be_stack_top(&vm->callstack);
|
||||
|
@ -353,20 +362,25 @@ static void update_upvalues(bvm *vm, intptr_t offset)
|
|||
}
|
||||
}
|
||||
|
||||
/* Resize the stack to new `size` as number of elements */
|
||||
/* Then update all pointers in callstack and upvalues with the new stack address */
|
||||
static void stack_resize(bvm *vm, size_t size)
|
||||
{
|
||||
intptr_t offset;
|
||||
bvalue *old = vm->stack;
|
||||
size_t os = (vm->stacktop - old) * sizeof(bvalue);
|
||||
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size);
|
||||
vm->stacktop = vm->stack + size;
|
||||
offset = ptr_offset(vm->stack, old);
|
||||
bvalue *old = vm->stack; /* save original pointer of stack before resize */
|
||||
size_t os = (vm->stacktop - old) * sizeof(bvalue); /* size of current stack allocated in bytes */
|
||||
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size); /* reallocate with the new size */
|
||||
vm->stacktop = vm->stack + size; /* compute new stacktop */
|
||||
offset = ptr_offset(vm->stack, old); /* compute the address difference between old and ne stack addresses */
|
||||
/* update callframes */
|
||||
update_callstack(vm, offset);
|
||||
/* update open upvalues */
|
||||
update_upvalues(vm, offset);
|
||||
}
|
||||
|
||||
/* Stack resize internal API */
|
||||
/* Increases the stack by `n` elements, reallocate stack if needed and update all callstacks and upvals */
|
||||
/* Check if we are above the max allowed stack */
|
||||
void be_stack_expansion(bvm *vm, int n)
|
||||
{
|
||||
size_t size = vm->stacktop - vm->stack;
|
||||
|
|
|
@ -83,4 +83,4 @@ module global (scope: global, depend: BE_USE_GLOBAL_MODULE) {
|
|||
#include "../generate/be_fixed_global.h"
|
||||
#endif
|
||||
|
||||
#endif /* BE_USE_INTROSPECT_MODULE */
|
||||
#endif /* BE_USE_GLOBAL_MODULE */
|
||||
|
|
|
@ -252,6 +252,16 @@ static void cache_module(bvm *vm, bstring *name)
|
|||
*v = vm->top[-1];
|
||||
}
|
||||
|
||||
/* Try to run '()' function of module. Module is already loaded. */
|
||||
static void module_init(bvm *vm) {
|
||||
if (be_getmember(vm, -1, "init")) {
|
||||
/* found, call it with no parameter */
|
||||
be_call(vm, 0);
|
||||
/* we don't care about the result */
|
||||
}
|
||||
be_pop(vm, 1);
|
||||
}
|
||||
|
||||
/* load module to vm->top */
|
||||
int be_module_load(bvm *vm, bstring *path)
|
||||
{
|
||||
|
@ -260,8 +270,11 @@ int be_module_load(bvm *vm, bstring *path)
|
|||
res = load_native(vm, path);
|
||||
if (res == BE_IO_ERROR)
|
||||
res = load_package(vm, path);
|
||||
if (res == BE_OK)
|
||||
if (res == BE_OK) {
|
||||
cache_module(vm, path);
|
||||
/* on first load of the module, try running the '()' function */
|
||||
module_init(vm);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ OPCODE(CLOSE), /* A | close upvalues */
|
|||
OPCODE(IMPORT), /* A, B, C | IF (A == C) import module name as RK(B) to RK(A), ELSE from module RK(C) import name as RK(B) to RK(A) */
|
||||
OPCODE(EXBLK), /* A, Bx | ... */
|
||||
OPCODE(CATCH), /* A, B, C | ... */
|
||||
OPCODE(RAISE), /* A, B, C | ... */
|
||||
OPCODE(RAISE), /* A, B, C | RAISE(B,C) B is code, C is description. A==0 only B provided, A==1 B and C are provided, A==2 rethrow with both parameters already on stack */
|
||||
OPCODE(CLASS), /* Bx | init class in K[Bx] */
|
||||
OPCODE(GETNGBL), /* A, B | R(A) <- GLOBAL[B] by name */
|
||||
OPCODE(SETNGBL) /* A, B | R(A) -> GLOBAL[B] by name */
|
||||
|
|
|
@ -87,6 +87,8 @@ static void match_token(bparser *parser, btokentype type)
|
|||
scan_next_token(parser); /* skip this token */
|
||||
}
|
||||
|
||||
/* Check that the next token is not of type `type` */
|
||||
/* or raise an exception */
|
||||
static void match_notoken(bparser *parser, btokentype type)
|
||||
{
|
||||
if (next_type(parser) == type) { /* error when token is match */
|
||||
|
@ -95,6 +97,7 @@ static void match_notoken(bparser *parser, btokentype type)
|
|||
}
|
||||
}
|
||||
|
||||
/* check that if the expdesc is a symbol, it is avalid one or raise an exception */
|
||||
static void check_symbol(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
if (e->type == ETVOID && e->v.s == NULL) { /* error when token is not a symbol */
|
||||
|
@ -103,6 +106,7 @@ static void check_symbol(bparser *parser, bexpdesc *e)
|
|||
}
|
||||
}
|
||||
|
||||
/* check that the value in `e` is valid for a variable, i.e. conatins a value or a valid symbol */
|
||||
static void check_var(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
check_symbol(parser, e); /* check the token is a symbol */
|
||||
|
@ -172,14 +176,15 @@ void end_varinfo(bparser *parser, int beginpc)
|
|||
|
||||
#endif
|
||||
|
||||
/* Initialize bblockinfo structure */
|
||||
static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type)
|
||||
{
|
||||
binfo->prev = finfo->binfo;
|
||||
finfo->binfo = binfo;
|
||||
binfo->prev = finfo->binfo; /* save previous block */
|
||||
finfo->binfo = binfo; /* tell parser this is the current block */
|
||||
binfo->type = (bbyte)type;
|
||||
binfo->hasupval = 0;
|
||||
binfo->beginpc = finfo->pc;
|
||||
binfo->nactlocals = (bbyte)be_list_count(finfo->local);
|
||||
binfo->beginpc = finfo->pc; /* set starting pc for this block */
|
||||
binfo->nactlocals = (bbyte)be_list_count(finfo->local); /* count number of local variables in previous block */
|
||||
if (type & BLOCK_LOOP) {
|
||||
binfo->breaklist = NO_JUMP;
|
||||
binfo->continuelist = NO_JUMP;
|
||||
|
@ -197,9 +202,9 @@ static void end_block_ex(bparser *parser, int beginpc)
|
|||
be_code_patchlist(finfo, binfo->continuelist, binfo->beginpc);
|
||||
}
|
||||
end_varinfo(parser, beginpc);
|
||||
be_list_resize(parser->vm, finfo->local, binfo->nactlocals);
|
||||
finfo->freereg = binfo->nactlocals;
|
||||
finfo->binfo = binfo->prev;
|
||||
be_list_resize(parser->vm, finfo->local, binfo->nactlocals); /* remove local variables from this block, they are now out of scope */
|
||||
finfo->freereg = binfo->nactlocals; /* adjust first free register accordingly */
|
||||
finfo->binfo = binfo->prev; /* restore previous block */
|
||||
}
|
||||
|
||||
static void end_block(bparser *parser)
|
||||
|
@ -207,6 +212,8 @@ static void end_block(bparser *parser)
|
|||
end_block_ex(parser, -1);
|
||||
}
|
||||
|
||||
/* Return the name of the source for this parser, could be `string`,
|
||||
`stdin` or the name of the current function */
|
||||
static bstring* parser_source(bparser *parser)
|
||||
{
|
||||
if (parser->finfo) {
|
||||
|
@ -215,29 +222,30 @@ static bstring* parser_source(bparser *parser)
|
|||
return be_newstr(parser->vm, parser->lexer.fname);
|
||||
}
|
||||
|
||||
/* Initialize a function block and create a new `bprotoˋ */
|
||||
static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
|
||||
{
|
||||
bvm *vm = parser->vm;
|
||||
bproto *proto = be_newproto(vm);
|
||||
var_setproto(vm->top, proto);
|
||||
be_stackpush(vm);
|
||||
be_vector_init(vm, &finfo->code, sizeof(binstruction));
|
||||
be_vector_init(vm, &finfo->code, sizeof(binstruction)); /* vector for code, vectors are not gced */
|
||||
proto->code = be_vector_data(&finfo->code);
|
||||
proto->codesize = be_vector_capacity(&finfo->code);
|
||||
be_vector_init(vm, &finfo->kvec, sizeof(bvalue));
|
||||
be_vector_init(vm, &finfo->kvec, sizeof(bvalue)); /* vector for constants */
|
||||
proto->ktab = be_vector_data(&finfo->kvec);
|
||||
proto->nconst = be_vector_capacity(&finfo->kvec);
|
||||
be_vector_init(vm, &finfo->pvec, sizeof(bproto*));
|
||||
be_vector_init(vm, &finfo->pvec, sizeof(bproto*)); /* vector for subprotos */
|
||||
proto->ptab = be_vector_data(&finfo->pvec);
|
||||
proto->nproto = be_vector_capacity(&finfo->pvec);
|
||||
proto->source = parser_source(parser);
|
||||
finfo->local = be_list_new(vm);
|
||||
var_setlist(vm->top, finfo->local);
|
||||
proto->source = parser_source(parser); /* keep a copy of source for function */
|
||||
finfo->local = be_list_new(vm); /* list for local variables */
|
||||
var_setlist(vm->top, finfo->local); /* push list of local variables on the stack (avoid gc) */
|
||||
be_stackpush(vm);
|
||||
finfo->upval = be_map_new(vm);
|
||||
finfo->upval = be_map_new(vm); /* push a map for upvals on stack */
|
||||
var_setmap(vm->top, finfo->upval);
|
||||
be_stackpush(vm);
|
||||
finfo->prev = parser->finfo;
|
||||
finfo->prev = parser->finfo; /* init finfo */
|
||||
finfo->lexer = &parser->lexer;
|
||||
finfo->proto = proto;
|
||||
finfo->freereg = 0;
|
||||
|
@ -258,6 +266,7 @@ static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo)
|
|||
begin_block(finfo, binfo, 0);
|
||||
}
|
||||
|
||||
/* compute the upval structure */
|
||||
static void setupvals(bfuncinfo *finfo)
|
||||
{
|
||||
bproto *proto = finfo->proto;
|
||||
|
@ -282,6 +291,7 @@ static void setupvals(bfuncinfo *finfo)
|
|||
}
|
||||
}
|
||||
|
||||
/* Function is complete, finalize bproto */
|
||||
static void end_func(bparser *parser)
|
||||
{
|
||||
bvm *vm = parser->vm;
|
||||
|
@ -289,9 +299,9 @@ static void end_func(bparser *parser)
|
|||
bproto *proto = finfo->proto;
|
||||
|
||||
be_code_ret(finfo, NULL); /* append a return to last code */
|
||||
end_block(parser);
|
||||
setupvals(finfo);
|
||||
proto->code = be_vector_release(vm, &finfo->code);
|
||||
end_block(parser); /* close block */
|
||||
setupvals(finfo); /* close upvals */
|
||||
proto->code = be_vector_release(vm, &finfo->code); /* compact all vectors and return NULL if empty */
|
||||
proto->codesize = finfo->pc;
|
||||
proto->ktab = be_vector_release(vm, &finfo->kvec);
|
||||
proto->nconst = be_vector_count(&finfo->kvec);
|
||||
|
@ -305,10 +315,11 @@ static void end_func(bparser *parser)
|
|||
proto->varinfo = be_vector_release(vm, &finfo->varvec);
|
||||
proto->nvarinfo = be_vector_count(&finfo->varvec);
|
||||
#endif
|
||||
parser->finfo = parser->finfo->prev;
|
||||
parser->finfo = parser->finfo->prev; /* restore previous `finfo` */
|
||||
be_stackpop(vm, 2); /* pop upval and local */
|
||||
}
|
||||
|
||||
/* is the next token a binary operator? If yes return the operator or `OP_NOT_BINARY` */
|
||||
static btokentype get_binop(bparser *parser)
|
||||
{
|
||||
btokentype op = next_type(parser);
|
||||
|
@ -318,6 +329,8 @@ static btokentype get_binop(bparser *parser)
|
|||
return OP_NOT_BINARY;
|
||||
}
|
||||
|
||||
/* is the next token a unary operator? If yes return the operator or `OP_NOT_BINARY` */
|
||||
/* operator 'negative' 'not' and 'flip' */
|
||||
static btokentype get_unary_op(bparser *parser)
|
||||
{
|
||||
btokentype op = next_type(parser);
|
||||
|
@ -327,6 +340,8 @@ static btokentype get_unary_op(bparser *parser)
|
|||
return OP_NOT_UNARY;
|
||||
}
|
||||
|
||||
/* is the next token an assignment operator? If yes return the operator or `OP_NOT_BINARY` */
|
||||
/* `=`, `+=`, ... `>>=` */
|
||||
static btokentype get_assign_op(bparser *parser)
|
||||
{
|
||||
btokentype op = next_type(parser);
|
||||
|
@ -336,6 +351,7 @@ static btokentype get_assign_op(bparser *parser)
|
|||
return OP_NOT_ASSIGN;
|
||||
}
|
||||
|
||||
/* Initialize bexpdesc structure with specific type and value as int */
|
||||
static void init_exp(bexpdesc *e, exptype_t type, bint i)
|
||||
{
|
||||
e->type = (bbyte)type;
|
||||
|
@ -346,6 +362,8 @@ static void init_exp(bexpdesc *e, exptype_t type, bint i)
|
|||
e->v.i = i;
|
||||
}
|
||||
|
||||
/* find local variable by name, starting at index `begin` */
|
||||
/* linear search, returns -1 if not found */
|
||||
static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
|
||||
{
|
||||
int i, count = be_list_count(finfo->local);
|
||||
|
@ -358,12 +376,22 @@ static int find_localvar(bfuncinfo *finfo, bstring *s, int begin)
|
|||
return -1; /* not found */
|
||||
}
|
||||
|
||||
/* create a new local variable by name, or return the current register if already exists */
|
||||
/* returns the Reg number for the variable */
|
||||
/* If STRICT, we don't allow a var to hide a var from outer scope */
|
||||
/* We don't allow the same var to be defined twice in same scope */
|
||||
static int new_localvar(bparser *parser, bstring *name)
|
||||
{
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
int reg = find_localvar(finfo, name, finfo->binfo->nactlocals);
|
||||
int reg = find_localvar(finfo, name, finfo->binfo->nactlocals); /* look if already exists skipping the local variables from upper blocks */
|
||||
/* 'strict': raise an exception if the local variable shadows another local variable */
|
||||
if (reg == -1) {
|
||||
bvalue *var;
|
||||
if (comp_is_strict(parser->vm)) {
|
||||
if (find_localvar(finfo, name, 0) >= 0 && str(name)[0] != '.') { /* we do accept nested redifinition of internal variables starting with ':' */
|
||||
push_error(parser, "strict: redefinition of '%s' from outer scope", str(name));
|
||||
}
|
||||
}
|
||||
reg = be_list_count(finfo->local); /* new local index */
|
||||
var = be_list_push(parser->vm, finfo->local, NULL);
|
||||
var_setstr(var, name);
|
||||
|
@ -371,10 +399,13 @@ static int new_localvar(bparser *parser, bstring *name)
|
|||
be_code_allocregs(finfo, 1); /* use a register */
|
||||
}
|
||||
begin_varinfo(parser, name);
|
||||
} else {
|
||||
push_error(parser, "redefinition of '%s'", str(name));
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Find upval by name, if found return its index number, or -1 */
|
||||
static int find_upval(bfuncinfo *finfo, bstring *s)
|
||||
{
|
||||
bvm *vm = finfo->lexer->vm;
|
||||
|
@ -385,6 +416,8 @@ static int find_upval(bfuncinfo *finfo, bstring *s)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Recursively search for upper blocks that are referenced in upvals */
|
||||
/* and mark them with `hasupval` */
|
||||
static void mark_upval(bfuncinfo *finfo, int level)
|
||||
{
|
||||
bblockinfo *binfo = finfo->prev->binfo;
|
||||
|
@ -413,12 +446,14 @@ static int new_upval(bvm *vm, bfuncinfo *finfo, bstring *name, bexpdesc *var)
|
|||
return index;
|
||||
}
|
||||
|
||||
/* create a new variable in currenr context as name, and create expdesc for it */
|
||||
/* If within a block, create as local, otherwise as global */
|
||||
static void new_var(bparser *parser, bstring *name, bexpdesc *var)
|
||||
{
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
if (finfo->prev || finfo->binfo->prev || parser->islocal) {
|
||||
init_exp(var, ETLOCAL, 0);
|
||||
var->v.idx = new_localvar(parser, name);
|
||||
var->v.idx = new_localvar(parser, name); /* if local, contains the index in current local var list */
|
||||
} else {
|
||||
init_exp(var, ETGLOBAL, 0);
|
||||
var->v.idx = be_global_new(parser->vm, name);
|
||||
|
@ -465,6 +500,9 @@ static int singlevaraux(bvm *vm, bfuncinfo *finfo, bstring *s, bexpdesc *var)
|
|||
}
|
||||
}
|
||||
|
||||
/* get variable from next toden as name */
|
||||
/* and create an expdesc from it */
|
||||
/* can be new, global, named global, upval */
|
||||
static void singlevar(bparser *parser, bexpdesc *var)
|
||||
{
|
||||
bexpdesc key;
|
||||
|
@ -490,6 +528,10 @@ static void singlevar(bparser *parser, bexpdesc *var)
|
|||
}
|
||||
}
|
||||
|
||||
/* Parse function or method definition variable list */
|
||||
/* Create an implicit local variable for each argument starting at R0 */
|
||||
/* Update function proto argc to the expected number or arguments */
|
||||
/* Raise an exception if multiple arguments have the same name */
|
||||
static void func_varlist(bparser *parser)
|
||||
{
|
||||
bexpdesc v;
|
||||
|
@ -502,36 +544,36 @@ static void func_varlist(bparser *parser)
|
|||
str = next_token(parser).u.s;
|
||||
match_token(parser, TokenId); /* match and skip ID */
|
||||
/* new local variable */
|
||||
if (find_localvar(parser->finfo, str, 0) == -1) {
|
||||
new_var(parser, str, &v);
|
||||
} else {
|
||||
push_error(parser, "redefinition of '%s'", str(str));
|
||||
}
|
||||
new_var(parser, str, &v);
|
||||
}
|
||||
}
|
||||
match_token(parser, OptRBK); /* skip ')' */
|
||||
parser->finfo->proto->argc = parser->finfo->freereg;
|
||||
}
|
||||
|
||||
/* Parse a function includind arg list and body */
|
||||
/* Given name and type (function or method) */
|
||||
/* Returns `bproto` object */
|
||||
static bproto* funcbody(bparser *parser, bstring *name, int type)
|
||||
{
|
||||
bfuncinfo finfo;
|
||||
bblockinfo binfo;
|
||||
|
||||
/* '(' varlist ')' block 'end' */
|
||||
begin_func(parser, &finfo, &binfo);
|
||||
begin_func(parser, &finfo, &binfo); /* init new function context */
|
||||
finfo.proto->name = name;
|
||||
if (type & FUNC_METHOD) {
|
||||
if (type & FUNC_METHOD) { /* If method, add an implicit first argument `self` */
|
||||
new_localvar(parser, parser_newstr(parser, "self"));
|
||||
}
|
||||
func_varlist(parser);
|
||||
stmtlist(parser);
|
||||
end_func(parser);
|
||||
func_varlist(parser); /* parse arg list */
|
||||
stmtlist(parser); /* parse statement without final `end` */
|
||||
end_func(parser); /* close function context */
|
||||
match_token(parser, KeyEnd); /* skip 'end' */
|
||||
return finfo.proto;
|
||||
return finfo.proto; /* return fully constructed `bproto` */
|
||||
}
|
||||
|
||||
/* anonymous function */
|
||||
/* anonymous function, build `bproto` object with name `<anonymous>` */
|
||||
/* and build a expdesc for the bproto */
|
||||
static void anon_func(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
bproto *proto;
|
||||
|
@ -559,11 +601,7 @@ static void lambda_varlist(bparser *parser)
|
|||
str = next_token(parser).u.s;
|
||||
match_token(parser, TokenId); /* match and skip ID */
|
||||
/* new local variable */
|
||||
if (find_localvar(parser->finfo, str, 0) == -1) {
|
||||
new_var(parser, str, &v);
|
||||
} else {
|
||||
push_error(parser, "redefinition of '%s'", str(str));
|
||||
}
|
||||
new_var(parser, str, &v);
|
||||
}
|
||||
}
|
||||
match_token(parser, OptArrow); /* skip '->' */
|
||||
|
@ -590,6 +628,9 @@ static void lambda_expr(bparser *parser, bexpdesc *e)
|
|||
be_stackpop(parser->vm, 1);
|
||||
}
|
||||
|
||||
/* Instanciate a builtin type by name */
|
||||
/* Allocates a new register for the value, and call empty constructor */
|
||||
/* Is allocated as LOCAL and must be changed to REG when completed */
|
||||
static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
|
||||
{
|
||||
int idx;
|
||||
|
@ -601,17 +642,19 @@ static void new_primtype(bparser *parser, const char *type, bexpdesc *e)
|
|||
init_exp(e, ETGLOBAL, idx);
|
||||
idx = be_code_nextreg(finfo, e);
|
||||
be_code_call(finfo, idx, 0);
|
||||
e->type = ETLOCAL;
|
||||
e->type = ETLOCAL; /* declare as local, will be changed to ETREG when completely initialized */
|
||||
}
|
||||
|
||||
/* Parse next member within a list */
|
||||
/* `l` contains the current list. The expr is evaluated and added to the list */
|
||||
static void list_nextmember(bparser *parser, bexpdesc *l)
|
||||
{
|
||||
bexpdesc e, v = *l;
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
expr(parser, &e); /* value */
|
||||
check_var(parser, &e);
|
||||
be_code_binop(finfo, OptConnect, &v, &e);
|
||||
be_code_freeregs(finfo, 1);
|
||||
check_var(parser, &e); /* check that we don´t have an unknown symbol */
|
||||
be_code_binop(finfo, OptConnect, &v, &e, -1); /* add it to list with CONNECT */
|
||||
be_code_freeregs(finfo, 1); /* since left arg is LOCAL, an ETREG was allocated. Free it */
|
||||
}
|
||||
|
||||
static void map_nextmember(bparser *parser, bexpdesc *l)
|
||||
|
@ -619,25 +662,25 @@ static void map_nextmember(bparser *parser, bexpdesc *l)
|
|||
bexpdesc e, v = *l;
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
expr(parser, &e); /* key */
|
||||
check_var(parser, &e);
|
||||
be_code_index(finfo, &v, &e);
|
||||
check_var(parser, &e); /* check if value is valid */
|
||||
be_code_index(finfo, &v, &e); /* package as `v` as INDEX suffix for value `e` */
|
||||
match_token(parser, OptColon); /* ':' */
|
||||
expr(parser, &e); /* value */
|
||||
check_var(parser, &e);
|
||||
be_code_setvar(finfo, &v, &e);
|
||||
expr(parser, &e); /* value in `e` */
|
||||
check_var(parser, &e); /* check if value is correct */
|
||||
be_code_setvar(finfo, &v, &e); /* set suffi INDEX value to e */
|
||||
}
|
||||
|
||||
static void list_expr(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
/* '[' {expr ','} [expr] ']' */
|
||||
new_primtype(parser, "list", e); /* new list */
|
||||
new_primtype(parser, "list", e); /* new list, created as LOCAL first */
|
||||
while (next_type(parser) != OptRSB) {
|
||||
list_nextmember(parser, e);
|
||||
if (!match_skip(parser, OptComma)) { /* ',' */
|
||||
break;
|
||||
}
|
||||
}
|
||||
e->type = ETREG;
|
||||
e->type = ETREG; /* then turned to REG */
|
||||
match_token(parser, OptRSB); /* skip ']' */
|
||||
}
|
||||
|
||||
|
@ -655,14 +698,16 @@ static void map_expr(bparser *parser, bexpdesc *e)
|
|||
match_token(parser, OptRBR); /* skip '}' */
|
||||
}
|
||||
|
||||
/* push each argument as new reg and return number of args */
|
||||
/* TODO `e` is ignored by caller */
|
||||
static int exprlist(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
int n = 1;
|
||||
/* expr { ',' expr } */
|
||||
expr(parser, e);
|
||||
check_var(parser, e);
|
||||
be_code_nextreg(finfo, e);
|
||||
expr(parser, e); /* parse expr */
|
||||
check_var(parser, e); /* check if valid */
|
||||
be_code_nextreg(finfo, e); /* move result to next reg */
|
||||
while (match_skip(parser, OptComma)) { /* ',' */
|
||||
expr(parser, e);
|
||||
check_var(parser, e);
|
||||
|
@ -672,6 +717,9 @@ static int exprlist(bparser *parser, bexpdesc *e)
|
|||
return n;
|
||||
}
|
||||
|
||||
/* parse call to method or function */
|
||||
/* `e` can be a member (method) or a register */
|
||||
/* On return, `e` is ETREG to the result of the call */
|
||||
static void call_expr(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
bexpdesc args;
|
||||
|
@ -685,14 +733,15 @@ static void call_expr(bparser *parser, bexpdesc *e)
|
|||
if (ismember) {
|
||||
base = be_code_getmethod(finfo, e);
|
||||
} else {
|
||||
base = be_code_nextreg(finfo, e);
|
||||
base = be_code_nextreg(finfo, e); /* allocate a new base reg if not at top already */
|
||||
}
|
||||
/* base is always taken at top of freereg and allocates 1 reg for function and 2 regs for method */
|
||||
scan_next_token(parser); /* skip '(' */
|
||||
if (next_type(parser) != OptRBK) {
|
||||
argc = exprlist(parser, &args);
|
||||
if (next_type(parser) != OptRBK) { /* if arg list is not empty */
|
||||
argc = exprlist(parser, &args); /* push each argument as new reg and return number of args */
|
||||
}
|
||||
match_token(parser, OptRBK); /* skip ')' */
|
||||
argc += ismember;
|
||||
argc += ismember; /* if method there is an additional implicit arg */
|
||||
be_code_call(finfo, base, argc);
|
||||
if (e->type != ETREG) {
|
||||
e->type = ETREG;
|
||||
|
@ -700,6 +749,8 @@ static void call_expr(bparser *parser, bexpdesc *e)
|
|||
}
|
||||
}
|
||||
|
||||
/* Parse member expression */
|
||||
/* Generates an ETMEMBER object that is materialized later into GETMBR, GETMET or SETMBR */
|
||||
static void member_expr(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
bstring *str;
|
||||
|
@ -823,9 +874,11 @@ static void suffix_alloc_reg(bparser *parser, bexpdesc *l)
|
|||
/* compound assignment */
|
||||
static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r)
|
||||
{
|
||||
int dst = -1; /* destination register in case of compound assignment */
|
||||
if (op != OptAssign) { /* check left variable */
|
||||
check_var(parser, l);
|
||||
/* cache the register of the object when continuously assigning */
|
||||
dst = parser->finfo->freereg;
|
||||
suffix_alloc_reg(parser, l);
|
||||
}
|
||||
expr(parser, r); /* right expression */
|
||||
|
@ -834,20 +887,37 @@ static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r)
|
|||
bexpdesc e = *l;
|
||||
op = op < OptAndAssign ? op - OptAddAssign + OptAdd
|
||||
: op - OptAndAssign + OptBitAnd;
|
||||
be_code_binop(parser->finfo, op, &e, r); /* coding operation */
|
||||
be_code_binop(parser->finfo, op, &e, r, dst); /* coding operation */
|
||||
*r = e;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if we need to create a new local variable with this name to be assigned to */
|
||||
/* Returns true if it´s a new local variable */
|
||||
/* A new implicit local variable is created if no global has the same name (excluding builtins) */
|
||||
/* This means that you can override a builtin silently */
|
||||
/* This also means that a function cannot create a global, they must preexist or create with `global` module */
|
||||
/* TODO add warning in strict mode */
|
||||
static int check_newvar(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
if (e->type == ETGLOBAL) {
|
||||
if (e->v.idx < be_builtin_count(parser->vm)) {
|
||||
e->v.s = be_builtin_name(parser->vm, e->v.idx);
|
||||
if (comp_is_strict(parser->vm)) {
|
||||
push_error(parser, "strict: redefinition of builtin '%s'",
|
||||
str(e->v.s));
|
||||
}
|
||||
return btrue;
|
||||
}
|
||||
return bfalse;
|
||||
}
|
||||
if (comp_is_strict(parser->vm)) {
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
if ((e->type == ETVOID) && (finfo->prev || finfo->binfo->prev || parser->islocal)) {
|
||||
push_error(parser, "strict: no global '%s', did you mean 'var %s'?",
|
||||
str(e->v.s), str(e->v.s));
|
||||
}
|
||||
}
|
||||
return e->type == ETVOID;
|
||||
}
|
||||
|
||||
|
@ -913,39 +983,42 @@ static void cond_expr(bparser *parser, bexpdesc *e)
|
|||
static void sub_expr(bparser *parser, bexpdesc *e, int prio)
|
||||
{
|
||||
bfuncinfo *finfo = parser->finfo;
|
||||
btokentype op = get_unary_op(parser);
|
||||
if (op != OP_NOT_UNARY) {
|
||||
btokentype op = get_unary_op(parser); /* check if first token in unary op */
|
||||
if (op != OP_NOT_UNARY) { /* unary op found */
|
||||
int line, res;
|
||||
scan_next_token(parser);
|
||||
line = parser->lexer.linenumber;
|
||||
sub_expr(parser, e, UNARY_OP_PRIO);
|
||||
check_var(parser, e);
|
||||
res = be_code_unop(finfo, op, e);
|
||||
scan_next_token(parser); /* move to next token */
|
||||
line = parser->lexer.linenumber; /* remember line number for error reporting */
|
||||
sub_expr(parser, e, UNARY_OP_PRIO); /* parse subexpr with new prio */
|
||||
check_var(parser, e); /* check that the value is ok */
|
||||
res = be_code_unop(finfo, op, e); /* apply unary op with optimizations if the token is a value */
|
||||
if (res) { /* encode unary op */
|
||||
parser->lexer.linenumber = line;
|
||||
push_error(parser, "wrong type argument to unary '%s'",
|
||||
res == 1 ? "negative" : "bit-flip");
|
||||
}
|
||||
} else {
|
||||
suffix_expr(parser, e);
|
||||
suffix_expr(parser, e); /* parse left part of binop */
|
||||
}
|
||||
op = get_binop(parser);
|
||||
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) {
|
||||
op = get_binop(parser); /* check if binop */
|
||||
while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) { /* is binop applicable */
|
||||
bexpdesc e2;
|
||||
check_var(parser, e);
|
||||
scan_next_token(parser);
|
||||
check_var(parser, e); /* check that left part is valid */
|
||||
scan_next_token(parser); /* move to next token */
|
||||
be_code_prebinop(finfo, op, e); /* and or */
|
||||
init_exp(&e2, ETVOID, 0);
|
||||
sub_expr(parser, &e2, binary_op_prio(op));
|
||||
check_var(parser, &e2);
|
||||
be_code_binop(finfo, op, e, &e2); /* encode binary op */
|
||||
op = get_binop(parser);
|
||||
sub_expr(parser, &e2, binary_op_prio(op)); /* parse right side */
|
||||
check_var(parser, &e2); /* check if valid */
|
||||
be_code_binop(finfo, op, e, &e2, -1); /* encode binary op */
|
||||
op = get_binop(parser); /* is there a following binop? */
|
||||
}
|
||||
if (prio == ASSIGN_OP_PRIO) {
|
||||
cond_expr(parser, e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse new expression and return value in `e` (overwritten) */
|
||||
/* Initializes an empty expdes and parse subexpr */
|
||||
/* Always allocates a new temp register at top of freereg */
|
||||
static void expr(bparser *parser, bexpdesc *e)
|
||||
{
|
||||
init_exp(e, ETVOID, 0);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "be_string.h"
|
||||
|
||||
typedef enum {
|
||||
ETVOID,
|
||||
ETVOID, /* unknown (new variable or error) */
|
||||
ETNIL,
|
||||
ETBOOL,
|
||||
ETREAL,
|
||||
|
@ -20,13 +20,13 @@ typedef enum {
|
|||
ETSTRING,
|
||||
ETPROTO,
|
||||
ETCONST,
|
||||
ETLOCAL,
|
||||
ETGLOBAL,
|
||||
ETLOCAL, /* local variable, allocated until end of scope */
|
||||
ETGLOBAL, /* global by index number */
|
||||
ETUPVAL,
|
||||
ETMEMBER,
|
||||
ETINDEX,
|
||||
ETREG,
|
||||
ETNGLOBAL
|
||||
ETMEMBER, /* member accessor (by name) */
|
||||
ETINDEX, /* index accessor (ex array index) */
|
||||
ETREG, /* temporary register, can be freed if top of stack */
|
||||
ETNGLOBAL /* named global */
|
||||
} exptype_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -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 */
|
|
@ -28,14 +28,14 @@
|
|||
#define vm_error(vm, except, ...) \
|
||||
be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__))
|
||||
|
||||
#define RA() (reg + IGET_RA(ins))
|
||||
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins)))
|
||||
#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins)))
|
||||
#define RA() (reg + IGET_RA(ins)) /* Get value of register A */
|
||||
#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins))) /* Get value of register or constant B */
|
||||
#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins))) /* Get value of register or constant C */
|
||||
|
||||
#define var2cl(_v) cast(bclosure*, var_toobj(_v))
|
||||
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i)
|
||||
#define val2bool(v) ((v) ? btrue : bfalse)
|
||||
#define ibinop(op, a, b) ((a)->v.i op (b)->v.i)
|
||||
#define var2cl(_v) cast(bclosure*, var_toobj(_v)) /* cast var to closure */
|
||||
#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i) /* get var as real or convert to real if integer */
|
||||
#define val2bool(v) ((v) ? btrue : bfalse) /* get var as bool (trur if non zero) */
|
||||
#define ibinop(op, a, b) ((a)->v.i op (b)->v.i) /* apply binary operator to both arguments as integers */
|
||||
|
||||
#if BE_USE_DEBUG_HOOK
|
||||
#define DEBUG_HOOK() \
|
||||
|
@ -149,6 +149,8 @@ static void call_error(bvm *vm, bvalue *v)
|
|||
"'%s' value is not callable", be_vtype2str(v));
|
||||
}
|
||||
|
||||
/* Check that the return value is bool or raise an exception */
|
||||
/* `obj` and `method` are only passed for error reporting */
|
||||
static void check_bool(bvm *vm, binstance *obj, const char *method)
|
||||
{
|
||||
if (!var_isbool(vm->top)) {
|
||||
|
@ -182,25 +184,29 @@ static void do_linehook(bvm *vm)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Prepare the stack for the function/method call */
|
||||
/* `func` is a pointer to the function/method on the stack, it contains the closure before call and the result after the call */
|
||||
/* `nstackˋ is the stack depth used by the function (determined by compiler), we add BE_STACK_FREE_MIN as a safety margin */
|
||||
static void precall(bvm *vm, bvalue *func, int nstack, int mode)
|
||||
{
|
||||
bcallframe *cf;
|
||||
int expan = nstack + BE_STACK_FREE_MIN;
|
||||
if (vm->stacktop < func + expan) {
|
||||
size_t fpos = func - vm->stack;
|
||||
be_stack_expansion(vm, expan);
|
||||
func = vm->stack + fpos;
|
||||
int expan = nstack + BE_STACK_FREE_MIN; /* `expan` is the minimum required space on the stack */
|
||||
if (vm->stacktop < func + expan) { /* do we have too little space left on the stack? */
|
||||
size_t fpos = func - vm->stack; /* compute offset of `func` from base stack, in case stack is reallocated and base address changes */
|
||||
be_stack_expansion(vm, expan); /* expand stack (vector object), warning stack address changes */
|
||||
func = vm->stack + fpos; /* recompute `func` address with new stack address */
|
||||
}
|
||||
be_stack_push(vm, &vm->callstack, NULL);
|
||||
cf = be_stack_top(&vm->callstack);
|
||||
be_stack_push(vm, &vm->callstack, NULL); /* push a NULL value on callstack */
|
||||
cf = be_stack_top(&vm->callstack); /* get address of new callframe at top of callstack */
|
||||
cf->func = func - mode;
|
||||
cf->top = vm->top;
|
||||
cf->reg = vm->reg;
|
||||
vm->reg = func + 1;
|
||||
vm->top = vm->reg + nstack;
|
||||
vm->cf = cf;
|
||||
cf->top = vm->top; /* save previous stack top */
|
||||
cf->reg = vm->reg; /* save previous stack base */
|
||||
vm->reg = func + 1; /* new stack base is right after function */
|
||||
vm->top = vm->reg + nstack; /* new stack top is above the registers used by the function, so we don´t mess with them */
|
||||
vm->cf = cf; /* set new current callframe */
|
||||
}
|
||||
|
||||
/* Prepare call of closure, setting the instruction pointer (ip) */
|
||||
static void push_closure(bvm *vm, bvalue *func, int nstack, int mode)
|
||||
{
|
||||
bclosure *cl = var_toobj(func);
|
||||
|
@ -469,9 +475,9 @@ static void vm_exec(bvm *vm)
|
|||
vm->cf->status |= BASE_FRAME;
|
||||
newframe: /* a new call frame */
|
||||
be_assert(var_isclosure(vm->cf->func));
|
||||
clos = var_toobj(vm->cf->func);
|
||||
ktab = clos->proto->ktab;
|
||||
reg = vm->reg;
|
||||
clos = var_toobj(vm->cf->func); /* `clos` is the current function/closure */
|
||||
ktab = clos->proto->ktab; /* `ktab` is the current constant table */
|
||||
reg = vm->reg; /* `reg` is the current stack base for the callframe */
|
||||
vm_exec_loop() {
|
||||
opcase(LDNIL): {
|
||||
var_setnil(RA());
|
||||
|
@ -1019,7 +1025,7 @@ newframe: /* a new call frame */
|
|||
dispatch();
|
||||
}
|
||||
opcase(RAISE): {
|
||||
if (IGET_RA(ins) < 2) {
|
||||
if (IGET_RA(ins) < 2) { /* A==2 means no arguments are passed to RAISE, i.e. rethrow with current exception */
|
||||
bvalue *top = vm->top;
|
||||
top[0] = *RKB(); /* push the exception value to top */
|
||||
if (IGET_RA(ins)) { /* has exception argument? */
|
||||
|
@ -1046,8 +1052,8 @@ newframe: /* a new call frame */
|
|||
dispatch();
|
||||
}
|
||||
opcase(CALL): {
|
||||
bvalue *var = RA();
|
||||
int mode = 0, argc = IGET_RKB(ins);
|
||||
bvalue *var = RA(); /* `var` is the register for the call followed by arguments */
|
||||
int mode = 0, argc = IGET_RKB(ins); /* B contains number of arguments pushed on stack */
|
||||
recall: /* goto: instantiation class and call constructor */
|
||||
switch (var_type(var)) {
|
||||
case NOT_METHOD:
|
||||
|
@ -1055,8 +1061,8 @@ newframe: /* a new call frame */
|
|||
++var, --argc, mode = 1;
|
||||
goto recall;
|
||||
case BE_CLASS:
|
||||
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) {
|
||||
reg = vm->reg + mode;
|
||||
if (be_class_newobj(vm, var_toobj(var), var, ++argc, mode)) { /* instanciate object and find constructor */
|
||||
reg = vm->reg + mode; /* constructor found */
|
||||
mode = 0;
|
||||
var = RA() + 1; /* to next register */
|
||||
goto recall; /* call constructor */
|
||||
|
@ -1072,15 +1078,15 @@ newframe: /* a new call frame */
|
|||
}
|
||||
case BE_CLOSURE: {
|
||||
bvalue *v, *end;
|
||||
bproto *proto = var2cl(var)->proto;
|
||||
push_closure(vm, var, proto->nstack, mode);
|
||||
reg = vm->reg;
|
||||
v = reg + argc;
|
||||
end = reg + proto->argc;
|
||||
for (; v < end; ++v) {
|
||||
bproto *proto = var2cl(var)->proto; /* get proto for closure */
|
||||
push_closure(vm, var, proto->nstack, mode); /* prepare stack for closure */
|
||||
reg = vm->reg; /* `reg` has changed, now new base register */
|
||||
v = reg + argc; /* end of provided arguments */
|
||||
end = reg + proto->argc; /* end of expected arguments */
|
||||
for (; v < end; ++v) { /* set all not provided arguments to nil */
|
||||
var_setnil(v);
|
||||
}
|
||||
goto newframe;
|
||||
goto newframe; /* continue execution of the closure */
|
||||
}
|
||||
case BE_NTVCLOS: {
|
||||
bntvclos *f = var_toobj(var);
|
||||
|
|
|
@ -14,9 +14,14 @@
|
|||
#define comp_set_named_gbl(vm) ((vm)->compopt |= (1<<COMP_NAMED_GBL))
|
||||
#define comp_clear_named_gbl(vm) ((vm)->compopt &= ~(1<<COMP_NAMED_GBL))
|
||||
|
||||
#define comp_is_strict(vm) ((vm)->compopt & (1<<COMP_STRICT))
|
||||
#define comp_set_strict(vm) ((vm)->compopt |= (1<<COMP_STRICT))
|
||||
#define comp_clear_strict(vm) ((vm)->compopt &= ~(1<<COMP_STRICT))
|
||||
|
||||
/* Compilation options */
|
||||
typedef enum {
|
||||
COMP_NAMED_GBL = 0x00, /* compile with named globals */
|
||||
COMP_STRICT = 0x01, /* compile with named globals */
|
||||
} compoptmask;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -352,6 +352,17 @@ typedef struct bntvmodule {
|
|||
PROTO_VAR_INFO_BLOCK \
|
||||
}
|
||||
|
||||
#define be_define_local_closure(_name) \
|
||||
const bclosure _name##_closure = { \
|
||||
NULL, /* bgcobject *next */ \
|
||||
BE_CLOSURE, /* type BE_CLOSURE */ \
|
||||
GC_CONST, /* marked GC_CONST */ \
|
||||
0, /* nupvals */ \
|
||||
NULL, /* bgcobject *gray */ \
|
||||
(bproto*) &_name##_proto, /* proto */ \
|
||||
{ NULL } /* upvals */ \
|
||||
}
|
||||
|
||||
/* new version for more compact literals */
|
||||
#define be_local_closure(_name, _proto) \
|
||||
static const bclosure _name##_closure = { \
|
||||
|
|
|
@ -32,12 +32,21 @@
|
|||
* ESP32 analogWrite emulation support
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define PWM_SUPPORTED_CHANNELS 8
|
||||
#define PWM_CHANNEL_OFFSET 2 // Webcam uses channel 0, so we offset standard PWM
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#define PWM_SUPPORTED_CHANNELS 6
|
||||
#define PWM_CHANNEL_OFFSET 1 // Webcam uses channel 0, so we offset standard PWM
|
||||
|
||||
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
|
||||
uint32_t _pwm_frequency = 977; // Default 977Hz
|
||||
uint8_t _pwm_bit_num = 10; // Default 1023
|
||||
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 };
|
||||
uint32_t _pwm_frequency = 977; // Default 977Hz
|
||||
uint8_t _pwm_bit_num = 10; // Default 1023
|
||||
#else // other ESP32
|
||||
#define PWM_SUPPORTED_CHANNELS 8
|
||||
#define PWM_CHANNEL_OFFSET 2 // Webcam uses channel 0, so we offset standard PWM
|
||||
|
||||
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
|
||||
uint32_t _pwm_frequency = 977; // Default 977Hz
|
||||
uint8_t _pwm_bit_num = 10; // Default 1023
|
||||
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32
|
||||
|
||||
inline uint32_t _analog_pin2chan(uint32_t pin) {
|
||||
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
|
||||
|
|
|
@ -1,60 +1,70 @@
|
|||
Import('env')
|
||||
Import("env")
|
||||
import os
|
||||
import shutil
|
||||
import gzip
|
||||
import pathlib
|
||||
|
||||
platform = env.PioPlatform()
|
||||
board = env.BoardConfig()
|
||||
mcu = board.get("build.mcu", "esp32")
|
||||
import tasmotapiolib
|
||||
|
||||
OUTPUT_DIR = "build_output{}".format(os.path.sep)
|
||||
|
||||
def map_gzip(source, target, env):
|
||||
variant = str(target[0]).split(os.path.sep)[2]
|
||||
|
||||
# create string with location and file names based on variant
|
||||
bin_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
map_file = tasmotapiolib.get_final_map_path(env)
|
||||
|
||||
if os.path.isfile(bin_file):
|
||||
gzip_file = "{}map{}{}.map.gz".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
if map_file.is_file():
|
||||
gzip_file = map_file.with_suffix(".map.gz")
|
||||
|
||||
# check if new target map files exist and remove if necessary
|
||||
if os.path.isfile(gzip_file): os.remove(gzip_file)
|
||||
if gzip_file.is_file():
|
||||
gzip_file.unlink()
|
||||
|
||||
# write gzip map file
|
||||
with open(bin_file,"rb") as fp:
|
||||
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
|
||||
with map_file.open("rb") as fp:
|
||||
with gzip.open(gzip_file, "wb", compresslevel=9) as f:
|
||||
shutil.copyfileobj(fp, f)
|
||||
|
||||
# remove map file
|
||||
if os.path.isfile(bin_file): os.remove(bin_file)
|
||||
if map_file.is_file():
|
||||
map_file.unlink()
|
||||
|
||||
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
|
||||
if env["PIOPLATFORM"] != "espressif32":
|
||||
|
||||
def bin_gzip(source, target, env):
|
||||
variant = str(target[0]).split(os.path.sep)[2]
|
||||
|
||||
# create string with location and file names based on variant
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
bin_file = tasmotapiolib.get_final_bin_path(env)
|
||||
gzip_file = bin_file.with_suffix(".bin.gz")
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
if os.path.isfile(gzip_file): os.remove(gzip_file)
|
||||
if os.path.isfile(gzip_file):
|
||||
os.remove(gzip_file)
|
||||
|
||||
# write gzip firmware file
|
||||
with open(bin_file,"rb") as fp:
|
||||
with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
|
||||
with open(bin_file, "rb") as fp:
|
||||
with gzip.open(gzip_file, "wb", compresslevel=9) as f:
|
||||
shutil.copyfileobj(fp, f)
|
||||
|
||||
ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
|
||||
GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
|
||||
ORG_FIRMWARE_SIZE = bin_file.stat().st_size
|
||||
GZ_FIRMWARE_SIZE = gzip_file.stat().st_size
|
||||
|
||||
if ORG_FIRMWARE_SIZE > 995326:
|
||||
print("\u001b[31;1m!!! Tasmota firmware size is too big with {} bytes. Max size is 995326 bytes !!! \u001b[0m".format(ORG_FIRMWARE_SIZE))
|
||||
print(
|
||||
"\u001b[31;1m!!! Tasmota firmware size is too big with {} bytes. Max size is 995326 bytes !!! \u001b[0m".format(
|
||||
ORG_FIRMWARE_SIZE
|
||||
)
|
||||
)
|
||||
else:
|
||||
print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
|
||||
print(
|
||||
"Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format(
|
||||
(GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100,
|
||||
ORG_FIRMWARE_SIZE,
|
||||
GZ_FIRMWARE_SIZE,
|
||||
)
|
||||
)
|
||||
|
||||
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])
|
||||
|
|
|
@ -1,54 +1,27 @@
|
|||
Import('env')
|
||||
Import('projenv')
|
||||
Import("env")
|
||||
Import("projenv")
|
||||
import os
|
||||
import shutil
|
||||
import pathlib
|
||||
|
||||
import tasmotapiolib
|
||||
|
||||
OUTPUT_DIR = "build_output{}".format(os.path.sep)
|
||||
|
||||
def bin_map_copy(source, target, env):
|
||||
variant = str(target[0]).split(os.path.sep)[2]
|
||||
firsttarget = pathlib.Path(target[0].path)
|
||||
|
||||
# check if output directories exist and create if necessary
|
||||
if not os.path.isdir(OUTPUT_DIR):
|
||||
os.mkdir(OUTPUT_DIR)
|
||||
|
||||
for d in ['firmware', 'map']:
|
||||
if not os.path.isdir("{}{}".format(OUTPUT_DIR, d)):
|
||||
os.mkdir("{}{}".format(OUTPUT_DIR, d))
|
||||
|
||||
# create string with location and file names based on variant
|
||||
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||
# get locations and file names based on variant
|
||||
map_file = tasmotapiolib.get_final_map_path(env)
|
||||
bin_file = tasmotapiolib.get_final_bin_path(env)
|
||||
|
||||
# check if new target files exist and remove if necessary
|
||||
for f in [map_file, bin_file]:
|
||||
if os.path.isfile(f):
|
||||
os.remove(f)
|
||||
if f.is_file():
|
||||
f.unlink()
|
||||
|
||||
# copy firmware.bin to firmware/<variant>.bin
|
||||
shutil.copy(str(target[0]), bin_file)
|
||||
# copy firmware.bin and map to final destination
|
||||
shutil.copy(firsttarget, bin_file)
|
||||
shutil.move(tasmotapiolib.get_source_map_path(env), map_file)
|
||||
|
||||
# move firmware.map to map/<variant>.map
|
||||
if os.path.isfile("firmware.map"):
|
||||
shutil.move("firmware.map", map_file)
|
||||
|
||||
#map_new_loc = str(target[0]).split(os.path.sep)[0] + os.path.sep + str(target[0]).split(os.path.sep)[1] + os.path.sep + str(target[0]).split(os.path.sep)[2] + os.path.sep + "Tasmota.map"
|
||||
# PIO env variables see: https://github.com/platformio/platformio-core/blob/develop/platformio/builder/main.py#L108:L128
|
||||
proj_build_dir = env["PROJECT_BUILD_DIR"]
|
||||
#build_dir = env["BUILD_DIR"]
|
||||
pio_env = env["PIOENV"]
|
||||
proj_dir = env["PROJECT_DIR"]
|
||||
map_name = str(proj_dir).split(os.path.sep)[-1]
|
||||
map_new_loc = proj_build_dir + os.path.sep + pio_env + os.path.sep + map_name + ".map"
|
||||
#print("proj_build_dir: {}".format(proj_build_dir))
|
||||
#print("pioenv: {}".format(pio_env))
|
||||
#print("build_dir: {}".format(build_dir))
|
||||
#print("map_name: {}".format(map_name))
|
||||
#print("proj_dir: {}".format(proj_dir))
|
||||
#print("map_new_loc: {}".format(map_new_loc))
|
||||
|
||||
# move Tasmota.map to map/<variant>.map
|
||||
if os.path.isfile(map_new_loc):
|
||||
shutil.move(map_new_loc, map_file)
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_map_copy])
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", bin_map_copy)
|
||||
|
|
|
@ -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
|
|
@ -16,6 +16,17 @@ build_flags = ${env.build_flags}
|
|||
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
|
||||
-Wno-switch-unreachable
|
||||
|
||||
; Settings in [tasmota] do NOT affect firmware building
|
||||
[tasmota]
|
||||
; Uncomment next line if you do NOT want gzipped map file(s)
|
||||
;disable_map_gz = 1
|
||||
; Uncomment and specify a folder where to place the map file(s) (default set to folder build_output)
|
||||
;map_dir = /tmp/map_files/
|
||||
; Uncomment next line if you do NOT want additionally gzipped firmware file(s)
|
||||
;disable_bin_gz = 1
|
||||
; Uncomment and specify a folder where to place the firmware file(s) (default set to folder build_output)
|
||||
;bin_dir = /tmp/bin_files/
|
||||
|
||||
[env:tasmota-rangeextender]
|
||||
build_flags = ${env.build_flags}
|
||||
-D FIRMWARE_RANGE_EXTENDER
|
||||
|
|
|
@ -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()
|
||||
|
||||
-#
|
|
@ -38,9 +38,9 @@ ren_sec.set_pos(110,10)
|
|||
|
||||
prev_day = -1
|
||||
def set_watch()
|
||||
now = tasmota.rtc()
|
||||
time_raw = now['local'] + now['timezone'] * 60
|
||||
time = tasmota.time_dump(time_raw)
|
||||
var now = tasmota.rtc()
|
||||
var time_raw = now['local'] + now['timezone'] * 60
|
||||
var time = tasmota.time_dump(time_raw)
|
||||
# set second
|
||||
ren_sec.set_angle(60 * time['sec'])
|
||||
# set minutes
|
||||
|
|
|
@ -44,7 +44,7 @@ def crc32_create_table()
|
|||
return a
|
||||
end
|
||||
|
||||
crc32_table = crc32_create_table()
|
||||
var crc32_table = crc32_create_table()
|
||||
|
||||
def crc32_update(buf, crc)
|
||||
crc ^= 0xffffffff
|
||||
|
@ -137,7 +137,6 @@ class Partition_info
|
|||
# print("Segment count", seg_count)
|
||||
|
||||
var seg_offset = addr + 0x20 # sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) = 24 + 8
|
||||
var seg_size = 0
|
||||
|
||||
for seg_num:0..seg_count-1
|
||||
# print(string.format("Reading 0x%08X", seg_offset))
|
||||
|
@ -375,7 +374,6 @@ class Partition
|
|||
|
||||
#- parse the raw bytes to a structured list of partition items -#
|
||||
def parse()
|
||||
var i
|
||||
for i:0..94 # there are maximum 95 slots + md5 (0xC00)
|
||||
var item_raw = self.raw[i*32..(i+1)*32-1]
|
||||
var magic = item_raw.get(0,2)
|
||||
|
@ -754,7 +752,7 @@ class Partition_manager : Driver
|
|||
end
|
||||
|
||||
#- create and register driver in Tasmota -#
|
||||
partition_manager = Partition_manager()
|
||||
var partition_manager = Partition_manager()
|
||||
tasmota.add_driver(partition_manager)
|
||||
## can be removed if put in 'autoexec.bat'
|
||||
partition_manager.web_add_handler()
|
||||
|
@ -771,4 +769,4 @@ import partition
|
|||
p = partition.Partition()
|
||||
print(p)
|
||||
|
||||
-#
|
||||
-#
|
||||
|
|
|
@ -316,7 +316,7 @@ float CharToFloat(const char *str)
|
|||
char *pt = strbuf;
|
||||
if (*pt == '\0') { return 0.0; }
|
||||
|
||||
while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces
|
||||
while ((*pt != '\0') && isspace(*pt)) { pt++; } // Trim leading spaces
|
||||
|
||||
signed char sign = 1;
|
||||
if (*pt == '-') { sign = -1; }
|
||||
|
@ -524,17 +524,18 @@ bool StrCaseStr_P(const char* source, const char* search) {
|
|||
}
|
||||
|
||||
bool IsNumeric(const char* value) {
|
||||
// Test for characters '-.0123456789'
|
||||
char *digit = (char*)value;
|
||||
while (isdigit(*digit) || *digit == '.' || *digit == '-') { digit++; }
|
||||
return (*digit == '\0');
|
||||
}
|
||||
|
||||
char* Trim(char* p)
|
||||
{
|
||||
char* Trim(char* p) {
|
||||
// Remove leading and trailing tab, \n, \v, \f, \r and space
|
||||
if (*p != '\0') {
|
||||
while ((*p != '\0') && isblank(*p)) { p++; } // Trim leading spaces
|
||||
while ((*p != '\0') && isspace(*p)) { p++; } // Trim leading spaces
|
||||
char* q = p + strlen(p) -1;
|
||||
while ((q >= p) && isblank(*q)) { q--; } // Trim trailing spaces
|
||||
while ((q >= p) && isspace(*q)) { q--; } // Trim trailing spaces
|
||||
q++;
|
||||
*q = '\0';
|
||||
}
|
||||
|
|
|
@ -666,7 +666,7 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Referer denied"));
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP "Referer denied. Use 'SO128 1' for HTTP API commands. 'Webpassword' is recommended."));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
|
|
@ -197,8 +197,8 @@ void MqttInit(void) {
|
|||
Settings->mqtt_port = 8883;
|
||||
#endif //USE_MQTT_AZURE_IOT
|
||||
#ifdef USE_MQTT_TLS
|
||||
if ((8883 == Settings->mqtt_port) || (8884 == Settings->mqtt_port)) {
|
||||
// Turn on TLS for port 8883 (TLS) and 8884 (TLS, client certificate)
|
||||
if ((8883 == Settings->mqtt_port) || (8884 == Settings->mqtt_port) || (443 == Settings->mqtt_port)) {
|
||||
// Turn on TLS for port 8883 (TLS), 8884 (TLS, client certificate), 443 (TLS, user/password)
|
||||
Settings->flag4.mqtt_tls = true;
|
||||
}
|
||||
Mqtt.mqtt_tls = Settings->flag4.mqtt_tls; // this flag should not change even if we change the SetOption (until reboot)
|
||||
|
|
|
@ -801,6 +801,11 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
|
|||
|
||||
bool RulesProcessEvent(const char *json_event)
|
||||
{
|
||||
#ifdef USE_BERRY
|
||||
// events are passed to Berry before Rules engine
|
||||
callBerryRule(json_event);
|
||||
#endif
|
||||
|
||||
if (Rules.busy) { return false; }
|
||||
|
||||
Rules.busy = true;
|
||||
|
@ -858,7 +863,11 @@ void RulesInit(void)
|
|||
|
||||
void RulesEvery50ms(void)
|
||||
{
|
||||
#ifdef USE_BERRY
|
||||
if (!Rules.busy) { // Emitting Rules events is always enabled with Berry
|
||||
#else
|
||||
if (Settings->rule_enabled && !Rules.busy) { // Any rule enabled
|
||||
#endif
|
||||
char json_event[120];
|
||||
|
||||
if (-1 == Rules.new_power) { Rules.new_power = TasmotaGlobal.power; }
|
||||
|
|
|
@ -93,12 +93,13 @@ extern "C" {
|
|||
\*********************************************************************************************/
|
||||
// // call a function (if exists) of type void -> void
|
||||
|
||||
bool callBerryRule(void) {
|
||||
// If event == nullptr, then take XdrvMailbox.data
|
||||
bool callBerryRule(const char *event) {
|
||||
if (berry.rules_busy) { return false; }
|
||||
berry.rules_busy = true;
|
||||
char * json_event = XdrvMailbox.data;
|
||||
bool serviced = false;
|
||||
serviced = callBerryEventDispatcher(PSTR("rule"), nullptr, 0, XdrvMailbox.data);
|
||||
serviced = callBerryEventDispatcher(PSTR("rule"), nullptr, 0, event ? event : XdrvMailbox.data);
|
||||
berry.rules_busy = false;
|
||||
return serviced; // TODO event not handled
|
||||
}
|
||||
|
@ -277,7 +278,8 @@ void BerryInit(void) {
|
|||
do {
|
||||
berry.vm = be_vm_new(); /* create a virtual machine instance */
|
||||
be_set_obs_hook(berry.vm, &BerryObservability);
|
||||
comp_set_named_gbl(berry.vm);
|
||||
comp_set_named_gbl(berry.vm); /* Enable named globals in Berry compiler */
|
||||
comp_set_strict(berry.vm); /* Enable strict mode in Berry compiler */
|
||||
be_load_custom_libs(berry.vm);
|
||||
|
||||
// Register functions
|
||||
|
@ -731,7 +733,7 @@ bool Xdrv52(uint8_t function)
|
|||
|
||||
// Berry wide commands and events
|
||||
case FUNC_RULES_PROCESS:
|
||||
result = callBerryRule();
|
||||
result = callBerryRule(nullptr);
|
||||
break;
|
||||
case FUNC_MQTT_DATA:
|
||||
result = callBerryEventDispatcher(PSTR("mqtt_data"), XdrvMailbox.topic, 0, XdrvMailbox.data, XdrvMailbox.data_len);
|
||||
|
|
|
@ -84,10 +84,11 @@ struct {
|
|||
String _serverUrl; // Connection info
|
||||
String _writeUrl; // Cached full write url
|
||||
String _lastErrorResponse; // Server reponse or library error message for last failed request
|
||||
uint32_t _lastRequestTime = 0; // Last time in ms we made are a request to server
|
||||
uint32_t _lastRequestTime = 0; // Last time in ms we made a request to server
|
||||
int interval = 0;
|
||||
int _lastStatusCode = 0; // HTTP status code of last request to server
|
||||
int _lastRetryAfter = 0; // Store retry timeout suggested by server after last request
|
||||
uint8_t log_level = LOG_LEVEL_DEBUG_MORE;
|
||||
bool _connectionReuse; // true if HTTP connection should be kept open. Usable for frequent writes. Default false
|
||||
bool init = false;
|
||||
} IFDB;
|
||||
|
@ -161,7 +162,7 @@ void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) {
|
|||
IFDB._lastRequestTime = millis();
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: HTTP status code %d"), IFDB._lastStatusCode);
|
||||
IFDB._lastRetryAfter = 0;
|
||||
if (IFDB._lastStatusCode >= 429) { //retryable server errors
|
||||
if (IFDB._lastStatusCode >= 429) { // Retryable server errors
|
||||
if (IFDBhttpClient->hasHeader(RetryAfter)) {
|
||||
IFDB._lastRetryAfter = IFDBhttpClient->header(RetryAfter).toInt();
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: Reply after %d"), IFDB._lastRetryAfter);
|
||||
|
@ -171,12 +172,12 @@ void InfluxDbAfterRequest(int expectedStatusCode, bool modifyLastConnStatus) {
|
|||
IFDB._lastErrorResponse = "";
|
||||
if (IFDB._lastStatusCode != expectedStatusCode) {
|
||||
if (IFDB._lastStatusCode > 0) {
|
||||
IFDB._lastErrorResponse = IFDBhttpClient->getString();
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("IFX: %s"), IFDB._lastErrorResponse.c_str()); // {"error":"database not found: \"db\""}
|
||||
IFDB._lastErrorResponse = IFDBhttpClient->getString(); // {"error":"database not found: \"db\""}\n
|
||||
} else {
|
||||
IFDB._lastErrorResponse = IFDBhttpClient->errorToString(IFDB._lastStatusCode);
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("IFX: Error %s"), IFDB._lastErrorResponse.c_str());
|
||||
}
|
||||
IFDB._lastErrorResponse.trim(); // Remove trailing \n
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("IFX: Error %s"), IFDB._lastErrorResponse.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,8 +192,6 @@ bool InfluxDbValidateConnection(void) {
|
|||
if (1 == Settings->influxdb_version) {
|
||||
url += InfluxDbAuth();
|
||||
}
|
||||
// on version 1.8.9 /health works fine
|
||||
// String url = IFDB._serverUrl + "/health";
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("IFX: Validating connection to %s"), url.c_str());
|
||||
|
||||
if (!IFDBhttpClient->begin(*IFDBwifiClient, url)) {
|
||||
|
@ -221,7 +220,8 @@ int InfluxDbPostData(const char *data) {
|
|||
return false;
|
||||
}
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("IFX: Sending\n%s"), data);
|
||||
Trim((char*)data); // Remove trailing \n
|
||||
AddLog(IFDB.log_level, PSTR("IFX: Sending\n%s"), data);
|
||||
IFDBhttpClient->addHeader(F("Content-Type"), F("text/plain"));
|
||||
InfluxDbBeforeRequest();
|
||||
IFDB._lastStatusCode = IFDBhttpClient->POST((uint8_t*)data, strlen(data));
|
||||
|
@ -235,19 +235,22 @@ int InfluxDbPostData(const char *data) {
|
|||
* Data preparation
|
||||
\*********************************************************************************************/
|
||||
|
||||
char* InfluxDbNumber(char* alternative, const char* source) {
|
||||
// Test for valid numeric data ('-.0123456789') or ON, OFF etc. as defined in kOptions
|
||||
if (source != nullptr) {
|
||||
char* out = (char*)source;
|
||||
// Convert special text as found in kOptions to a number
|
||||
// Like "OFF" -> 0, "ON" -> 1, "TOGGLE" -> 2
|
||||
int number = GetStateNumber(source);
|
||||
if (number >= 0) {
|
||||
itoa(number, alternative, 10);
|
||||
out = alternative;
|
||||
}
|
||||
if (IsNumeric(out)) {
|
||||
return out;
|
||||
char* InfluxDbNumber(char* alternative, JsonParserToken value) {
|
||||
if (value.isValid()) {
|
||||
char* source = (char*)value.getStr();
|
||||
// Test for valid numeric data ('-.0123456789') or ON, OFF etc. as defined in kOptions
|
||||
if (source != nullptr) {
|
||||
char* out = source;
|
||||
// Convert special text as found in kOptions to a number
|
||||
// Like "OFF" -> 0, "ON" -> 1, "TOGGLE" -> 2
|
||||
int number = GetStateNumber(source);
|
||||
if (number >= 0) {
|
||||
itoa(number, alternative, 10);
|
||||
out = alternative;
|
||||
}
|
||||
if (IsNumeric(out)) {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -256,10 +259,12 @@ char* InfluxDbNumber(char* alternative, const char* source) {
|
|||
void InfluxDbProcessJson(void) {
|
||||
if (!IFDB.init) { return; }
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX: JSON %s"), ResponseData());
|
||||
AddLog(IFDB.log_level, PSTR("IFX: Process %s"), ResponseData());
|
||||
|
||||
// String jsonStr = ResponseData(); // Make a copy before use
|
||||
// JsonParser parser((char *)jsonStr.c_str());
|
||||
JsonParser parser((char *)ResponseData()); // Destroys ResponseData but saves heap space
|
||||
|
||||
String jsonStr = ResponseData();
|
||||
JsonParser parser((char *)jsonStr.c_str());
|
||||
JsonParserObject root = parser.getRootObject();
|
||||
if (root) {
|
||||
char number[12]; // '1' to '255'
|
||||
|
@ -280,8 +285,8 @@ void InfluxDbProcessJson(void) {
|
|||
if (value2.isObject()) {
|
||||
JsonParserObject Object3 = value2.getObject();
|
||||
for (auto key3 : Object3) {
|
||||
const char* value = InfluxDbNumber(number, key3.getValue().getStr());
|
||||
if (value != nullptr) {
|
||||
char* value = InfluxDbNumber(number, key3.getValue());
|
||||
if ((value != nullptr) && key2.isValid() && key3.isValid()) {
|
||||
// Level 3
|
||||
LowerCase(sensor, key2.getStr());
|
||||
LowerCase(type, key3.getStr());
|
||||
|
@ -294,44 +299,47 @@ void InfluxDbProcessJson(void) {
|
|||
} else {
|
||||
// Level 2
|
||||
// { ... "ANALOG":{"Temperature":184.72},"DS18B20":{"Id":"01144A0CB2AA","Temperature":24.88},"HTU21":{"Temperature":25.32,"Humidity":49.2,"DewPoint":13.88},"Global":{"Temperature":24.88,"Humidity":49.2,"DewPoint":13.47}, ... }
|
||||
bool isarray = value2.isArray();
|
||||
const char* value = InfluxDbNumber(number, (isarray) ? (value2.getArray())[0].getStr() : value2.getStr());
|
||||
if (value != nullptr) {
|
||||
if (!key1.isValid() || !value2.isValid()) { continue; }
|
||||
LowerCase(type, key2.getStr());
|
||||
bool is_id = (!strcmp_P(type, PSTR("id"))); // Index for DS18B20
|
||||
bool is_array = value2.isArray();
|
||||
char* value = nullptr;
|
||||
if (is_id && !is_array) {
|
||||
snprintf_P(sensor_id, sizeof(sensor_id), PSTR(",id=%s"), value2.getStr());
|
||||
} else {
|
||||
value = InfluxDbNumber(number, (is_array) ? (value2.getArray())[0] : value2);
|
||||
}
|
||||
if ((value != nullptr) && key2.isValid()) {
|
||||
LowerCase(sensor, key1.getStr());
|
||||
LowerCase(type, key2.getStr());
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR("IFX2: sensor %s (%s), type %s (%s)"), key1.getStr(), sensor, key2.getStr(), type);
|
||||
|
||||
if (strcmp(type, "id") == 0) { // Index for DS18B20
|
||||
snprintf_P(sensor_id, sizeof(sensor_id), PSTR(",id=%s"), value);
|
||||
} else {
|
||||
if (isarray) {
|
||||
JsonParserArray arr = value2.getArray();
|
||||
uint32_t i = 0;
|
||||
for (auto val : arr) {
|
||||
i++;
|
||||
// power1,device=shelly25,sensor=energy value=0.00
|
||||
// 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);
|
||||
if (is_array) {
|
||||
JsonParserArray arr = value2.getArray();
|
||||
uint32_t i = 0;
|
||||
for (auto val : arr) {
|
||||
i++;
|
||||
// power1,device=shelly25,sensor=energy value=0.00
|
||||
// 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;
|
||||
}
|
||||
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 {
|
||||
// Level 1
|
||||
// {"Time":"2021-08-13T14:15:56","Switch1":"ON","Switch2":"OFF", ... "TempUnit":"C"}
|
||||
const char* value = InfluxDbNumber(number, value1.getStr());
|
||||
if (value != nullptr) {
|
||||
char* value = InfluxDbNumber(number, value1);
|
||||
if ((value != nullptr) && key1.isValid()) {
|
||||
LowerCase(type, key1.getStr());
|
||||
// switch1,device=demo,sensor=device value=0
|
||||
// power1,device=demo,sensor=device value=1
|
||||
|
@ -391,6 +399,7 @@ void InfluxDbLoop(void) {
|
|||
\*********************************************************************************************/
|
||||
|
||||
#define D_PRFX_INFLUXDB "Ifx"
|
||||
#define D_CMND_INFLUXDBLOG "Log"
|
||||
#define D_CMND_INFLUXDBHOST "Host"
|
||||
#define D_CMND_INFLUXDBPORT "Port"
|
||||
#define D_CMND_INFLUXDBUSER "User"
|
||||
|
@ -401,14 +410,14 @@ void InfluxDbLoop(void) {
|
|||
#define D_CMND_INFLUXDBBUCKET "Bucket"
|
||||
|
||||
const char kInfluxDbCommands[] PROGMEM = D_PRFX_INFLUXDB "|" // Prefix
|
||||
"|"
|
||||
"|" D_CMND_INFLUXDBLOG "|"
|
||||
D_CMND_INFLUXDBHOST "|" D_CMND_INFLUXDBPORT "|"
|
||||
D_CMND_INFLUXDBUSER "|" D_CMND_INFLUXDBORG "|"
|
||||
D_CMND_INFLUXDBPASSWORD "|" D_CMND_INFLUXDBTOKEN "|"
|
||||
D_CMND_INFLUXDBDATABASE "|" D_CMND_INFLUXDBBUCKET;
|
||||
|
||||
void (* const InfluxCommand[])(void) PROGMEM = {
|
||||
&CmndInfluxDbState,
|
||||
&CmndInfluxDbState, &CmndInfluxDbLog,
|
||||
&CmndInfluxDbHost, &CmndInfluxDbPort,
|
||||
&CmndInfluxDbUser, &CmndInfluxDbUser,
|
||||
&CmndInfluxDbPassword, &CmndInfluxDbPassword,
|
||||
|
@ -437,6 +446,13 @@ void CmndInfluxDbState(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void CmndInfluxDbLog(void) {
|
||||
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
|
||||
IFDB.log_level = XdrvMailbox.payload;
|
||||
}
|
||||
ResponseCmndNumber(IFDB.log_level);
|
||||
}
|
||||
|
||||
void CmndInfluxDbHost(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
SettingsUpdateText(SET_INFLUXDB_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(INFLUXDB_HOST) : XdrvMailbox.data);
|
||||
|
|
|
@ -1083,8 +1083,8 @@ bool XdrvRulesProcess(bool teleperiod, const char* event) {
|
|||
char* data_save = XdrvMailbox.data;
|
||||
XdrvMailbox.data = (char*)event;
|
||||
bool rule_handled = XdrvCallDriver(10, (teleperiod) ? FUNC_TELEPERIOD_RULES_PROCESS : FUNC_RULES_PROCESS);
|
||||
#ifdef USE_BERRY
|
||||
// events are passed to both Rules engine AND Berry engine
|
||||
#if defined(USE_BERRY) && !defined(USE_RULES)
|
||||
// events are sent to Berry in Rules driver, or here if USE_RULES is not defined (only on a subset)
|
||||
bool berry_handled = XdrvCallDriver(52, FUNC_RULES_PROCESS);
|
||||
rule_handled |= berry_handled;
|
||||
#endif
|
||||
|
|
|
@ -1198,7 +1198,7 @@ double CharToDouble(const char *str)
|
|||
|
||||
strlcpy(strbuf, str, sizeof(strbuf));
|
||||
char *pt = strbuf;
|
||||
while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces
|
||||
while ((*pt != '\0') && isspace(*pt)) { pt++; } // Trim leading spaces
|
||||
|
||||
signed char sign = 1;
|
||||
if (*pt == '-') { sign = -1; }
|
||||
|
|
Loading…
Reference in New Issue