Merge pull request #11491 from s-hadinger/berry_mar_27

Berry stabilized and methods solidified
This commit is contained in:
s-hadinger 2021-03-27 19:17:55 +01:00 committed by GitHub
commit 62edcccb1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2959 additions and 318 deletions

View File

@ -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);

View File

@ -0,0 +1,2 @@
#include "be_constobj.h"

View File

@ -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,

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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 */

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -21,6 +21,7 @@
#include "be_debug.h"
#include "be_libs.h"
#include <string.h>
#include <math.h>
#define NOT_METHOD BE_NONE
@ -51,6 +52,12 @@
DEBUG_HOOK(); \
switch (IGET_OP(ins = *vm->ip++))
#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
@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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)
@ -40,9 +225,13 @@ void be_load_wirelib(bvm *vm)
{ "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

View File

@ -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

122
tasmota/berry/mpu6886.be Normal file
View File

@ -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)

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 "
;

View File

@ -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: