mirror of https://github.com/arendst/Tasmota.git
Merge pull request #11491 from s-hadinger/berry_mar_27
Berry stabilized and methods solidified
This commit is contained in:
commit
62edcccb1f
|
@ -0,0 +1,17 @@
|
|||
#include "be_constobj.h"
|
||||
|
||||
static be_define_const_map_slots(m_libsolidify_map) {
|
||||
{ be_const_key(dump, -1), be_const_func(m_dump) },
|
||||
};
|
||||
|
||||
static be_define_const_map(
|
||||
m_libsolidify_map,
|
||||
1
|
||||
);
|
||||
|
||||
static be_define_const_module(
|
||||
m_libsolidify,
|
||||
"solidify"
|
||||
);
|
||||
|
||||
BE_EXPORT_VARIABLE be_define_const_native_module(solidify, NULL);
|
|
@ -0,0 +1,2 @@
|
|||
#include "be_constobj.h"
|
||||
|
|
@ -35,6 +35,17 @@ static void class_init(bvm *vm, bclass *c, const bnfuncinfo *lib)
|
|||
}
|
||||
++lib;
|
||||
}
|
||||
if (lib->function == (bntvfunc) BE_CLOSURE) {
|
||||
/* next section is closures */
|
||||
++lib;
|
||||
while (lib->name) {
|
||||
if (lib->function) { /* method */
|
||||
bstring *s = be_newstr(vm, lib->name);
|
||||
be_closure_method_bind(vm, c, s, (bclosure*) lib->function);
|
||||
}
|
||||
++lib;
|
||||
}
|
||||
}
|
||||
be_map_release(vm, c->members); /* clear space */
|
||||
}
|
||||
}
|
||||
|
@ -346,6 +357,13 @@ BERRY_API void be_pushvalue(bvm *vm, int index)
|
|||
be_incrtop(vm);
|
||||
}
|
||||
|
||||
BERRY_API void be_pushclosure(bvm *vm, void *cl)
|
||||
{
|
||||
bvalue *reg = be_incrtop(vm);
|
||||
bclosure * closure = (bclosure*) cl;
|
||||
var_setclosure(reg, closure);
|
||||
}
|
||||
|
||||
BERRY_API void be_pushntvclosure(bvm *vm, bntvfunc f, int nupvals)
|
||||
{
|
||||
/* to create a native closure and then push the top registor,
|
||||
|
|
|
@ -32,12 +32,14 @@ typedef struct buf_impl {
|
|||
**
|
||||
** Extracted from Tasmota SBuffer lib
|
||||
********************************************************************/
|
||||
static inline uint8_t* buf_get_buf(buf_impl* buf) {
|
||||
static inline uint8_t* buf_get_buf(buf_impl* buf)
|
||||
{
|
||||
return &buf->buf[0];
|
||||
}
|
||||
|
||||
// shrink or increase. If increase, fill with zeores. Cannot go beyond `size`
|
||||
static void buf_set_len(buf_impl* buf, const size_t len) {
|
||||
static void buf_set_len(buf_impl* buf, const size_t len)
|
||||
{
|
||||
uint16_t old_len = buf->len;
|
||||
buf->len = (len <= buf->size) ? len : buf->size;
|
||||
if (old_len < buf->len) {
|
||||
|
@ -45,33 +47,41 @@ static void buf_set_len(buf_impl* buf, const size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void buf_set1(buf_impl* buf, const size_t offset, const uint8_t data) {
|
||||
static void buf_set1(buf_impl* buf, const size_t offset, const uint8_t data)
|
||||
{
|
||||
if (offset < buf->len) {
|
||||
buf->buf[offset] = data;
|
||||
}
|
||||
}
|
||||
static size_t buf_add1(buf_impl* buf, const uint8_t data) { // append 8 bits value
|
||||
|
||||
static size_t buf_add1(buf_impl* buf, const uint8_t data) // append 8 bits value
|
||||
{
|
||||
if (buf->len < buf->size) { // do we have room for 1 byte
|
||||
buf->buf[buf->len++] = data;
|
||||
}
|
||||
return buf->len;
|
||||
}
|
||||
static size_t buf_add2_le(buf_impl* buf, const uint16_t data) { // append 16 bits value
|
||||
|
||||
static size_t buf_add2_le(buf_impl* buf, const uint16_t data) // append 16 bits value
|
||||
{
|
||||
if (buf->len < buf->size - 1) { // do we have room for 2 bytes
|
||||
buf->buf[buf->len++] = data;
|
||||
buf->buf[buf->len++] = data >> 8;
|
||||
}
|
||||
return buf->len;
|
||||
}
|
||||
static size_t buf_add2_be(buf_impl* buf, const uint16_t data) { // append 16 bits value
|
||||
|
||||
static size_t buf_add2_be(buf_impl* buf, const uint16_t data) // append 16 bits value
|
||||
{
|
||||
if (buf->len < buf->size - 1) { // do we have room for 2 bytes
|
||||
buf->buf[buf->len++] = data >> 8;
|
||||
buf->buf[buf->len++] = data;
|
||||
}
|
||||
return buf->len;
|
||||
}
|
||||
static size_t buf_add4_le(buf_impl* buf, const uint32_t data) { // append 32 bits value
|
||||
|
||||
static size_t buf_add4_le(buf_impl* buf, const uint32_t data) // append 32 bits value
|
||||
{
|
||||
if (buf->len < buf->size - 3) { // do we have room for 4 bytes
|
||||
buf->buf[buf->len++] = data;
|
||||
buf->buf[buf->len++] = data >> 8;
|
||||
|
@ -80,7 +90,9 @@ static size_t buf_add4_le(buf_impl* buf, const uint32_t data) { // app
|
|||
}
|
||||
return buf->len;
|
||||
}
|
||||
size_t buf_add4_be(buf_impl* buf, const uint32_t data) { // append 32 bits value
|
||||
|
||||
size_t buf_add4_be(buf_impl* buf, const uint32_t data) // append 32 bits value
|
||||
{
|
||||
if (buf->len < buf->size - 3) { // do we have room for 4 bytes
|
||||
buf->buf[buf->len++] = data >> 24;
|
||||
buf->buf[buf->len++] = data >> 16;
|
||||
|
@ -90,7 +102,8 @@ size_t buf_add4_be(buf_impl* buf, const uint32_t data) { // append 32
|
|||
return buf->len;
|
||||
}
|
||||
|
||||
static size_t buf_add_buf(buf_impl* buf, buf_impl* buf2) {
|
||||
static size_t buf_add_buf(buf_impl* buf, buf_impl* buf2)
|
||||
{
|
||||
if (buf->len + buf2->len <= buf->size) {
|
||||
for (uint32_t i = 0; i < buf2->len; i++) {
|
||||
buf->buf[buf->len++] = buf2->buf[i];
|
||||
|
@ -99,32 +112,40 @@ static size_t buf_add_buf(buf_impl* buf, buf_impl* buf2) {
|
|||
return buf->len;
|
||||
}
|
||||
|
||||
static uint8_t buf_get1(buf_impl* buf, int offset) {
|
||||
static uint8_t buf_get1(buf_impl* buf, int offset)
|
||||
{
|
||||
if ((offset >= 0) && (offset < buf->len)) {
|
||||
return buf->buf[offset];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t buf_get2_le(buf_impl* buf, int offset) {
|
||||
if ((offset >= 0) && (offset < buf->len - 1)) {
|
||||
return buf->buf[offset] | (buf->buf[offset+1] << 8);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static uint16_t buf_get2_be(buf_impl* buf, int offset) {
|
||||
|
||||
static uint16_t buf_get2_be(buf_impl* buf, int offset)
|
||||
{
|
||||
if (offset < buf->len - 1) {
|
||||
return buf->buf[offset+1] | (buf->buf[offset] << 8);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static uint32_t buf_get4_le(buf_impl* buf, int offset) {
|
||||
|
||||
static uint32_t buf_get4_le(buf_impl* buf, int offset)
|
||||
{
|
||||
if ((offset >= 0) && (offset < buf->len - 3)) {
|
||||
return buf->buf[offset] | (buf->buf[offset+1] << 8) |
|
||||
(buf->buf[offset+2] << 16) | (buf->buf[offset+3] << 24);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static uint32_t buf_get4_be(buf_impl* buf, int offset) {
|
||||
|
||||
static uint32_t buf_get4_be(buf_impl* buf, int offset)
|
||||
{
|
||||
if (offset < buf->len - 3) {
|
||||
return buf->buf[offset+3] | (buf->buf[offset+2] << 8) |
|
||||
(buf->buf[offset+1] << 16) | (buf->buf[offset] << 24);
|
||||
|
@ -133,7 +154,8 @@ static uint32_t buf_get4_be(buf_impl* buf, int offset) {
|
|||
}
|
||||
|
||||
// nullptr accepted
|
||||
static bbool buf_equals(buf_impl* buf1, buf_impl* buf2) {
|
||||
static bbool buf_equals(buf_impl* buf1, buf_impl* buf2)
|
||||
{
|
||||
if (buf1 == buf2) { return btrue; }
|
||||
if (!buf1 || !buf2) { return bfalse; } // at least one buf is not empty
|
||||
// we know that both buf1 and buf2 are non-null
|
||||
|
@ -145,7 +167,8 @@ static bbool buf_equals(buf_impl* buf1, buf_impl* buf2) {
|
|||
return btrue;
|
||||
}
|
||||
|
||||
static uint8_t asc2byte(char chr) {
|
||||
static uint8_t asc2byte(char chr)
|
||||
{
|
||||
uint8_t rVal = 0;
|
||||
if (isdigit(chr)) { rVal = chr - '0'; }
|
||||
else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; }
|
||||
|
@ -153,7 +176,8 @@ static uint8_t asc2byte(char chr) {
|
|||
return rVal;
|
||||
}
|
||||
// does not check if there is enough room before hand, truncated if buffer too small
|
||||
static void buf_add_hex(buf_impl* buf, const char *hex, size_t len) {
|
||||
static void buf_add_hex(buf_impl* buf, const char *hex, size_t len)
|
||||
{
|
||||
uint8_t val;
|
||||
for (; len > 1; len -= 2) {
|
||||
val = asc2byte(*hex++) << 4;
|
||||
|
@ -166,7 +190,8 @@ static void buf_add_hex(buf_impl* buf, const char *hex, size_t len) {
|
|||
** Wrapping into lib
|
||||
********************************************************************/
|
||||
// typedef int (*bntvfunc)(bvm*); /* native function pointer */
|
||||
int free_bytes_buf(bvm* vm) {
|
||||
int free_bytes_buf(bvm* vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
if (argc > 0) {
|
||||
buf_impl * buf = (buf_impl*) be_tocomptr(vm, 1);
|
||||
|
@ -285,9 +310,9 @@ static int m_tostring(bvm *vm)
|
|||
size_t hex_len = len * 2 + 5 + 2 + 2 + 1; /* reserve size for `bytes("")\0` - 9 chars */
|
||||
|
||||
char * hex_out = be_pushbuffer(vm, hex_len);
|
||||
size_t l = strlcpy(hex_out, "bytes('", hex_len);
|
||||
size_t l = be_strlcpy(hex_out, "bytes('", hex_len);
|
||||
l += tohex(&hex_out[l], hex_len - l, buf_get_buf(buf), buf->len);
|
||||
l += strlcpy(&hex_out[l], "')", hex_len - l);
|
||||
l += be_strlcpy(&hex_out[l], "')", hex_len - l);
|
||||
|
||||
be_pushnstring(vm, hex_out, l); /* make escape string from buffer */
|
||||
be_remove(vm, -2); /* remove buffer */
|
||||
|
@ -303,6 +328,7 @@ static int m_asstring(bvm *vm)
|
|||
be_pushnstring(vm, (const char*) buf_get_buf(buf), buf->len);
|
||||
be_return(vm);
|
||||
}
|
||||
|
||||
static int m_fromstring(bvm *vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
|
@ -395,7 +421,6 @@ static int m_get(bvm *vm)
|
|||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
|
||||
static int m_setitem(bvm *vm)
|
||||
{
|
||||
int argc = be_top(vm);
|
||||
|
|
|
@ -59,8 +59,10 @@ int be_class_attribute(bvm *vm, bclass *c, bstring *attr)
|
|||
void be_member_bind(bvm *vm, bclass *c, bstring *name)
|
||||
{
|
||||
bvalue *attr;
|
||||
set_fixed(name);
|
||||
check_members(vm, c);
|
||||
attr = be_map_insertstr(vm, c->members, name, NULL);
|
||||
restore_fixed(name);
|
||||
attr->v.i = c->nvar++;
|
||||
attr->type = MT_VARIABLE;
|
||||
}
|
||||
|
@ -69,8 +71,10 @@ void be_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p)
|
|||
{
|
||||
bclosure *cl;
|
||||
bvalue *attr;
|
||||
set_fixed(name);
|
||||
check_members(vm, c);
|
||||
attr = be_map_insertstr(vm, c->members, name, NULL);
|
||||
restore_fixed(name);
|
||||
var_setnil(attr);
|
||||
cl = be_newclosure(vm, p->nupvals);
|
||||
cl->proto = p;
|
||||
|
@ -80,12 +84,23 @@ void be_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p)
|
|||
void be_prim_method_bind(bvm *vm, bclass *c, bstring *name, bntvfunc f)
|
||||
{
|
||||
bvalue *attr;
|
||||
set_fixed(name);
|
||||
check_members(vm, c);
|
||||
attr = be_map_insertstr(vm, c->members, name, NULL);
|
||||
restore_fixed(name);
|
||||
attr->v.nf = f;
|
||||
attr->type = MT_PRIMMETHOD;
|
||||
}
|
||||
|
||||
void be_closure_method_bind(bvm *vm, bclass *c, bstring *name, bclosure *cl)
|
||||
{
|
||||
bvalue *attr;
|
||||
check_members(vm, c);
|
||||
attr = be_map_insertstr(vm, c->members, name, NULL);
|
||||
attr->v.gc = (bgcobject*) cl;
|
||||
attr->type = MT_METHOD;
|
||||
}
|
||||
|
||||
/* get the closure method count that need upvalues */
|
||||
int be_class_closure_count(bclass *c)
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@ int be_class_attribute(bvm *vm, bclass *c, bstring *attr);
|
|||
void be_member_bind(bvm *vm, bclass *c, bstring *name);
|
||||
void be_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p);
|
||||
void be_prim_method_bind(bvm *vm, bclass *c, bstring *name, bntvfunc f);
|
||||
void be_closure_method_bind(bvm *vm, bclass *c, bstring *name, bclosure *cl);
|
||||
int be_class_closure_count(bclass *c);
|
||||
void be_class_upvalue_init(bvm *vm, bclass *c);
|
||||
bbool be_class_newobj(bvm *vm, bclass *c, bvalue *argv, int argc);
|
||||
|
|
|
@ -46,7 +46,7 @@ static const char* opc2str(bopcode op)
|
|||
return op < array_count(opc_tab) ? opc_tab[op] : "ERROP";
|
||||
}
|
||||
|
||||
static void print_inst(binstruction ins, int pc)
|
||||
void be_print_inst(binstruction ins, int pc)
|
||||
{
|
||||
char __lbuf[INST_BUF_SIZE];
|
||||
bopcode op = IGET_OP(ins);
|
||||
|
@ -145,7 +145,7 @@ void be_dumpclosure(bclosure *cl)
|
|||
logfmt("; line %d\n", (++lineinfo)->linenumber);
|
||||
}
|
||||
#endif
|
||||
print_inst(*code++, pc);
|
||||
be_print_inst(*code++, pc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -21,4 +21,8 @@ void be_callhook(bvm *vm, int mask);
|
|||
bbool be_debug_varname(bvm *vm, int level, int index);
|
||||
bbool be_debug_upvname(bvm *vm, int level, int index);
|
||||
|
||||
#if BE_USE_DEBUG_MODULE
|
||||
void be_print_inst(binstruction ins, int pc);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -120,6 +120,20 @@ void be_gc_unfix(bvm *vm, bgcobject *obj)
|
|||
}
|
||||
}
|
||||
|
||||
bbool be_gc_fix_set(bvm *vm, bgcobject *obj, bbool fix)
|
||||
{
|
||||
(void)vm;
|
||||
bbool was_fixed = gc_isfixed(obj);
|
||||
if (!gc_isconst(obj)) {
|
||||
if (fix) {
|
||||
gc_setfixed(obj);
|
||||
} else {
|
||||
gc_clearfixed(obj);
|
||||
}
|
||||
}
|
||||
return was_fixed;
|
||||
}
|
||||
|
||||
static void mark_gray(bvm *vm, bgcobject *obj)
|
||||
{
|
||||
if (obj && gc_iswhite(obj) && !gc_isconst(obj)) {
|
||||
|
@ -285,6 +299,9 @@ static void free_proto(bvm *vm, bgcobject *obj)
|
|||
be_free(vm, proto->code, proto->codesize * sizeof(binstruction));
|
||||
#if BE_DEBUG_RUNTIME_INFO
|
||||
be_free(vm, proto->lineinfo, proto->nlineinfo * sizeof(blineinfo));
|
||||
#endif
|
||||
#if BE_DEBUG_VAR_INFO
|
||||
be_free(vm, proto->varinfo, proto->nvarinfo * sizeof(bvarinfo));
|
||||
#endif
|
||||
be_free(vm, proto, sizeof(bproto));
|
||||
}
|
||||
|
@ -489,9 +506,15 @@ static void reset_fixedlist(bvm *vm)
|
|||
|
||||
void be_gc_auto(bvm *vm)
|
||||
{
|
||||
#if BE_USE_DEBUG_GC
|
||||
if (vm->gc.status & GC_PAUSE) { /* force gc each time it's possible */
|
||||
be_gc_collect(vm);
|
||||
}
|
||||
#else
|
||||
if (vm->gc.status & GC_PAUSE && vm->gc.usage > vm->gc.threshold) {
|
||||
be_gc_collect(vm);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t be_gc_memcount(bvm *vm)
|
||||
|
@ -505,7 +528,8 @@ void be_gc_collect(bvm *vm)
|
|||
return; /* the GC cannot run for some reason */
|
||||
}
|
||||
#if BE_USE_OBSERVABILITY_HOOK
|
||||
if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_START, vm->gc.usage);
|
||||
if (vm->obshook != NULL)
|
||||
(*vm->obshook)(vm, BE_OBS_GC_START, vm->gc.usage);
|
||||
#endif
|
||||
/* step 1: set root-set reference objects to unscanned */
|
||||
premark_internal(vm); /* object internal the VM */
|
||||
|
@ -524,6 +548,7 @@ void be_gc_collect(bvm *vm)
|
|||
/* step 5: calculate the next GC threshold */
|
||||
vm->gc.threshold = next_threshold(vm->gc);
|
||||
#if BE_USE_OBSERVABILITY_HOOK
|
||||
if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_END, vm->gc.usage);
|
||||
if (vm->obshook != NULL)
|
||||
(*vm->obshook)(vm, BE_OBS_GC_END, vm->gc.usage);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -49,6 +49,9 @@ if (!gc_isconst(o)) { \
|
|||
#define be_isgcobj(o) be_isgctype(var_type(o))
|
||||
#define be_gcnew(v, t, s) be_newgcobj((v), (t), sizeof(s))
|
||||
|
||||
#define set_fixed(s) bbool _was_fixed = be_gc_fix_set(vm, cast(bgcobject*, (s)), 1)
|
||||
#define restore_fixed(s) be_gc_fix_set(vm, cast(bgcobject*, (s)), _was_fixed);
|
||||
|
||||
/* the GC mark uses bit4:0 of the `object->marked` field,
|
||||
* so other bits can be used for special flags (ex-mark). */
|
||||
typedef enum {
|
||||
|
@ -68,6 +71,7 @@ bgcobject *be_newgcobj(bvm *vm, int type, size_t size);
|
|||
bgcobject* be_gc_newstr(bvm *vm, size_t size, int islong);
|
||||
void be_gc_fix(bvm *vm, bgcobject *obj);
|
||||
void be_gc_unfix(bvm *vm, bgcobject *obj);
|
||||
bbool be_gc_fix_set(bvm *vm, bgcobject *obj, bbool fix);
|
||||
void be_gc_collect(bvm *vm);
|
||||
void be_gc_auto(bvm *vm);
|
||||
|
||||
|
|
|
@ -12,9 +12,7 @@ extern void be_load_listlib(bvm *vm);
|
|||
extern void be_load_maplib(bvm *vm);
|
||||
extern void be_load_rangelib(bvm *vm);
|
||||
extern void be_load_filelib(bvm *vm);
|
||||
|
||||
extern void be_load_tasmota_ntvlib(bvm *vm);
|
||||
extern void be_load_wirelib(bvm *vm);
|
||||
extern void be_load_byteslib(bvm *vm);
|
||||
|
||||
void be_loadlibs(bvm *vm)
|
||||
{
|
||||
|
@ -26,6 +24,4 @@ void be_loadlibs(bvm *vm)
|
|||
be_load_filelib(vm);
|
||||
be_load_byteslib(vm);
|
||||
#endif
|
||||
be_load_tasmota_ntvlib(vm);
|
||||
be_load_wirelib(vm);
|
||||
}
|
||||
|
|
|
@ -315,7 +315,10 @@ bvalue* be_map_insertstr(bvm *vm, bmap *map, bstring *key, bvalue *value)
|
|||
{
|
||||
bvalue v;
|
||||
var_setstr(&v, key);
|
||||
return be_map_insert(vm, map, &v, value);
|
||||
set_fixed(key);
|
||||
bvalue * r = be_map_insert(vm, map, &v, value);
|
||||
restore_fixed(key);
|
||||
return r;
|
||||
}
|
||||
|
||||
void be_map_removestr(bvm *vm, bmap *map, bstring *key)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "be_vm.h"
|
||||
#include "be_gc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define GC_ALLOC (1 << 2) /* GC in alloc */
|
||||
|
||||
|
@ -53,6 +54,10 @@ static void* _realloc(void *ptr, size_t old_size, size_t new_size)
|
|||
return malloc(new_size);
|
||||
}
|
||||
be_assert(new_size == 0);
|
||||
|
||||
#if BE_USE_DEBUG_GC
|
||||
memset(ptr, 0xFF, old_size); /* fill the structure with invalid pointers */
|
||||
#endif
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
/********************************************************************
|
||||
** Copyright (c) 2018-2020 Guan Wenliang
|
||||
** 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"
|
||||
#include "be_decoder.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if BE_USE_SOLIDIFY_MODULE
|
||||
|
||||
#ifndef INST_BUF_SIZE
|
||||
#define INST_BUF_SIZE 96
|
||||
#endif
|
||||
|
||||
#define logbuf(...) snprintf(__lbuf, sizeof(__lbuf), __VA_ARGS__)
|
||||
|
||||
#define logfmt(...) \
|
||||
do { \
|
||||
char __lbuf[INST_BUF_SIZE]; \
|
||||
logbuf(__VA_ARGS__); \
|
||||
be_writestring(__lbuf); \
|
||||
} while (0)
|
||||
|
||||
/* output only valid types for ktab, or NULL */
|
||||
static const char * m_type_ktab(int type)
|
||||
{
|
||||
switch (type){
|
||||
case BE_NIL: return "BE_NIL";
|
||||
case BE_INT: return "BE_INT";
|
||||
case BE_REAL: return "BE_REAL";
|
||||
case BE_BOOL: return "BE_BOOL";
|
||||
case BE_STRING: return "BE_STRING";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void m_solidify_closure(bvm *vm, bclosure *cl, int builtins)
|
||||
{
|
||||
bproto *pr = cl->proto;
|
||||
const char * func_name = str(pr->name);
|
||||
const char * func_source = str(pr->source);
|
||||
// logfmt("// == builtin_count %i\n", builtins);
|
||||
|
||||
// logfmt("// type %i, ", cl->type);
|
||||
// logfmt("// marked %i, ", cl->marked);
|
||||
// logfmt("// nupvals %i\n", cl->nupvals);
|
||||
|
||||
// logfmt("// PROTO:\n");
|
||||
// logfmt("// type %i, ", pr->type);
|
||||
// logfmt("// marked %i, ", pr->marked);
|
||||
// logfmt("// nstack %i, ", pr->nstack);
|
||||
// logfmt("// argcs %i, ", pr->argc);
|
||||
// // logfmt("// varg %i\n", pr->varg);
|
||||
|
||||
// logfmt("// gray %p\n", (void*)pr->gray);
|
||||
// logfmt("// upvals %p\n", (void*)pr->upvals);
|
||||
// logfmt("// proto_tab %p (%i)\n", (void*)pr->ptab, pr->nproto);
|
||||
|
||||
// logfmt("// name %s\n", str(pr->name));
|
||||
// logfmt("// source %s\n", str(pr->source));
|
||||
|
||||
// logfmt("\n");
|
||||
|
||||
// logfmt("// ktab %p (%i)\n", (void*)pr->ktab, pr->nconst);
|
||||
// for (int i = 0; i < pr->nconst; i++) {
|
||||
// logfmt("// const[%i] type %i (%s) %p", i, pr->ktab[i].type, be_vtype2str(&pr->ktab[i]), pr->ktab[i].v.p);
|
||||
// if (pr->ktab[i].type == BE_STRING) {
|
||||
// logfmt(" = '%s'", str(pr->ktab[i].v.s));
|
||||
// }
|
||||
// logfmt("\n");
|
||||
// }
|
||||
|
||||
logfmt("\n");
|
||||
logfmt("/********************************************************************\n");
|
||||
logfmt("** Solidified function: %s\n", func_name);
|
||||
logfmt("********************************************************************/\n\n");
|
||||
|
||||
/* create static strings for name and source */
|
||||
logfmt("be_define_local_const_str(%s_str_name, \"%s\", %i, 0, %u, 0);\n",
|
||||
func_name, func_name, be_strhash(pr->name), str_len(pr->name));
|
||||
logfmt("be_define_local_const_str(%s_str_source, \"%s\", %i, 0, %u, 0);\n",
|
||||
func_name, func_source, be_strhash(pr->source), str_len(pr->source));
|
||||
|
||||
/* create static strings first */
|
||||
for (int i = 0; i < pr->nconst; i++) {
|
||||
if (pr->ktab[i].type == BE_STRING) {
|
||||
logfmt("be_define_local_const_str(%s_str_%i, \"",
|
||||
func_name, i);
|
||||
be_writestring(str(pr->ktab[i].v.s));
|
||||
size_t len = strlen(str(pr->ktab[i].v.s));
|
||||
if (len >= 255) {
|
||||
be_raise(vm, "internal_error", "Strings greater than 255 chars not supported yet");
|
||||
}
|
||||
logfmt("\", %i, 0, %zu, 0);\n", be_strhash(pr->ktab[i].v.s), len >= 255 ? 255 : len);
|
||||
}
|
||||
}
|
||||
logfmt("\n");
|
||||
|
||||
logfmt("static const bvalue %s_ktab[%i] = {\n", func_name, pr->nconst);
|
||||
for (int k = 0; k < pr->nconst; k++) {
|
||||
int type = pr->ktab[k].type;
|
||||
const char *type_name = m_type_ktab(type);
|
||||
if (type_name == NULL) {
|
||||
char error[64];
|
||||
snprintf(error, sizeof(error), "Unsupported type in function constants: %i", type);
|
||||
be_raise(vm, "internal_error", error);
|
||||
}
|
||||
if (type == BE_STRING) {
|
||||
logfmt(" { { .s=be_local_const_str(%s_str_%i) }, %s},\n", func_name, k, type_name);
|
||||
} else if (type == BE_INT) {
|
||||
logfmt(" { { .i=%" BE_INT_FMTLEN "i }, %s},\n", pr->ktab[k].v.i, type_name);
|
||||
} else if (type == BE_REAL) {
|
||||
#if BE_USE_SINGLE_FLOAT
|
||||
logfmt(" { { .p=(void*)0x%08X }, %s},\n", (uint32_t) pr->ktab[k].v.p, type_name);
|
||||
#else
|
||||
logfmt(" { { .p=(void*)0x%016llX }, %s},\n", (uint64_t) pr->ktab[k].v.p, type_name);
|
||||
#endif
|
||||
} else if (type == BE_BOOL) {
|
||||
logfmt(" { { .b=%i }, %s},\n", pr->ktab[k].v.b, type_name);
|
||||
}
|
||||
}
|
||||
logfmt("};\n\n");
|
||||
|
||||
logfmt("static const uint32_t %s_code[%i] = {\n", func_name, pr->codesize);
|
||||
for (int pc = 0; pc < pr->codesize; pc++) {
|
||||
uint32_t ins = pr->code[pc];
|
||||
logfmt(" 0x%04X, //", ins);
|
||||
be_print_inst(ins, pc);
|
||||
bopcode op = IGET_OP(ins);
|
||||
if (op == OP_GETGBL || op == OP_SETGBL) {
|
||||
// check if the global is in built-ins
|
||||
int glb = IGET_Bx(ins);
|
||||
if (glb > builtins) {
|
||||
// not supported
|
||||
logfmt("\n===== unsupported global G%d\n", glb);
|
||||
be_raise(vm, "internal_error", "Unsupported access to non-builtin global");
|
||||
}
|
||||
}
|
||||
}
|
||||
logfmt("};\n\n");
|
||||
|
||||
logfmt("static const bproto %s_proto = {\n", func_name);
|
||||
// bcommon_header
|
||||
logfmt(" NULL, // bgcobject *next\n");
|
||||
logfmt(" %i, // type\n", pr->type);
|
||||
logfmt(" GC_CONST, // marked\n");
|
||||
//
|
||||
logfmt(" %i, // nstack\n", pr->nstack);
|
||||
logfmt(" %i, // nupvals\n", pr->nupvals);
|
||||
logfmt(" %i, // argc\n", pr->argc);
|
||||
logfmt(" %i, // varg\n", pr->varg);
|
||||
if (pr->nproto > 0) {
|
||||
be_raise(vm, "internal_error", "unsupported non-null proto list");
|
||||
}
|
||||
logfmt(" NULL, // bgcobject *gray\n");
|
||||
logfmt(" NULL, // bupvaldesc *upvals\n");
|
||||
logfmt(" (bvalue*) &%s_ktab, // ktab\n", func_name);
|
||||
logfmt(" NULL, // bproto **ptab\n");
|
||||
logfmt(" (binstruction*) &%s_code, // code\n", func_name);
|
||||
logfmt(" be_local_const_str(%s_str_name), // name\n", func_name);
|
||||
logfmt(" %i, // codesize\n", pr->codesize);
|
||||
logfmt(" %i, // nconst\n", pr->nconst);
|
||||
logfmt(" %i, // nproto\n", pr->nproto);
|
||||
logfmt(" be_local_const_str(%s_str_source), // source\n", func_name);
|
||||
//
|
||||
logfmt("#if BE_DEBUG_RUNTIME_INFO /* debug information */\n");
|
||||
logfmt(" NULL, // lineinfo\n");
|
||||
logfmt(" 0, // nlineinfo\n");
|
||||
logfmt("#endif\n");
|
||||
logfmt("#if BE_DEBUG_VAR_INFO\n");
|
||||
logfmt(" NULL, // varinfo\n");
|
||||
logfmt(" 0, // nvarinfo\n");
|
||||
logfmt("#endif\n");
|
||||
logfmt("};\n\n");
|
||||
|
||||
// closure
|
||||
logfmt("static const bclosure %s_closure = {\n", func_name);
|
||||
// bcommon_header
|
||||
logfmt(" NULL, // bgcobject *next\n");
|
||||
logfmt(" %i, // type\n", cl->type);
|
||||
logfmt(" GC_CONST, // marked\n");
|
||||
//
|
||||
logfmt(" %i, // nupvals\n", cl->nupvals);
|
||||
logfmt(" NULL, // bgcobject *gray\n");
|
||||
logfmt(" (bproto*) &%s_proto, // proto\n", func_name);
|
||||
logfmt(" { NULL } // upvals\n");
|
||||
logfmt("};\n\n");
|
||||
|
||||
logfmt("/*******************************************************************/\n\n");
|
||||
}
|
||||
|
||||
#define be_builtin_count(vm) \
|
||||
be_vector_count(&(vm)->gbldesc.builtin.vlist)
|
||||
|
||||
static int m_dump(bvm *vm)
|
||||
{
|
||||
if (be_top(vm) >= 1) {
|
||||
bvalue *v = be_indexof(vm, 1);
|
||||
if (var_isclosure(v)) {
|
||||
m_solidify_closure(vm, var_toobj(v), be_builtin_count(vm));
|
||||
}
|
||||
}
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
#if !BE_USE_PRECOMPILED_OBJECT
|
||||
be_native_module_attr_table(solidify) {
|
||||
be_native_module_function("dump", m_dump),
|
||||
};
|
||||
|
||||
be_define_native_module(solidify, NULL);
|
||||
#else
|
||||
/* @const_object_info_begin
|
||||
module solidify (scope: global, depend: BE_USE_SOLIDIFY_MODULE) {
|
||||
dump, func(m_dump)
|
||||
}
|
||||
@const_object_info_end */
|
||||
#include "../generate/be_fixed_solidify.h"
|
||||
#endif
|
||||
|
||||
#endif /* BE_USE_SOLIFIDY_MODULE */
|
|
@ -14,7 +14,7 @@
|
|||
#define next(_s) cast(void*, cast(bstring*, (_s)->next))
|
||||
#define sstr(_s) cast(char*, cast(bsstring*, _s) + 1)
|
||||
#define lstr(_s) cast(char*, cast(blstring*, _s) + 1)
|
||||
#define cstr(_s) (cast(bcstring*, s)->s)
|
||||
#define cstr(_s) (cast(bcstring*, _s)->s)
|
||||
|
||||
#define be_define_const_str(_name, _s, _hash, _extra, _len, _next) \
|
||||
BERRY_LOCAL const bcstring be_const_str_##_name = { \
|
||||
|
@ -45,12 +45,25 @@ int be_eqstr(bstring *s1, bstring *s2)
|
|||
return 1;
|
||||
}
|
||||
slen = s1->slen;
|
||||
/* discard different lengths */
|
||||
if (slen != s2->slen) {
|
||||
return 0;
|
||||
}
|
||||
/* long string */
|
||||
if (slen == 255 && slen == s2->slen) {
|
||||
if (slen == 255) { /* s2->slen is also 255 */
|
||||
blstring *ls1 = cast(blstring*, s1);
|
||||
blstring *ls2 = cast(blstring*, s2);
|
||||
return ls1->llen == ls2->llen && !strcmp(lstr(ls1), lstr(ls2));
|
||||
}
|
||||
// TODO one is long const and the other is long string
|
||||
/* const short strings */
|
||||
if (gc_isconst(s1) || gc_isconst(s2)) { /* one of the two string is short const */
|
||||
if (cast(bcstring*, s1)->hash && cast(bcstring*, s2)->hash) {
|
||||
return 0; /* if they both have a hash, then we know they are different */
|
||||
}
|
||||
return !strcmp(str(s1), str(s2));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -249,7 +262,12 @@ void be_gcstrtab(bvm *vm)
|
|||
uint32_t be_strhash(const bstring *s)
|
||||
{
|
||||
if (gc_isconst(s)) {
|
||||
return cast(bcstring*, s)->hash;
|
||||
bcstring* cs = cast(bcstring*, s);
|
||||
if (cs->hash) { /* if hash is null we need to compute it */
|
||||
return cs->hash;
|
||||
} else {
|
||||
return str_hash(cstr(s), str_len(s));
|
||||
}
|
||||
}
|
||||
#if BE_USE_STR_HASH_CACHE
|
||||
if (s->slen != 255) {
|
||||
|
|
|
@ -336,6 +336,18 @@ bstring* be_strindex(bvm *vm, bstring *str, bvalue *idx)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
size_t be_strlcpy(char *dst, const char *src, size_t maxlen)
|
||||
{
|
||||
const size_t srclen = strlen(src);
|
||||
if (srclen + 1 < maxlen) {
|
||||
memcpy(dst, src, srclen + 1);
|
||||
} else if (maxlen != 0) {
|
||||
memcpy(dst, src, maxlen - 1);
|
||||
dst[maxlen-1] = '\0';
|
||||
}
|
||||
return srclen;
|
||||
}
|
||||
|
||||
const char* be_splitpath(const char *path)
|
||||
{
|
||||
const char *p;
|
||||
|
|
|
@ -19,6 +19,7 @@ bstring* be_strcat(bvm *vm, bstring *s1, bstring *s2);
|
|||
int be_strcmp(bstring *s1, bstring *s2);
|
||||
bstring* be_num2str(bvm *vm, bvalue *v);
|
||||
void be_val2str(bvm *vm, int index);
|
||||
size_t be_strlcpy(char *dst, const char *src, size_t size);
|
||||
const char* be_splitpath(const char *path);
|
||||
const char* be_splitname(const char *path);
|
||||
const char* be_pushvfstr(bvm *vm, const char *format, va_list arg);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "be_debug.h"
|
||||
#include "be_libs.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define NOT_METHOD BE_NONE
|
||||
|
||||
|
@ -51,8 +52,14 @@
|
|||
DEBUG_HOOK(); \
|
||||
switch (IGET_OP(ins = *vm->ip++))
|
||||
|
||||
#define opcase(opcode) case OP_##opcode
|
||||
#define dispatch() goto loop
|
||||
#if BE_USE_SINGLE_FLOAT
|
||||
#define mathfunc(func) func##f
|
||||
#else
|
||||
#define mathfunc(func) func
|
||||
#endif
|
||||
|
||||
#define opcase(opcode) case OP_##opcode
|
||||
#define dispatch() goto loop
|
||||
|
||||
#define equal_rule(op, iseq) \
|
||||
bbool res; \
|
||||
|
@ -561,6 +568,8 @@ newframe: /* a new call frame */
|
|||
bvalue *dst = RA(), *a = RKB(), *b = RKC();
|
||||
if (var_isint(a) && var_isint(b)) {
|
||||
var_setint(dst, ibinop(%, a, b));
|
||||
} else if (var_isnumber(a) && var_isnumber(b)) {
|
||||
var_setreal(dst, mathfunc(fmod)(var_toreal(a), var_toreal(b)));
|
||||
} else if (var_isinstance(a)) {
|
||||
ins_binop(vm, "%", ins);
|
||||
} else {
|
||||
|
@ -1055,8 +1064,11 @@ void be_dofunc(bvm *vm, bvalue *v, int argc)
|
|||
}
|
||||
}
|
||||
|
||||
BERRY_API void be_set_obs_hook(bvm *vm, beobshook hook)
|
||||
BERRY_API void be_set_obs_hook(bvm *vm, bobshook hook)
|
||||
{
|
||||
(void)vm; /* avoid comiler warning */
|
||||
(void)hook; /* avoid comiler warning */
|
||||
|
||||
#if BE_USE_OBSERVABILITY_HOOK
|
||||
vm->obshook = hook;
|
||||
#endif
|
||||
|
|
|
@ -87,7 +87,7 @@ struct bvm {
|
|||
blist *registry; /* registry list */
|
||||
struct bgc gc;
|
||||
#if BE_USE_OBSERVABILITY_HOOK
|
||||
beobshook obshook;
|
||||
bobshook obshook;
|
||||
#endif
|
||||
#if BE_USE_DEBUG_HOOK
|
||||
bvalue hook;
|
||||
|
|
|
@ -101,7 +101,7 @@ enum berrorcode {
|
|||
#elif defined(__GNUC__) /* in GCC */
|
||||
#define BERRY_LOCAL __attribute__ ((visibility ("hidden")))
|
||||
#else /* other platforms */
|
||||
#define BERRY_LOCAL
|
||||
#define BERRY_LOCAL static
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -249,6 +249,22 @@ typedef struct bntvmodule {
|
|||
}
|
||||
#endif
|
||||
|
||||
/* support for solidified berry functions */
|
||||
/* native const strings outside of global string hash */
|
||||
#define be_define_local_const_str(_name, _s, _hash, _extra, _len, _next) \
|
||||
BERRY_LOCAL const bcstring be_local_const_str_##_name = { \
|
||||
.next = (bgcobject *)NULL, \
|
||||
.type = BE_STRING, \
|
||||
.marked = GC_CONST, \
|
||||
.extra = 0, \
|
||||
.slen = _len, \
|
||||
.hash = 0, \
|
||||
.s = _s \
|
||||
}
|
||||
|
||||
#define be_local_const_str(_name) (bstring*) &be_local_const_str_##_name
|
||||
|
||||
|
||||
/* debug hook typedefs */
|
||||
#define BE_HOOK_LINE 1
|
||||
#define BE_HOOK_CALL 2
|
||||
|
@ -275,7 +291,7 @@ typedef void(*bntvhook)(bvm *vm, bhookinfo *info);
|
|||
|
||||
/* Observability hook */
|
||||
|
||||
typedef void(*beobshook)(bvm *vm, int event, ...);
|
||||
typedef void(*bobshook)(bvm *vm, int event, ...);
|
||||
enum beobshookevents {
|
||||
BE_OBS_GC_START, // start of GC, arg = allocated size
|
||||
BE_OBS_GC_END, // end of GC, arg = allocated size
|
||||
|
@ -346,6 +362,7 @@ BERRY_API void be_pushnstring(bvm *vm, const char *str, size_t n);
|
|||
BERRY_API const char* be_pushfstring(bvm *vm, const char *format, ...);
|
||||
BERRY_API void* be_pushbuffer(bvm *vm, size_t size);
|
||||
BERRY_API void be_pushvalue(bvm *vm, int index);
|
||||
BERRY_API void be_pushclosure(bvm *vm, void *cl);
|
||||
BERRY_API void be_pushntvclosure(bvm *vm, bntvfunc f, int nupvals);
|
||||
BERRY_API void be_pushntvfunction(bvm *vm, bntvfunc f);
|
||||
BERRY_API void be_pushclass(bvm *vm, const char *name, const bnfuncinfo *lib);
|
||||
|
@ -416,7 +433,7 @@ BERRY_API bvm* be_vm_new(void);
|
|||
BERRY_API void be_vm_delete(bvm *vm);
|
||||
|
||||
/* Observability hook */
|
||||
BERRY_API void be_set_obs_hook(bvm *vm, beobshook hook);
|
||||
BERRY_API void be_set_obs_hook(bvm *vm, bobshook hook);
|
||||
|
||||
/* code load APIs */
|
||||
BERRY_API int be_loadbuffer(bvm *vm,
|
||||
|
@ -431,7 +448,7 @@ BERRY_API void be_module_path_set(bvm *vm, const char *path);
|
|||
|
||||
/* bytes operations */
|
||||
BERRY_API void be_pushbytes(bvm *vm, const void *buf, size_t len);
|
||||
BERRY_API const void *be_tobytes(bvm *vm, int index, size_t *len);
|
||||
BERRY_API const void* be_tobytes(bvm *vm, int index, size_t *len);
|
||||
|
||||
/* registry operation */
|
||||
BERRY_API int be_register(bvm *vm, int index);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/********************************************************************
|
||||
* Tasmota lib
|
||||
*
|
||||
* To use: `d = Driver()`
|
||||
*
|
||||
*******************************************************************/
|
||||
#include "be_object.h"
|
||||
|
||||
// #if !BE_USE_PRECOMPILED_OBJECT
|
||||
#if 1 // TODO we will do pre-compiled later
|
||||
void be_load_driverlib(bvm *vm)
|
||||
{
|
||||
static const bnfuncinfo members[] = {
|
||||
{ "every_second", NULL },
|
||||
{ "every_100ms", NULL },
|
||||
{ "web_add_button", NULL },
|
||||
{ "web_add_main_button", NULL },
|
||||
{ "save_before_restart", NULL },
|
||||
{ "web_sensor", NULL },
|
||||
{ "json_append", NULL },
|
||||
{ "button_pressed", NULL },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
be_regclass(vm, "Driver", members);
|
||||
}
|
||||
#else
|
||||
/* @const_object_info_begin
|
||||
module tasmota (scope: global, depend: 1) {
|
||||
get_free_heap, func(l_getFreeHeap)
|
||||
}
|
||||
@const_object_info_end */
|
||||
#include "../generate/be_fixed_tasmota.h"
|
||||
#endif
|
|
@ -18,6 +18,7 @@ be_extern_native_module(os);
|
|||
be_extern_native_module(sys);
|
||||
be_extern_native_module(debug);
|
||||
be_extern_native_module(gc);
|
||||
be_extern_native_module(solidify);
|
||||
|
||||
/* Tasmota specific */
|
||||
// be_extern_native_module(tasmota_ntv);
|
||||
|
@ -54,6 +55,9 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
|
|||
#endif
|
||||
#if BE_USE_GC_MODULE
|
||||
&be_native_module(gc),
|
||||
#endif
|
||||
#if BE_USE_SOLIDIFY_MODULE
|
||||
&be_native_module(solidify),
|
||||
#endif
|
||||
/* user-defined modules register start */
|
||||
|
||||
|
@ -62,3 +66,21 @@ BERRY_LOCAL const bntvmodule* const be_module_table[] = {
|
|||
/* user-defined modules register end */
|
||||
NULL /* do not remove */
|
||||
};
|
||||
|
||||
extern void be_load_tasmota_ntvlib(bvm *vm);
|
||||
extern void be_load_wirelib(bvm *vm);
|
||||
extern void be_load_driverlib(bvm *vm);
|
||||
|
||||
/* this code loads the native class definitions */
|
||||
BERRY_API void be_load_custom_libs(bvm *vm)
|
||||
{
|
||||
(void)vm; /* prevent a compiler warning */
|
||||
|
||||
/* add here custom libs */
|
||||
#if !BE_USE_PRECOMPILED_OBJECT
|
||||
/* be_load_xxxlib(vm); */
|
||||
#endif
|
||||
be_load_tasmota_ntvlib(vm);
|
||||
be_load_wirelib(vm);
|
||||
be_load_driverlib(vm);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,6 +6,8 @@
|
|||
* 2 wire communication - I2C
|
||||
*******************************************************************/
|
||||
#include "be_object.h"
|
||||
#include "be_string.h"
|
||||
#include "be_gc.h"
|
||||
|
||||
extern int b_wire_init(bvm *vm);
|
||||
|
||||
|
@ -22,6 +24,189 @@ extern int b_wire_validwrite(bvm *vm);
|
|||
extern int b_wire_validread(bvm *vm);
|
||||
extern int b_wire_detect(bvm *vm);
|
||||
|
||||
/********************************************************************
|
||||
"def read_bytes(addr,reg,size) "
|
||||
"self._begin_transmission(addr) "
|
||||
"self._write(reg) "
|
||||
"self._end_transmission(false) "
|
||||
"self._request_from(addr,size) "
|
||||
"var ret=bytes(size) "
|
||||
"while (self._available()) "
|
||||
"ret..self._read() "
|
||||
"end "
|
||||
"return ret "
|
||||
"end "
|
||||
********************************************************************/
|
||||
/********************************************************************
|
||||
** Solidified function: read_bytes
|
||||
********************************************************************/
|
||||
|
||||
be_define_local_const_str(read_bytes_str_name, "read_bytes", -718234123, 0, 10, 0);
|
||||
be_define_local_const_str(read_bytes_str_source, "string", 398550328, 0, 6, 0);
|
||||
be_define_local_const_str(read_bytes_str_0, "_begin_transmission", -1515506120, 0, 19, 0);
|
||||
be_define_local_const_str(read_bytes_str_1, "_write", -2079504471, 0, 6, 0);
|
||||
be_define_local_const_str(read_bytes_str_2, "_end_transmission", -1057486896, 0, 17, 0);
|
||||
be_define_local_const_str(read_bytes_str_3, "_request_from", -329818692, 0, 13, 0);
|
||||
be_define_local_const_str(read_bytes_str_4, "_available", 1306196581, 0, 10, 0);
|
||||
be_define_local_const_str(read_bytes_str_5, "_read", 346717030, 0, 5, 0);
|
||||
|
||||
static const bvalue read_bytes_ktab[6] = {
|
||||
{ { .s=be_local_const_str(read_bytes_str_0) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(read_bytes_str_1) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(read_bytes_str_2) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(read_bytes_str_3) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(read_bytes_str_4) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(read_bytes_str_5) }, BE_STRING},
|
||||
};
|
||||
|
||||
static const uint32_t read_bytes_code[24] = {
|
||||
0x8C100100, // 0000 GETMET R4 R0 R256
|
||||
0x5C180200, // 0001 MOVE R6 R1
|
||||
0x7C100400, // 0002 CALL R4 2
|
||||
0x8C100101, // 0003 GETMET R4 R0 R257
|
||||
0x5C180400, // 0004 MOVE R6 R2
|
||||
0x7C100400, // 0005 CALL R4 2
|
||||
0x8C100102, // 0006 GETMET R4 R0 R258
|
||||
0x50180000, // 0007 LDBOOL R6 0 0
|
||||
0x7C100400, // 0008 CALL R4 2
|
||||
0x8C100103, // 0009 GETMET R4 R0 R259
|
||||
0x5C180200, // 000A MOVE R6 R1
|
||||
0x5C1C0600, // 000B MOVE R7 R3
|
||||
0x7C100600, // 000C CALL R4 3
|
||||
0x60100002, // 000D GETGBL R4 G2
|
||||
0x5C140600, // 000E MOVE R5 R3
|
||||
0x7C100200, // 000F CALL R4 1
|
||||
0x8C140104, // 0010 GETMET R5 R0 R260
|
||||
0x7C140200, // 0011 CALL R5 1
|
||||
0x78160003, // 0012 JMPF R5 #0017
|
||||
0x8C140105, // 0013 GETMET R5 R0 R261
|
||||
0x7C140200, // 0014 CALL R5 1
|
||||
0x40140805, // 0015 CONNECT R5 R4 R5
|
||||
0x7001FFF8, // 0016 JMP #0010
|
||||
0x80040800, // 0017 RET 1 R4
|
||||
};
|
||||
|
||||
static const bproto read_bytes_proto = {
|
||||
NULL, // bgcobject *next
|
||||
8, // type
|
||||
GC_CONST, // marked
|
||||
8, // nstack
|
||||
0, // nupvals
|
||||
4, // argc
|
||||
0, // varg
|
||||
NULL, // bgcobject *gray
|
||||
NULL, // bupvaldesc *upvals
|
||||
(bvalue*) &read_bytes_ktab, // ktab
|
||||
NULL, // bproto **ptab
|
||||
(binstruction*) &read_bytes_code, // code
|
||||
be_local_const_str(read_bytes_str_name), // name
|
||||
24, // codesize
|
||||
6, // nconst
|
||||
0, // nproto
|
||||
be_local_const_str(read_bytes_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 read_bytes_closure = {
|
||||
NULL, // bgcobject *next
|
||||
36, // type
|
||||
GC_CONST, // marked
|
||||
0, // nupvals
|
||||
NULL, // bgcobject *gray
|
||||
(bproto*) &read_bytes_proto, // proto
|
||||
{ NULL } // upvals
|
||||
};
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
/********************************************************************
|
||||
"def write_bytes(addr,reg,b) "
|
||||
"self._begin_transmission(addr) "
|
||||
"self._write(reg) "
|
||||
"self._write(b) "
|
||||
"self._end_transmission() "
|
||||
"end "
|
||||
********************************************************************/
|
||||
/********************************************************************
|
||||
** Solidified function: write_bytes
|
||||
********************************************************************/
|
||||
|
||||
be_define_local_const_str(write_bytes_str_name, "write_bytes", 1227543792, 0, 11, 0);
|
||||
be_define_local_const_str(write_bytes_str_source, "string", 398550328, 0, 6, 0);
|
||||
be_define_local_const_str(write_bytes_str_0, "_begin_transmission", -1515506120, 0, 19, 0);
|
||||
be_define_local_const_str(write_bytes_str_1, "_write", -2079504471, 0, 6, 0);
|
||||
be_define_local_const_str(write_bytes_str_2, "_end_transmission", -1057486896, 0, 17, 0);
|
||||
|
||||
static const bvalue write_bytes_ktab[3] = {
|
||||
{ { .s=be_local_const_str(write_bytes_str_0) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(write_bytes_str_1) }, BE_STRING},
|
||||
{ { .s=be_local_const_str(write_bytes_str_2) }, BE_STRING},
|
||||
};
|
||||
|
||||
static const uint32_t write_bytes_code[12] = {
|
||||
0x8C100100, // 0000 GETMET R4 R0 R256
|
||||
0x5C180200, // 0001 MOVE R6 R1
|
||||
0x7C100400, // 0002 CALL R4 2
|
||||
0x8C100101, // 0003 GETMET R4 R0 R257
|
||||
0x5C180400, // 0004 MOVE R6 R2
|
||||
0x7C100400, // 0005 CALL R4 2
|
||||
0x8C100101, // 0006 GETMET R4 R0 R257
|
||||
0x5C180600, // 0007 MOVE R6 R3
|
||||
0x7C100400, // 0008 CALL R4 2
|
||||
0x8C100102, // 0009 GETMET R4 R0 R258
|
||||
0x7C100200, // 000A CALL R4 1
|
||||
0x80000000, // 000B RET 0 R0
|
||||
};
|
||||
|
||||
static const bproto write_bytes_proto = {
|
||||
NULL, // bgcobject *next
|
||||
8, // type
|
||||
GC_CONST, // marked
|
||||
7, // nstack
|
||||
0, // nupvals
|
||||
4, // argc
|
||||
0, // varg
|
||||
NULL, // bgcobject *gray
|
||||
NULL, // bupvaldesc *upvals
|
||||
(bvalue*) &write_bytes_ktab, // ktab
|
||||
NULL, // bproto **ptab
|
||||
(binstruction*) &write_bytes_code, // code
|
||||
be_local_const_str(write_bytes_str_name), // name
|
||||
12, // codesize
|
||||
3, // nconst
|
||||
0, // nproto
|
||||
be_local_const_str(write_bytes_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 write_bytes_closure = {
|
||||
NULL, // bgcobject *next
|
||||
36, // type
|
||||
GC_CONST, // marked
|
||||
0, // nupvals
|
||||
NULL, // bgcobject *gray
|
||||
(bproto*) &write_bytes_proto, // proto
|
||||
{ NULL } // upvals
|
||||
};
|
||||
|
||||
/*******************************************************************/
|
||||
|
||||
|
||||
// #if !BE_USE_PRECOMPILED_OBJECT
|
||||
#if 1 // TODO we will do pre-compiled later
|
||||
void be_load_wirelib(bvm *vm)
|
||||
|
@ -39,10 +224,14 @@ void be_load_wirelib(bvm *vm)
|
|||
{ "write", b_wire_validwrite },
|
||||
{ "read", b_wire_validread },
|
||||
{ "detect", b_wire_detect },
|
||||
|
||||
{ NULL, (bntvfunc) BE_CLOSURE }, /* mark section for berry closures */
|
||||
{ "read_bytes", (bntvfunc) &read_bytes_closure },
|
||||
{ "write_bytes", (bntvfunc) &write_bytes_closure },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
be_regclass(vm, "Wire_ntv", members);
|
||||
be_regclass(vm, "Wire", members);
|
||||
}
|
||||
#else
|
||||
/* @const_object_info_begin
|
||||
|
|
|
@ -155,6 +155,7 @@
|
|||
#define BE_USE_SYS_MODULE 0
|
||||
#define BE_USE_DEBUG_MODULE 1
|
||||
#define BE_USE_GC_MODULE 1
|
||||
#define BE_USE_SOLIDIFY_MODULE 1
|
||||
|
||||
/* Macro: BE_EXPLICIT_XXX
|
||||
* If these macros are defined, the corresponding function will
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#-
|
||||
- Example of I2C driver written in Berry
|
||||
-
|
||||
- Support for MPU6886 device found in M5Stack
|
||||
- Alternative to xsns_85_mpu6886.ino
|
||||
-#
|
||||
|
||||
class MPU6886 : Driver
|
||||
var enabled, wire
|
||||
var gres, ares
|
||||
var accel, gyro
|
||||
|
||||
def init()
|
||||
self.enabled = false
|
||||
if tasmota.i2c_enabled(58) || 1
|
||||
var bus = 1
|
||||
if wire1.detect(0x68)
|
||||
self.wire = wire1
|
||||
elif wire2.detect(0x68)
|
||||
self.wire = wire2
|
||||
bus = 2
|
||||
end
|
||||
|
||||
if self.wire
|
||||
var v = self.wire.read(0x68,0x75,1)
|
||||
if v != 0x19 return end #- wrong device -#
|
||||
|
||||
self.wire.write(0x68, 0x6B, 0, 1)
|
||||
tasmota.delay(10)
|
||||
self.wire.write(0x68, 0x6B, 1<<7, 1) # MPU6886_PWR_MGMT_1
|
||||
tasmota.delay(10)
|
||||
self.wire.write(0x68, 0x6B, 1<<0, 1) # MPU6886_PWR_MGMT_1
|
||||
tasmota.delay(10)
|
||||
self.wire.write(0x68, 0x1C, 0x10, 1) # MPU6886_ACCEL_CONFIG - AFS_8G
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x1B, 0x18, 1) # MPU6886_GYRO_CONFIG - GFS_2000DPS
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x1A, 0x01, 1) # MPU6886_CONFIG
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x19, 0x05, 1) # MPU6886_SMPLRT_DIV
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x38, 0x00, 1) # MPU6886_INT_ENABLE
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x1D, 0x00, 1) # MPU6886_ACCEL_CONFIG2
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x6A, 0x00, 1) # MPU6886_USER_CTRL
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x23, 0x00, 1) # MPU6886_FIFO_EN
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x37, 0x22, 1) # MPU6886_INT_PIN_CFG
|
||||
tasmota.delay(1)
|
||||
self.wire.write(0x68, 0x38, 0x01, 1) # MPU6886_INT_ENABLE
|
||||
tasmota.delay(100)
|
||||
|
||||
self.enabled = true
|
||||
self.gres = 2000.0/32768.0
|
||||
self.ares = 8.0/32678.0
|
||||
print("I2C: MPU6886 detected on bus "+str(bus))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#- returns a list of 3 axis, float as g acceleration -#
|
||||
def read_accel()
|
||||
var b = self.wire.read_bytes(0x68,0x3B,6)
|
||||
var a1 = b.get(0,-2)
|
||||
if a1 >= 0x8000 a1 -= 0x10000 end
|
||||
var a2 = b.get(2,-2)
|
||||
if a2 >= 0x8000 a2 -= 0x10000 end
|
||||
var a3 = b.get(4,-2)
|
||||
if a3 >= 0x8000 a3 -= 0x10000 end
|
||||
self.accel = [a1 * self.ares, a2 * self.ares, a3 * self.ares]
|
||||
return self.accel
|
||||
end
|
||||
|
||||
#- returns a list of 3 gyroscopes, int as dps (degree per second) -#
|
||||
def read_gyro()
|
||||
var b = self.wire.read_bytes(0x68,0x43,6)
|
||||
var g1 = b.get(0,-2)
|
||||
if g1 >= 0x8000 g1 -= 0x10000 end
|
||||
var g2 = b.get(2,-2)
|
||||
if g2 >= 0x8000 g2 -= 0x10000 end
|
||||
var g3 = b.get(4,-2)
|
||||
if g3 >= 0x8000 g3 -= 0x10000 end
|
||||
self.gyro = [int(g1 * self.gres), int(g2 * self.gres), int(g3 * self.gres)]
|
||||
return self.gyro
|
||||
end
|
||||
|
||||
#- trigger a read every second -#
|
||||
def every_second()
|
||||
self.read_accel()
|
||||
self.read_gyro()
|
||||
end
|
||||
|
||||
#- display sensor value in the web UI -#
|
||||
def web_sensor()
|
||||
import string
|
||||
var msg = string.format(
|
||||
"{s}MPU6886 acc_x{m}%f G{e}"..
|
||||
"{s}MPU6886 acc_y{m}%f G{e}"..
|
||||
"{s}MPU6886 acc_z{m}%f G{e}"..
|
||||
"{s}MPU6886 gyr_x{m}%i dps{e}"..
|
||||
"{s}MPU6886 gyr_y{m}%i dps{e}"..
|
||||
"{s}MPU6886 gyr_z{m}%i dps{e}",
|
||||
self.accel[0], self.accel[1], self.accel[2], self.gyro[0], self.gyro[1], self.gyro[2])
|
||||
tasmota.web_send_decimal(msg)
|
||||
end
|
||||
|
||||
#- add sensor value to teleperiod -#
|
||||
def json_append()
|
||||
import string
|
||||
var ax = int(self.accel[0] * 1000)
|
||||
var ay = int(self.accel[1] * 1000)
|
||||
var az = int(self.accel[2] * 1000)
|
||||
var msg = string.format(",\"MPU6886\":{\"AX\":%i,\"AY\":%i,\"AZ\":%i,\"GX\":%i,\"GY\":%i,\"GZ\":%i}",
|
||||
ax, ay, az, self.gyro[0], self.gyro[1], self.gyro[2])
|
||||
tasmota.response_append(msg)
|
||||
end
|
||||
|
||||
end
|
||||
mpu = MPU6886()
|
||||
tasmota.add_driver(mpu)
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <berry.h>
|
||||
|
||||
#define BERRY_CONSOLE_CMD_DELIMITER "\x01"
|
||||
|
||||
typedef LList_elt<char[0]> log_elt; // store the string after the header to avoid double allocation if we had used char*
|
||||
|
||||
class BerryLog {
|
||||
|
|
|
@ -218,6 +218,30 @@ extern "C" {
|
|||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// Response_append
|
||||
int32_t l_respAppend(bvm *vm);
|
||||
int32_t l_respAppend(bvm *vm) {
|
||||
int32_t top = be_top(vm); // Get the number of arguments
|
||||
if (top == 2 && be_isstring(vm, 2)) {
|
||||
const char *msg = be_tostring(vm, 2);
|
||||
ResponseAppend_P(PSTR("%s"), msg);
|
||||
be_return_nil(vm); // Return nil when something goes wrong
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// web append with decimal conversion
|
||||
int32_t l_webSendDecimal(bvm *vm);
|
||||
int32_t l_webSendDecimal(bvm *vm) {
|
||||
int32_t top = be_top(vm); // Get the number of arguments
|
||||
if (top == 2 && be_isstring(vm, 2)) {
|
||||
const char *msg = be_tostring(vm, 2);
|
||||
WSContentSend_PD(PSTR("%s"), msg);
|
||||
be_return_nil(vm); // Return nil when something goes wrong
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// push the light status object on the vm stack
|
||||
void push_getlight(bvm *vm, uint32_t light_num) {
|
||||
bool data_present = false; // do we have relevant data
|
||||
|
@ -501,17 +525,19 @@ extern "C" {
|
|||
extern "C" {
|
||||
// Berry: `log(msg:string [,log_level:int]) ->nil`
|
||||
// Logs the string at LOG_LEVEL_INFO (loglevel=2)
|
||||
// We allow this function to be called as a method or a direct function
|
||||
// if the first argument is an instance, we remove it
|
||||
int32_t l_logInfo(struct bvm *vm);
|
||||
int32_t l_logInfo(struct bvm *vm) {
|
||||
int32_t top = be_top(vm); // Get the number of arguments
|
||||
if (top >= 1 && be_isstring(vm, 1)) { // only 1 argument of type string accepted
|
||||
const char * msg = be_tostring(vm, 1);
|
||||
if (top >= 2 && be_isstring(vm, 2)) { // only 1 argument of type string accepted
|
||||
const char * msg = be_tostring(vm, 2);
|
||||
uint32_t log_level = LOG_LEVEL_INFO;
|
||||
if (top >= 2 && be_isint(vm, 2)) {
|
||||
log_level = be_toint(vm, 2);
|
||||
if (top >= 3 && be_isint(vm, 3)) {
|
||||
log_level = be_toint(vm, 3);
|
||||
if (log_level > LOG_LEVEL_DEBUG_MORE) { log_level = LOG_LEVEL_DEBUG_MORE; }
|
||||
}
|
||||
AddLog(log_level, PSTR("%s"), msg);
|
||||
AddLog_P(log_level, PSTR("%s"), msg);
|
||||
be_return(vm); // Return
|
||||
}
|
||||
be_return_nil(vm); // Return nil when something goes wrong
|
||||
|
@ -529,8 +555,8 @@ extern "C" {
|
|||
int32_t l_save(struct bvm *vm);
|
||||
int32_t l_save(struct bvm *vm) {
|
||||
int32_t top = be_top(vm); // Get the number of arguments
|
||||
if (top == 2 && be_isstring(vm, 1) && be_isclosure(vm, 2)) { // only 1 argument of type string accepted
|
||||
const char *fname = be_tostring(vm, 1);
|
||||
if (top == 3 && be_isstring(vm, 2) && be_isclosure(vm, 3)) { // only 1 argument of type string accepted
|
||||
const char *fname = be_tostring(vm, 2);
|
||||
int32_t ret = be_savecode(vm, fname);
|
||||
be_pushint(vm, ret);
|
||||
be_return(vm); // Return
|
||||
|
@ -542,14 +568,19 @@ extern "C" {
|
|||
// called as a replacement to Berry `print()`
|
||||
void berry_log(const char * berry_buf);
|
||||
void berry_log(const char * berry_buf) {
|
||||
if (berry.repl_active) {
|
||||
const char * pre_delimiter = nullptr; // do we need to prepend a delimiter if no REPL command
|
||||
if (!berry.repl_active) {
|
||||
// if no REPL in flight, we limit the number of logs
|
||||
if (berry.log.log.length() == 0) {
|
||||
pre_delimiter = BERRY_CONSOLE_CMD_DELIMITER;
|
||||
}
|
||||
if (berry.log.log.length() >= BERRY_MAX_LOGS) {
|
||||
berry.log.log.remove(berry.log.log.head());
|
||||
}
|
||||
}
|
||||
// AddLog(LOG_LEVEL_INFO, PSTR("[Add to log] %s"), berry_buf);
|
||||
berry.log.addString(berry_buf, nullptr, "\n");
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf);
|
||||
berry.log.addString(berry_buf, pre_delimiter, "\n");
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("%s"), berry_buf);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,13 +27,8 @@
|
|||
|
||||
const char berry_prog[] =
|
||||
""
|
||||
//"def func(x) for i:1..x print('a') end end "
|
||||
//"def testreal() return str(1.2+1) end "
|
||||
//"def noop() log('noop before'); yield(); log('middle after'); yield(); log('noop after'); end "
|
||||
//"log(\"foobar\") "
|
||||
|
||||
// create a 'ntv' module to allow functions to be registered in a safe namespace
|
||||
"ntv = module('ntv') "
|
||||
// "ntv = module('ntv') "
|
||||
|
||||
// auto-import modules
|
||||
// // import alias
|
||||
|
@ -42,140 +37,141 @@ const char berry_prog[] =
|
|||
// Phase 1
|
||||
"class Tasmota: Tasmota_ntv "
|
||||
// for now the variables are built, need to find a way to push in Flash
|
||||
"def init() "
|
||||
"self._op = ['==', '!==', '=', '!=', '>=', '<=', '>', '<'] "
|
||||
"self._opf = [ "
|
||||
"/s1,s2-> str(s1) == str(s2),"
|
||||
"/s1,s2-> str(s1) != str(s2),"
|
||||
"/f1,f2-> real(f1) == real(f2),"
|
||||
"/f1,f2-> real(f1) != real(f2),"
|
||||
"/f1,f2-> real(f1) >= real(f2),"
|
||||
"/f1,f2-> real(f1) <= real(f2),"
|
||||
"/f1,f2-> real(f1) > real(f2),"
|
||||
"/f1,f2-> real(f1) < real(f2),"
|
||||
"] "
|
||||
"self._operators = \"=<>!|\" "
|
||||
"end "
|
||||
// add `chars_in_string(s:string,c:string) -> int``
|
||||
// looks for any char in c, and return the position of the first chat
|
||||
// or -1 if not found
|
||||
"def chars_in_string(s,c) "
|
||||
"for i:0..size(s)-1 "
|
||||
"for j:0..size(c)-1 "
|
||||
"if s[i] == c[j] return i end "
|
||||
"end "
|
||||
"end "
|
||||
"return -1 "
|
||||
"end "
|
||||
// "def init() "
|
||||
// "end "
|
||||
// // add `chars_in_string(s:string,c:string) -> int``
|
||||
// // looks for any char in c, and return the position of the first char
|
||||
// // or -1 if not found
|
||||
// // inv is optional and inverses the behavior, i.e. look for chars not in the list
|
||||
// "def chars_in_string(s,c,inv) "
|
||||
// "var inverted = inv ? true : false "
|
||||
// "for i:0..size(s)-1 "
|
||||
// "var found = false "
|
||||
// "for j:0..size(c)-1 "
|
||||
// "if s[i] == c[j] found = true end "
|
||||
// "end "
|
||||
// "if inverted != found return i end "
|
||||
// "end "
|
||||
// "return -1 "
|
||||
// "end "
|
||||
|
||||
// find a key in map, case insensitive, return actual key or nil if not found
|
||||
"def find_key_i(m,keyi) "
|
||||
"import string "
|
||||
"var keyu = string.toupper(keyi) "
|
||||
"if classof(m) == map "
|
||||
"for k:m.keys() "
|
||||
"if string.toupper(k)==keyu || keyi=='?' "
|
||||
"return k "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
// // find a key in map, case insensitive, return actual key or nil if not found
|
||||
// "def find_key_i(m,keyi) "
|
||||
// "import string "
|
||||
// "var keyu = string.toupper(keyi) "
|
||||
// "if classof(m) == map "
|
||||
// "for k:m.keys() "
|
||||
// "if string.toupper(k)==keyu || keyi=='?' "
|
||||
// "return k "
|
||||
// "end "
|
||||
// "end "
|
||||
// "end "
|
||||
// "end "
|
||||
|
||||
// # split the item when there is an operator, returns a list of (left,op,right)
|
||||
// # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"]
|
||||
"def find_op(item) "
|
||||
"import string "
|
||||
"var pos = self.chars_in_string(item, self._operators) "
|
||||
"if pos>=0 "
|
||||
"var op_split = string.split(item,pos) "
|
||||
// #print(op_split)
|
||||
"var op_left = op_split[0] "
|
||||
"var op_rest = op_split[1] "
|
||||
// # iterate through operators
|
||||
"for i: 0..size(self._op)-1 "
|
||||
"var op = self._op[i] "
|
||||
"if string.find(op_rest,op) == 0 "
|
||||
"var op_func = self._opf[i] "
|
||||
"var op_right = string.split(op_rest,size(op))[1] "
|
||||
"return [op_left,op_func,op_right] "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
"return [item, nil, nil] "
|
||||
"end "
|
||||
// // # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"]
|
||||
// "def find_op(item) "
|
||||
// "import string "
|
||||
// "var op_chars = '=<>!' "
|
||||
// "var pos = self.chars_in_string(item, op_chars) "
|
||||
// "if pos >= 0 "
|
||||
// "var op_split = string.split(item,pos) "
|
||||
// "var op_left = op_split[0] "
|
||||
// "var op_rest = op_split[1] "
|
||||
// "pos = self.chars_in_string(op_rest, op_chars, true) "
|
||||
// "if pos >= 0 "
|
||||
// "var op_split2 = string.split(op_rest,pos) "
|
||||
// "var op_middle = op_split2[0] "
|
||||
// "var op_right = op_split2[1] "
|
||||
// "return [op_left,op_middle,op_right] "
|
||||
// "end "
|
||||
// "end "
|
||||
// "return [item, nil, nil] "
|
||||
// "end "
|
||||
|
||||
// Rules
|
||||
"def add_rule(pat,f) "
|
||||
"if !self._rules "
|
||||
"self._rules={} "
|
||||
"end "
|
||||
"self._rules[pat] = f "
|
||||
"end "
|
||||
// // Rules
|
||||
// "def add_rule(pat,f) "
|
||||
// "if !self._rules "
|
||||
// "self._rules={} "
|
||||
// "end "
|
||||
// "self._rules[pat] = f "
|
||||
// "end "
|
||||
|
||||
// Rules trigger if match. return true if match, false if not
|
||||
"def try_rule(ev, rule, f) "
|
||||
"import string "
|
||||
"var rl_list = self.find_op(rule) "
|
||||
"var e=ev "
|
||||
"var rl=string.split(rl_list[0],'#') "
|
||||
"for it:rl "
|
||||
"found=self.find_key_i(e,it) "
|
||||
"if found == nil "
|
||||
"return false "
|
||||
"end "
|
||||
"e=e[found] "
|
||||
"end "
|
||||
// # check if condition is true
|
||||
"if rl_list[1] "
|
||||
// # did we find a function
|
||||
"if !rl_list[1](e,rl_list[2]) "
|
||||
// # condition is not met
|
||||
"return false "
|
||||
"end "
|
||||
"end "
|
||||
"f(e,ev) "
|
||||
"return true "
|
||||
"end "
|
||||
// // Rules trigger if match. return true if match, false if not
|
||||
// "def try_rule(ev, rule, f) "
|
||||
// "import string "
|
||||
// "var rl_list = self.find_op(rule) "
|
||||
// "var e=ev "
|
||||
// "var rl=string.split(rl_list[0],'#') "
|
||||
// "for it:rl "
|
||||
// "found=self.find_key_i(e,it) "
|
||||
// "if found == nil return false end "
|
||||
// "e=e[found] "
|
||||
// "end "
|
||||
// "var op=rl_list[1]"
|
||||
// "var op2=rl_list[2]"
|
||||
// "if op "
|
||||
// "if op=='==' "
|
||||
// "if str(e) != str(op2) return false end "
|
||||
// "elif op=='!==' "
|
||||
// "if str(e) == str(op2) return false end "
|
||||
// "elif op=='=' "
|
||||
// "if real(e) != real(op2) return false end "
|
||||
// "elif op=='!=' "
|
||||
// "if real(e) == real(op2) return false end "
|
||||
// "elif op=='>' "
|
||||
// "if real(e) <= real(op2) return false end "
|
||||
// "elif op=='>=' "
|
||||
// "if real(e) < real(op2) return false end "
|
||||
// "elif op=='<' "
|
||||
// "if real(e) >= real(op2) return false end "
|
||||
// "elif op=='<=' "
|
||||
// "if real(e) > real(op2) return false end "
|
||||
// "end "
|
||||
// "end "
|
||||
// "f(e,ev) "
|
||||
// "return true "
|
||||
// "end "
|
||||
|
||||
// Run rules, i.e. check each individual rule
|
||||
// Returns true if at least one rule matched, false if none
|
||||
"def exec_rules(ev_json) "
|
||||
"if self._rules "
|
||||
"import json "
|
||||
"var ev = json.load(ev_json) "
|
||||
"var ret = false "
|
||||
"if ev == nil "
|
||||
"print('BRY: ERROR, bad json: '+ev_json, 3) "
|
||||
"else "
|
||||
"for r: self._rules.keys() "
|
||||
"ret = self.try_rule(ev,r,self._rules[r]) || ret "
|
||||
"end "
|
||||
"end "
|
||||
"return ret "
|
||||
"end "
|
||||
"return false "
|
||||
"end "
|
||||
// // Run rules, i.e. check each individual rule
|
||||
// // Returns true if at least one rule matched, false if none
|
||||
// "def exec_rules(ev_json) "
|
||||
// "if self._rules "
|
||||
// "import json "
|
||||
// "var ev = json.load(ev_json) "
|
||||
// "var ret = false "
|
||||
// "if ev == nil "
|
||||
// "print('BRY: ERROR, bad json: '+ev_json, 3) "
|
||||
// "else "
|
||||
// "for r: self._rules.keys() "
|
||||
// "ret = self.try_rule(ev,r,self._rules[r]) || ret "
|
||||
// "end "
|
||||
// "end "
|
||||
// "return ret "
|
||||
// "end "
|
||||
// "return false "
|
||||
// "end "
|
||||
|
||||
"def set_timer(delay,f) "
|
||||
"if !self._timers self._timers=[] end "
|
||||
"self._timers.push([self.millis(delay),f]) "
|
||||
"end "
|
||||
// "def set_timer(delay,f) "
|
||||
// "if !self._timers self._timers=[] end "
|
||||
// "self._timers.push([self.millis(delay),f]) "
|
||||
// "end "
|
||||
|
||||
// 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 "
|
||||
// // 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 "
|
||||
|
||||
// // Delay function, internally calls yield() every 10ms to avoid WDT
|
||||
// "def delay(ms) "
|
||||
|
@ -185,147 +181,143 @@ const char berry_prog[] =
|
|||
// "end "
|
||||
// "end "
|
||||
|
||||
// Add command to list
|
||||
"def add_cmd(c,f) "
|
||||
"if !self._cmd "
|
||||
"self._cmd={} "
|
||||
"end "
|
||||
"self._cmd[c]=f "
|
||||
"end "
|
||||
// // Add command to list
|
||||
// "def add_cmd(c,f) "
|
||||
// "if !self._cmd "
|
||||
// "self._cmd={} "
|
||||
// "end "
|
||||
// "self._cmd[c]=f "
|
||||
// "end "
|
||||
|
||||
"def exec_cmd(cmd, idx, payload) "
|
||||
"if self._cmd "
|
||||
"import json "
|
||||
"var payload_json = json.load(payload) "
|
||||
"var cmd_found = self.find_key_i(self._cmd, cmd) "
|
||||
"if cmd_found != nil "
|
||||
"self.resolvecmnd(cmd_found) " // set the command name in XdrvMailbox.command
|
||||
"self._cmd[cmd_found](cmd_found, idx, payload, payload_json) "
|
||||
"return true "
|
||||
"end "
|
||||
"end "
|
||||
"return false "
|
||||
"end "
|
||||
// "def exec_cmd(cmd, idx, payload) "
|
||||
// "if self._cmd "
|
||||
// "import json "
|
||||
// "var payload_json = json.load(payload) "
|
||||
// "var cmd_found = self.find_key_i(self._cmd, cmd) "
|
||||
// "if cmd_found != nil "
|
||||
// "self.resolvecmnd(cmd_found) " // set the command name in XdrvMailbox.command
|
||||
// "self._cmd[cmd_found](cmd_found, idx, payload, payload_json) "
|
||||
// "return true "
|
||||
// "end "
|
||||
// "end "
|
||||
// "return false "
|
||||
// "end "
|
||||
|
||||
// Force gc and return allocated memory
|
||||
"def gc() "
|
||||
"import gc "
|
||||
"gc.collect() "
|
||||
"return gc.allocated() "
|
||||
"end "
|
||||
// // Force gc and return allocated memory
|
||||
// "def gc() "
|
||||
// "import gc "
|
||||
// "gc.collect() "
|
||||
// "return gc.allocated() "
|
||||
// // "end "
|
||||
// // simple wrapper to load a file
|
||||
// // prefixes '/' if needed, and simpler to use than `compile()`
|
||||
// "def load(f) "
|
||||
// "import string "
|
||||
// "try "
|
||||
// // check that the file ends with '.be' of '.bec'
|
||||
// "var fl = string.split(f,'.') "
|
||||
// "if (size(fl) <= 1 || (fl[-1] != 'be' && fl[-1] != 'bec')) "
|
||||
// "raise \"file extension is not '.be' or '.bec'\" "
|
||||
// "end "
|
||||
// "var native = f[size(f)-1] == 'c' "
|
||||
// // add prefix if needed
|
||||
// "if f[0] != '/' f = '/' + f end "
|
||||
// // load - works the same for .be and .bec
|
||||
// "var c = compile(f,'file') "
|
||||
// // save the compiled bytecode
|
||||
// "if !native "
|
||||
// "try "
|
||||
// "self.save(f+'c', c) "
|
||||
// "except .. as e "
|
||||
// "self.log(string.format('BRY: could not save compiled file %s (%s)',f+'c',e)) "
|
||||
// "end "
|
||||
// "end "
|
||||
// // call the compiled code
|
||||
// "c() "
|
||||
// "self.log(string.format(\"BRY: sucessfully loaded '%s'\",f)) "
|
||||
// "except .. as e "
|
||||
// "raise \"io_error\",string.format(\"Could not load file '%s'\",f) "
|
||||
// "end "
|
||||
|
||||
//
|
||||
// Event from Tasmota is:
|
||||
// 1. event:string -- type of event (cmd, rule, ...)
|
||||
// 2. cmd:string -- name of the command to process
|
||||
// 3. index:int -- index number
|
||||
// 4. payload:string -- payload as text, analyzed as json
|
||||
//
|
||||
"def event(type, cmd, idx, payload) "
|
||||
"if type=='cmd' return self.exec_cmd(cmd, idx, payload) "
|
||||
"elif type=='rule' return self.exec_rules(payload) "
|
||||
"elif type=='mqtt_data' return nil " // not yet implemented
|
||||
"elif type=='gc' return self.gc() "
|
||||
"elif type=='every_50ms' return self.run_deferred() "
|
||||
"elif self._drivers "
|
||||
"for d:self._drivers "
|
||||
"try "
|
||||
"if d.dispatch && d.dispatch(type, cmd, idx, payload) nil " // nil for `nop`
|
||||
"elif type=='every_second' && d.every_second return d.every_second() "
|
||||
"elif type=='every_100ms' && d.every_100ms return d.every_100ms() "
|
||||
"end "
|
||||
"except .. as e,m "
|
||||
"import string "
|
||||
"log(string.format('BRY: exception %s - %m',3)) "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
"end "
|
||||
// "end "
|
||||
|
||||
// //
|
||||
// // Event from Tasmota is:
|
||||
// // 1. event:string -- type of event (cmd, rule, ...)
|
||||
// // 2. cmd:string -- name of the command to process
|
||||
// // 3. index:int -- index number
|
||||
// // 4. payload:string -- payload as text, analyzed as json
|
||||
// // //
|
||||
// "def event(type, cmd, idx, payload) "
|
||||
// "if type=='cmd' return self.exec_cmd(cmd, idx, payload) "
|
||||
// "elif type=='rule' return self.exec_rules(payload) "
|
||||
// "elif type=='mqtt_data' return nil " // not yet implemented
|
||||
// "elif type=='gc' return self.gc() "
|
||||
// "elif type=='every_50ms' return self.run_deferred() "
|
||||
// "elif self._drivers "
|
||||
// "for d:self._drivers "
|
||||
// "try "
|
||||
// "if type=='every_second' && d.every_second return d.every_second() "
|
||||
// "elif type=='every_100ms' && d.every_100ms return d.every_100ms() "
|
||||
// "elif type=='web_add_button' && d.web_add_button return d.web_add_button() "
|
||||
// "elif type=='web_add_main_button' && d.web_add_main_button return d.web_add_main_button() "
|
||||
// "elif type=='save_before_restart' && d.save_before_restart return d.save_before_restart() "
|
||||
// "elif type=='web_sensor' && d.web_sensor return d.web_sensor() "
|
||||
// "elif type=='json_append' && d.json_append return d.json_append() "
|
||||
// "elif type=='button_pressed' && d.button_pressed return d.button_pressed() "
|
||||
// "end "
|
||||
// "except .. as e,m "
|
||||
// "import string "
|
||||
// "self.log(string.format('BRY: exception %s - %m',3)) "
|
||||
// "end "
|
||||
// "end "
|
||||
// "end "
|
||||
// "end "
|
||||
//
|
||||
// add driver to the queue of event dispatching
|
||||
//
|
||||
"def add_driver(d) "
|
||||
"if self._drivers "
|
||||
"self._drivers.push(d) "
|
||||
"else "
|
||||
"self._drivers = [d]"
|
||||
"end "
|
||||
"end "
|
||||
// "def add_driver(d) "
|
||||
// "if self._drivers "
|
||||
// "self._drivers.push(d) "
|
||||
// "else "
|
||||
// "self._drivers = [d]"
|
||||
// "end "
|
||||
// "end "
|
||||
|
||||
"end "
|
||||
|
||||
// Instantiate tasmota object
|
||||
"tasmota = Tasmota() "
|
||||
"def log(m,l) tasmota.log(m,l) end "
|
||||
"def load(f) tasmota.load(f) end "
|
||||
|
||||
// Wire class
|
||||
"class Wire : Wire_ntv "
|
||||
// read bytes as `bytes()` object
|
||||
"def read_bytes(addr,reg,size) "
|
||||
"self._begin_transmission(addr) "
|
||||
"self._write(reg) "
|
||||
"self._end_transmission(false) "
|
||||
"self._request_from(addr,size) "
|
||||
"var ret=bytes(size) "
|
||||
"while (self._available()) "
|
||||
"ret..self._read() "
|
||||
"end "
|
||||
"return ret "
|
||||
"end "
|
||||
// write bytes from `bytes` object
|
||||
"def write_bytes(addr,reg,b) "
|
||||
"self._begin_transmission(addr) "
|
||||
"self._write(reg) "
|
||||
"self._write(b) "
|
||||
"self._end_transmission() "
|
||||
"end "
|
||||
"end "
|
||||
// "class Wire : Wire_ntv "
|
||||
// // read bytes as `bytes()` object
|
||||
// "def read_bytes(addr,reg,size) "
|
||||
// "self._begin_transmission(addr) "
|
||||
// "self._write(reg) "
|
||||
// "self._end_transmission(false) "
|
||||
// "self._request_from(addr,size) "
|
||||
// "var ret=bytes(size) "
|
||||
// "while (self._available()) "
|
||||
// "ret..self._read() "
|
||||
// "end "
|
||||
// "return ret "
|
||||
// "end "
|
||||
// // write bytes from `bytes` object
|
||||
// "def write_bytes(addr,reg,b) "
|
||||
// "self._begin_transmission(addr) "
|
||||
// "self._write(reg) "
|
||||
// "self._write(b) "
|
||||
// "self._end_transmission() "
|
||||
// "end "
|
||||
// "end "
|
||||
|
||||
"wire = Wire(0) "
|
||||
"wire1 = wire "
|
||||
"wire2 = Wire(1) "
|
||||
|
||||
//
|
||||
// Base class for Tasmota Driver
|
||||
//
|
||||
"class Driver "
|
||||
// functions are needs to be set to trigger an event
|
||||
"var dispatch " // general dispatcher, returns true if serviced
|
||||
"var every_second " // called every_second
|
||||
"var every_100ms " // called every 100ms
|
||||
// ...
|
||||
"end "
|
||||
|
||||
// simple wrapper to load a file
|
||||
// prefixes '/' if needed, and simpler to use than `compile()`
|
||||
"def load(f) "
|
||||
"import string "
|
||||
"try "
|
||||
// check that the file ends with '.be' of '.bec'
|
||||
"var fl = string.split(f,'.') "
|
||||
"if (size(fl) <= 1 || (fl[-1] != 'be' && fl[-1] != 'bec')) "
|
||||
"raise \"file extension is not '.be' or '.bec'\" "
|
||||
"end "
|
||||
"var native = f[size(f)-1] == 'c' "
|
||||
// add prefix if needed
|
||||
"if f[0] != '/' f = '/' + f end "
|
||||
// load - works the same for .be and .bec
|
||||
"var c = compile(f,'file') "
|
||||
// save the compiled bytecode
|
||||
"if !native "
|
||||
"try "
|
||||
"save(f+'c', c) "
|
||||
"except .. as e "
|
||||
"log(string.format('BRY: could not save compiled file %s (%s)',f+'c',e)) "
|
||||
"end "
|
||||
"end "
|
||||
// call the compiled code
|
||||
"c() "
|
||||
"log(string.format(\"BRY: sucessfully loaded '%s'\",f)) "
|
||||
"except .. as e "
|
||||
"raise \"io_error\",string.format(\"Could not load file '%s'\",f) "
|
||||
"end "
|
||||
"end "
|
||||
|
||||
// try to load "/autoexec.be"
|
||||
// "try compile('/autoexec.be','file')() except .. log('BRY: no /autoexec.bat file') end "
|
||||
;
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
|
||||
#include <berry.h>
|
||||
|
||||
#define BERRY_CONSOLE_CMD_DELIMITER "\x01"
|
||||
extern "C" {
|
||||
extern void be_load_custom_libs(bvm *vm);
|
||||
}
|
||||
|
||||
const char kBrCommands[] PROGMEM = D_PRFX_BR "|" // prefix
|
||||
D_CMND_BR_RUN "|" D_CMND_BR_RESET
|
||||
|
@ -179,8 +181,9 @@ int32_t callBerryEventDispatcher(const char *type, const char *cmd, int32_t idx,
|
|||
be_pushstring(berry.vm, payload != nullptr ? payload : "{}"); // empty json
|
||||
be_pcall(berry.vm, 5); // 5 arguments
|
||||
be_pop(berry.vm, 5);
|
||||
if (be_isint(berry.vm, -1)) {
|
||||
ret = be_toint(berry.vm, -1);
|
||||
if (be_isint(berry.vm, -1) || be_isbool(berry.vm, -1)) {
|
||||
if (be_isint(berry.vm, -1)) { ret = be_toint(berry.vm, -1); }
|
||||
if (be_isbool(berry.vm, -1)) { ret = be_tobool(berry.vm, -1); }
|
||||
}
|
||||
}
|
||||
be_pop(berry.vm, 1); // remove method
|
||||
|
@ -238,11 +241,12 @@ void BrReset(void) {
|
|||
do {
|
||||
berry.vm = be_vm_new(); /* create a virtual machine instance */
|
||||
be_set_obs_hook(berry.vm, &BerryObservability);
|
||||
be_load_custom_libs(berry.vm);
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Berry VM created, RAM used=%u"), be_gc_memcount(berry.vm));
|
||||
|
||||
// Register functions
|
||||
be_regfunc(berry.vm, PSTR("log"), l_logInfo);
|
||||
be_regfunc(berry.vm, PSTR("save"), l_save);
|
||||
// be_regfunc(berry.vm, PSTR("log"), l_logInfo);
|
||||
// be_regfunc(berry.vm, PSTR("save"), l_save);
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Berry function registered, RAM used=%u"), be_gc_memcount(berry.vm));
|
||||
|
||||
|
@ -727,7 +731,7 @@ bool Xdrv52(uint8_t function)
|
|||
callBerryEventDispatcher(PSTR("web_add_main_button"), nullptr, 0, nullptr);
|
||||
break;
|
||||
case FUNC_WEB_ADD_HANDLER:
|
||||
callBerryEventDispatcher(PSTR("web_add_handler"), nullptr, 0, nullptr);
|
||||
// callBerryEventDispatcher(PSTR("web_add_handler"), nullptr, 0, nullptr);
|
||||
WebServer_on(PSTR("/bs"), HandleBerryConsole);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
|
@ -739,7 +743,7 @@ bool Xdrv52(uint8_t function)
|
|||
break;
|
||||
|
||||
case FUNC_JSON_APPEND:
|
||||
callBerryEventDispatcher(PSTR("json_aooend"), nullptr, 0, nullptr);
|
||||
callBerryEventDispatcher(PSTR("json_append"), nullptr, 0, nullptr);
|
||||
break;
|
||||
|
||||
case FUNC_BUTTON_PRESSED:
|
||||
|
|
Loading…
Reference in New Issue