Merge pull request #10870 from s-hadinger/berry_early_1

Preview of Berry language for Tasmota32
This commit is contained in:
Theo Arends 2021-02-08 10:31:06 +01:00 committed by GitHub
commit d178b0e021
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 16583 additions and 0 deletions

View File

@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- ESP32 Increase number of switch GPIOs from 8 to 28
- ESP32 Increase number of interlock groups from 4 to 14
- Increase number of button GPIOs from 4 to 8
- Preview of Berry language for Tasmota32
## [9.2.0.5] 20210205
### Changed

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2020 Guan Wenliang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,125 @@
extern const bcstring be_const_str_count;
extern const bcstring be_const_str_rad;
extern const bcstring be_const_str_str;
extern const bcstring be_const_str_import;
extern const bcstring be_const_str_opt_connect;
extern const bcstring be_const_str_open;
extern const bcstring be_const_str_codedump;
extern const bcstring be_const_str_imax;
extern const bcstring be_const_str_item;
extern const bcstring be_const_str_splitext;
extern const bcstring be_const_str___upper__;
extern const bcstring be_const_str_byte;
extern const bcstring be_const_str_isfile;
extern const bcstring be_const_str_issubclass;
extern const bcstring be_const_str_iter;
extern const bcstring be_const_str_classname;
extern const bcstring be_const_str_cos;
extern const bcstring be_const_str_number;
extern const bcstring be_const_str_true;
extern const bcstring be_const_str_hex;
extern const bcstring be_const_str_range;
extern const bcstring be_const_str_cosh;
extern const bcstring be_const_str_exp;
extern const bcstring be_const_str_exit;
extern const bcstring be_const_str_list;
extern const bcstring be_const_str_path;
extern const bcstring be_const_str_dot_p;
extern const bcstring be_const_str___iterator__;
extern const bcstring be_const_str_dump;
extern const bcstring be_const_str_deinit;
extern const bcstring be_const_str_size;
extern const bcstring be_const_str_false;
extern const bcstring be_const_str_clock;
extern const bcstring be_const_str_floor;
extern const bcstring be_const_str_module;
extern const bcstring be_const_str_tostring;
extern const bcstring be_const_str_type;
extern const bcstring be_const_str_upvname;
extern const bcstring be_const_str_opt_add;
extern const bcstring be_const_str___lower__;
extern const bcstring be_const_str_pi;
extern const bcstring be_const_str_assert;
extern const bcstring be_const_str_ceil;
extern const bcstring be_const_str_setitem;
extern const bcstring be_const_str_except;
extern const bcstring be_const_str_pow;
extern const bcstring be_const_str_sin;
extern const bcstring be_const_str_traceback;
extern const bcstring be_const_str_calldepth;
extern const bcstring be_const_str_classof;
extern const bcstring be_const_str_init;
extern const bcstring be_const_str_sinh;
extern const bcstring be_const_str_split;
extern const bcstring be_const_str_raise;
extern const bcstring be_const_str_map;
extern const bcstring be_const_str_srand;
extern const bcstring be_const_str_allocated;
extern const bcstring be_const_str_asin;
extern const bcstring be_const_str_int;
extern const bcstring be_const_str_lower;
extern const bcstring be_const_str_var;
extern const bcstring be_const_str_abs;
extern const bcstring be_const_str_deg;
extern const bcstring be_const_str_setrange;
extern const bcstring be_const_str_time;
extern const bcstring be_const_str_real;
extern const bcstring be_const_str_end;
extern const bcstring be_const_str_isdir;
extern const bcstring be_const_str_;
extern const bcstring be_const_str_getcwd;
extern const bcstring be_const_str_attrdump;
extern const bcstring be_const_str_copy;
extern const bcstring be_const_str_imin;
extern const bcstring be_const_str_system;
extern const bcstring be_const_str_top;
extern const bcstring be_const_str_nil;
extern const bcstring be_const_str_push;
extern const bcstring be_const_str_remove;
extern const bcstring be_const_str_compile;
extern const bcstring be_const_str_atan;
extern const bcstring be_const_str_class;
extern const bcstring be_const_str_insert;
extern const bcstring be_const_str_pop;
extern const bcstring be_const_str_tanh;
extern const bcstring be_const_str_chdir;
extern const bcstring be_const_str_load;
extern const bcstring be_const_str_log;
extern const bcstring be_const_str_varname;
extern const bcstring be_const_str_find;
extern const bcstring be_const_str_isinstance;
extern const bcstring be_const_str_if;
extern const bcstring be_const_str_super;
extern const bcstring be_const_str_break;
extern const bcstring be_const_str_do;
extern const bcstring be_const_str_else;
extern const bcstring be_const_str_for;
extern const bcstring be_const_str_concat;
extern const bcstring be_const_str_resize;
extern const bcstring be_const_str_reverse;
extern const bcstring be_const_str_while;
extern const bcstring be_const_str_input;
extern const bcstring be_const_str_sqrt;
extern const bcstring be_const_str_elif;
extern const bcstring be_const_str_log10;
extern const bcstring be_const_str_print;
extern const bcstring be_const_str_continue;
extern const bcstring be_const_str_collect;
extern const bcstring be_const_str_clear;
extern const bcstring be_const_str_rand;
extern const bcstring be_const_str_opt_neq;
extern const bcstring be_const_str_opt_eq;
extern const bcstring be_const_str_acos;
extern const bcstring be_const_str_listdir;
extern const bcstring be_const_str_mkdir;
extern const bcstring be_const_str_sethook;
extern const bcstring be_const_str_tan;
extern const bcstring be_const_str_def;
extern const bcstring be_const_str_char;
extern const bcstring be_const_str_exists;
extern const bcstring be_const_str_join;
extern const bcstring be_const_str_as;
extern const bcstring be_const_str_format;
extern const bcstring be_const_str_try;
extern const bcstring be_const_str_upper;
extern const bcstring be_const_str_return;

View File

@ -0,0 +1,186 @@
be_define_const_str(count, "count", 967958004u, 0, 5, &be_const_str_rad);
be_define_const_str(rad, "rad", 1358899048u, 0, 3, &be_const_str_str);
be_define_const_str(str, "str", 3259748752u, 0, 3, &be_const_str_import);
be_define_const_str(import, "import", 288002260u, 66, 6, NULL);
be_define_const_str(opt_connect, "..", 2748622605u, 0, 2, &be_const_str_open);
be_define_const_str(open, "open", 3546203337u, 0, 4, NULL);
be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_imax);
be_define_const_str(imax, "imax", 3084515410u, 0, 4, &be_const_str_item);
be_define_const_str(item, "item", 2671260646u, 0, 4, &be_const_str_splitext);
be_define_const_str(splitext, "splitext", 2150391934u, 0, 8, NULL);
be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, &be_const_str_byte);
be_define_const_str(byte, "byte", 1683620383u, 0, 4, &be_const_str_isfile);
be_define_const_str(isfile, "isfile", 3131505107u, 0, 6, &be_const_str_issubclass);
be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, &be_const_str_iter);
be_define_const_str(iter, "iter", 3124256359u, 0, 4, NULL);
be_define_const_str(classname, "classname", 1998589948u, 0, 9, &be_const_str_cos);
be_define_const_str(cos, "cos", 4220379804u, 0, 3, &be_const_str_number);
be_define_const_str(number, "number", 467038368u, 0, 6, NULL);
be_define_const_str(true, "true", 1303515621u, 61, 4, NULL);
be_define_const_str(hex, "hex", 4273249610u, 0, 3, &be_const_str_range);
be_define_const_str(range, "range", 4208725202u, 0, 5, NULL);
be_define_const_str(cosh, "cosh", 4099687964u, 0, 4, &be_const_str_exp);
be_define_const_str(exp, "exp", 1923516200u, 0, 3, NULL);
be_define_const_str(exit, "exit", 3454868101u, 0, 4, &be_const_str_list);
be_define_const_str(list, "list", 217798785u, 0, 4, NULL);
be_define_const_str(path, "path", 2223459638u, 0, 4, NULL);
be_define_const_str(dot_p, ".p", 1171526419u, 0, 2, &be_const_str___iterator__);
be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_dump);
be_define_const_str(dump, "dump", 3663001223u, 0, 4, NULL);
be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, &be_const_str_size);
be_define_const_str(size, "size", 597743964u, 0, 4, &be_const_str_false);
be_define_const_str(false, "false", 184981848u, 62, 5, NULL);
be_define_const_str(clock, "clock", 363073373u, 0, 5, &be_const_str_floor);
be_define_const_str(floor, "floor", 3102149661u, 0, 5, &be_const_str_module);
be_define_const_str(module, "module", 3617558685u, 0, 6, &be_const_str_tostring);
be_define_const_str(tostring, "tostring", 2299708645u, 0, 8, &be_const_str_type);
be_define_const_str(type, "type", 1361572173u, 0, 4, &be_const_str_upvname);
be_define_const_str(upvname, "upvname", 3848760617u, 0, 7, NULL);
be_define_const_str(opt_add, "+", 772578730u, 0, 1, &be_const_str___lower__);
be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_pi);
be_define_const_str(pi, "pi", 1213090802u, 0, 2, NULL);
be_define_const_str(assert, "assert", 2774883451u, 0, 6, NULL);
be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, &be_const_str_setitem);
be_define_const_str(setitem, "setitem", 1554834596u, 0, 7, &be_const_str_except);
be_define_const_str(except, "except", 950914032u, 69, 6, NULL);
be_define_const_str(pow, "pow", 1479764693u, 0, 3, &be_const_str_sin);
be_define_const_str(sin, "sin", 3761252941u, 0, 3, &be_const_str_traceback);
be_define_const_str(traceback, "traceback", 3385188109u, 0, 9, NULL);
be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, &be_const_str_classof);
be_define_const_str(classof, "classof", 1796577762u, 0, 7, NULL);
be_define_const_str(init, "init", 380752755u, 0, 4, &be_const_str_sinh);
be_define_const_str(sinh, "sinh", 282220607u, 0, 4, &be_const_str_split);
be_define_const_str(split, "split", 2276994531u, 0, 5, &be_const_str_raise);
be_define_const_str(raise, "raise", 1593437475u, 70, 5, NULL);
be_define_const_str(map, "map", 3751997361u, 0, 3, &be_const_str_srand);
be_define_const_str(srand, "srand", 465518633u, 0, 5, NULL);
be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_asin);
be_define_const_str(asin, "asin", 4272848550u, 0, 4, &be_const_str_int);
be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_lower);
be_define_const_str(lower, "lower", 3038577850u, 0, 5, &be_const_str_var);
be_define_const_str(var, "var", 2317739966u, 64, 3, NULL);
be_define_const_str(abs, "abs", 709362235u, 0, 3, &be_const_str_deg);
be_define_const_str(deg, "deg", 3327754271u, 0, 3, NULL);
be_define_const_str(setrange, "setrange", 3794019032u, 0, 8, &be_const_str_time);
be_define_const_str(time, "time", 1564253156u, 0, 4, NULL);
be_define_const_str(real, "real", 3604983901u, 0, 4, NULL);
be_define_const_str(end, "end", 1787721130u, 56, 3, NULL);
be_define_const_str(isdir, "isdir", 2340917412u, 0, 5, NULL);
be_define_const_str(, "", 2166136261u, 0, 0, NULL);
be_define_const_str(getcwd, "getcwd", 652026575u, 0, 6, NULL);
be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, &be_const_str_copy);
be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_imin);
be_define_const_str(imin, "imin", 2714127864u, 0, 4, &be_const_str_system);
be_define_const_str(system, "system", 1226705564u, 0, 6, &be_const_str_top);
be_define_const_str(top, "top", 2802900028u, 0, 3, &be_const_str_nil);
be_define_const_str(nil, "nil", 228849900u, 63, 3, NULL);
be_define_const_str(push, "push", 2272264157u, 0, 4, &be_const_str_remove);
be_define_const_str(remove, "remove", 3683784189u, 0, 6, NULL);
be_define_const_str(compile, "compile", 1000265118u, 0, 7, NULL);
be_define_const_str(atan, "atan", 108579519u, 0, 4, &be_const_str_class);
be_define_const_str(class, "class", 2872970239u, 57, 5, NULL);
be_define_const_str(insert, "insert", 3332609576u, 0, 6, &be_const_str_pop);
be_define_const_str(pop, "pop", 1362321360u, 0, 3, &be_const_str_tanh);
be_define_const_str(tanh, "tanh", 153638352u, 0, 4, NULL);
be_define_const_str(chdir, "chdir", 806634853u, 0, 5, &be_const_str_load);
be_define_const_str(load, "load", 3859241449u, 0, 4, &be_const_str_log);
be_define_const_str(log, "log", 1062293841u, 0, 3, &be_const_str_varname);
be_define_const_str(varname, "varname", 2273276445u, 0, 7, NULL);
be_define_const_str(find, "find", 3186656602u, 0, 4, &be_const_str_isinstance);
be_define_const_str(isinstance, "isinstance", 3669352738u, 0, 10, &be_const_str_if);
be_define_const_str(if, "if", 959999494u, 50, 2, NULL);
be_define_const_str(super, "super", 4152230356u, 0, 5, &be_const_str_break);
be_define_const_str(break, "break", 3378807160u, 58, 5, &be_const_str_do);
be_define_const_str(do, "do", 1646057492u, 65, 2, &be_const_str_else);
be_define_const_str(else, "else", 3183434736u, 52, 4, &be_const_str_for);
be_define_const_str(for, "for", 2901640080u, 54, 3, NULL);
be_define_const_str(concat, "concat", 4124019837u, 0, 6, &be_const_str_resize);
be_define_const_str(resize, "resize", 3514612129u, 0, 6, &be_const_str_reverse);
be_define_const_str(reverse, "reverse", 558918661u, 0, 7, NULL);
be_define_const_str(while, "while", 231090382u, 53, 5, NULL);
be_define_const_str(input, "input", 4191711099u, 0, 5, &be_const_str_sqrt);
be_define_const_str(sqrt, "sqrt", 2112764879u, 0, 4, &be_const_str_elif);
be_define_const_str(elif, "elif", 3232090307u, 51, 4, NULL);
be_define_const_str(log10, "log10", 2346846000u, 0, 5, &be_const_str_print);
be_define_const_str(print, "print", 372738696u, 0, 5, &be_const_str_continue);
be_define_const_str(continue, "continue", 2977070660u, 59, 8, NULL);
be_define_const_str(collect, "collect", 2399039025u, 0, 7, NULL);
be_define_const_str(clear, "clear", 1550717474u, 0, 5, &be_const_str_rand);
be_define_const_str(rand, "rand", 2711325910u, 0, 4, NULL);
be_define_const_str(opt_neq, "!=", 2428715011u, 0, 2, &be_const_str_opt_eq);
be_define_const_str(opt_eq, "==", 2431966415u, 0, 2, &be_const_str_acos);
be_define_const_str(acos, "acos", 1006755615u, 0, 4, NULL);
be_define_const_str(listdir, "listdir", 2005220720u, 0, 7, &be_const_str_mkdir);
be_define_const_str(mkdir, "mkdir", 2883839448u, 0, 5, &be_const_str_sethook);
be_define_const_str(sethook, "sethook", 3963967276u, 0, 7, &be_const_str_tan);
be_define_const_str(tan, "tan", 2633446552u, 0, 3, &be_const_str_def);
be_define_const_str(def, "def", 3310976652u, 55, 3, NULL);
be_define_const_str(char, "char", 2823553821u, 0, 4, &be_const_str_exists);
be_define_const_str(exists, "exists", 1002329533u, 0, 6, &be_const_str_join);
be_define_const_str(join, "join", 3374496889u, 0, 4, &be_const_str_as);
be_define_const_str(as, "as", 1579491469u, 67, 2, NULL);
be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_try);
be_define_const_str(try, "try", 2887626766u, 68, 3, NULL);
be_define_const_str(upper, "upper", 176974407u, 0, 5, &be_const_str_return);
be_define_const_str(return, "return", 2246981567u, 60, 6, NULL);
static const bstring* const m_string_table[] = {
(const bstring *)&be_const_str_count,
(const bstring *)&be_const_str_opt_connect,
(const bstring *)&be_const_str_codedump,
(const bstring *)&be_const_str___upper__,
(const bstring *)&be_const_str_classname,
(const bstring *)&be_const_str_true,
(const bstring *)&be_const_str_hex,
NULL,
(const bstring *)&be_const_str_cosh,
(const bstring *)&be_const_str_exit,
(const bstring *)&be_const_str_path,
(const bstring *)&be_const_str_dot_p,
(const bstring *)&be_const_str_deinit,
(const bstring *)&be_const_str_clock,
(const bstring *)&be_const_str_opt_add,
(const bstring *)&be_const_str_assert,
(const bstring *)&be_const_str_ceil,
(const bstring *)&be_const_str_pow,
(const bstring *)&be_const_str_calldepth,
(const bstring *)&be_const_str_init,
NULL,
(const bstring *)&be_const_str_map,
(const bstring *)&be_const_str_allocated,
(const bstring *)&be_const_str_abs,
(const bstring *)&be_const_str_setrange,
(const bstring *)&be_const_str_real,
(const bstring *)&be_const_str_end,
NULL,
(const bstring *)&be_const_str_isdir,
(const bstring *)&be_const_str_,
NULL,
(const bstring *)&be_const_str_getcwd,
(const bstring *)&be_const_str_attrdump,
(const bstring *)&be_const_str_push,
(const bstring *)&be_const_str_compile,
(const bstring *)&be_const_str_atan,
(const bstring *)&be_const_str_insert,
(const bstring *)&be_const_str_chdir,
(const bstring *)&be_const_str_find,
NULL,
(const bstring *)&be_const_str_super,
(const bstring *)&be_const_str_concat,
(const bstring *)&be_const_str_while,
(const bstring *)&be_const_str_input,
(const bstring *)&be_const_str_log10,
(const bstring *)&be_const_str_collect,
(const bstring *)&be_const_str_clear,
(const bstring *)&be_const_str_opt_neq,
(const bstring *)&be_const_str_listdir,
(const bstring *)&be_const_str_char,
(const bstring *)&be_const_str_format,
(const bstring *)&be_const_str_upper
};
static const struct bconststrtab m_const_string_table = {
.size = 52,
.count = 104,
.table = m_string_table
};

View File

@ -0,0 +1,36 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_list_map) {
{ be_const_key(pop, -1), be_const_func(m_pop) },
{ be_const_key(reverse, -1), be_const_func(m_reverse) },
{ be_const_key(push, -1), be_const_func(m_push) },
{ be_const_key(init, -1), be_const_func(m_init) },
{ be_const_key(copy, 8), be_const_func(m_copy) },
{ be_const_key(opt_connect, 13), be_const_func(m_connect) },
{ be_const_key(item, -1), be_const_func(m_item) },
{ be_const_key(remove, -1), be_const_func(m_remove) },
{ be_const_key(size, -1), be_const_func(m_size) },
{ be_const_key(resize, 7), be_const_func(m_resize) },
{ be_const_key(opt_add, -1), be_const_func(m_merge) },
{ be_const_key(opt_neq, -1), be_const_func(m_nequal) },
{ be_const_key(setitem, -1), be_const_func(m_setitem) },
{ be_const_key(tostring, -1), be_const_func(m_tostring) },
{ be_const_key(clear, -1), be_const_func(m_clear) },
{ be_const_key(opt_eq, 3), be_const_func(m_equal) },
{ be_const_key(insert, 12), be_const_func(m_insert) },
{ be_const_key(concat, 2), be_const_func(m_concat) },
{ be_const_key(dot_p, -1), be_const_int(0) },
{ be_const_key(iter, 18), be_const_func(m_iter) },
};
static be_define_const_map(
be_class_list_map,
20
);
BE_EXPORT_VARIABLE be_define_const_class(
be_class_list,
1,
NULL,
list
);

View File

@ -0,0 +1,26 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_map_map) {
{ be_const_key(init, -1), be_const_func(m_init) },
{ be_const_key(remove, 7), be_const_func(m_remove) },
{ be_const_key(find, -1), be_const_func(m_find) },
{ be_const_key(insert, 8), be_const_func(m_insert) },
{ be_const_key(size, -1), be_const_func(m_size) },
{ be_const_key(tostring, 0), be_const_func(m_tostring) },
{ be_const_key(setitem, 3), be_const_func(m_setitem) },
{ be_const_key(iter, -1), be_const_func(m_iter) },
{ be_const_key(item, -1), be_const_func(m_item) },
{ be_const_key(dot_p, 1), be_const_int(0) },
};
static be_define_const_map(
be_class_map_map,
10
);
BE_EXPORT_VARIABLE be_define_const_class(
be_class_map,
1,
NULL,
map
);

View File

@ -0,0 +1,24 @@
#include "be_constobj.h"
static be_define_const_map_slots(be_class_range_map) {
{ be_const_key(setrange, -1), be_const_func(m_setrange) },
{ be_const_key(iter, -1), be_const_func(m_iter) },
{ be_const_key(lower, -1), be_const_func(m_lower) },
{ be_const_key(init, 4), be_const_func(m_init) },
{ be_const_key(__upper__, -1), be_const_int(0) },
{ be_const_key(tostring, -1), be_const_func(m_tostring) },
{ be_const_key(__lower__, -1), be_const_int(1) },
{ be_const_key(upper, 1), be_const_func(m_upper) },
};
static be_define_const_map(
be_class_range_map,
8
);
BE_EXPORT_VARIABLE be_define_const_class(
be_class_range,
2,
NULL,
range
);

View File

@ -0,0 +1,24 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libdebug_map) {
{ be_const_key(attrdump, -1), be_const_func(m_attrdump) },
{ be_const_key(upvname, -1), be_const_func(m_upvname) },
{ be_const_key(codedump, -1), be_const_func(m_codedump) },
{ be_const_key(top, -1), be_const_func(m_top) },
{ be_const_key(sethook, 3), be_const_func(m_sethook) },
{ be_const_key(varname, 7), be_const_func(m_varname) },
{ be_const_key(calldepth, -1), be_const_func(m_calldepth) },
{ be_const_key(traceback, -1), be_const_func(m_traceback) },
};
static be_define_const_map(
m_libdebug_map,
8
);
static be_define_const_module(
m_libdebug,
"debug"
);
BE_EXPORT_VARIABLE be_define_const_native_module(debug, NULL);

View File

@ -0,0 +1,18 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libgc_map) {
{ be_const_key(allocated, -1), be_const_func(m_allocated) },
{ be_const_key(collect, -1), be_const_func(m_collect) },
};
static be_define_const_map(
m_libgc_map,
2
);
static be_define_const_module(
m_libgc,
"gc"
);
BE_EXPORT_VARIABLE be_define_const_native_module(gc, NULL);

View File

@ -0,0 +1,18 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libjson_map) {
{ be_const_key(dump, -1), be_const_func(m_json_dump) },
{ be_const_key(load, 0), be_const_func(m_json_load) },
};
static be_define_const_map(
m_libjson_map,
2
);
static be_define_const_module(
m_libjson,
"json"
);
BE_EXPORT_VARIABLE be_define_const_native_module(json, NULL);

View File

@ -0,0 +1,60 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_builtin_map) {
{ be_const_key(map, 11), be_const_int(10) },
{ be_const_key(str, 14), be_const_int(18) },
{ be_const_key(int, -1), be_const_int(6) },
{ be_const_key(type, -1), be_const_int(20) },
{ be_const_key(assert, -1), be_const_int(1) },
{ be_const_key(issubclass, -1), be_const_int(8) },
{ be_const_key(number, 7), be_const_int(12) },
{ be_const_key(compile, 8), be_const_int(4) },
{ be_const_key(module, 18), be_const_int(11) },
{ be_const_key(input, 5), be_const_int(5) },
{ be_const_key(super, -1), be_const_int(19) },
{ be_const_key(classof, -1), be_const_int(3) },
{ be_const_key(open, 13), be_const_int(13) },
{ be_const_key(__iterator__, -1), be_const_int(0) },
{ be_const_key(real, 10), be_const_int(16) },
{ be_const_key(list, 20), be_const_int(9) },
{ be_const_key(isinstance, -1), be_const_int(7) },
{ be_const_key(range, -1), be_const_int(15) },
{ be_const_key(size, -1), be_const_int(17) },
{ be_const_key(classname, -1), be_const_int(2) },
{ be_const_key(print, -1), be_const_int(14) },
};
static be_define_const_map(
m_builtin_map,
21
);
static const bvalue __vlist_array[] = {
be_const_func(l_iterator),
be_const_func(l_assert),
be_const_func(l_classname),
be_const_func(l_classof),
be_const_func(l_compile),
be_const_func(l_input),
be_const_func(l_int),
be_const_func(l_isinstance),
be_const_func(l_issubclass),
be_const_class(be_class_list),
be_const_class(be_class_map),
be_const_func(l_module),
be_const_func(l_number),
be_const_func(be_nfunc_open),
be_const_func(l_print),
be_const_class(be_class_range),
be_const_func(l_real),
be_const_func(l_size),
be_const_func(l_str),
be_const_func(l_super),
be_const_func(l_type),
};
static be_define_const_vector(
m_builtin_vector,
__vlist_array,
21
);

View File

@ -0,0 +1,40 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libmath_map) {
{ be_const_key(tanh, 11), be_const_func(m_tanh) },
{ be_const_key(acos, -1), be_const_func(m_acos) },
{ be_const_key(pi, -1), be_const_real(M_PI) },
{ be_const_key(log10, -1), be_const_func(m_log10) },
{ be_const_key(deg, 18), be_const_func(m_deg) },
{ be_const_key(pow, -1), be_const_func(m_pow) },
{ be_const_key(asin, -1), be_const_func(m_asin) },
{ be_const_key(rad, -1), be_const_func(m_rad) },
{ be_const_key(ceil, 14), be_const_func(m_ceil) },
{ be_const_key(log, -1), be_const_func(m_log) },
{ be_const_key(imax, -1), be_const_int(M_IMAX) },
{ be_const_key(imin, 3), be_const_int(M_IMIN) },
{ be_const_key(cos, -1), be_const_func(m_cos) },
{ be_const_key(sin, -1), be_const_func(m_sin) },
{ be_const_key(exp, -1), be_const_func(m_exp) },
{ be_const_key(atan, 1), be_const_func(m_atan) },
{ be_const_key(tan, 7), be_const_func(m_tan) },
{ be_const_key(srand, -1), be_const_func(m_srand) },
{ be_const_key(sqrt, -1), be_const_func(m_sqrt) },
{ be_const_key(abs, -1), be_const_func(m_abs) },
{ be_const_key(cosh, -1), be_const_func(m_cosh) },
{ be_const_key(floor, -1), be_const_func(m_floor) },
{ be_const_key(rand, -1), be_const_func(m_rand) },
{ be_const_key(sinh, 4), be_const_func(m_sinh) },
};
static be_define_const_map(
m_libmath_map,
24
);
static be_define_const_module(
m_libmath,
"math"
);
BE_EXPORT_VARIABLE be_define_const_native_module(math, NULL);

View File

@ -0,0 +1,24 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libos_map) {
{ be_const_key(listdir, 1), be_const_func(m_listdir) },
{ be_const_key(mkdir, -1), be_const_func(m_mkdir) },
{ be_const_key(chdir, -1), be_const_func(m_chdir) },
{ be_const_key(exit, 2), be_const_func(m_exit) },
{ be_const_key(system, -1), be_const_func(m_system) },
{ be_const_key(remove, 3), be_const_func(m_remove) },
{ be_const_key(path, -1), be_const_module(m_libpath) },
{ be_const_key(getcwd, -1), be_const_func(m_getcwd) },
};
static be_define_const_map(
m_libos_map,
8
);
static be_define_const_module(
m_libos,
"os"
);
BE_EXPORT_VARIABLE be_define_const_native_module(os, NULL);

View File

@ -0,0 +1,20 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libpath_map) {
{ be_const_key(isdir, -1), be_const_func(m_path_isdir) },
{ be_const_key(join, 2), be_const_func(m_path_join) },
{ be_const_key(exists, -1), be_const_func(m_path_exists) },
{ be_const_key(split, -1), be_const_func(m_path_split) },
{ be_const_key(splitext, -1), be_const_func(m_path_splitext) },
{ be_const_key(isfile, -1), be_const_func(m_path_isfile) },
};
static be_define_const_map(
m_libpath_map,
6
);
static be_define_const_module(
m_libpath,
"path"
);

View File

@ -0,0 +1,23 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libstring_map) {
{ be_const_key(hex, 2), be_const_func(str_i2hex) },
{ be_const_key(byte, -1), be_const_func(str_byte) },
{ be_const_key(format, 5), be_const_func(str_format) },
{ be_const_key(find, -1), be_const_func(str_find) },
{ be_const_key(char, 1), be_const_func(str_char) },
{ be_const_key(split, -1), be_const_func(str_split) },
{ be_const_key(count, -1), be_const_func(str_count) },
};
static be_define_const_map(
m_libstring_map,
7
);
static be_define_const_module(
m_libstring,
"string"
);
BE_EXPORT_VARIABLE be_define_const_native_module(string, NULL);

View File

@ -0,0 +1,17 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libsys_map) {
{ be_const_key(path, -1), be_const_func(m_path) },
};
static be_define_const_map(
m_libsys_map,
1
);
static be_define_const_module(
m_libsys,
"sys"
);
BE_EXPORT_VARIABLE be_define_const_native_module(sys, NULL);

View File

@ -0,0 +1,19 @@
#include "be_constobj.h"
static be_define_const_map_slots(m_libtime_map) {
{ be_const_key(dump, 1), be_const_func(m_dump) },
{ be_const_key(clock, -1), be_const_func(m_clock) },
{ be_const_key(time, 0), be_const_func(m_time) },
};
static be_define_const_map(
m_libtime_map,
3
);
static be_define_const_module(
m_libtime,
"time"
);
BE_EXPORT_VARIABLE be_define_const_native_module(time, NULL);

View File

@ -0,0 +1,7 @@
name=Berry
version=0.1.10
author=Guan Wenliang <skiars@qq.com>,
maintainer=Stephan Hadinger <stephan.hadinger@gmail.com>
sentence=Berry scripting language for Tasmota32
paragraph=Berry is a ultra-lightweight dynamically typed embedded scripting language.
architectures=esp32

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,349 @@
/********************************************************************
** 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_exec.h"
#include "be_mem.h"
#include "be_gc.h"
#include <string.h>
#define READLINE_STEP 100
static int l_assert(bvm *vm)
{
int argc = be_top(vm);
/* assertion fails when there is no argument
* or the first argument is nil or false. */
if (!argc || !be_tobool(vm, 1)) {
const char *msg = "assert failed!";
if (argc >= 2 && be_isstring(vm, 2)) {
msg = be_tostring(vm, 2);
}
be_raise(vm, "assert_failed", msg);
}
be_return_nil(vm);
}
static int l_print(bvm *vm)
{
int i, argc = be_top(vm);
for (i = 1; i <= argc; ++i) {
const char *str = be_tostring(vm, i);
size_t len = be_strlen(vm, i);
be_writebuffer(str, len);
if (i < argc) {
be_writebuffer(" ", 1);
}
}
be_writenewline();
be_return_nil(vm);
}
static int m_readline(bvm *vm)
{
size_t pos = 0, size = READLINE_STEP;
char *buffer = be_malloc(vm, size);
char *res = be_readstring(buffer, (int)size);
while (res) {
pos += strlen(buffer + pos) - 1;
if (!pos || buffer[pos] == '\n') {
buffer[pos] = '\0'; /* trim \n */
break;
}
buffer = be_realloc(vm, buffer, size, size + READLINE_STEP);
res = be_readstring(buffer + pos + 1, READLINE_STEP);
size += READLINE_STEP;
}
be_pushstring(vm, buffer);
be_free(vm, buffer, size);
be_return(vm);
}
static int l_input(bvm *vm)
{
if (be_top(vm) && be_isstring(vm, 1)) { /* echo prompt */
be_writestring(be_tostring(vm, 1));
}
return m_readline(vm);
}
static int l_super(bvm *vm)
{
if (be_top(vm)) {
be_getsuper(vm, 1);
be_return(vm);
}
be_return_nil(vm);
}
static int l_type(bvm *vm)
{
if (be_top(vm)) {
be_pushstring(vm, be_typename(vm, 1));
be_return(vm);
}
be_return_nil(vm);
}
static int l_classname(bvm *vm)
{
if (be_top(vm)) {
const char *t = be_classname(vm, 1);
if (t) {
be_pushstring(vm, t);
be_return(vm);
}
}
be_return_nil(vm);
}
static int l_classof(bvm *vm)
{
if (be_top(vm) && be_classof(vm, 1)) {
be_return(vm);
}
be_return_nil(vm);
}
static int l_number(bvm *vm)
{
if (be_top(vm)) {
if (be_isstring(vm, 1)) {
be_str2num(vm, be_tostring(vm, 1));
be_return(vm);
} else if (be_isnumber(vm, 1)) {
be_pushvalue(vm, 1);
be_return(vm);
}
}
be_return_nil(vm);
}
static int l_int(bvm *vm)
{
if (be_top(vm)) {
if (be_isstring(vm, 1)) {
const char *s = be_tostring(vm, 1);
be_pushint(vm, be_str2int(s, NULL));
} else if (be_isreal(vm, 1)) {
be_pushint(vm, (bint)be_toreal(vm, 1));
} else if (be_isint(vm, 1)) {
be_pushvalue(vm, 1);
} else {
be_return_nil(vm);
}
be_return(vm);
}
be_return_nil(vm);
}
static int l_real(bvm *vm)
{
if (be_top(vm)) {
if (be_isstring(vm, 1)) {
const char *s = be_tostring(vm, 1);
be_pushreal(vm, be_str2real(s, NULL));
} else if (be_isint(vm, 1)) {
be_pushreal(vm, (breal)be_toint(vm, 1));
} else if (be_isreal(vm, 1)) {
be_pushvalue(vm, 1);
} else {
be_return_nil(vm);
}
be_return(vm);
}
be_return_nil(vm);
}
static int check_method(bvm *vm, const char *attr)
{
return be_top(vm) &&
be_isinstance(vm, 1) && be_getmethod(vm, 1, attr);
}
static int l_iterator(bvm *vm)
{
if (be_top(vm) && be_isfunction(vm, 1)) {
be_return(vm); /* return the argument[0]::function */
}
if (check_method(vm, "iter")) {
be_pushvalue(vm, 1);
be_call(vm, 1);
be_pop(vm, 1);
be_return(vm);
}
be_return_nil(vm);
}
static int l_str(bvm *vm)
{
if (be_top(vm)) {
be_tostring(vm, 1);
} else {
be_pushstring(vm, "");
}
be_return(vm);
}
static int l_size(bvm *vm)
{
if (be_top(vm) && be_isstring(vm, 1)) {
be_pushint(vm, be_strlen(vm, 1));
be_return(vm);
}
if (check_method(vm, "size")) {
be_pushvalue(vm, 1);
be_call(vm, 1);
be_pop(vm, 1);
be_return(vm);
}
be_return_nil(vm);
}
static int l_module(bvm *vm)
{
int argc = be_top(vm);
be_newmodule(vm);
if (argc > 0 && be_isstring(vm, 1)) {
be_setname(vm, -1, be_tostring(vm, 1));
}
be_return(vm);
}
#if BE_USE_SCRIPT_COMPILER
static int raise_compile_error(bvm *vm)
{
be_pop(vm, 2); /* pop the exception value and message */
be_throw(vm, BE_EXCEPTION);
return 0;
}
static int m_compile_str(bvm *vm)
{
int len = be_strlen(vm, 1);
const char *src = be_tostring(vm, 1);
int res = be_loadbuffer(vm, "string", src, len);
if (res == BE_OK) {
be_return(vm);
}
return raise_compile_error(vm);
}
static int m_compile_file(bvm *vm)
{
const char *fname = be_tostring(vm, 1);
int res = be_loadfile(vm, fname);
if (res == BE_OK) {
be_return(vm);
} else if (res == BE_IO_ERROR) {
be_pushstring(vm, "io_error");
be_pushvalue(vm, -2);
}
return raise_compile_error(vm);
}
#endif
static int l_compile(bvm *vm)
{
#if BE_USE_SCRIPT_COMPILER
if (be_top(vm) && be_isstring(vm, 1)) {
if (be_top(vm) >= 2 && be_isstring(vm, 2)) {
const char *s = be_tostring(vm, 2);
if (!strcmp(s, "string")) {
return m_compile_str(vm);
}
if (!strcmp(s, "file")) {
return m_compile_file(vm);
}
} else {
return m_compile_str(vm);
}
}
#endif
be_return_nil(vm);
}
static int _issubv(bvm *vm, bbool (*filter)(bvm*, int))
{
bbool status = bfalse;
if (be_top(vm) >= 2 && filter(vm, 1)) {
be_pushvalue(vm, 2);
status = be_isderived(vm, 1);
}
be_pushbool(vm, status);
be_return(vm);
}
static int l_issubclass(bvm *vm)
{
return _issubv(vm, be_isclass);
}
static int l_isinstance(bvm *vm)
{
return _issubv(vm, be_isinstance);
}
#if !BE_USE_PRECOMPILED_OBJECT
void be_load_baselib(bvm *vm)
{
be_regfunc(vm, "assert", l_assert);
be_regfunc(vm, "print", l_print);
be_regfunc(vm, "input", l_input);
be_regfunc(vm, "super", l_super);
be_regfunc(vm, "type", l_type);
be_regfunc(vm, "classname", l_classname);
be_regfunc(vm, "classof", l_classof);
be_regfunc(vm, "number", l_number);
be_regfunc(vm, "str", l_str);
be_regfunc(vm, "int", l_int);
be_regfunc(vm, "real", l_real);
be_regfunc(vm, "module", l_module);
be_regfunc(vm, "size", l_size);
be_regfunc(vm, "compile", l_compile);
be_regfunc(vm, "issubclass", l_issubclass);
be_regfunc(vm, "isinstance", l_isinstance);
be_regfunc(vm, "__iterator__", l_iterator);
}
#else
extern const bclass be_class_list;
extern const bclass be_class_map;
extern const bclass be_class_range;
extern int be_nfunc_open(bvm *vm);
/* @const_object_info_begin
vartab m_builtin (scope: local) {
assert, func(l_assert)
print, func(l_print)
input, func(l_input)
super, func(l_super)
type, func(l_type)
classname, func(l_classname)
classof, func(l_classof)
number, func(l_number)
str, func(l_str)
int, func(l_int)
real, func(l_real)
module, func(l_module)
size, func(l_size)
compile, func(l_compile)
issubclass, func(l_issubclass)
isinstance, func(l_isinstance)
__iterator__, func(l_iterator)
open, func(be_nfunc_open)
list, class(be_class_list)
map, class(be_class_map)
range, class(be_class_range)
}
@const_object_info_end */
#include "../generate/be_fixed_m_builtin.h"
#include "be_var.h"
void be_load_baselib(bvm *vm)
{
be_const_builtin_set(vm, &m_builtin_map, &m_builtin_vector);
}
#endif

View File

@ -0,0 +1,547 @@
/********************************************************************
** 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_bytecode.h"
#include "be_decoder.h"
#include "be_vector.h"
#include "be_string.h"
#include "be_class.h"
#include "be_func.h"
#include "be_exec.h"
#include "be_list.h"
#include "be_map.h"
#include "be_mem.h"
#include "be_sys.h"
#include "be_var.h"
#include "be_vm.h"
#include <string.h>
#define MAGIC_NUMBER1 0xBE
#define MAGIC_NUMBER2 0xCD
#define MAGIC_NUMBER3 0xFE
#define BYTECODE_VERSION 1
#define USE_64BIT_INT (BE_INTGER_TYPE == 2 \
|| BE_INTGER_TYPE == 1 && LONG_MAX == 9223372036854775807L)
#if !BE_USE_SCRIPT_COMPILER && BE_USE_BYTECODE_SAVER
#error bytecode generation dependent compiler (require BE_USE_SCRIPT_COMPILER != 0)
#endif
#if BE_USE_BYTECODE_SAVER || BE_USE_BYTECODE_LOADER
static void bytecode_error(bvm *vm, const char *msg)
{
be_raise(vm, "io_error", msg);
}
static uint8_t vm_sizeinfo(void)
{
uint8_t res = sizeof(bint) == 8;
res |= (sizeof(breal) == 8) << 1;
return res;
}
#endif
#if BE_USE_BYTECODE_SAVER
static void save_proto(bvm *vm, void *fp, bproto *proto);
static void save_byte(void *fp, uint8_t value)
{
be_fwrite(fp, &value, 1);
}
static void save_word(void *fp, uint16_t value)
{
uint8_t buffer[2];
buffer[0] = value & 0xff;
buffer[1] = value >> 8;
be_fwrite(fp, buffer, sizeof(buffer));
}
static void save_long(void *fp, uint32_t value)
{
uint8_t buffer[4];
buffer[0] = value & 0xff;
buffer[1] = (value >> 8) & 0xff;
buffer[2] = (value >> 16) & 0xff;
buffer[3] = (value >> 24) & 0xff;
be_fwrite(fp, buffer, sizeof(buffer));
}
static void save_header(void *fp)
{
uint8_t buffer[8] = { 0 };
buffer[0] = MAGIC_NUMBER1;
buffer[1] = MAGIC_NUMBER2;
buffer[2] = MAGIC_NUMBER3;
buffer[3] = BYTECODE_VERSION;
buffer[4] = vm_sizeinfo();
be_fwrite(fp, buffer, sizeof(buffer));
}
static void save_int(void *fp, bint i)
{
#if USE_64BIT_INT
save_long(fp, i & 0xffffffff);
save_long(fp, (i >> 32) & 0xffffffff);
#else
save_long(fp, (uint32_t)i);
#endif
}
static void save_real(void *fp, breal r)
{
#if BE_USE_SINGLE_FLOAT
union { breal r; uint32_t i; } u;
u.r = r;
save_long(fp, u.i);
#else
union { breal r; uint64_t i; } u;
u.r = r;
save_long(fp, u.i & 0xffffffff);
save_long(fp, (u.i >> 32) & 0xffffffff);
#endif
}
static void save_string(void *fp, bstring *s)
{
if (s) {
uint16_t length = (uint16_t)str_len(s);
const char *data = str(s);
save_word(fp, length);
be_fwrite(fp, data, length);
}
}
static bstring** save_members(bvm *vm, void *fp, bclass *c, int nvar)
{
bmapnode *node;
bstring **vars = NULL;
bmap *members = c->members;
bmapiter iter = be_map_iter();
if (nvar) {
/* allocate the member-variable name cache */
vars = be_malloc(vm, sizeof(bstring *) * nvar);
}
while ((node = be_map_next(members, &iter)) != NULL) {
be_assert(var_isstr(&node->key));
if (var_isint(&node->value)) { /* cache member name */
if (vars == NULL) {
return NULL; /* should never be executed */
}
vars[var_toidx(&node->value)] = var_tostr(&node->key);
} else { /* save method's name and function */
bproto *proto;
bvalue *value = &node->value;
be_assert(var_isclosure(value) || var_isproto(value));
save_string(fp, var_tostr(&node->key)); /* save method name */
if (var_isproto(value)) { /* the method is a prototype */
proto = var_toobj(value);
} else { /* the method is a closure */
proto = cast(bclosure *, var_toobj(value))->proto;
}
save_proto(vm, fp, proto); /* only save prototype */
}
}
return vars;
}
static void save_class(bvm *vm, void *fp, bclass *c)
{
bstring **vars;
int i, count = be_map_count(c->members);
int nvar = c->nvar - be_class_closure_count(c);
save_string(fp, c->name);
save_long(fp, nvar); /* member variables count */
save_long(fp, count - nvar); /* method count */
vars = save_members(vm, fp, c, nvar);
if (vars != NULL) {
for (i = 0; i < nvar; ++i) {
save_string(fp, vars[i]);
}
be_free(vm, vars, sizeof(bstring *) * nvar);
}
}
static void save_value(bvm *vm, void *fp, bvalue *v)
{
save_byte(fp, (uint8_t)var_type(v)); /* type */
switch (var_type(v)) {
case BE_INT: save_int(fp, var_toint(v)); break;
case BE_REAL: save_real(fp, var_toreal(v)); break;
case BE_STRING: save_string(fp, var_tostr(v)); break;
case BE_CLASS: save_class(vm, fp, var_toobj(v)); break;
default: break;
}
}
static void save_bytecode(void *fp, bproto *proto)
{
binstruction *code = proto->code, *end;
save_long(fp, (uint32_t)proto->codesize);
for (end = code + proto->codesize; code < end; ++code) {
save_long(fp, (uint32_t)*code);
}
}
static void save_constants(bvm *vm, void *fp, bproto *proto)
{
bvalue *v = proto->ktab, *end;
save_long(fp, proto->nconst); /* constants count */
for (end = v + proto->nconst; v < end; ++v) {
save_value(vm, fp, v);
}
}
static void save_proto_table(bvm *vm, void *fp, bproto *proto)
{
bproto **p = proto->ptab, **end;
save_long(fp, proto->nproto); /* proto count */
for (end = p + proto->nproto; p < end; ++p) {
save_proto(vm, fp, *p);
}
}
static void save_upvals(void *fp, bproto *proto)
{
bupvaldesc *uv = proto->upvals, *end;
save_byte(fp, proto->nupvals); /* upvals count */
for (end = uv + proto->nupvals; uv < end; ++uv) {
save_byte(fp, uv->instack);
save_byte(fp, uv->idx);
}
}
static void save_proto(bvm *vm, void *fp, bproto *proto)
{
if (proto) {
save_string(fp, proto->name); /* name */
save_string(fp, proto->source); /* source */
save_byte(fp, proto->argc); /* argc */
save_byte(fp, proto->nstack); /* nstack */
save_bytecode(fp, proto); /* bytecode */
save_constants(vm, fp, proto); /* constant */
save_proto_table(vm, fp, proto); /* proto table */
save_upvals(fp, proto); /* upvals description table */
}
}
static void save_globals(bvm *vm, void *fp)
{
bmapnode *node;
bmapiter iter = be_map_iter();
bmap *map = vm->gbldesc.global.vtab;
int i, count = be_global_count(vm);
bstring **list = be_malloc(vm, sizeof(bstring*) * count);
while ((node = be_map_next(map, &iter)) != NULL) {
if (var_isstr(&node->key)) {
int idx = var_toidx(&node->value);
be_assert(idx < count);
list[idx] = var_tostr(&node->key);
}
}
for (i = 0; i < count; ++i) {
save_string(fp, list[i]);
}
be_free(vm, list, sizeof(bstring*) * count);
}
static void save_global_info(bvm *vm, void *fp)
{
save_long(fp, be_builtin_count(vm));
save_long(fp, be_global_count(vm));
save_globals(vm, fp);
}
void be_bytecode_save(bvm *vm, const char *filename, bproto *proto)
{
void *fp = be_fopen(filename, "wb");
if (fp == NULL) {
bytecode_error(vm, be_pushfstring(vm,
"can not open file '%s'.", filename));
} else {
save_header(fp);
save_global_info(vm, fp);
save_proto(vm, fp, proto);
be_fclose(fp);
}
}
#endif /* BE_USE_BYTECODE_SAVER */
#if BE_USE_BYTECODE_LOADER
static void load_proto(bvm *vm, void *fp, bproto **proto, int info);
static uint8_t load_byte(void *fp)
{
uint8_t buffer[1];
if (be_fread(fp, buffer, sizeof(buffer)) == sizeof(buffer)) {
return buffer[0];
}
return 0;
}
static uint16_t load_word(void *fp)
{
uint8_t buffer[2];
if (be_fread(fp, buffer, sizeof(buffer)) == sizeof(buffer)) {
return ((uint16_t)buffer[1] << 8) | buffer[0];
}
return 0;
}
static uint32_t load_long(void *fp)
{
uint8_t buffer[4];
if (be_fread(fp, buffer, sizeof(buffer)) == sizeof(buffer)) {
return ((uint32_t)buffer[3] << 24)
| ((uint32_t)buffer[2] << 16)
| ((uint32_t)buffer[1] << 8)
| buffer[0];
}
return 0;
}
static int load_head(void *fp)
{
int res;
uint8_t buffer[8] = { 0 };
be_fread(fp, buffer, sizeof(buffer));
res = buffer[0] == MAGIC_NUMBER1 &&
buffer[1] == MAGIC_NUMBER2 &&
buffer[2] == MAGIC_NUMBER3 &&
buffer[3] == BYTECODE_VERSION &&
buffer[4] == vm_sizeinfo();
return res;
}
bbool be_bytecode_check(const char *path)
{
void *fp = be_fopen(path, "r");
if (fp) {
uint8_t buffer[3], rb;
rb = (uint8_t)be_fread(fp, buffer, 3);
be_fclose(fp);
/* check magic number */
return rb == 3 &&
buffer[0] == MAGIC_NUMBER1 &&
buffer[1] == MAGIC_NUMBER2 &&
buffer[2] == MAGIC_NUMBER3;
}
return bfalse;
}
static bint load_int(void *fp)
{
#if USE_64BIT_INT
bint i;
i = load_long(fp);
i |= (bint)load_long(fp) << 32;
return i;
#else
return load_long(fp);
#endif
}
static breal load_real(void *fp)
{
#if BE_USE_SINGLE_FLOAT
union { breal r; uint32_t i; } u;
u.i = load_long(fp);
return u.r;
#else
union {
breal r;
uint64_t i;
} u;
u.i = load_long(fp);
u.i |= (uint64_t)load_long(fp) << 32;
return u.r;
#endif
}
static bstring* load_string(bvm *vm, void *fp)
{
uint16_t len = load_word(fp);
if (len > 0) {
bstring *str;
char *buf = be_malloc(vm, len);
be_fread(fp, buf, len);
str = be_newstrn(vm, buf, len);
be_free(vm, buf, len);
return str;
}
return str_literal(vm, "");
}
static bstring* cache_string(bvm *vm, void *fp)
{
bstring *str = load_string(vm, fp);
var_setstr(vm->top, str);
be_incrtop(vm);
return str;
}
static void load_class(bvm *vm, void *fp, bvalue *v)
{
int nvar, count;
bclass *c = be_newclass(vm, NULL, NULL);
var_setclass(v, c);
c->name = load_string(vm, fp);
nvar = load_long(fp);
count = load_long(fp);
while (count--) { /* load method table */
bvalue *value;
bstring *name = cache_string(vm, fp);
value = vm->top;
var_setproto(value, NULL);
be_incrtop(vm);
load_proto(vm, fp, (bproto**)&var_toobj(value), -3);
be_method_bind(vm, c, name, var_toobj(value));
be_stackpop(vm, 2); /* pop the cached string and proto */
}
for (count = 0; count < nvar; ++count) { /* load member-variable table */
bstring *name = cache_string(vm, fp);
be_member_bind(vm, c, name);
be_stackpop(vm, 1); /* pop the cached string */
}
}
static void load_value(bvm *vm, void *fp, bvalue *v)
{
switch (load_byte(fp)) {
case BE_INT: var_setint(v, load_int(fp)); break;
case BE_REAL: var_setreal(v, load_real(fp)); break;
case BE_STRING: var_setstr(v, load_string(vm, fp)); break;
case BE_CLASS: load_class(vm, fp, v); break;
default: break;
}
}
static void load_bytecode(bvm *vm, void *fp, bproto *proto, int info)
{
int size = (int)load_long(fp);
if (size) {
binstruction *code, *end;
int bcnt = be_builtin_count(vm);
blist *list = var_toobj(be_indexof(vm, info));
be_assert(be_islist(vm, info));
proto->code = be_malloc(vm, sizeof(binstruction) * size);
proto->codesize = size;
code = proto->code;
for (end = code + size; code < end; ++code) {
binstruction ins = (binstruction)load_long(fp);
binstruction op = IGET_OP(ins);
/* fix global variable index */
if (op == OP_GETGBL || op == OP_SETGBL) {
int idx = IGET_Bx(ins);
if (idx >= bcnt) { /* does not fix builtin index */
bvalue *name = be_list_at(list, idx - bcnt);
idx = be_global_find(vm, var_tostr(name));
ins = (ins & ~IBx_MASK) | ISET_Bx(idx);
}
}
*code = ins;
}
}
}
static void load_constant(bvm *vm, void *fp, bproto *proto)
{
int size = (int)load_long(fp); /* nconst */
if (size) {
bvalue *end, *v = be_malloc(vm, sizeof(bvalue) * size);
memset(v, 0, sizeof(bvalue) * size);
proto->ktab = v;
proto->nconst = size;
for (end = v + size; v < end; ++v) {
load_value(vm, fp, v);
}
}
}
static void load_proto_table(bvm *vm, void *fp, bproto *proto)
{
int size = (int)load_long(fp); /* proto count */
if (size) {
bproto **p = be_malloc(vm, sizeof(bproto *) * size);
memset(p, 0, sizeof(bproto *) * size);
proto->ptab = p;
proto->nproto = size;
while (size--) {
load_proto(vm, fp, p++, -1);
}
}
}
static void load_upvals(bvm *vm, void *fp, bproto *proto)
{
int size = (int)load_byte(fp);
if (size) {
bupvaldesc *uv, *end;
proto->upvals = be_malloc(vm, sizeof(bupvaldesc) * size);
proto->nupvals = (bbyte)size;
uv = proto->upvals;
for (end = uv + size; uv < end; ++uv) {
uv->instack = load_byte(fp);
uv->idx = load_byte(fp);
}
}
}
static void load_proto(bvm *vm, void *fp, bproto **proto, int info)
{
*proto = be_newproto(vm);
(*proto)->name = load_string(vm, fp);
(*proto)->source = load_string(vm, fp);
(*proto)->argc = load_byte(fp);
(*proto)->nstack = load_byte(fp);
load_bytecode(vm, fp, *proto, info);
load_constant(vm, fp, *proto);
load_proto_table(vm, fp, *proto);
load_upvals(vm, fp, *proto);
}
void load_global_info(bvm *vm, void *fp)
{
int i;
int bcnt = (int)load_long(fp); /* builtin count */
int gcnt = (int)load_long(fp); /* global count */
if (bcnt != be_builtin_count(vm)) {
bytecode_error(vm, be_pushfstring(vm,
"inconsistent number of builtin objects."));
}
be_newlist(vm);
for (i = 0; i < gcnt; ++i) {
bstring *name = cache_string(vm, fp);
be_global_new(vm, name);
be_data_push(vm, -2); /* push the variable name to list */
be_stackpop(vm, 1); /* pop the cached string */
}
be_global_release_space(vm);
}
bclosure* be_bytecode_load(bvm *vm, const char *filename)
{
void *fp = be_fopen(filename, "rb");
if (fp == NULL) {
bytecode_error(vm, be_pushfstring(vm,
"can not open file '%s'.", filename));
} else if (load_head(fp)) {
bclosure *cl = be_newclosure(vm, 0);
var_setclosure(vm->top, cl);
be_stackpush(vm);
load_global_info(vm, fp);
load_proto(vm, fp, &cl->proto, -1);
be_stackpop(vm, 2); /* pop the closure and list */
be_fclose(fp);
return cl;
}
bytecode_error(vm, be_pushfstring(vm,
"invalid bytecode file '%s'.", filename));
return NULL;
}
#endif /* BE_USE_BYTECODE_LOADER */

View File

@ -0,0 +1,17 @@
/********************************************************************
** 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
********************************************************************/
#ifndef __BE_BYTECODE_H
#define __BE_BYTECODE_H
#include "be_object.h"
void be_bytecode_save(bvm *vm, const char *filename, bproto *proto);
bclosure* be_bytecode_load(bvm *vm, const char *filename);
bbool be_bytecode_check(const char *path);
#endif

View File

@ -0,0 +1,214 @@
/********************************************************************
** 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_class.h"
#include "be_string.h"
#include "be_vector.h"
#include "be_map.h"
#include "be_exec.h"
#include "be_gc.h"
#include "be_vm.h"
#include "be_func.h"
#include "be_var.h"
#define check_members(vm, c) \
if (!(c)->members) { \
(c)->members = be_map_new(vm); \
}
bclass* be_newclass(bvm *vm, bstring *name, bclass *super)
{
bgcobject *gco = be_gcnew(vm, BE_CLASS, bclass);
bclass *obj = cast_class(gco);
bvalue *buf = be_incrtop(vm); /* protect new objects from GC */
var_setclass(buf, obj);
if (obj) {
obj->super = super;
obj->members = NULL; /* gc protection */
obj->nvar = 0;
obj->name = name;
}
be_stackpop(vm, 1);
return obj;
}
void be_class_compress(bvm *vm, bclass *c)
{
if (!gc_isconst(c) && c->members) {
be_map_release(vm, c->members); /* clear space */
}
}
int be_class_attribute(bvm *vm, bclass *c, bstring *attr)
{
for (; c; c = c->super) {
if (c->members) {
bvalue *v = be_map_findstr(vm, c->members, attr);
if (v) {
return var_type(v);
}
}
}
return BE_NIL;
}
void be_member_bind(bvm *vm, bclass *c, bstring *name)
{
bvalue *attr;
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
attr->v.i = c->nvar++;
attr->type = MT_VARIABLE;
}
void be_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p)
{
bclosure *cl;
bvalue *attr;
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
var_setnil(attr);
cl = be_newclosure(vm, p->nupvals);
cl->proto = p;
var_setclosure(attr, cl);
}
void be_prim_method_bind(bvm *vm, bclass *c, bstring *name, bntvfunc f)
{
bvalue *attr;
check_members(vm, c);
attr = be_map_insertstr(vm, c->members, name, NULL);
attr->v.nf = f;
attr->type = MT_PRIMMETHOD;
}
/* get the closure method count that need upvalues */
int be_class_closure_count(bclass *c)
{
int count = 0;
if (c->members) {
bmapnode *node;
bmap *members = c->members;
bmapiter iter = be_map_iter();
while ((node = be_map_next(members, &iter)) != NULL) {
if (var_isproto(&node->value)) {
++count;
}
}
}
return count;
}
static binstance* instance_member(bvm *vm,
binstance *obj, bstring *name, bvalue *dst)
{
for (; obj; obj = obj->super) {
bmap *members = obj->_class->members;
if (members) {
bvalue *v = be_map_findstr(vm, members, name);
if (v) {
*dst = *v;
return obj;
}
}
}
var_setnil(dst);
return NULL;
}
void be_class_upvalue_init(bvm *vm, bclass *c)
{
bmap *mbr = c->members;
if (mbr != NULL) {
bmapnode *node;
bmapiter iter = be_map_iter();
while ((node = be_map_next(mbr, &iter)) != NULL) {
if (var_isclosure(&node->value)) {
bclosure *cl = var_toobj(&node->value);
if (cl->proto->nupvals) {
/* initialize the closure's upvalues */
be_release_upvalues(vm, cl);
be_initupvals(vm, cl);
}
}
}
}
}
static binstance* newobjself(bvm *vm, bclass *c)
{
size_t size = sizeof(binstance) + sizeof(bvalue) * (c->nvar - 1);
bgcobject *gco = be_newgcobj(vm, BE_INSTANCE, size);
binstance *obj = cast_instance(gco);
be_assert(obj != NULL);
if (obj) { /* initialize members */
bvalue *v = obj->members, *end = v + c->nvar;
while (v < end) { var_setnil(v); ++v; }
obj->_class = c;
obj->super = NULL;
}
return obj;
}
static binstance* newobject(bvm *vm, bclass *c)
{
binstance *obj, *prev;
be_assert(c != NULL);
obj = prev = newobjself(vm, c);
var_setinstance(vm->top, obj);
be_incrtop(vm); /* protect new objects from GC */
for (c = c->super; c; c = c->super) {
prev->super = newobjself(vm, c);
prev = prev->super;
}
be_stackpop(vm, 1);
return obj;
}
bbool be_class_newobj(bvm *vm, bclass *c, bvalue *reg, int argc)
{
bvalue init;
size_t pos = reg - vm->reg;
binstance *obj = newobject(vm, c);
reg = vm->reg + pos; /* the stack may have changed */
var_setinstance(reg, obj);
/* find constructor */
obj = instance_member(vm, obj, str_literal(vm, "init"), &init);
if (obj && var_type(&init) != MT_VARIABLE) {
/* copy argv */
for (reg = vm->reg + pos + 1; argc > 0; --argc) {
reg[argc] = reg[argc - 2];
}
*reg = init; /* set constructor */
return btrue;
}
return bfalse;
}
int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst)
{
int type;
be_assert(name != NULL);
obj = instance_member(vm, obj, name, dst);
type = var_type(dst);
if (obj && type == MT_VARIABLE) {
*dst = obj->members[dst->v.i];
}
return type;
}
bbool be_instance_setmember(bvm *vm, binstance *obj, bstring *name, bvalue *src)
{
bvalue v;
be_assert(name != NULL);
obj = instance_member(vm, obj, name, &v);
if (obj && var_istype(&v, MT_VARIABLE)) {
obj->members[var_toint(&v)] = *src;
return btrue;
}
return bfalse;
}

View File

@ -0,0 +1,61 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_CLASS_H
#define BE_CLASS_H
#include "be_object.h"
#define MT_VARIABLE BE_INT
#define MT_METHOD BE_CLOSURE
#define MT_PRIMMETHOD BE_NTVFUNC
#define be_class_name(cl) ((cl)->name)
#define be_class_members(cl) ((cl)->members)
#define be_class_super(cl) ((cl)->super)
#define be_class_setsuper(self, sup) ((self)->super = (sup))
#define be_instance_name(obj) ((obj)->_class->name)
#define be_instance_class(obj) ((obj)->_class)
#define be_instance_members(obj) ((obj)->members)
#define be_instance_member_count(obj) ((obj)->_class->nvar)
#define be_instance_super(obj) ((obj)->super)
struct bclass {
bcommon_header;
uint16_t nvar; /* members variable data field count */
struct bclass *super;
bmap *members;
bstring *name;
bgcobject *gray; /* for gc gray list */
#ifdef __cplusplus
BE_CONSTEXPR bclass(uint16_t nv, bclass *sup, bmap *mem, bstring *s) :
next(0), type(BE_CLASS), marked(GC_CONST), nvar(nv),
super(sup), members(mem), name(s), gray(0) {}
#endif
};
struct binstance {
bcommon_header;
struct binstance *super;
bclass *_class;
bgcobject *gray; /* for gc gray list */
bvalue members[1]; /* members variable data field */
};
bclass* be_newclass(bvm *vm, bstring *name, bclass *super);
void be_class_compress(bvm *vm, bclass *c);
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);
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);
int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst);
bbool be_instance_setmember(bvm *vm, binstance *obj, bstring *name, bvalue *src);
#endif

View File

@ -0,0 +1,797 @@
/********************************************************************
** 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_code.h"
#include "be_decoder.h"
#include "be_parser.h"
#include "be_lexer.h"
#include "be_vector.h"
#include "be_list.h"
#include "be_var.h"
#include "be_exec.h"
#include "be_vm.h"
#include <stdio.h>
#define NOT_MASK (1 << 0)
#define NOT_EXPR (1 << 1)
#define FUNC_RET_FLAG (1 << 0)
#define isset(v, mask) (((v) & (mask)) != 0)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define notexpr(e) isset((e)->not, NOT_EXPR)
#define notmask(e) isset((e)->not, NOT_MASK)
#define exp2anyreg(f, e) exp2reg(f, e, (f)->freereg)
#define var2anyreg(f, e) var2reg(f, e, (f)->freereg)
#define hasjump(e) ((e)->t != (e)->f || notexpr(e))
#define code_bool(f, r, b, j) codeABC(f, OP_LDBOOL, r, b, j)
#define code_call(f, a, b) codeABC(f, OP_CALL, a, b, 0)
#define code_getmbr(f, a, b, c) codeABC(f, OP_GETMBR, a, b, c)
#define jumpboolop(e, b) ((b) != notmask(e) ? OP_JMPT : OP_JMPF)
#if BE_USE_SCRIPT_COMPILER
static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst);
#if BE_DEBUG_RUNTIME_INFO
static void codelineinfo(bfuncinfo *finfo)
{
bvector *vec = &finfo->linevec;
int line = finfo->lexer->lastline;
blineinfo *li = be_vector_end(vec);
if (be_vector_isempty(vec) || li->linenumber != line) {
be_vector_push(finfo->lexer->vm, vec, NULL);
li = be_vector_end(vec);
li->endpc = finfo->pc;
li->linenumber = line;
finfo->proto->lineinfo = be_vector_data(vec);
finfo->proto->nlineinfo = be_vector_capacity(vec);
} else {
li->endpc = finfo->pc;
}
}
#else
#define codelineinfo(finfo)
#endif
static int codeinst(bfuncinfo *finfo, binstruction ins)
{
/* put new instruction in code array */
be_vector_push_c(finfo->lexer->vm, &finfo->code, &ins);
finfo->proto->code = be_vector_data(&finfo->code);
finfo->proto->codesize = be_vector_capacity(&finfo->code);
codelineinfo(finfo);
return finfo->pc++;
}
static int codeABC(bfuncinfo *finfo, bopcode op, int a, int b, int c)
{
return codeinst(finfo, ISET_OP(op)
| ISET_RA(a) | ISET_RKB(b) | ISET_RKC(c));
}
static int codeABx(bfuncinfo *finfo, bopcode op, int a, int bx)
{
return codeinst(finfo, ISET_OP(op) | ISET_RA(a) | ISET_Bx(bx));
}
static void code_move(bfuncinfo *finfo, int a, int b)
{
if (finfo->pc) {
binstruction *i = be_vector_end(&finfo->code);
bopcode op = IGET_OP(*i);
if (op <= OP_LDNIL) { /* binop or unop */
/* remove redundant MOVE instruction */
int x = IGET_RA(*i), y = IGET_RKB(*i), z = IGET_RKC(*i);
if (b == x && (a == y || (op < OP_NEG && a == z))) {
*i = (*i & ~IRA_MASK) | ISET_RA(a);
return;
}
}
}
if (isK(b)) {
codeABx(finfo, OP_LDCONST, a, b & 0xFF);
} else {
codeABC(finfo, OP_MOVE, a, b, 0);
}
}
static void free_expreg(bfuncinfo *finfo, bexpdesc *e)
{
/* release temporary register */
if (e && e->type == ETREG) {
be_code_freeregs(finfo, 1);
}
}
static void allocstack(bfuncinfo *finfo, int count)
{
int nstack = finfo->freereg + count;
if (nstack > finfo->proto->nstack) {
if (nstack >= 255) {
be_lexerror(finfo->lexer, "register overflow (more than 255)");
}
finfo->proto->nstack = (bbyte)nstack;
}
}
int be_code_allocregs(bfuncinfo *finfo, int count)
{
int base = finfo->freereg;
allocstack(finfo, count);
finfo->freereg += (char)count;
return base;
}
static void setjump(bfuncinfo *finfo, int pc, int dst)
{
binstruction *p = be_vector_at(&finfo->code, pc);
int offset = dst - (pc + 1);
/* instruction edit jump destination */
*p = (*p & ~IBx_MASK) | ISET_sBx(offset);
}
static int isjumpbool(bfuncinfo *finfo, int pc)
{
binstruction *p = be_vector_at(&finfo->code, pc);
bopcode op = IGET_OP(*p);
if (op == OP_JMPT || op == OP_JMPF) {
return 1;
}
return 0;
}
static int get_jump(bfuncinfo *finfo, int pc)
{
binstruction *i = be_vector_at(&finfo->code, pc);
int offset = IGET_sBx(*i);
return offset == NO_JUMP ? NO_JUMP : pc + 1 + offset;
}
static void patchlistaux(bfuncinfo *finfo, int list, int vtarget, int dtarget)
{
while (list != NO_JUMP) {
int next = get_jump(finfo, list);
if (isjumpbool(finfo, list)) {
setjump(finfo, list, dtarget); /* jump to default target */
} else {
setjump(finfo, list, vtarget);
}
list = next;
}
}
static int appendjump(bfuncinfo *finfo, bopcode op, bexpdesc *e)
{
int reg = e ? var2anyreg(finfo, e) : 0;
if (isK(reg)) {
reg = be_code_allocregs(finfo, 1);
code_move(finfo, reg, e->v.idx);
e->v.idx = reg;
e->type = ETREG;
}
return codeABx(finfo, op, reg, NO_JUMP + IsBx_MAX);
}
int be_code_jump(bfuncinfo *finfo)
{
return appendjump(finfo, OP_JMP, NULL);
}
void be_code_jumpto(bfuncinfo *finfo, int dst)
{
be_code_patchlist(finfo, be_code_jump(finfo), dst);
}
void be_code_jumpbool(bfuncinfo *finfo, bexpdesc *e, int jture)
{
int pc = appendjump(finfo, jumpboolop(e, jture), e);
be_code_conjump(finfo, jture ? &e->t : &e->f, pc);
be_code_patchjump(finfo, jture ? e->f : e->t);
free_expreg(finfo, e);
jture ? (e->f = NO_JUMP) : (e->t = NO_JUMP);
e->not = 0;
}
/* connect jump */
void be_code_conjump(bfuncinfo *finfo, int *list, int jmp)
{
if (jmp == NO_JUMP) {
return;
}
if (*list == NO_JUMP) {
*list = jmp;
} else {
int next, l = *list;
while ((next = (get_jump(finfo, l))) != NO_JUMP) {
l = next;
}
setjump(finfo, l, jmp);
}
}
void be_code_patchlist(bfuncinfo *finfo, int list, int dst)
{
if (dst == finfo->pc) {
be_code_patchjump(finfo, list);
} else {
patchlistaux(finfo, list, dst, dst);
}
}
void be_code_patchjump(bfuncinfo *finfo, int jmp)
{
patchlistaux(finfo, jmp, finfo->pc, finfo->pc);
}
static int newconst(bfuncinfo *finfo, bvalue *k)
{
int idx = be_vector_count(&finfo->kvec);
be_vector_push_c(finfo->lexer->vm, &finfo->kvec, k);
finfo->proto->ktab = be_vector_data(&finfo->kvec);
finfo->proto->nconst = be_vector_capacity(&finfo->kvec);
if (k == NULL) {
var_setnil(&finfo->proto->ktab[idx]);
}
return idx;
}
static int findconst(bfuncinfo *finfo, bexpdesc *e)
{
int i, count = be_vector_count(&finfo->kvec);
/* if the constant table is too large, the lookup
* operation will become very time consuming.
* so only search the constant table for the
* previous value.
**/
count = count < 50 ? count : 50;
for (i = 0; i < count; ++i) {
bvalue *k = be_vector_at(&finfo->kvec, i);
switch (e->type) {
case ETINT:
if (var_isint(k) && k->v.i == e->v.i) {
return i;
}
break;
case ETREAL:
if (var_isreal(k) && k->v.r == e->v.r) {
return i;
}
break;
case ETSTRING:
if (var_isstr(k) && be_eqstr(k->v.p, e->v.s)) {
return i;
}
break;
default:
break;
}
}
return -1;
}
static int exp2const(bfuncinfo *finfo, bexpdesc *e)
{
int idx = findconst(finfo, e);
if (idx == -1) {
bvalue k;
switch (e->type) {
case ETINT:
k.type = BE_INT;
k.v.i = e->v.i;
break;
case ETREAL:
k.type = BE_REAL;
k.v.r = e->v.r;
break;
case ETSTRING:
k.type = BE_STRING;
k.v.s = e->v.s;
break;
default:
break;
}
idx = newconst(finfo, &k);
}
if (idx < 256) {
e->type = ETCONST;
e->v.idx = setK(idx);
} else { /* index value is too large */
e->type = ETREG;
e->v.idx = be_code_allocregs(finfo, 1);
codeABx(finfo, OP_LDCONST, e->v.idx, idx);
}
return e->v.idx;
}
static void free_suffix(bfuncinfo *finfo, bexpdesc *e)
{
int idx = e->v.ss.idx;
int nlocal = be_list_count(finfo->local);
/* release suffix register */
if (!isK(idx) && idx >= nlocal) {
be_code_freeregs(finfo, 1);
}
/* release object register */
if (e->v.ss.tt == ETREG && (int)e->v.ss.obj >= nlocal) {
be_code_freeregs(finfo, 1);
}
}
static int code_suffix(bfuncinfo *finfo, bopcode op, bexpdesc *e, int dst)
{
free_suffix(finfo, e); /* free temporary registers */
if (dst > finfo->freereg) {
dst = finfo->freereg;
}
codeABC(finfo, op, dst, e->v.ss.obj, e->v.ss.idx);
return dst;
}
/* idx: the proto index in proto_table
* dst: the destination register
**/
static void code_closure(bfuncinfo *finfo, int idx, int dst)
{
codeABx(finfo, OP_CLOSURE, dst, idx); /* load closure to register */
}
static bbool constint(bfuncinfo *finfo, bint i)
{
/* cache common numbers */
if ((i < IsBx_MIN || i > IsBx_MAX) ||
(i >= 0 && i <= 3 && be_vector_count(&finfo->kvec) < 256)) {
return btrue;
}
return bfalse;
}
static int var2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{
be_assert(e != NULL);
switch (e->type) {
case ETINT:
if (constint(finfo, e->v.i)) {
return exp2const(finfo, e);
} else {
codeABx(finfo, OP_LDINT, dst, var_toidx(e) + IsBx_MAX);
}
break;
case ETBOOL:
code_bool(finfo, dst, e->v.i != 0, 0);
break;
case ETNIL:
codeABx(finfo, OP_LDNIL, dst, 0);
break;
case ETREAL:
case ETSTRING:
return exp2const(finfo, e);
case ETPROTO:
code_closure(finfo, e->v.idx, dst);
break;
case ETGLOBAL:
codeABx(finfo, OP_GETGBL, dst, e->v.idx);
break;
case ETUPVAL:
codeABx(finfo, OP_GETUPV, dst, e->v.idx);
break;
case ETMEMBER:
dst = code_suffix(finfo, OP_GETMBR, e, dst);
break;
case ETINDEX:
dst = code_suffix(finfo, OP_GETIDX, e, dst);
break;
case ETLOCAL: case ETREG: case ETCONST:
return e->v.idx;
default:
return dst; /* error */
}
if (dst == finfo->freereg) { /* use a new register */
be_code_allocregs(finfo, 1);
}
e->type = ETREG;
e->v.idx = dst;
return dst;
}
static int exp2reg(bfuncinfo *finfo, bexpdesc *e, int dst)
{
int reg = var2reg(finfo, e, dst);
if (hasjump(e)) {
int pcf = NO_JUMP; /* position of an eventual LOAD false */
int pct = NO_JUMP; /* position of an eventual LOAD true */
int jpt = appendjump(finfo, jumpboolop(e, 1), e);
reg = e->v.idx;
be_code_conjump(finfo, &e->t, jpt);
pcf = code_bool(finfo, reg, 0, 1);
pct = code_bool(finfo, reg, 1, 0);
patchlistaux(finfo, e->f, finfo->pc, pcf);
patchlistaux(finfo, e->t, finfo->pc, pct);
e->t = NO_JUMP;
e->f = NO_JUMP;
e->not = 0;
}
return reg;
}
static int codedestreg(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
int dst, con1 = e1->type == ETREG, con2 = e2->type == ETREG;
if (con1 && con2) {
dst = min(e1->v.idx, e2->v.idx);
be_code_freeregs(finfo, 1);
} else if (con1) {
dst = e1->v.idx;
} else if (con2) {
dst = e2->v.idx;
} else {
dst = be_code_allocregs(finfo, 1);
}
return dst;
}
static void binaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e1, bexpdesc *e2)
{
int src1 = exp2anyreg(finfo, e1);
int src2 = exp2anyreg(finfo, e2);
int dst = codedestreg(finfo, e1, e2);
codeABC(finfo, op, dst, src1, src2);
e1->type = ETREG;
e1->v.idx = dst;
}
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e)
{
switch (op) {
case OptAnd:
be_code_jumpbool(finfo, e, bfalse);
break;
case OptOr:
be_code_jumpbool(finfo, e, btrue);
break;
default:
exp2anyreg(finfo, e);
break;
}
}
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2)
{
switch (op) {
case OptAnd:
var2anyreg(finfo, e2);
be_code_conjump(finfo, &e2->f, e1->f);
*e1 = *e2;
break;
case OptOr:
var2anyreg(finfo, e2);
be_code_conjump(finfo, &e2->t, e1->t);
*e1 = *e2;
break;
case OptAdd: case OptSub: case OptMul: case OptDiv:
case OptMod: case OptLT: case OptLE: case OptEQ:
case OptNE: case OptGT: case OptGE: case OptConnect:
case OptBitAnd: case OptBitOr: case OptBitXor:
case OptShiftL: case OptShiftR:
binaryexp(finfo, (bopcode)(op - OptAdd), e1, e2);
break;
default: break;
}
}
static void unaryexp(bfuncinfo *finfo, bopcode op, bexpdesc *e)
{
int src = exp2anyreg(finfo, e);
int dst = e->type == ETREG ? src : be_code_allocregs(finfo, 1);
codeABC(finfo, op, dst, src, 0);
e->type = ETREG;
e->v.idx = dst;
}
static void code_not(bexpdesc *e)
{
switch (e->type) {
case ETINT: e->v.i = e->v.i == 0; break;
case ETREAL: e->v.i = e->v.r == cast(breal, 0); break;
case ETNIL: e->v.i = 1; break;
case ETBOOL: e->v.i = !e->v.i; break;
case ETSTRING: e->v.i = 0; break;
default: {
int temp = e->t;
e->t = e->f;
e->f = temp;
e->not = NOT_EXPR | (e->not ^ NOT_MASK);
return;
}
}
e->type = ETBOOL;
}
static int code_neg(bfuncinfo *finfo, bexpdesc *e)
{
switch (e->type) {
case ETINT: e->v.i = -e->v.i; break;
case ETREAL: e->v.r = -e->v.r; break;
case ETNIL: case ETBOOL: case ETSTRING:
return 1; /* error */
default:
unaryexp(finfo, OP_NEG, e);
}
return 0;
}
static int code_flip(bfuncinfo *finfo, bexpdesc *e)
{
switch (e->type) {
case ETINT: e->v.i = ~e->v.i; break;
case ETREAL: case ETNIL: case ETBOOL: case ETSTRING:
return 2; /* error */
default:
unaryexp(finfo, OP_FLIP, e);
}
return 0;
}
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e)
{
switch (op) {
case OptNot:
code_not(e); break;
case OptFlip: /* do nothing */
return code_flip(finfo, e);
case OptSub:
return code_neg(finfo, e);
default:
break;
}
return 0;
}
static void setsupvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src)
{
if (isK(src)) { /* move const to register */
code_move(finfo, finfo->freereg, src);
src = finfo->freereg;
}
codeABx(finfo, op, src, e1->v.idx);
}
static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src)
{
int obj = e1->v.ss.obj;
free_suffix(finfo, e1);
if (isK(obj)) { /* move const to register */
code_move(finfo, finfo->freereg, obj);
obj = finfo->freereg;
}
codeABC(finfo, op, obj, e1->v.ss.idx, src);
}
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
int src = exp2reg(finfo, e2,
e1->type == ETLOCAL ? e1->v.idx : finfo->freereg);
if (e1->type != ETLOCAL || e1->v.idx != src) {
free_expreg(finfo, e2); /* free source (only ETREG) */
}
switch (e1->type) {
case ETLOCAL: /* It can't be ETREG. */
if (e1->v.idx != src) {
code_move(finfo, e1->v.idx, src);
}
break;
case ETGLOBAL: /* store to grobal R(A) -> G(Bx) */
setsupvar(finfo, OP_SETGBL, e1, src);
break;
case ETUPVAL:
setsupvar(finfo, OP_SETUPV, e1, src);
break;
case ETMEMBER: /* store to member R(A).RK(B) <- RK(C) */
setsfxvar(finfo, OP_SETMBR, e1, src);
break;
case ETINDEX: /* store to member R(A)[RK(B)] <- RK(C) */
setsfxvar(finfo, OP_SETIDX, e1, src);
break;
default:
return 1;
}
return 0;
}
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e)
{
int dst = finfo->freereg;
int src = exp2anyreg(finfo, e); /* get variable register index */
if (e->type != ETREG) { /* move local and const to new register */
code_move(finfo, dst, src);
be_code_allocregs(finfo, 1);
} else {
dst = src;
}
return dst;
}
int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e)
{
int dst = finfo->freereg;
be_assert(e->type == ETMEMBER);
dst = code_suffix(finfo, OP_GETMET, e, dst);
/* method [object] args */
be_code_allocregs(finfo, dst == finfo->freereg ? 2 : 1);
return dst;
}
void be_code_call(bfuncinfo *finfo, int base, int argc)
{
codeABC(finfo, OP_CALL, base, argc, 0);
be_code_freeregs(finfo, argc);
}
int be_code_proto(bfuncinfo *finfo, bproto *proto)
{
int idx = be_vector_count(&finfo->pvec);
/* append proto to current function proto table */
be_vector_push_c(finfo->lexer->vm, &finfo->pvec, &proto);
finfo->proto->ptab = be_vector_data(&finfo->pvec);
finfo->proto->nproto = be_vector_capacity(&finfo->pvec);
return idx;
}
void be_code_closure(bfuncinfo *finfo, bexpdesc *e, int idx)
{
int reg = e->type == ETGLOBAL ? finfo->freereg: e->v.idx;
code_closure(finfo, idx, reg);
if (e->type == ETGLOBAL) { /* store to grobal R(A) -> G(Bx) */
codeABx(finfo, OP_SETGBL, reg, e->v.idx);
}
}
void be_code_close(bfuncinfo *finfo, int isret)
{
bblockinfo *binfo = finfo->binfo;
if (isret) { /* in 'return' */
while (binfo && !binfo->hasupval) {
binfo = binfo->prev;
}
if (binfo) {
codeABC(finfo, OP_CLOSE, 0, 0, 0);
}
} else if (binfo->prev) { /* leave block */
if (binfo->hasupval) {
codeABC(finfo, OP_CLOSE, binfo->nactlocals, 0, 0);
}
}
}
static void leave_function(bfuncinfo *finfo)
{
int try_depth = 0; /* count of exception catch blocks */
bblockinfo *binfo = finfo->binfo;
for (; binfo; binfo = binfo->prev) {
if (binfo->type & BLOCK_EXCEPT) {
++try_depth; /* leave the exception catch block */
}
}
if (try_depth) { /* exception catch blocks that needs to leave */
be_code_exblk(finfo, try_depth);
}
}
void be_code_ret(bfuncinfo *finfo, bexpdesc *e)
{
if (finfo->binfo->prev == NULL) {
if (finfo->flags & FUNC_RET_FLAG) {
return;
}
finfo->flags |= FUNC_RET_FLAG;
}
if (e) {
int reg = exp2anyreg(finfo, e);
be_code_close(finfo, 1);
leave_function(finfo);
codeABC(finfo, OP_RET, e->type != ETVOID, reg, 0);
free_expreg(finfo, e);
} else {
be_code_close(finfo, 1);
codeABC(finfo, OP_RET, 0, 0, 0);
}
}
static void package_suffix(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
int key = exp2anyreg(finfo, k);
c->v.ss.obj = exp2anyreg(finfo, c);
c->v.ss.tt = c->type;
c->v.ss.idx = key;
}
void be_code_member(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
package_suffix(finfo, c, k);
c->type = ETMEMBER;
}
void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k)
{
package_suffix(finfo, c, k);
c->type = ETINDEX;
}
void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c)
{
int src;
bvalue var;
var_setclass(&var, c);
src = newconst(finfo, &var);
if (dst->type == ETLOCAL) {
codeABx(finfo, OP_LDCONST, dst->v.idx, src);
} else {
codeABx(finfo, OP_LDCONST, finfo->freereg, src);
codeABx(finfo, OP_SETGBL, finfo->freereg, dst->v.idx);
}
codeABx(finfo, OP_CLASS, 0, src);
}
void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s)
{
int self = exp2anyreg(finfo, c);
int super = exp2anyreg(finfo, s);
codeABC(finfo, OP_SETSUPER, self, super, 0);
free_expreg(finfo, c);
free_expreg(finfo, s);
}
void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v)
{
int dst, src = exp2anyreg(finfo, m);
if (v->type == ETLOCAL) {
dst = v->v.idx;
codeABC(finfo, OP_IMPORT, dst, src, 0);
} else {
dst = be_code_allocregs(finfo, 1);
codeABC(finfo, OP_IMPORT, dst, src, 0);
m->type = ETREG;
m->v.idx = dst;
be_code_setvar(finfo, v, m);
}
}
int be_code_exblk(bfuncinfo *finfo, int depth)
{
if (depth == 0) {
return appendjump(finfo, OP_EXBLK, NULL);
}
codeABx(finfo, OP_EXBLK, 1, depth);
return 0;
}
void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp)
{
codeABC(finfo, OP_CATCH, base, ecnt, vcnt);
if (jmp) {
*jmp = NO_JUMP; /* reset jump point */
be_code_conjump(finfo, jmp, be_code_jump(finfo));
}
}
void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2)
{
if (e1) {
int src1 = exp2anyreg(finfo, e1);
int src2 = e2 ? exp2anyreg(finfo, e2) : 0;
codeABC(finfo, OP_RAISE, e2 != NULL, src1, src2);
} else {
codeABC(finfo, OP_RAISE, 2, 0, 0);
}
/* release the register occupied by the expression */
free_expreg(finfo, e1);
free_expreg(finfo, e2);
}
#endif

View File

@ -0,0 +1,42 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_CODE_H
#define BE_CODE_H
#include "be_parser.h"
#define be_code_freeregs(f, n) ((f)->freereg -= (bbyte)(n))
int be_code_allocregs(bfuncinfo *finfo, int count);
void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e);
void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2);
int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e);
int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2);
int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e);
int be_code_jump(bfuncinfo *finfo);
void be_code_jumpto(bfuncinfo *finfo, int dst);
void be_code_jumpbool(bfuncinfo *finfo, bexpdesc *e, int jumptrue);
void be_code_conjump(bfuncinfo *finfo, int *list, int jmp);
void be_code_patchlist(bfuncinfo *finfo, int list, int dst);
void be_code_patchjump(bfuncinfo *finfo, int jmp);
int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e);
void be_code_call(bfuncinfo *finfo, int base, int argc);
int be_code_proto(bfuncinfo *finfo, bproto *proto);
void be_code_closure(bfuncinfo *finfo, bexpdesc *e, int idx);
void be_code_close(bfuncinfo *finfo, int isret);
void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c);
void be_code_ret(bfuncinfo *finfo, bexpdesc *e);
void be_code_member(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2);
void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k);
void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s);
void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v);
int be_code_exblk(bfuncinfo *finfo, int depth);
void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp);
void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2);
#endif

View File

@ -0,0 +1,176 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_CONSTOBJ_H
#define BE_CONSTOBJ_H
#ifdef __cplusplus
extern "C" {
#endif
#include "be_object.h"
#include "be_gc.h"
#include "be_map.h"
#include "be_class.h"
#include "be_string.h"
#include "be_module.h"
#ifndef __cplusplus
#define be_const_header(_t) \
.next = NULL, \
.type = (_t), \
.marked = GC_CONST
#define be_const_key(_str, _next) { \
.v.c = &be_const_str_##_str, \
.type = BE_STRING, \
.next = (uint32_t)(_next) & 0xFFFFFF \
}
#define be_const_func(_func) { \
.v.nf = (_func), \
.type = BE_FUNCTION \
}
#define be_const_int(_val) { \
.v.i = (bint)(_val), \
.type = BE_INT \
}
#define be_const_real(_val) { \
.v.r = (breal)(_val), \
.type = BE_REAL \
}
#define be_const_class(_class) { \
.v.c = &(_class), \
.type = BE_CLASS \
}
#define be_const_module(_module) { \
.v.c = &(_module), \
.type = BE_MODULE \
}
#define be_define_const_map_slots(_name) \
const bmapnode _name##_slots[] =
#define be_define_const_map(_name, _size) \
const bmap _name = { \
be_const_header(BE_MAP), \
.slots = (bmapnode*)_name##_slots, \
.lastfree = NULL, \
.size = _size, \
.count = _size \
}
#define be_define_const_class(_name, _nvar, _super, _name_) \
const bclass _name = { \
be_const_header(BE_CLASS), \
.nvar = _nvar, \
.super = _super, \
.members = (bmap*)&_name##_map, \
.name = (bstring*)&be_const_str_##_name_ \
}
#define be_define_const_module(_name, _name_) \
const bmodule _name = { \
be_const_header(BE_MODULE), \
.table = (bmap*)&_name##_map, \
.info.name = _name_, \
}
#define be_define_const_vector(_name, _data, _size) \
const bvector _name = { \
.capacity = _size, \
.size = sizeof(bvalue), \
.count = _size, \
.data = (void*)_data, \
.end = (void*)(_data + (_size) - 1) \
}
#define be_define_const_native_module(_module, _init) \
const bntvmodule be_native_module(_module) = { \
.name = #_module, \
.attrs = NULL, \
.size = 0, \
.module = (bmodule*)&(m_lib##_module), \
.init = _init \
}
#else
#define be_const_key(_str, _next) { \
bvaldata(&be_const_str_##_str), \
BE_STRING, \
uint32_t((_next)&0xFFFFFF) \
}
#define be_const_func(_func) { \
bvaldata(_func), \
BE_FUNCTION \
}
#define be_const_int(_val) { \
bvaldata(bint(_val)), \
BE_INT \
}
#define be_const_real(_val) { \
bvaldata(breal(_val)), \
BE_REAL \
}
#define be_const_class(_class) { \
bvaldata(&(_class)), \
BE_CLASS \
}
#define be_const_module(_module) { \
bvaldata(&(_module)), \
BE_MODULE \
}
#define be_define_const_map_slots(_name) \
const bmapnode _name##_slots[] =
#define be_define_const_map(_name, _size) \
const bmap _name( \
(bmapnode*)_name##_slots, _size \
)
#define be_define_const_class(_name, _nvar, _super, _name_) \
const bclass _name( \
_nvar, _super, (bmap*)&_name##_map, \
(bstring*)&be_const_str_##_name_ \
)
#define be_define_const_module(_name, _name_) \
const bmodule _name((bmap*)&_name##_map, _name_)
#define be_define_const_vector(_name, _data, _size) \
const bvector _name = { \
_size, sizeof(bvalue), _size, \
(void*)_data, (void*)(_data + (_size) - 1) \
}
#define be_define_const_native_module(_module, _init) \
const bntvmodule be_native_module(_module) = { \
#_module, \
0, 0, \
(bmodule*)&(m_lib##_module), \
_init \
}
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,388 @@
/********************************************************************
** 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_debug.h"
#include "be_func.h"
#include "be_decoder.h"
#include "be_string.h"
#include "be_class.h"
#include "be_vm.h"
#include "be_vector.h"
#include "be_strlib.h"
#include "be_exec.h"
#include "be_mem.h"
#include <stdio.h>
#include <string.h>
#if BE_USE_DEBUG_HOOK && !BE_DEBUG_RUNTIME_INFO
#error This macro BE_USE_DEBUG_HOOK requires BE_DEBUG_RUNTIME_INFO != 0
#endif
#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)
#if BE_USE_DEBUG_MODULE
static const char* opc2str(bopcode op)
{
static const char* const opc_tab[] = {
#define OPCODE(opc) #opc
#include "be_opcodes.h"
#undef OPCODE
};
return op < array_count(opc_tab) ? opc_tab[op] : "ERROP";
}
static void print_inst(binstruction ins, int pc)
{
char __lbuf[INST_BUF_SIZE];
bopcode op = IGET_OP(ins);
logbuf(" %.4X ", pc);
be_writestring(__lbuf);
switch (op) {
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
case OP_MOD: case OP_LT: case OP_LE: case OP_EQ:
case OP_NE: case OP_GT: case OP_GE: case OP_CONNECT:
case OP_GETMBR: case OP_SETMBR: case OP_GETMET:
case OP_GETIDX: case OP_SETIDX: case OP_AND:
case OP_OR: case OP_XOR: case OP_SHL: case OP_SHR:
logbuf("%s\tR%d\tR%d\tR%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins), IGET_RKC(ins));
break;
case OP_GETGBL: case OP_SETGBL:
logbuf("%s\tR%d\tG%d", opc2str(op), IGET_RA(ins), IGET_Bx(ins));
break;
case OP_MOVE: case OP_SETSUPER: case OP_NEG: case OP_FLIP: case OP_IMPORT:
logbuf("%s\tR%d\tR%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins));
break;
case OP_JMP:
logbuf("%s\t\t#%.4X", opc2str(op), IGET_sBx(ins) + pc + 1);
break;
case OP_JMPT: case OP_JMPF:
logbuf("%s\tR%d\t#%.4X", opc2str(op), IGET_RA(ins), IGET_sBx(ins) + pc + 1);
break;
case OP_LDINT:
logbuf("%s\tR%d\t%d", opc2str(op), IGET_RA(ins), IGET_sBx(ins));
break;
case OP_LDBOOL:
logbuf("%s\tR%d\t%d\t%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins), IGET_RKC(ins));
break;
case OP_RET:
logbuf("%s\t%d\tR%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins));
break;
case OP_GETUPV: case OP_SETUPV:
logbuf("%s\tR%d\tU%d", opc2str(op), IGET_RA(ins), IGET_Bx(ins));
break;
case OP_LDCONST:
logbuf("%s\tR%d\tK%d", opc2str(op), IGET_RA(ins), IGET_Bx(ins));
break;
case OP_CALL:
logbuf("%s\tR%d\t%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins));
break;
case OP_CLOSURE:
logbuf("%s\tR%d\tP%d", opc2str(op), IGET_RA(ins), IGET_Bx(ins));
break;
case OP_CLASS:
logbuf("%s\tK%d", opc2str(op), IGET_Bx(ins));
break;
case OP_CLOSE: case OP_LDNIL:
logbuf("%s\t%d", opc2str(op), IGET_RA(ins));
break;
case OP_RAISE:
logbuf("%s\t%d\tR%d\tR%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins), IGET_RKC(ins));
break;
case OP_EXBLK:
if (IGET_RA(ins)) {
logbuf("%s\t%d\t%d", opc2str(op), IGET_RA(ins), IGET_Bx(ins));
} else {
logbuf("%s\t%d\t#%.4X", opc2str(op), IGET_RA(ins), IGET_sBx(ins) + pc + 1);
}
break;
case OP_CATCH:
logbuf("%s\tR%d\t%d\t%d", opc2str(op), IGET_RA(ins), IGET_RKB(ins), IGET_RKC(ins));
break;
default:
logbuf("%s", opc2str(op));
break;
}
be_writestring(__lbuf);
be_writenewline();
}
#endif
#if BE_USE_DEBUG_MODULE
void be_dumpclosure(bclosure *cl)
{
int pc;
bproto *proto = cl->proto;
binstruction *code = proto->code;
#if BE_DEBUG_RUNTIME_INFO
blineinfo *lineinfo = proto->lineinfo;
#endif
logfmt("source '%s', ", str(proto->source));
logfmt("function '%s':\n", str(proto->name));
#if BE_DEBUG_RUNTIME_INFO
if (lineinfo) { /* first line */
logfmt("; line %d\n", lineinfo->linenumber);
}
#endif
for (pc = 0; pc < proto->codesize; pc++) {
#if BE_DEBUG_RUNTIME_INFO
if (lineinfo && pc > lineinfo->endpc) {
logfmt("; line %d\n", (++lineinfo)->linenumber);
}
#endif
print_inst(*code++, pc);
}
}
#endif
static void sourceinfo(bproto *proto, binstruction *ip)
{
#if BE_DEBUG_RUNTIME_INFO
char buf[24];
be_assert(proto != NULL);
if (proto->lineinfo && proto->nlineinfo) {
blineinfo *it = proto->lineinfo;
blineinfo *end = it + proto->nlineinfo;
int pc = cast_int(ip - proto->code - 1); /* now vm->ip has been increased */
for (; it < end && pc > it->endpc; ++it);
sprintf(buf, ":%d:", it->linenumber);
be_writestring(str(proto->source));
be_writestring(buf);
} else {
be_writestring("<unknow source>:");
}
#else
(void)proto; (void)ip;
be_writestring("<unknow source>:");
#endif
}
static void tracestack(bvm *vm)
{
bcallsnapshot *cf;
bcallsnapshot *base = be_stack_base(&vm->tracestack);
bcallsnapshot *top = be_stack_top(&vm->tracestack);
be_writestring("stack traceback:\n");
for (cf = top; cf >= base; --cf) {
if (cf <= top - 10 && cf >= base + 10) {
if (cf == top - 10)
be_writestring("\t...\n");
continue;
}
if (var_isclosure(&cf->func)) {
bclosure *cl = var_toobj(&cf->func);
be_writestring("\t");
sourceinfo(cl->proto, cf->ip);
be_writestring(" in function `");
be_writestring(str(cl->proto->name));
be_writestring("`\n");
} else {
be_writestring("\t<native>: in native function\n");
}
}
}
static void repair_stack(bvm *vm)
{
bcallsnapshot *cf;
bcallsnapshot *base = be_stack_base(&vm->tracestack);
bcallsnapshot *top = be_stack_top(&vm->tracestack);
/* Because the native function does not push `ip` to the
* stack, the ip on the native function frame corresponds
* to the previous Berry closure. */
for (cf = top; cf >= base; --cf) {
if (!var_isclosure(&cf->func)) {
/* the last native function stack frame has the `ip` of
* the previous Berry frame */
binstruction *ip = cf->ip;
/* skip native function stack frames */
for (; cf >= base && !var_isclosure(&cf->func); --cf);
/* fixed `ip` of Berry closure frame near native function frame */
if (cf >= base) cf->ip = ip;
}
}
}
void be_tracestack(bvm *vm)
{
if (be_stack_count(&vm->tracestack)) {
repair_stack(vm);
tracestack(vm);
}
}
#if BE_USE_DEBUG_HOOK
static void hook_callnative(bvm *vm, int mask)
{
bhookinfo info;
int top = be_top(vm);
bcallframe *cf = vm->cf;
bclosure *cl = var_toobj(cf->func);
struct bhookblock *hb = var_toobj(&vm->hook);
be_stack_require(vm, BE_STACK_FREE_MIN + 2);
info.type = mask;
info.line = cf->lineinfo->linenumber;
info.source = str(cl->proto->source);
info.func_name = str(cl->proto->name);
info.data = hb->data;
hb->hook(vm, &info);
vm->top += 2;
be_pop(vm, be_top(vm) - top);
}
static int hook_pushargs(bvm *vm, int mask)
{
bcallframe *cf = vm->cf;
if (mask == BE_HOOK_LINE) {
be_pushstring(vm, "line");
be_pushint(vm, cf->lineinfo->linenumber);
return 2;
}
if (mask == BE_HOOK_CALL) {
bclosure *cl = var_toobj(cf->func);
be_pushstring(vm, "call");
var_setstr(vm->top, cl->proto->name);
vm->top++;
return 2;
}
if (mask == BE_HOOK_RET) {
be_pushstring(vm, "return");
return 1;
}
return 0;
}
static void hook_call(bvm *vm, int mask)
{
int argc;
vm->top[2] = vm->hook;
be_stack_require(vm, 5);
vm->top += 3;
argc = hook_pushargs(vm, mask);
be_call(vm, argc);
be_pop(vm, 3 + argc);
}
void be_callhook(bvm *vm, int mask)
{
if (vm->hookmask & mask) {
int hookmask = vm->hookmask;
vm->hookmask = 0;
if (var_istype(&vm->hook, BE_COMPTR)) {
hook_callnative(vm, mask);
} else {
hook_call(vm, mask);
}
vm->hookmask = (bbyte)hookmask;
}
}
static bbyte parse_hookmask(const char *mask)
{
int c, res = 0;
if (mask) {
while ((c = *mask++) != '\0') {
switch (c) {
case 'l': res |= BE_HOOK_LINE; break;
case 'c': res |= BE_HOOK_CALL; break;
case 'r': res |= BE_HOOK_RET; break;
default: break;
}
}
}
return (bbyte)res;
}
BERRY_API void be_sethook(bvm *vm, const char *mask)
{
vm->hookmask = parse_hookmask(mask);
if (vm->hookmask && var_istype(&vm->hook, BE_COMPTR)) /* free native hook */
be_free(vm, var_toobj(&vm->hook), sizeof(struct bhookblock));
if (vm->hookmask) {
vm->hook = *be_indexof(vm, -1);
} else if (!var_istype(&vm->hook, BE_COMPTR)) {
var_setnil(&vm->hook);
}
}
BERRY_API void be_setntvhook(bvm *vm, bntvhook hook, void *data, int mask)
{
struct bhookblock *hb;
if (mask) {
if (!var_istype(&vm->hook, BE_COMPTR)) {
var_setobj(&vm->hook, BE_COMPTR,
be_malloc(vm, sizeof(struct bhookblock)));
}
hb = var_toobj(&vm->hook);
be_assert(hb != NULL);
hb->hook = hook;
hb->data = data;
} else if (!var_istype(&vm->hook, BE_COMPTR)) {
var_setnil(&vm->hook);
}
vm->hookmask = (bbyte)mask;
}
#endif
#if BE_DEBUG_VAR_INFO
static binstruction* callstack_fixip(bvm *vm, int level)
{
bcallframe *top = (bcallframe*)be_stack_top(&vm->callstack);
bcallframe *cf = top - level + 2;
for (; cf <= top && cf->status & PRIM_FUNC; ++cf);
return cf <= top ? cf->ip : vm->ip;
}
bbool be_debug_varname(bvm *vm, int level, int index)
{
int depth = be_stack_count(&vm->callstack);
if (level > 0 && level <= depth) {
bcallframe *cf = be_vector_at(&vm->callstack, depth - level);
if ((cf->status & PRIM_FUNC) == 0) {
bproto *proto = cast(bclosure*, var_toobj(cf->func))->proto;
int pc = (int)(callstack_fixip(vm, level) - proto->code);
bstring *name = be_func_varname(proto, index, pc);
if (name) {
bvalue *reg = be_incrtop(vm);
var_setstr(reg, name);
return btrue;
}
}
}
return bfalse;
}
bbool be_debug_upvname(bvm *vm, int level, int index)
{
int depth = be_stack_count(&vm->callstack);
if (level > 0 && level <= depth) {
bcallframe *cf = be_vector_at(&vm->callstack, depth - level);
if ((cf->status & PRIM_FUNC) == 0) {
bproto *proto = cast(bclosure*, var_toobj(cf->func))->proto;
if (index >= 0 && index < proto->nupvals) {
bvalue *reg = be_incrtop(vm);
var_setstr(reg, proto->upvals[index].name);
return btrue;
}
}
}
return bfalse;
}
#endif

View File

@ -0,0 +1,24 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_DEBUG_H
#define BE_DEBUG_H
#include "be_object.h"
struct bhookblock {
void *data;
bntvhook hook;
};
void be_dumpclosure(bclosure *cl);
void be_tracestack(bvm *vm);
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);
#endif

View File

@ -0,0 +1,183 @@
/********************************************************************
** 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 <string.h>
#if BE_USE_DEBUG_MODULE
static void dump_map(bmap *map)
{
bmapnode *node;
bmapiter iter = be_map_iter();
while ((node = be_map_next(map, &iter)) != NULL) {
if (var_isstr(&node->key)) {
bstring *s = var_tostr(&node->key);
be_writestring("\t");
be_writebuffer(str(s), str_len(s));
be_writestring(": <");
be_writestring(be_vtype2str(&node->value));
be_writestring(">\n");
}
}
}
static void dump_module(bmodule *module)
{
dump_map(module->table);
}
static void dump_class(bclass *class)
{
dump_map(class->members);
}
static void dump_instanse(binstance *ins)
{
dump_class(ins->_class);
}
static void dump_value(bvalue *value)
{
be_writestring("value type <");
be_writestring(be_vtype2str(value));
be_writestring(">, attributes:\n");
}
static int m_attrdump(bvm *vm)
{
if (be_top(vm) >= 1) {
bvalue *v = be_indexof(vm, 1);
void *obj = var_toobj(v);
dump_value(v);
switch (var_type(v)) {
case BE_MODULE: dump_module(obj); break;
case BE_CLASS: dump_class(obj); break;
case BE_INSTANCE: dump_instanse(obj); break;
default: break;
}
}
be_return_nil(vm);
}
static int m_codedump(bvm *vm)
{
if (be_top(vm) >= 1) {
bvalue *v = be_indexof(vm, 1);
if (var_isclosure(v)) {
be_dumpclosure(var_toobj(v));
}
}
be_return_nil(vm);
}
static int m_traceback(bvm *vm)
{
be_tracestack(vm);
be_return_nil(vm);
}
#if BE_USE_DEBUG_HOOK
static int m_sethook(bvm *vm)
{
if (be_top(vm) >= 2) {
be_pushvalue(vm, 1);
be_sethook(vm, be_tostring(vm, 2));
} else {
be_sethook(vm, NULL);
}
be_return_nil(vm);
}
#endif
static int m_top(bvm *vm)
{
bint top = vm->top - vm->stack + 1;
be_pushint(vm, top);
be_return(vm);
}
static int m_calldepth(bvm *vm)
{
bint depth = be_stack_count(&vm->callstack);
be_pushint(vm, depth);
be_return(vm);
}
#if BE_DEBUG_VAR_INFO
static int v_getname(bvm *vm, bbool(*getter)(bvm *vm, int, int))
{
int index, level = 1;
if (be_top(vm) < 1)
be_raise(vm, "value_error", "too few arguments");
if (!be_isint(vm, 1) || (be_top(vm) >= 2 && !be_isint(vm, 2)))
be_raise(vm, "value_error", "invalid argument(s) value");
if (be_top(vm) >= 2)
level = be_toindex(vm, 2);
index = be_toindex(vm, 1);
if (index < 0)
be_raise(vm, "value_error", "variable index cannot be less than 0");
if (level < 1 || level >= be_stack_count(&vm->callstack))
be_raise(vm, "value_error", "invalid call depth level");
if (getter(vm, level + 1, index)) {
be_return(vm);
}
be_return_nil(vm);
}
static int m_varname(bvm *vm)
{
return v_getname(vm, be_debug_varname);
}
static int m_upvname(bvm *vm)
{
return v_getname(vm, be_debug_upvname);
}
#endif
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(debug) {
be_native_module_function("attrdump", m_attrdump),
be_native_module_function("codedump", m_codedump),
be_native_module_function("traceback", m_traceback),
#if BE_USE_DEBUG_HOOK
be_native_module_function("sethook", m_sethook),
#endif
be_native_module_function("calldepth", m_calldepth),
be_native_module_function("top", m_top),
#if BE_DEBUG_VAR_INFO
be_native_module_function("varname", m_varname),
be_native_module_function("upvname", m_upvname)
#endif
};
be_define_native_module(debug, NULL);
#else
/* @const_object_info_begin
module debug (scope: global, depend: BE_USE_DEBUG_MODULE) {
attrdump, func(m_attrdump)
codedump, func(m_codedump)
traceback, func(m_traceback)
sethook, func(m_sethook), BE_USE_DEBUG_HOOK
calldepth, func(m_calldepth)
top, func(m_top)
varname, func(m_varname), BE_DEBUG_VAR_INFO
upvname, func(m_upvname), BE_DEBUG_VAR_INFO
}
@const_object_info_end */
#include "../generate/be_fixed_debug.h"
#endif
#endif /* BE_USE_DEBUG_MODULE */

View File

@ -0,0 +1,71 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_OPCODE_H
#define BE_OPCODE_H
#define NO_JUMP -1
/* define field's bit-width */
#define IOP_BITS 6u
#define IRA_BITS 8u
#define IRKB_BITS 9u
#define IRKC_BITS 9u
/* define field's bit-width and positions */
#define IRKC_POS 0u
#define IRKB_POS (IRKC_POS + IRKC_BITS)
#define IRA_POS (IRKB_POS + IRKB_BITS)
#define IOP_POS (IRA_POS + IRA_BITS)
#define IAx_BITS (IRA_BITS + IRKB_BITS + IRKC_BITS)
#define IBx_BITS (IRKC_BITS + IRKB_BITS)
/* basic field operation */
#define INS_MASK(pos, bits) ((binstruction)((1 << (bits)) - 1) << (pos))
#define INS_GETx(i, mask, pos) cast_int(((binstruction)(i) & (mask)) >> (pos))
#define INS_SETx(v, mask, pos) (((binstruction)(v) << (pos)) & (mask))
/* instruction operation */
#define isK(v) (((v) & (1 << (IRKB_BITS - 1))) != 0)
#define setK(v) ((v) | (1 << (IRKB_BITS - 1)))
#define KR2idx(v) ((v) & 0xFF)
#define isKB(v) (((v) & (1 << (IRA_POS - 1))) != 0)
#define isKC(v) (((v) & (1 << (IRKB_POS - 1))) != 0)
/* define masks and limit values */
#define IOP_MASK INS_MASK(IOP_POS, IOP_BITS)
#define IRA_MASK INS_MASK(IRA_POS, IRA_BITS)
#define IRKB_MASK INS_MASK(IRKB_POS, IRKB_BITS)
#define IRKC_MASK INS_MASK(IRKC_POS, IRKC_BITS)
#define IAx_MASK INS_MASK(0, IAx_BITS)
#define IBx_MASK INS_MASK(0, IBx_BITS)
#define IsBx_MAX cast_int(IBx_MASK >> 1)
#define IsBx_MIN cast_int(-IsBx_MAX - 1)
/* get field */
#define IGET_OP(i) cast(bopcode, INS_GETx(i, IOP_MASK, IOP_POS))
#define IGET_RA(i) INS_GETx(i, IRA_MASK, IRA_POS)
#define IGET_RKB(i) INS_GETx(i, IRKB_MASK, IRKB_POS)
#define IGET_RKC(i) INS_GETx(i, IRKC_MASK, IRKC_POS)
#define IGET_Bx(i) INS_GETx(i, IBx_MASK, 0)
#define IGET_sBx(i) (IGET_Bx(i) - IsBx_MAX)
/* set field */
#define ISET_OP(i) INS_SETx(i, IOP_MASK, IOP_POS)
#define ISET_RA(i) INS_SETx(i, IRA_MASK, IRA_POS)
#define ISET_RKB(i) INS_SETx(i, IRKB_MASK, IRKB_POS)
#define ISET_RKC(i) INS_SETx(i, IRKC_MASK, IRKC_POS)
#define ISET_Bx(i) INS_SETx(i, IBx_MASK, 0)
#define ISET_sBx(i) (ISET_Bx(cast_int(i) + IsBx_MAX))
typedef enum {
#define OPCODE(opc) OP_##opc
#include "be_opcodes.h"
#undef OPCODE
} bopcode;
#endif

View File

@ -0,0 +1,479 @@
/********************************************************************
** 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_exec.h"
#include "be_parser.h"
#include "be_vm.h"
#include "be_vector.h"
#include "be_mem.h"
#include "be_sys.h"
#include "be_debug.h"
#include "be_bytecode.h"
#include "be_decoder.h"
#include <stdlib.h>
#if !BE_USE_SCRIPT_COMPILER && !BE_USE_BYTECODE_LOADER
#error no compiler or bytecode loader enabled.
#endif
#define FILE_BUFFER_SIZE 256
#define __STR(s) #s
#define STR(s) __STR(s)
#define STACK_OVER_MSG(n) \
"stack overflow (maximum stack size is " STR(n) ")"
#ifdef BE_EXPLICIT_ABORT
#define _os_abort BE_EXPLICIT_ABORT
#else
#define _os_abort abort
#endif
#ifdef BE_EXPLICIT_EXIT
#define _os_exit BE_EXPLICIT_EXIT
#else
#define _os_exit exit
#endif
#define exec_try(j) if (be_setjmp((j)->b) == 0)
#define exec_throw(j) be_longjmp((j)->b, 1)
#define fixup_ptr(ptr, offset) ((ptr) = (void*)((bbyte*)(ptr) + (offset)))
#define ptr_offset(ptr1, ptr2) ((bbyte*)(ptr1) - (bbyte*)(ptr2))
struct pparser {
const char *fname;
breader reader;
void *data;
bbyte islocal;
};
struct pcall {
bvalue *v;
int argc;
};
struct vmstate {
int top, reg, depth;
};
struct strbuf {
size_t len;
const char *s;
};
struct filebuf {
void *fp;
char buf[FILE_BUFFER_SIZE];
};
void be_throw(bvm *vm, int errorcode)
{
if (vm->errjmp) {
vm->errjmp->status = errorcode;
exec_throw(vm->errjmp);
} else {
_os_abort();
}
}
BERRY_API void be_exit(bvm *vm, int status)
{
if (vm->errjmp) {
be_pushint(vm, status);
be_pop(vm, 1);
be_throw(vm, BE_EXIT);
} else {
_os_exit(status);
}
}
void be_throw_message(bvm *vm, int errorcode, const char *msg)
{
be_pushstring(vm, msg);
be_throw(vm, errorcode);
}
int be_execprotected(bvm *vm, bpfunc f, void *data)
{
struct blongjmp jmp;
jmp.status = 0;
jmp.prev = vm->errjmp; /* save long jump position */
vm->errjmp = &jmp;
exec_try(vm->errjmp) {
f(vm, data);
}
vm->errjmp = jmp.prev; /* restore long jump position */
return jmp.status;
}
static void vm_state_save(bvm *vm, struct vmstate *state)
{
state->depth = be_stack_count(&vm->callstack);
state->top = cast_int(vm->top - vm->stack);
state->reg = cast_int(vm->reg - vm->stack);
}
static void copy_exception(bvm *vm, int res, int dstindex)
{
bvalue *dst = vm->stack + dstindex;
if (res == BE_EXCEPTION || res == BE_EXIT) {
bvalue *src = vm->top;
*dst++ = *src++;
if (res == BE_EXCEPTION) {
*dst++ = *src++;
}
}
vm->top = dst;
}
static void vm_state_restore(bvm *vm, const struct vmstate *state, int res)
{
vm->reg = vm->stack + state->reg;
/* copy exception information to top */
copy_exception(vm, res, state->top);
be_assert(be_stack_count(&vm->callstack) >= state->depth);
if (be_stack_count(&vm->callstack) > state->depth) {
be_vector_resize(vm, &vm->callstack, state->depth);
vm->cf = be_stack_top(&vm->callstack);
}
}
#if BE_USE_SCRIPT_COMPILER
static void m_parser(bvm *vm, void *data)
{
struct pparser *p = cast(struct pparser*, data);
bclosure *cl = be_parser_source(vm,
p->fname, p->reader, p->data, p->islocal);
var_setclosure(vm->top, cl);
be_incrtop(vm);
}
int be_protectedparser(bvm *vm,
const char *fname, breader reader, void *data, bbool islocal)
{
int res;
struct pparser s;
struct vmstate state;
s.fname = fname;
s.reader = reader;
s.data = data;
s.islocal = (bbyte)(islocal != 0);
vm_state_save(vm, &state);
res = be_execprotected(vm, m_parser, &s);
if (res) { /* restore call stack */
vm_state_restore(vm, &state, res);
}
return res;
}
static const char* _sgets(void *data, size_t *size)
{
struct strbuf *sb = data;
*size = sb->len;
if (sb->len) {
sb->len = 0;
return sb->s;
}
return NULL;
}
static const char* _fgets(void *data, size_t *size)
{
struct filebuf *fb = data;
*size = be_fread(fb->fp, fb->buf, sizeof(fb->buf));
if (*size) {
return fb->buf;
}
return NULL;
}
BERRY_API int be_loadbuffer(bvm *vm,
const char *name, const char *buffer, size_t length)
{
struct strbuf sbuf;
sbuf.s = buffer;
sbuf.len = length;
return be_protectedparser(vm, name, _sgets, &sbuf, bfalse);
}
static int fileparser(bvm *vm, const char *name, bbool islocal)
{
int res = BE_IO_ERROR;
struct filebuf *fbuf = be_malloc(vm, sizeof(struct filebuf));
fbuf->fp = be_fopen(name, "r");
if (fbuf->fp) {
res = be_protectedparser(vm, name, _fgets, fbuf, islocal);
be_fclose(fbuf->fp);
}
be_free(vm, fbuf, sizeof(struct filebuf));
return res;
}
#endif /* BE_USE_SCRIPT_COMPILER */
#if BE_USE_BYTECODE_LOADER
static void bytecode_loader(bvm *vm, void *data)
{
bclosure *cl = be_bytecode_load(vm, (const char *)data);
if (cl != NULL) {
var_setclosure(vm->top, cl);
} else {
var_setnil(vm->top);
}
be_incrtop(vm);
}
/* load bytecode file */
static int load_bytecode(bvm *vm, const char *name)
{
int res = BE_SYNTAX_ERROR;
if (be_bytecode_check(name)) {
struct vmstate state;
vm_state_save(vm, &state);
res = be_execprotected(vm, bytecode_loader, (void*)name);
if (res) { /* restore call stack */
vm_state_restore(vm, &state, res);
}
}
return res;
}
#else
#define load_bytecode(vm, name) BE_SYNTAX_ERROR
#endif /* BE_USE_BYTECODE_LOADER */
BERRY_API int be_loadmode(bvm *vm, const char *name, bbool islocal)
{
int res = load_bytecode(vm, name);
#if BE_USE_SCRIPT_COMPILER
if (res && res != BE_IO_ERROR) {
res = fileparser(vm, name, islocal);
}
#else
(void)islocal;
#endif
if (res == BE_IO_ERROR) {
be_pushfstring(vm, "cannot open file '%s'.", name);
}
return res;
}
#if BE_USE_BYTECODE_SAVER
static void _bytecode_save(bvm *vm, void *data)
{
if (be_top(vm) > 0 && var_isclosure(vm->top - 1)) {
bclosure *cl = var_toobj(vm->top - 1);
be_bytecode_save(vm, (const char *)data, cl->proto);
}
}
/* save bytecode file */
BERRY_API int be_savecode(bvm *vm, const char *name)
{
int res;
struct vmstate state;
vm_state_save(vm, &state);
res = be_execprotected(vm, _bytecode_save, (void *)name);
if (res) { /* restore call stack */
vm_state_restore(vm, &state, res);
}
return res;
}
#endif
static void m_pcall(bvm *vm, void *data)
{
struct pcall *p = cast(struct pcall*, data);
be_dofunc(vm, p->v, p->argc);
}
int be_protectedcall(bvm *vm, bvalue *v, int argc)
{
int res;
struct pcall s;
struct vmstate state;
s.v = v;
s.argc = argc;
vm_state_save(vm, &state);
res = be_execprotected(vm, m_pcall, &s);
if (res) { /* restore call stack */
vm_state_restore(vm, &state, res);
}
return res;
}
#if BE_DEBUG && defined(be_assert)
/* increase top register */
bvalue* be_incrtop(bvm *vm)
{
bvalue *top = vm->top++;
be_assert(top < vm->stacktop);
return top;
}
#endif
void be_stackpush(bvm *vm)
{
/* make sure there is enough stack space */
be_stack_require(vm, 1 + BE_STACK_FREE_MIN);
be_incrtop(vm);
}
void be_stack_require(bvm *vm, int count)
{
if (vm->top + count >= vm->stacktop) {
be_stack_expansion(vm, count);
}
}
static void update_callstack(bvm *vm, intptr_t offset)
{
bcallframe *cf = be_stack_top(&vm->callstack);
bcallframe *base = be_stack_base(&vm->callstack);
for (; cf >= base; --cf) {
fixup_ptr(cf->func, offset);
fixup_ptr(cf->top, offset);
fixup_ptr(cf->reg, offset);
}
fixup_ptr(vm->top, offset);
fixup_ptr(vm->reg, offset);
}
static void update_upvalues(bvm *vm, intptr_t offset)
{
bupval *node = vm->upvalist;
/* update the value referenced by open upvalues */
for (; node != NULL; node = node->u.next) {
fixup_ptr(node->value, offset);
}
}
static void stack_resize(bvm *vm, size_t size)
{
intptr_t offset;
bvalue *old = vm->stack;
size_t os = (vm->stacktop - old) * sizeof(bvalue);
vm->stack = be_realloc(vm, old, os, sizeof(bvalue) * size);
vm->stacktop = vm->stack + size;
offset = ptr_offset(vm->stack, old);
/* update callframes */
update_callstack(vm, offset);
/* update open upvalues */
update_upvalues(vm, offset);
}
void be_stack_expansion(bvm *vm, int n)
{
size_t size = vm->stacktop - vm->stack;
/* check new stack size */
if (size + n > BE_STACK_TOTAL_MAX) {
/* ensure the stack is enough when generating error messages. */
stack_resize(vm, size + 1);
be_raise(vm, "runtime_error", STACK_OVER_MSG(BE_STACK_TOTAL_MAX));
}
stack_resize(vm, size + n);
}
static void fixup_exceptstack(bvm* vm, struct bexecptframe* lbase)
{
struct bexecptframe *base = be_stack_base(&vm->exceptstack);
if (lbase != base) { /* the address has changed when the stack is expanded */
struct bexecptframe *top = be_stack_top(&vm->exceptstack);
bbyte *begin = (bbyte*)&lbase->errjmp;
bbyte *end = (bbyte*)&(lbase + (top - base))->errjmp;
intptr_t offset = ptr_offset(base, lbase);
struct blongjmp *errjmp = vm->errjmp;
while (errjmp) {
bbyte *prev = (bbyte*)errjmp->prev;
if (prev >= begin && prev < end) {
fixup_ptr(prev, offset); /* fixup the prev pointer */
errjmp->prev = (struct blongjmp*)prev;
}
errjmp = (struct blongjmp*)prev;
}
}
}
/* set an exception handling recovery point. To do this, we have to
* push some VM states into the exception stack. */
void be_except_block_setup(bvm *vm)
{
struct bexecptframe *frame;
struct bexecptframe *lbase = be_stack_base(&vm->exceptstack);
be_stack_push(vm, &vm->exceptstack, NULL);
frame = be_stack_top(&vm->exceptstack);
frame->depth = be_stack_count(&vm->callstack); /* the call stack depth */
frame->ip = vm->ip; /* OP_EXBLK's next instruction pointer */
/* set longjmp() jump point */
frame->errjmp.status = 0;
frame->errjmp.prev = vm->errjmp; /* save long jump list */
vm->errjmp = &frame->errjmp;
fixup_exceptstack(vm, lbase);
}
/* resumes to the state of the previous frame when an exception occurs. */
void be_except_block_resume(bvm *vm)
{
int errorcode = vm->errjmp->status;
struct bexecptframe *frame = be_stack_top(&vm->exceptstack);
if (errorcode == BE_EXCEPTION) {
vm->errjmp = vm->errjmp->prev;
/* jump to except instruction */
vm->ip = frame->ip + IGET_sBx(frame->ip[-1]);
if (be_stack_count(&vm->callstack) > frame->depth) {
bvalue *top = vm->top;
bcallframe *cf = be_vector_at(&vm->callstack, frame->depth);
vm->top = cf->top;
vm->reg = cf->reg;
vm->cf = frame->depth ? cf - 1 : NULL;
be_vector_resize(vm, &vm->callstack, frame->depth);
/* copy the exception value and argument to the top of
* the current function */
vm->top[0] = top[0]; /* exception value */
vm->top[1] = top[1]; /* exception argument */
}
be_stack_pop(&vm->exceptstack);
} else { /* other errors cannot be catch by the except block */
/* find the next error handling location */
while (vm->errjmp == &frame->errjmp) {
vm->errjmp = vm->errjmp->prev;
be_stack_pop(&vm->exceptstack);
frame = be_stack_top(&vm->exceptstack);
}
be_throw(vm, errorcode); /* rethrow this exception */
}
}
/* only close the except block, no other operations */
void be_except_block_close(bvm *vm, int count)
{
struct bexecptframe *frame;
int size = be_stack_count(&vm->exceptstack);
be_assert(count > 0 && count <= size);
frame = be_vector_at(&vm->exceptstack, size - count);
vm->errjmp = frame->errjmp.prev;
be_vector_resize(vm, &vm->exceptstack, size - count);
}
void be_save_stacktrace(bvm *vm)
{
bstack *stack = &vm->tracestack;
be_stack_clear(stack);
if (be_stack_count(&vm->callstack)) {
bcallframe *cf;
bcallframe *base = be_stack_base(&vm->callstack);
bcallframe *top = be_stack_top(&vm->callstack);
for (cf = base; cf <= top; ++cf) {
bcallsnapshot *st;
be_stack_push(vm, stack, NULL);
st = be_stack_top(stack);
st->func = *cf->func;
st->ip = cf == top ? vm->ip : cf[1].ip;
}
}
}

View File

@ -0,0 +1,62 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_EXEC_H
#define BE_EXEC_H
#include "be_object.h"
#include <setjmp.h>
/* protected-call function */
typedef void (*bpfunc)(bvm *vm, void *data);
#if BE_DEBUG
bvalue* be_incrtop(bvm *vm);
#else
/* increase top register */
#define be_incrtop(vm) ((vm)->top++)
#endif
#define be_stackpop(vm, n) ((vm)->top -= (n))
/* in MinGW-w64, setjmp / longjmp may be broken,
* so here is replaced by __builtin version. */
#if defined(__GNUC__) && defined(__MINGW32__)
#define be_setjmp(env) __builtin_setjmp(env)
#define be_longjmp(env, v) __builtin_longjmp(env, v)
#else
#define be_setjmp(env) setjmp(env)
#define be_longjmp(env, v) longjmp(env, v)
#endif
typedef jmp_buf bjmpbuf;
struct blongjmp {
bjmpbuf b;
struct blongjmp *prev;
volatile int status; /* error code */
};
struct bexecptframe {
struct blongjmp errjmp; /* long jump information */
int depth; /* function call stack depth */
binstruction *ip; /* instruction pointer */
};
void be_throw(bvm *vm, int errorcode);
int be_execprotected(bvm *vm, bpfunc f, void *data);
int be_protectedparser(bvm *vm, const char *fname,
breader reader, void *data, bbool islocal);
int be_protectedcall(bvm *vm, bvalue *v, int argc);
void be_stackpush(bvm *vm);
void be_stack_expansion(bvm *vm, int n);
void be_except_block_setup(bvm *vm);
void be_except_block_resume(bvm *vm);
void be_except_block_close(bvm *vm, int count);
void be_save_stacktrace(bvm *vm);
#endif

View File

@ -0,0 +1,179 @@
/********************************************************************
** 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_mem.h"
#include "be_sys.h"
#include "be_gc.h"
#include <string.h>
#define READLINE_STEP 100
static int i_write(bvm *vm)
{
be_getmember(vm, 1, ".p");
if(be_iscomptr(vm, -1) && be_isstring(vm, 2)) {
void *fh = be_tocomptr(vm, -1);
const char *data = be_tostring(vm, 2);
be_fwrite(fh, data, be_strlen(vm, 2));
}
be_return_nil(vm);
}
static size_t readsize(bvm *vm, int argc, void *fh)
{
if (argc >=2 && be_isint(vm, 2)) {
return be_toindex(vm, 2);
}
return be_fsize(fh) - be_ftell(fh);
}
static int i_read(bvm *vm)
{
int argc = be_top(vm);
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1)) {
void *fh = be_tocomptr(vm, -1);
size_t size = readsize(vm, argc, fh);
if (size) {
char *buffer = be_malloc(vm, size);
size = be_fread(fh, buffer, size);
be_pushnstring(vm, buffer, size);
be_free(vm, buffer, size);
} else {
be_pushstring(vm, "");
}
be_return(vm);
}
be_return_nil(vm);
}
static int i_readline(bvm *vm)
{
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1)) {
void *fh = be_tocomptr(vm, -1);
size_t pos = 0, size = READLINE_STEP;
char *buffer = be_malloc(vm, size);
char *res = be_fgets(fh, buffer, (int)size);
while (res) {
pos += strlen(buffer + pos);
if (!pos || buffer[pos - 1] == '\n') {
break;
}
buffer = be_realloc(vm, buffer, size, size + READLINE_STEP);
res = be_fgets(fh, buffer + pos, READLINE_STEP);
size += READLINE_STEP;
}
be_pushnstring(vm, buffer, pos);
be_free(vm, buffer, size);
be_return(vm);
}
be_return_nil(vm);
}
static int i_seek(bvm *vm)
{
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1) && be_isint(vm, 2)) {
void *fh = be_tocomptr(vm, -1);
be_fseek(fh, be_toindex(vm, 2));
}
be_return_nil(vm);
}
static int i_tell(bvm *vm)
{
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1)) {
void *fh = be_tocomptr(vm, -1);
size_t pos = be_ftell(fh);
be_pushint(vm, cast(bint, pos));
be_return(vm);
}
be_return_nil(vm);
}
static int i_size(bvm *vm)
{
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1)) {
void *fh = be_tocomptr(vm, -1);
size_t pos = be_fsize(fh);
be_pushint(vm, cast(bint, pos));
be_return(vm);
}
be_return_nil(vm);
}
static int i_flush(bvm *vm)
{
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1)) {
void *fh = be_tocomptr(vm, -1);
be_fflush(fh);
}
be_return_nil(vm);
}
static int i_close(bvm *vm)
{
be_getmember(vm, 1, ".p");
if (be_iscomptr(vm, -1)) {
void *fh = be_tocomptr(vm, -1);
be_fclose(fh);
be_pushnil(vm);
be_setmember(vm, 1, ".p");
}
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
static int m_open(bvm *vm)
#else
int be_nfunc_open(bvm *vm)
#endif
{
int argc = be_top(vm);
const char *fname, *mode;
static const bnfuncinfo members[] = {
{ ".p", NULL },
{ "write", i_write },
{ "read", i_read },
{ "readline", i_readline },
{ "seek", i_seek },
{ "tell", i_tell },
{ "size", i_size },
{ "flush", i_flush },
{ "close", i_close },
{ "deinit", i_close },
{ NULL, NULL }
};
fname = argc >= 1 && be_isstring(vm, 1) ? be_tostring(vm, 1) : NULL;
mode = argc >= 2 && be_isstring(vm, 2) ? be_tostring(vm, 2) : "r";
if (fname) {
void *fh = be_fopen(fname, mode);
if (fh == NULL) {
be_raise(vm, "io_error",
be_pushfstring(vm, "cannot open file '%s'", fname));
}
be_pushclass(vm, "file", members);
be_call(vm, 0);
be_pushcomptr(vm, fh);
be_setmember(vm, -2, ".p");
be_pop(vm, 1);
be_return(vm);
}
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
void be_load_filelib(bvm *vm)
{
be_regfunc(vm, "open", m_open);
}
#endif

View File

@ -0,0 +1,176 @@
/********************************************************************
** 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_func.h"
#include "be_gc.h"
#include "be_mem.h"
#include "be_vm.h"
#include "be_exec.h"
#include <string.h>
#define clousersize(n) \
(sizeof(bclosure) + sizeof(bupval*) * ((size_t)(n) - 1))
static bupval* findupval(bvm *vm, bvalue *level)
{
bupval *node = vm->upvalist;
while (node != NULL && node->value > level) {
node = node->u.next;
}
if (!node || node->value != level) {
/* not found */
node = be_malloc(vm, sizeof(bupval));
node->value = level;
node->refcnt = 0;
/* insert to list head */
node->u.next = vm->upvalist;
vm->upvalist = node;
}
return node;
}
void be_initupvals(bvm *vm, bclosure *cl)
{
int count = cl->proto->nupvals;
bupvaldesc *desc = cl->proto->upvals;
bvalue *stack = vm->reg;
bupval **uv = cl->upvals;
bupval **superuv = cast(bclosure*, var_toobj(vm->cf->func))->upvals;
for (; count--; desc++, uv++) {
if (desc->instack) {
bvalue *ref = stack + desc->idx;
*uv = findupval(vm, ref);
} else {
*uv = superuv[desc->idx];
}
(*uv)->refcnt++;
}
}
void be_upvals_close(bvm *vm, bvalue *level)
{
bupval *node = vm->upvalist, *next;
while (node && node->value >= level) {
next = node->u.next;
if (!node->refcnt) {
be_free(vm, node, sizeof(bupval));
} else {
node->u.value = *node->value; /* move value to upvalue slot */
node->value = &node->u.value;
}
node = next;
}
vm->upvalist = node;
}
void be_release_upvalues(bvm *vm, bclosure *cl)
{
int i, count = cl->nupvals;
for (i = 0; i < count; ++i) {
bupval *uv = cl->upvals[i];
if (uv) {
if (uv->refcnt) {
--uv->refcnt;
}
/* delete non-referenced closed upvalue */
if (uv->value == &uv->u.value && !uv->refcnt) {
be_free(vm, uv, sizeof(bupval));
}
}
}
}
bproto* be_newproto(bvm *vm)
{
bgcobject *gco = be_gcnew(vm, BE_PROTO, bproto);
bproto *p = cast_proto(gco);
if (p) {
p->upvals = NULL;
p->ktab = NULL;
p->ptab = NULL;
p->code = NULL;
p->name = NULL;
p->gray = NULL;
p->codesize = 0;
p->nupvals = 0;
p->nproto = 0;
p->nconst = 0;
p->nstack = 0;
p->codesize = 0;
p->argc = 0;
p->varg = 0;
p->source = NULL;
#if BE_DEBUG_RUNTIME_INFO
p->lineinfo = NULL;
p->nlineinfo = 0;
#endif
#if BE_DEBUG_VAR_INFO
p->varinfo = NULL;
p->nvarinfo = 0;
#endif
}
return p;
}
bclosure* be_newclosure(bvm *vm, int nupval)
{
bgcobject *gco = be_newgcobj(vm, BE_CLOSURE, clousersize(nupval));
bclosure *cl = cast_closure(gco);
if (cl) {
cl->proto = NULL;
cl->nupvals = (bbyte)nupval;
while (nupval--) {
cl->upvals[nupval] = NULL;
}
}
return cl;
}
static void init_upvals(bvm *vm, bntvclos *f)
{
int count = f->nupvals;
bupval **upvals = &be_ntvclos_upval(f, 0);
while (count--) {
bupval *uv = be_malloc(vm, sizeof(bupval)); /* was closed */
uv->value = &uv->u.value;
uv->refcnt = 1;
var_setnil(uv->value);
*upvals++ = uv;
}
}
bntvclos* be_newntvclosure(bvm *vm, bntvfunc cf, int nupvals)
{
size_t size = sizeof(bntvclos) + sizeof(bupval*) * nupvals;
bgcobject *gco = be_newgcobj(vm, BE_NTVCLOS, size);
bntvclos *f = cast_ntvclos(gco);
if (f) {
f->f = cf;
f->nupvals = (bbyte)nupvals;
if (nupvals) {
var_setntvclos(vm->top, f);
be_incrtop(vm);
init_upvals(vm, f); /* may be GC */
be_stackpop(vm, 1);
}
}
return f;
}
#if BE_DEBUG_VAR_INFO
bstring* be_func_varname(bproto *proto, int index, int pc)
{
int i, nvarinfo = proto->nvarinfo;
bvarinfo *varinfo = proto->varinfo;
for (i = 0; i < nvarinfo && varinfo[i].beginpc <= pc; ++i) {
if (pc <= varinfo[i].endpc && index-- == 0) {
return varinfo[i].name;
}
}
return NULL;
}
#endif

View File

@ -0,0 +1,27 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_FUNC_H
#define BE_FUNC_H
#include "be_object.h"
#define be_newntvclos(vm, cf) \
be_newntvclosure(vm, cf, 0)
#define be_ntvclos_upval(cc, n) \
(((bupval**)((size_t)cc + sizeof(bntvclos)))[n])
void be_initupvals(bvm *vm, bclosure *cl);
void be_upvals_close(bvm *vm, bvalue *level);
void be_release_upvalues(bvm *vm, bclosure *cl);
bproto* be_newproto(bvm *vm);
bclosure* be_newclosure(bvm *vm, int nupval);
bntvclos* be_newntvclosure(bvm *vm, bntvfunc cf, int nupvals);
bstring* be_func_varname(bproto *proto, int index, int pc);
#endif

View File

@ -0,0 +1,523 @@
/********************************************************************
** 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_gc.h"
#include "be_vm.h"
#include "be_mem.h"
#include "be_var.h"
#include "be_vector.h"
#include "be_string.h"
#include "be_class.h"
#include "be_list.h"
#include "be_func.h"
#include "be_map.h"
#include "be_module.h"
#include "be_exec.h"
#include "be_debug.h"
#define GC_PAUSE (1 << 0) /* GC will not be executed automatically */
#define GC_HALT (1 << 1) /* GC completely stopped */
#define GC_ALLOC (1 << 2) /* GC in alloc */
#define gc_try(expr) be_assert(expr); if (expr)
#define gc_setdark_safe(o) if (o) gc_setdark(o)
#define next_threshold(gc) ((gc).usage * ((size_t)(gc).steprate + 100) / 100)
#define link_gray(vm, obj) { \
(obj)->gray = (vm)->gc.gray; \
(vm)->gc.gray = gc_object(obj); \
}
static void destruct_object(bvm *vm, bgcobject *obj);
static void free_object(bvm *vm, bgcobject *obj);
void be_gc_init(bvm *vm)
{
vm->gc.usage = sizeof(bvm);
be_gc_setsteprate(vm, 200);
}
void be_gc_deleteall(bvm *vm)
{
bupval *uv, *uvnext;
bgcobject *node, *next;
/* halt GC and delete all objects */
vm->gc.status |= GC_HALT;
/* first: call destructor */
for (node = vm->gc.list; node; node = node->next) {
destruct_object(vm, node);
}
/* second: free objects */
for (node = vm->gc.list; node; node = next) {
next = node->next;
free_object(vm, node);
}
/* delete open upvalue list */
for (uv = vm->upvalist; uv; uv = uvnext) {
uvnext = uv->u.next;
be_free(vm, uv, sizeof(bupval));
}
}
void be_gc_setsteprate(bvm *vm, int rate)
{
be_assert(rate >= 100 && rate <= 355);
vm->gc.steprate = (bbyte)(rate - 100);
vm->gc.threshold = next_threshold(vm->gc);
}
void be_gc_setpause(bvm *vm, int pause)
{
if (pause) {
vm->gc.status |= GC_PAUSE;
} else {
vm->gc.status &= ~GC_PAUSE;
}
}
bgcobject* be_newgcobj(bvm *vm, int type, size_t size)
{
bgcobject *obj = be_malloc(vm, size);
be_gc_auto(vm);
var_settype(obj, (bbyte)type); /* mark the object type */
obj->marked = GC_WHITE; /* default gc object type is white */
obj->next = vm->gc.list; /* link to the next field */
vm->gc.list = obj; /* insert to head */
return obj;
}
bgcobject* be_gc_newstr(bvm *vm, size_t size, int islong)
{
bgcobject *obj;
if (islong) { /* creating long strings is similar to ordinary GC objects */
return be_newgcobj(vm, BE_STRING, size);
}
obj = be_malloc(vm, size);
be_gc_auto(vm);
var_settype(obj, BE_STRING); /* mark the object type to BE_STRING */
obj->marked = GC_WHITE; /* default string type is white */
return obj;
}
void be_gc_fix(bvm *vm, bgcobject *obj)
{
(void)vm;
if (!gc_isconst(obj)) {
gc_setfixed(obj);
}
}
void be_gc_unfix(bvm *vm, bgcobject *obj)
{
(void)vm;
if (!gc_isconst(obj)) {
gc_clearfixed(obj);
}
}
static void mark_gray(bvm *vm, bgcobject *obj)
{
if (obj && gc_iswhite(obj) && !gc_isconst(obj)) {
gc_setgray(obj);
switch (var_type(obj)) {
case BE_STRING: gc_setdark(obj); break; /* just set dark */
case BE_CLASS: link_gray(vm, cast_class(obj)); break;
case BE_PROTO: link_gray(vm, cast_proto(obj)); break;
case BE_INSTANCE: link_gray(vm, cast_instance(obj)); break;
case BE_MAP: link_gray(vm, cast_map(obj)); break;
case BE_LIST: link_gray(vm, cast_list(obj)); break;
case BE_CLOSURE: link_gray(vm, cast_closure(obj)); break;
case BE_NTVCLOS: link_gray(vm, cast_ntvclos(obj)); break;
case BE_MODULE: link_gray(vm, cast_module(obj)); break;
case BE_COMOBJ: gc_setdark(obj); break; /* just set dark */
default: break;
}
}
}
static void mark_gray_var(bvm *vm, bvalue *value)
{
if (be_isgcobj(value)) {
mark_gray(vm, var_togc(value));
}
}
static void mark_map(bvm *vm, bgcobject *obj)
{
bmap *map = cast_map(obj);
gc_try (map != NULL) {
bmapnode *node;
bmapiter iter = be_map_iter();
vm->gc.gray = map->gray; /* remove object from gray list */
while ((node = be_map_next(map, &iter)) != NULL) {
bmapkey *key = &node->key;
bvalue *val = &node->value;
if (be_isgctype((signed char)key->type)) {
mark_gray(vm, var_togc(key));
}
mark_gray_var(vm, val);
}
}
}
static void mark_list(bvm *vm, bgcobject *obj)
{
blist *list = cast_list(obj);
gc_try (list != NULL) {
bvalue *val = be_list_data(list);
bvalue *end = be_list_end(list);
vm->gc.gray = list->gray; /* remove object from gray list */
for (; val < end; val++) {
mark_gray_var(vm, val);
}
}
}
static void mark_proto(bvm *vm, bgcobject *obj)
{
bproto *p = cast_proto(obj);
gc_try (p != NULL) {
int count;
bvalue *k = p->ktab;
bproto **ptab = p->ptab;
vm->gc.gray = p->gray; /* remove object from gray list */
for (count = p->nconst; count--; ++k) {
mark_gray_var(vm, k);
}
for (count = p->nproto; count--; ++ptab) {
mark_gray(vm, gc_object(*ptab));
}
gc_setdark_safe(p->name);
gc_setdark_safe(p->source);
#if BE_DEBUG_VAR_INFO
if (p->nvarinfo) {
bvarinfo *vinfo = p->varinfo;
be_assert(vinfo != NULL);
for (count = p->nvarinfo; count--; ++vinfo) {
gc_setdark_safe(vinfo->name);
}
}
#endif
}
}
static void mark_closure(bvm *vm, bgcobject *obj)
{
bclosure *cl = cast_closure(obj);
gc_try (cl != NULL) {
int count = cl->nupvals;
bupval **uv = cl->upvals;
vm->gc.gray = cl->gray; /* remove object from gray list */
for (; count--; ++uv) {
if (*uv && (*uv)->refcnt) {
mark_gray_var(vm, (*uv)->value);
}
}
mark_gray(vm, gc_object(cl->proto));
}
}
static void mark_ntvclos(bvm *vm, bgcobject *obj)
{
bntvclos *f = cast_ntvclos(obj);
gc_try (f != NULL) {
int count = f->nupvals;
bupval **uv = &be_ntvclos_upval(f, 0);
vm->gc.gray = f->gray; /* remove object from gray list */
for (; count--; ++uv) {
if (*uv && (*uv)->refcnt) {
mark_gray_var(vm, (*uv)->value);
}
}
}
}
static void mark_class(bvm *vm, bgcobject *obj)
{
bclass *c = cast_class(obj);
gc_try (c != NULL) {
vm->gc.gray = c->gray; /* remove object from gray list */
mark_gray(vm, gc_object(be_class_name(c)));
mark_gray(vm, gc_object(be_class_members(c)));
mark_gray(vm, gc_object(be_class_super(c)));
}
}
static void mark_instance(bvm *vm, bgcobject *obj)
{
binstance *o = cast_instance(obj);
gc_try (o != NULL) {
bvalue *var = be_instance_members(o);
int nvar = be_instance_member_count(o);
vm->gc.gray = o->gray; /* remove object from gray list */
mark_gray(vm, gc_object(be_instance_class(o)));
mark_gray(vm, gc_object(be_instance_super(o)));
for (; nvar--; var++) { /* mark variables */
mark_gray_var(vm, var);
}
}
}
static void mark_module(bvm *vm, bgcobject *obj)
{
bmodule *o = cast_module(obj);
gc_try (o != NULL) {
vm->gc.gray = o->gray; /* remove object from gray list */
mark_gray(vm, gc_object(o->table));
if (!gc_isconst(o) && gc_exmark(o) & BE_MODULE_NAME) {
mark_gray(vm, gc_object(o->info.sname));
}
}
}
static void free_proto(bvm *vm, bgcobject *obj)
{
bproto *proto = cast_proto(obj);
gc_try (proto != NULL) {
be_free(vm, proto->upvals, proto->nupvals * sizeof(bupvaldesc));
be_free(vm, proto->ktab, proto->nconst * sizeof(bvalue));
be_free(vm, proto->ptab, proto->nproto * sizeof(bproto*));
be_free(vm, proto->code, proto->codesize * sizeof(binstruction));
#if BE_DEBUG_RUNTIME_INFO
be_free(vm, proto->lineinfo, proto->nlineinfo * sizeof(blineinfo));
#endif
be_free(vm, proto, sizeof(bproto));
}
}
static void free_closure(bvm *vm, bgcobject *obj)
{
bclosure *cl = cast_closure(obj);
gc_try (cl != NULL) {
int count = cl->nupvals;
be_release_upvalues(vm, cl);
be_free(vm, cl, sizeof(bclosure)
+ sizeof(bupval*) * ((size_t)count - 1));
}
}
static void free_ntvclos(bvm *vm, bgcobject *obj)
{
bntvclos *f = cast_ntvclos(obj);
gc_try (f != NULL) {
int count = f->nupvals;
bupval **uv = &be_ntvclos_upval(f, 0);
while (count--) {
be_free(vm, *uv++, sizeof(bupval));
}
be_free(vm, f, sizeof(bntvclos));
}
}
static void free_lstring(bvm *vm, bgcobject *obj)
{
blstring *ls = gc_cast(obj, BE_STRING, blstring);
gc_try (ls != NULL) {
be_free(vm, ls, sizeof(blstring) + ls->llen + 1);
}
}
static void free_object(bvm *vm, bgcobject *obj)
{
switch (obj->type) {
case BE_STRING: free_lstring(vm, obj); break; /* long string */
case BE_CLASS: be_free(vm, obj, sizeof(bclass)); break;
case BE_INSTANCE: be_free(vm, obj, sizeof(binstance)); break;
case BE_MAP: be_map_delete(vm, cast_map(obj)); break;
case BE_LIST: be_list_delete(vm, cast_list(obj)); break;
case BE_CLOSURE: free_closure(vm, obj); break;
case BE_NTVCLOS: free_ntvclos(vm, obj); break;
case BE_PROTO: free_proto(vm, obj); break;
case BE_MODULE: be_module_delete(vm, cast_module(obj)); break;
case BE_COMOBJ: be_commonobj_delete(vm, obj); break;
default: break; /* case BE_STRING: break; */
}
}
static void premark_internal(bvm *vm)
{
mark_gray(vm, gc_object(vm->module.loaded));
mark_gray(vm, gc_object(vm->module.path));
mark_gray(vm, gc_object(vm->ntvclass));
mark_gray(vm, gc_object(vm->registry));
#if BE_USE_DEBUG_HOOK
if (be_isgcobj(&vm->hook)) {
mark_gray(vm, gc_object(var_toobj(&vm->hook)));
}
#endif
}
static void premark_global(bvm *vm)
{
bvalue *v = vm->gbldesc.global.vlist.data;
bvalue *end = v + be_global_count(vm);
while (v < end) {
if (be_isgcobj(v)) {
mark_gray(vm, var_togc(v));
}
++v;
}
v = vm->gbldesc.builtin.vlist.data;
end = v + be_builtin_count(vm);
while (v < end) {
mark_gray_var(vm, v++);
}
}
static void premark_stack(bvm *vm)
{
bvalue *v = vm->stack, *end = vm->top;
/* mark live objects */
for (; v < end; ++v) {
mark_gray_var(vm, v);
}
/* set other values to nil */
end = vm->stacktop;
for (; v < end; ++v) {
var_setnil(v);
}
}
static void premark_tracestack(bvm *vm)
{
bcallsnapshot *cf = be_vector_data(&vm->tracestack);
bcallsnapshot *end = be_vector_end(&vm->tracestack);
for (; cf <= end; ++cf) {
mark_gray_var(vm, &cf->func);
}
}
static void premark_fixed(bvm *vm)
{
bgcobject *node = vm->gc.list;
for (; node; node = node->next) {
if (gc_isfixed(node) && gc_iswhite(node)) {
mark_gray(vm, node);
}
}
}
static void mark_unscanned(bvm *vm)
{
while (vm->gc.gray) {
bgcobject *obj = vm->gc.gray;
if (obj && !gc_isdark(obj) && !gc_isconst(obj)) {
gc_setdark(obj);
switch (var_type(obj)) {
case BE_CLASS: mark_class(vm, obj); break;
case BE_PROTO: mark_proto(vm, obj); break;
case BE_INSTANCE: mark_instance(vm, obj); break;
case BE_MAP: mark_map(vm, obj); break;
case BE_LIST: mark_list(vm, obj); break;
case BE_CLOSURE: mark_closure(vm, obj); break;
case BE_NTVCLOS: mark_ntvclos(vm, obj); break;
case BE_MODULE: mark_module(vm, obj); break;
default:
be_assert(0); /* error */
break;
}
}
}
}
static void destruct_object(bvm *vm, bgcobject *obj)
{
if (vm->gc.status & GC_ALLOC) {
return; /* no destructor is called during the allocation. */
}
if (obj->type == BE_INSTANCE) {
int type;
binstance *ins = cast_instance(obj);
/* does not GC when creating the string "deinit". */
type = be_instance_member(vm, ins, str_literal(vm, "deinit"), vm->top);
be_incrtop(vm);
if (basetype(type) == BE_FUNCTION) {
be_dofunc(vm, vm->top - 1, 1);
}
be_stackpop(vm, 1);
}
}
static void destruct_white(bvm *vm)
{
bgcobject *node = vm->gc.list;
/* since the destructor may allocate objects, we must first suspend the GC */
vm->gc.status |= GC_HALT; /* mark GC is halt */
while (node) {
if (gc_iswhite(node)) {
destruct_object(vm, node);
}
node = node->next;
}
vm->gc.status &= ~GC_HALT; /* reset GC halt flag */
}
static void delete_white(bvm *vm)
{
bgcobject *node, *prev, *next;
for (node = vm->gc.list, prev = node; node; node = next) {
next = node->next;
if (gc_iswhite(node)) {
if (node == vm->gc.list) { /* first node */
vm->gc.list = node->next;
prev = node->next;
} else { /* not first node */
prev->next = next;
}
free_object(vm, node);
} else {
gc_setwhite(node);
prev = node;
}
}
}
static void reset_fixedlist(bvm *vm)
{
bgcobject *node;
for (node = vm->gc.fixed; node; node = node->next) {
if (gc_isdark(node)) {
gc_setwhite(node);
}
}
}
void be_gc_auto(bvm *vm)
{
if (vm->gc.status & GC_PAUSE && vm->gc.usage > vm->gc.threshold) {
be_gc_collect(vm);
}
}
size_t be_gc_memcount(bvm *vm)
{
return vm->gc.usage;
}
void be_gc_collect(bvm *vm)
{
if (vm->gc.status & GC_HALT) {
return; /* the GC cannot run for some reason */
}
/* step 1: set root-set reference objects to unscanned */
premark_internal(vm); /* object internal the VM */
premark_global(vm); /* global objects */
premark_stack(vm); /* stack objects */
premark_tracestack(vm); /* trace stack objects */
premark_fixed(vm); /* fixed objects */
/* step 2: set unscanned objects to black */
mark_unscanned(vm);
/* step 3: destruct and delete unreachable objects */
destruct_white(vm);
delete_white(vm);
be_gcstrtab(vm);
/* step 4: reset the fixed objects */
reset_fixedlist(vm);
/* step 5: calculate the next GC threshold */
vm->gc.threshold = next_threshold(vm->gc);
}

View File

@ -0,0 +1,74 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_GC_H
#define BE_GC_H
#include "be_object.h"
#define BE_GCOBJECT BE_STRING
#define gc_object(o) cast(bgcobject*, o)
#define gc_cast(o, t, T) ((o) && (o)->type == (t) ? (T*)(o) : NULL)
#define cast_proto(o) gc_cast(o, BE_PROTO, bproto)
#define cast_closure(o) gc_cast(o, BE_CLOSURE, bclosure)
#define cast_ntvclos(o) gc_cast(o, BE_NTVCLOS, bntvclos)
#define cast_str(o) gc_cast(o, BE_STRING, bstring)
#define cast_class(o) gc_cast(o, BE_CLASS, bclass)
#define cast_instance(o) gc_cast(o, BE_INSTANCE, binstance)
#define cast_map(o) gc_cast(o, BE_MAP, bmap)
#define cast_list(o) gc_cast(o, BE_LIST, blist)
#define cast_module(o) gc_cast(o, BE_MODULE, bmodule)
#define gc_ismark(o, m) (((o)->marked & 0x03) == m)
#define gc_iswhite(o) gc_ismark((o), GC_WHITE)
#define gc_isgray(o) gc_ismark((o), GC_GRAY)
#define gc_isdark(o) gc_ismark((o), GC_DARK)
#define gc_setmark(o, m) \
if (!gc_isconst(o)) { \
(o)->marked &= ~0x03; \
(o)->marked |= (m) & 0x03; \
}
#define gc_setwhite(o) gc_setmark((o), GC_WHITE)
#define gc_setgray(o) gc_setmark((o), GC_GRAY)
#define gc_setdark(o) gc_setmark((o), GC_DARK)
#define gc_isfixed(o) (((o)->marked & GC_FIXED) != 0)
#define gc_setfixed(o) ((o)->marked |= GC_FIXED)
#define gc_clearfixed(o) ((o)->marked &= ~GC_FIXED)
#define gc_isconst(o) (((o)->marked & GC_CONST) != 0)
#define gc_exmark(o) (((o)->marked >> 4) & 0x0F)
#define gc_setexmark(o, k) ((o)->marked |= (k) << 4)
#define be_isgctype(t) ((t) >= BE_GCOBJECT)
#define be_isgcobj(o) be_isgctype(var_type(o))
#define be_gcnew(v, t, s) be_newgcobj((v), (t), sizeof(s))
/* the GC mark uses bit4:0 of the `object->marked` field,
* so other bits can be used for special flags (ex-mark). */
typedef enum {
GC_WHITE = 0x00, /* unreachable object */
GC_GRAY = 0x01, /* unscanned object */
GC_DARK = 0x02, /* scanned object */
GC_FIXED = 0x04, /* disable collection mark */
GC_CONST = 0x08 /* constant object mark */
} bgcmark;
void be_gc_init(bvm *vm);
void be_gc_deleteall(bvm *vm);
void be_gc_setsteprate(bvm *vm, int rate);
void be_gc_setpause(bvm *vm, int pause);
size_t be_gc_memcount(bvm *vm);
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);
void be_gc_collect(bvm *vm);
void be_gc_auto(bvm *vm);
#endif

View File

@ -0,0 +1,47 @@
/********************************************************************
** 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_gc.h"
#if BE_USE_GC_MODULE
static int m_allocated(bvm *vm)
{
size_t count = be_gc_memcount(vm);
if (count < 0x80000000) {
be_pushint(vm, (bint)count);
} else {
be_pushreal(vm, (breal)count);
}
be_return(vm);
}
static int m_collect(bvm *vm)
{
be_gc_collect(vm);
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(gc){
be_native_module_function("allocated", m_allocated),
be_native_module_function("collect", m_collect)
};
be_define_native_module(gc, NULL);
#else
/* @const_object_info_begin
module gc (scope: global, depend: BE_USE_GC_MODULE) {
allocated, func(m_allocated)
collect, func(m_collect)
}
@const_object_info_end */
#include "../generate/be_fixed_gc.h"
#endif
#endif /* BE_USE_SYS_MODULE */

View File

@ -0,0 +1,437 @@
/********************************************************************
** 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_mem.h"
#include <string.h>
#if BE_USE_JSON_MODULE
#define MAX_INDENT 24
#define INDENT_WIDTH 2
#define INDENT_CHAR ' '
static const char* parser_value(bvm *vm, const char *json);
static void value_dump(bvm *vm, int *indent, int idx, int fmt);
static const char* skip_space(const char *s)
{
int c;
while (((c = *s) != '\0') && ((c == ' ')
|| (c == '\t') || (c == '\r') || (c == '\n'))) {
++s;
}
return s;
}
static int is_digit(int c)
{
return c >= '0' && c <= '9';
}
static const char* match_char(const char *json, int ch)
{
json = skip_space(json);
if (*json == ch) {
return skip_space(json + 1);
}
return NULL;
}
static int is_object(bvm *vm, const char *class, int idx)
{
if (be_isinstance(vm, idx)) {
const char *name = be_classname(vm, idx);
return !strcmp(name, class);
}
return 0;
}
static int json_strlen(const char *json)
{
int ch;
const char *s = json + 1; /* skip '"' */
/* get string length "(\\.|[^"])*" */
while ((ch = *s) != '\0' && ch != '"') {
++s;
if (ch == '\\') {
ch = *s++;
if (ch == '\0') {
return -1;
}
}
}
return ch ? cast_int(s - json - 1) : -1;
}
static void json2berry(bvm *vm, const char *class)
{
be_getbuiltin(vm, class);
be_pushvalue(vm, -2);
be_call(vm, 1);
be_moveto(vm, -2, -3);
be_pop(vm, 2);
}
static const char* parser_true(bvm *vm, const char *json)
{
if (!strncmp(json, "true", 4)) {
be_pushbool(vm, btrue);
return json + 4;
}
return NULL;
}
static const char* parser_false(bvm *vm, const char *json)
{
if (!strncmp(json, "false", 5)) {
be_pushbool(vm, bfalse);
return json + 5;
}
return NULL;
}
static const char* parser_null(bvm *vm, const char *json)
{
if (!strncmp(json, "null", 4)) {
be_pushnil(vm);
return json + 4;
}
return NULL;
}
static char* load_unicode(char *dst, const char *json)
{
int ucode = 0, i = 4;
while (i--) {
int ch = *json++;
if (ch >= '0' && ch <= '9') {
ucode = (ucode << 4) | (ch - '0');
} else if (ch >= 'A' && ch <= 'F') {
ucode = (ucode << 4) | (ch - 'A' + 0x0A);
} else if (ch >= 'a' && ch <= 'f') {
ucode = (ucode << 4) | (ch - 'a' + 0x0A);
} else {
return NULL;
}
}
/* convert unicode to utf8 */
if (ucode < 0x007F) {
/* unicode: 0000 - 007F -> utf8: 0xxxxxxx */
*dst++ = (char)(ucode & 0x7F);
} else if (ucode < 0x7FF) {
/* unicode: 0080 - 07FF -> utf8: 110xxxxx 10xxxxxx */
*dst++ = (char)(((ucode >> 6) & 0x1F) | 0xC0);
*dst++ = (char)((ucode & 0x3F) | 0x80);
} else {
/* unicode: 0800 - FFFF -> utf8: 1110xxxx 10xxxxxx 10xxxxxx */
*dst++ = (char)(((ucode >> 12) & 0x0F) | 0xE0);
*dst++ = (char)(((ucode >> 6) & 0x03F) | 0x80);
*dst++ = (char)((ucode & 0x3F) | 0x80);
}
return dst;
}
static const char* parser_string(bvm *vm, const char *json)
{
if (*json == '"') {
int len = json_strlen(json++);
if (len > -1) {
int ch;
char *buf, *dst = buf = be_malloc(vm, len);
while ((ch = *json) != '\0' && ch != '"') {
++json;
if (ch == '\\') {
ch = *json++; /* skip '\' */
switch (ch) {
case '"': *dst++ = '"'; break;
case '\\': *dst++ = '\\'; break;
case '/': *dst++ = '/'; break;
case 'b': *dst++ = '\b'; break;
case 'f': *dst++ = '\f'; break;
case 'n': *dst++ = '\n'; break;
case 'r': *dst++ = '\r'; break;
case 't': *dst++ = '\t'; break;
case 'u': { /* load unicode */
dst = load_unicode(dst, json);
if (dst == NULL) {
be_free(vm, buf, len);
return NULL;
}
json += 4;
break;
}
default: be_free(vm, buf, len); return NULL; /* error */
}
} else {
*dst++ = (char)ch;
}
}
be_assert(ch == '"');
be_pushnstring(vm, buf, cast_int(dst - buf));
be_free(vm, buf, len);
return json + 1; /* skip '"' */
}
}
return NULL;
}
static const char* parser_field(bvm *vm, const char *json)
{
if (json && *json == '"') {
json = parser_string(vm, json);
if (json) {
json = match_char(json, ':');
if (json) {
json = parser_value(vm, json);
if (json) {
be_data_insert(vm, -3);
be_pop(vm, 2); /* pop key and value */
return json;
}
}
be_pop(vm, 1); /* pop key */
}
}
return NULL;
}
static const char* parser_object(bvm *vm, const char *json)
{
json = match_char(json, '{');
be_newmap(vm);
if (*json != '}') {
const char *s;
json = parser_field(vm, json);
if (json == NULL) {
be_pop(vm, 1); /* pop map */
return NULL;
}
while ((s = match_char(json, ',')) != NULL) {
json = parser_field(vm, s);
if (json == NULL) {
be_pop(vm, 1); /* pop map */
return NULL;
}
}
}
if ((json = match_char(json, '}')) == NULL) {
be_pop(vm, 1); /* pop map */
return NULL;
}
json2berry(vm, "map");
return json;
}
static const char* parser_array(bvm *vm, const char *json)
{
json = match_char(json, '[');
be_newlist(vm);
if (*json != ']') {
const char *s;
json = parser_value(vm, json);
if (json == NULL) {
be_pop(vm, 1); /* pop map */
return NULL;
}
be_data_push(vm, -2);
be_pop(vm, 1); /* pop value */
while ((s = match_char(json, ',')) != NULL) {
json = parser_value(vm, s);
if (json == NULL) {
be_pop(vm, 1); /* pop map */
return NULL;
}
be_data_push(vm, -2);
be_pop(vm, 1); /* pop value */
}
}
if ((json = match_char(json, ']')) == NULL) {
be_pop(vm, 1); /* pop map */
return NULL;
}
json2berry(vm, "list");
return json;
}
/* parser json value */
static const char* parser_value(bvm *vm, const char *json)
{
json = skip_space(json);
switch (*json) {
case '{': /* object */
return parser_object(vm, json);
case '[': /* array */
return parser_array(vm, json);
case '"': /* string */
return parser_string(vm, json);
case 't': /* true */
return parser_true(vm, json);
case 'f': /* false */
return parser_false(vm, json);
case 'n': /* null */
return parser_null(vm, json);
default: /* number */
if (*json == '-' || is_digit(*json)) {
/* check invalid JSON syntax: 0\d+ */
if (json[0] == '0' && is_digit(json[1])) {
return NULL;
}
return be_str2num(vm, json);
}
}
return NULL;
}
static int m_json_load(bvm *vm)
{
if (be_isstring(vm, 1)) {
const char *json = be_tostring(vm, 1);
json = parser_value(vm, json);
if (json != NULL && *json == '\0') {
be_return(vm);
}
}
be_return_nil(vm);
}
static void make_indent(bvm *vm, int stridx, int indent)
{
if (indent) {
char buf[MAX_INDENT * INDENT_WIDTH + 1];
indent = (indent < MAX_INDENT ? indent : MAX_INDENT) * INDENT_WIDTH;
memset(buf, INDENT_CHAR, indent);
buf[indent] = '\0';
stridx = be_absindex(vm, stridx);
be_pushstring(vm, buf);
be_strconcat(vm, stridx);
be_pop(vm, 1);
}
}
void string_dump(bvm *vm, int index)
{
be_tostring(vm, index); /* convert value to string */
be_toescape(vm, index, 'u');
be_pushvalue(vm, index);
}
static void object_dump(bvm *vm, int *indent, int idx, int fmt)
{
be_getmember(vm, idx, ".p");
be_pushstring(vm, fmt ? "{\n" : "{");
be_pushiter(vm, -2); /* map iterator use 1 register */
*indent += fmt;
while (be_iter_hasnext(vm, -3)) {
make_indent(vm, -2, fmt ? *indent : 0);
be_iter_next(vm, -3);
/* key.tostring() */
string_dump(vm, -2);
be_strconcat(vm, -5);
be_pop(vm, 1);
be_pushstring(vm, fmt ? ": " : ":"); /* add ': ' */
be_strconcat(vm, -5);
be_pop(vm, 1);
/* value.tostring() */
value_dump(vm, indent, -1, fmt);
be_strconcat(vm, -5);
be_pop(vm, 3);
if (be_iter_hasnext(vm, -3)) {
be_pushstring(vm, fmt ? ",\n" : ",");
be_strconcat(vm, -3);
be_pop(vm, 1);
} else if (fmt) {
be_pushstring(vm, "\n");
be_strconcat(vm, -3);
be_pop(vm, 1);
}
}
*indent -= fmt;
be_pop(vm, 1); /* pop iterator */
make_indent(vm, -1, fmt ? *indent : 0);
be_pushstring(vm, "}");
be_strconcat(vm, -2);
be_moveto(vm, -2, -3);
be_pop(vm, 2);
}
static void array_dump(bvm *vm, int *indent, int idx, int fmt)
{
be_getmember(vm, idx, ".p");
be_pushstring(vm, fmt ? "[\n" : "[");
be_pushiter(vm, -2);
*indent += fmt;
while (be_iter_hasnext(vm, -3)) {
make_indent(vm, -2, fmt ? *indent : 0);
be_iter_next(vm, -3);
value_dump(vm, indent, -1, fmt);
be_strconcat(vm, -4);
be_pop(vm, 2);
if (be_iter_hasnext(vm, -3)) {
be_pushstring(vm, fmt ? ",\n" : ",");
be_strconcat(vm, -3);
be_pop(vm, 1);
} else if (fmt) {
be_pushstring(vm, "\n");
be_strconcat(vm, -3);
be_pop(vm, 1);
}
}
*indent -= fmt;
be_pop(vm, 1); /* pop iterator */
make_indent(vm, -1, fmt ? *indent : 0);
be_pushstring(vm, "]");
be_strconcat(vm, -2);
be_moveto(vm, -2, -3);
be_pop(vm, 2);
}
static void value_dump(bvm *vm, int *indent, int idx, int fmt)
{
if (is_object(vm, "map", idx)) { /* convert to json object */
object_dump(vm, indent, idx, fmt);
} else if (is_object(vm, "list", idx)) { /* convert to json array */
array_dump(vm, indent, idx, fmt);
} else if (be_isnil(vm, idx)) { /* convert to json null */
be_pushstring(vm, "null");
} else if (be_isnumber(vm, idx) || be_isbool(vm, idx)) { /* convert to json number and boolean */
be_tostring(vm, idx);
be_pushvalue(vm, idx); /* push to top */
} else { /* convert to string */
string_dump(vm, idx);
}
}
static int m_json_dump(bvm *vm)
{
int indent = 0, argc = be_top(vm);
int fmt = 0;
if (argc > 1) {
fmt = !strcmp(be_tostring(vm, 2), "format");
}
value_dump(vm, &indent, 1, fmt);
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(json) {
be_native_module_function("load", m_json_load),
be_native_module_function("dump", m_json_dump)
};
be_define_native_module(json, NULL);
#else
/* @const_object_info_begin
module json (scope: global, depend: BE_USE_JSON_MODULE) {
load, func(m_json_load)
dump, func(m_json_dump)
}
@const_object_info_end */
#include "../generate/be_fixed_json.h"
#endif
#endif /* BE_USE_JSON_MODULE */

View File

@ -0,0 +1,622 @@
/********************************************************************
** 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_lexer.h"
#include "be_string.h"
#include "be_mem.h"
#include "be_gc.h"
#include "be_exec.h"
#include "be_map.h"
#include "be_vm.h"
#define SHORT_STR_LEN 32
#define EOS '\0' /* end of source */
#define type_count() (int)array_count(kwords_tab)
#define lexbuf(lex) ((lex)->buf.s)
#define isvalid(lex) ((lex)->cursor < (lex)->endbuf)
#define lgetc(lex) ((lex)->cursor)
#define setstr(lex, v) ((lex)->token.u.s = (v))
#define setint(lex, v) ((lex)->token.u.i = (v))
#define setreal(lex, v) ((lex)->token.u.r = (v))
#define match(lex, pattern) while (pattern(lgetc(lex))) { save(lex); }
#if BE_USE_SCRIPT_COMPILER
/* IMPORTANT: This must follow the enum found in be_lexer.h !!! */
static const char* const kwords_tab[] = {
"NONE", "EOS", "ID", "INT", "REAL", "STR",
"=", "+=","-=", "*=", "/=", "%=", "&=", "|=",
"^=", "<<=", ">>=", "+", "-", "*", "/", "%",
"<", "<=", "==", "!=", ">", ">=", "&", "|",
"^", "<<", ">>", "..", "&&", "||", "!", "~",
"(", ")", "[", "]", "{", "}", ".", ",", ";",
":", "?", "->", "if", "elif", "else", "while",
"for", "def", "end", "class", "break", "continue",
"return", "true", "false", "nil", "var", "do",
"import", "as", "try", "except", "raise"
};
void be_lexerror(blexer *lexer, const char *msg)
{
bvm *vm = lexer->vm;
const char *error = be_pushfstring(vm,
"%s:%d: %s", lexer->fname, lexer->linenumber, msg);
be_lexer_deinit(lexer);
be_raise(vm, "syntax_error", error);
}
static void keyword_registe(bvm *vm)
{
int i;
for (i = KeyIf; i < type_count(); ++i) {
bstring *s = be_newstr(vm, kwords_tab[i]);
be_gc_fix(vm, gc_object(s));
be_str_setextra(s, i);
}
}
static void keyword_unregiste(bvm *vm)
{
int i;
for (i = KeyIf; i < type_count(); ++i) {
bstring *s = be_newstr(vm, kwords_tab[i]);
be_gc_unfix(vm, gc_object(s));
}
}
static bstring* cache_string(blexer *lexer, bstring *s)
{
bvalue *res;
bvm *vm = lexer->vm;
var_setstr(vm->top, s);
be_stackpush(vm); /* cache string to stack */
res = be_map_findstr(lexer->vm, lexer->strtab, s);
if (res) {
s = var_tostr(&be_map_val2node(res)->key);
} else {
res = be_map_insertstr(vm, lexer->strtab, s, NULL);
var_setnil(res);
}
be_stackpop(vm, 1); /* pop string frome stack */
return s;
}
static bstring* lexer_newstrn(blexer *lexer, const char *str, size_t len)
{
return cache_string(lexer, be_newstrn(lexer->vm, str, len));
}
bstring* be_lexer_newstr(blexer *lexer, const char *str)
{
return cache_string(lexer, be_newstr(lexer->vm, str));
}
static int next(blexer *lexer)
{
struct blexerreader *lr = &lexer->reader;
if (!(lr->len--)) {
static const char eos = EOS;
const char *s = lr->readf(lr->data, &lr->len);
lr->s = s ? s : &eos;
--lr->len;
}
lexer->cursor = *lr->s++;
return lexer->cursor;
}
static void clear_buf(blexer *lexer)
{
lexer->buf.len = 0;
}
/* save and next */
static int save(blexer *lexer)
{
int ch = lgetc(lexer);
struct blexerbuf *buf = &lexer->buf;
if (buf->len >= buf->size) {
size_t size = buf->size << 1;
buf->s = be_realloc(lexer->vm, buf->s, buf->size, size);
buf->size = size;
}
buf->s[buf->len++] = (char)ch;
return next(lexer);
}
static bstring* buf_tostr(blexer *lexer)
{
struct blexerbuf *buf = &lexer->buf;
return lexer_newstrn(lexer, buf->s, buf->len);
}
static int is_newline(int c)
{
return c == '\n' || c == '\r';
}
static int is_digit(int c)
{
return c >= '0' && c <= '9';
}
static int is_octal(int c)
{
return c >= '0' && c <= '7';
}
static int is_letter(int c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_');
}
static int is_word(int c)
{
return is_letter(c) || is_digit(c);
}
static int check_next(blexer *lexer, int c)
{
if (lgetc(lexer) == c) {
next(lexer);
return 1;
}
return 0;
}
static int char2hex(int c)
{
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'a' && c <= 'f') {
return c - 'a' + 0x0A;
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 0x0A;
}
return -1;
}
static int check2hex(blexer *lexer, int c)
{
c = char2hex(c);
if (c < 0) {
be_lexerror(lexer, "invalid hexadecimal number");
}
return c;
}
static int read_hex(blexer *lexer, const char *src)
{
int c = check2hex(lexer, *src++);
return ((unsigned)c << 4) + check2hex(lexer, *src);
}
static int read_oct(blexer *lexer, const char *src)
{
int c = 0;
const char *end = src + 3;
while (src < end && is_octal(*src)) {
c = 8 * c + *src++ - '0';
}
if (src < end) {
be_lexerror(lexer, "invalid octal number");
}
return c;
}
static void tr_string(blexer *lexer)
{
char *dst, *src, *end;
dst = src = lexbuf(lexer);
end = lexbuf(lexer) + lexer->buf.len;
while (src < end) {
int c = *src++;
switch (c) {
case '\n': case '\r':
be_lexerror(lexer, "unfinished string");
break;
case '\\':
switch (*src) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\\': c = '\\'; break;
case '\'': c = '\''; break;
case '"': c = '"'; break;
case '?': c = '?'; break;
case 'x': c = read_hex(lexer, ++src); ++src; break;
default:
c = read_oct(lexer, src);
if (c != EOS) {
src += 2;
}
break;
}
++src;
break;
default:
break;
}
*dst++ = (char)c;
}
lexer->buf.len = dst - lexbuf(lexer);
}
static int skip_newline(blexer *lexer)
{
int lc = lgetc(lexer);
next(lexer);
if (is_newline(lgetc(lexer)) && lgetc(lexer) != lc) {
next(lexer); /* skip "\n\r" or "\r\n" */
}
lexer->linenumber++;
return lexer->cursor;
}
static void skip_comment(blexer *lexer)
{
next(lexer); /* skip '#' */
if (lgetc(lexer) == '-') { /* mult-line comment */
int mark, c = 'x'; /* skip first '-' (#- ... -#) */
do {
mark = c == '-';
if (is_newline(c)) {
c = skip_newline(lexer);
continue;
}
c = next(lexer);
} while (!(mark && c == '#') && c != EOS);
next(lexer); /* skip '#' */
} else { /* line comment */
while (!is_newline(lgetc(lexer)) && lgetc(lexer)) {
next(lexer);
}
}
}
static bbool scan_realexp(blexer *lexer)
{
int c = lgetc(lexer);
if (c == 'e' || c == 'E') {
c = save(lexer);
if (c == '+' || c == '-') {
c = save(lexer);
}
if (!is_digit(c)) {
be_lexerror(lexer, "malformed number");
}
match(lexer, is_digit);
return btrue;
}
return bfalse;
}
static btokentype scan_dot_real(blexer *lexer)
{
if (save(lexer) == '.') { /* is '..' */
next(lexer);
return OptConnect;
}
if (is_digit(lgetc(lexer))) {
match(lexer, is_digit);
scan_realexp(lexer);
setreal(lexer, be_str2real(lexbuf(lexer), NULL));
return TokenReal;
}
return OptDot;
}
/* check the dots is a decimal dot or '..' operator */
static bbool decimal_dots(blexer *lexer)
{
if (lgetc(lexer) == '.') { /* '..' or real */
if (save(lexer) != '.') { /* read numberic => \.\b* */
match(lexer, is_digit); /* match and skip numberic */
return btrue;
}
/* token '..' */
next(lexer); /* skip the second '.' */
lexer->cacheType = OptConnect;
}
return bfalse; /* operator '..' */
}
static bint scan_hexadecimal(blexer *lexer)
{
bint res = 0;
int dig, num = 0;
while ((dig = char2hex(lgetc(lexer))) >= 0) {
res = ((bint)res << 4) + dig;
next(lexer);
++num;
}
if (num == 0) {
be_lexerror(lexer, "invalid hexadecimal number");
}
return res;
}
static btokentype scan_decimal(blexer *lexer)
{
btokentype type = TokenInteger;
match(lexer, is_digit);
if (decimal_dots(lexer) | scan_realexp(lexer)) {
type = TokenReal;
}
lexer->buf.s[lexer->buf.len] = '\0';
if (type == TokenReal) {
setreal(lexer, be_str2real(lexbuf(lexer), NULL));
} else {
setint(lexer, be_str2int(lexbuf(lexer), NULL));
}
return type;
}
static btokentype scan_numeral(blexer *lexer)
{
btokentype type = TokenInteger;
int c0 = lgetc(lexer), c1 = save(lexer);
/* hex: 0[xX][0-9a-fA-F]+ */
if (c0 == '0' && (c1 == 'x' || c1 == 'X')) {
next(lexer);
setint(lexer, scan_hexadecimal(lexer));
} else {
type = scan_decimal(lexer);
}
/* can't follow decimal or letter after numeral */
if (lexer->cacheType == TokenNone) {
if (is_letter(lgetc(lexer)) || decimal_dots(lexer)) {
be_lexerror(lexer, "malformed number");
}
}
return type;
}
static btokentype scan_identifier(blexer *lexer)
{
int type;
bstring *s;
save(lexer);
match(lexer, is_word);
s = buf_tostr(lexer);
type = str_extra(s);
if (type >= KeyIf && type < type_count()) {
lexer->token.type = (btokentype)type;
return lexer->token.type;
}
setstr(lexer, s); /* set identifier name */
return TokenId;
}
static btokentype scan_string(blexer *lexer)
{
int c, end = lgetc(lexer);
next(lexer); /* skip '"' or '\'' */
while ((c = lgetc(lexer)) != EOS && (c != end)) {
save(lexer);
if (c == '\\') {
save(lexer); /* skip '\\.' */
}
}
tr_string(lexer);
setstr(lexer, buf_tostr(lexer));
next(lexer); /* skip '"' or '\'' */
return TokenString;
}
static btokentype scan_assign(blexer *lexer, btokentype is, btokentype not)
{
next(lexer);
return check_next(lexer, '=') ? is : not;
}
static btokentype scan_sub(blexer *lexer)
{
btokentype op;
switch (next(lexer)) {
case '>': op = OptArrow; break;
case '=': op = OptSubAssign; break;
default: return OptSub;
}
next(lexer);
return op;
}
static btokentype scan_and(blexer *lexer)
{
btokentype op;
switch (next(lexer)) {
case '&': op = OptAnd; break;
case '=': op = OptAndAssign; break;
default: return OptBitAnd;
}
next(lexer);
return op;
}
static btokentype scan_or(blexer *lexer)
{
btokentype op;
switch (next(lexer)) {
case '|': op = OptOr; break;
case '=': op = OptOrAssign; break;
default: return OptBitOr;
}
next(lexer);
return op;
}
static btokentype scan_le(blexer *lexer)
{
switch (next(lexer)) {
case '=':
next(lexer);
return OptLE;
case '<':
next(lexer);
return check_next(lexer, '=') ? OptLsfAssign : OptShiftL;
default:
return OptLT;
}
}
static btokentype scan_ge(blexer *lexer)
{
switch (next(lexer)) {
case '=':
next(lexer);
return OptGE;
case '>':
next(lexer);
return check_next(lexer, '=') ? OptRsfAssign : OptShiftR;
default:
return OptGT;
}
}
static btokentype lexer_next(blexer *lexer)
{
for (;;) {
switch (lgetc(lexer)) {
case '\r': case '\n': /* newline */
skip_newline(lexer);
break;
case ' ': case '\t': case '\f': case '\v': /* spaces */
next(lexer);
break;
case '#': /* comment */
skip_comment(lexer);
break;
case EOS: return TokenEOS; /* end of source stream */
/* operator */
case '+': return scan_assign(lexer, OptAddAssign, OptAdd);
case '-': return scan_sub(lexer);
case '*': return scan_assign(lexer, OptMulAssign, OptMul);
case '/': return scan_assign(lexer, OptDivAssign, OptDiv);
case '%': return scan_assign(lexer, OptModAssign, OptMod);
case '(': next(lexer); return OptLBK;
case ')': next(lexer); return OptRBK;
case '[': next(lexer); return OptLSB;
case ']': next(lexer); return OptRSB;
case '{': next(lexer); return OptLBR;
case '}': next(lexer); return OptRBR;
case ',': next(lexer); return OptComma;
case ';': next(lexer); return OptSemic;
case ':': next(lexer); return OptColon;
case '?': next(lexer); return OptQuestion;
case '^': return scan_assign(lexer, OptXorAssign, OptBitXor);
case '~': next(lexer); return OptFlip;
case '&': return scan_and(lexer);
case '|': return scan_or(lexer);
case '<': return scan_le(lexer);
case '>': return scan_ge(lexer);
case '=':
next(lexer);
return check_next(lexer, '=') ? OptEQ : OptAssign;
case '!':
next(lexer);
return check_next(lexer, '=') ? OptNE : OptNot;
case '\'': case '"':
return scan_string(lexer);
case '.':
return scan_dot_real(lexer);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return scan_numeral(lexer);
default:
if (is_letter(lgetc(lexer))) {
return scan_identifier(lexer);
}
be_lexerror(lexer, be_pushfstring(lexer->vm,
"stray '\\%d' in program", (unsigned char)lgetc(lexer)));
return TokenNone; /* error */
}
}
}
static void lexerbuf_init(blexer *lexer)
{
lexer->buf.size = SHORT_STR_LEN;
lexer->buf.s = be_malloc(lexer->vm, SHORT_STR_LEN);
lexer->buf.len = 0;
}
void be_lexer_init(blexer *lexer, bvm *vm,
const char *fname, breader reader, void *data)
{
lexer->vm = vm;
lexer->cacheType = TokenNone;
lexer->fname = fname;
lexer->linenumber = 1;
lexer->lastline = 1;
lexer->reader.readf = reader;
lexer->reader.data = data;
lexer->reader.len = 0;
lexerbuf_init(lexer);
keyword_registe(vm);
lexer->strtab = be_map_new(vm);
var_setmap(vm->top, lexer->strtab);
be_stackpush(vm); /* save string to cache */
next(lexer); /* read the first character */
}
void be_lexer_deinit(blexer *lexer)
{
be_free(lexer->vm, lexer->buf.s, lexer->buf.size);
keyword_unregiste(lexer->vm);
be_stackpop(lexer->vm, 1); /* pop strtab */
}
int be_lexer_scan_next(blexer *lexer)
{
btokentype type;
if (lexer->cacheType != TokenNone) {
lexer->token.type = lexer->cacheType;
lexer->cacheType = TokenNone;
return 1;
}
if (lgetc(lexer) == EOS) { /* clear lexer */
lexer->token.type = TokenEOS;
return 0;
}
lexer->lastline = lexer->linenumber;
type = lexer_next(lexer);
clear_buf(lexer);
if (type != TokenNone) {
lexer->token.type = type;
} else {
lexer->token.type = TokenEOS;
return 0;
}
return 1;
}
const char* be_token2str(bvm *vm, btoken *token)
{
switch (token->type) {
case TokenString:
case TokenId:
return str(token->u.s);
case TokenInteger:
return be_pushfstring(vm, "%d", token->u.i);
case TokenReal:
return be_pushfstring(vm, "%g", token->u.r);
default:
return kwords_tab[token->type];
}
}
const char* be_tokentype2str(btokentype type)
{
return kwords_tab[type];
}
#endif

View File

@ -0,0 +1,137 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_LEXER_H
#define BE_LEXER_H
#include "be_object.h"
typedef enum {
TokenNone = 0,
TokenEOS, /* end of source */
TokenId, /* identifier */
TokenInteger,
TokenReal,
TokenString,
/* operator, don't change order */
/* assign operator */
OptAssign, /* operator, = */
OptAddAssign, /* operator, += */
OptSubAssign, /* operator, -= */
OptMulAssign, /* operator, *= */
OptDivAssign, /* operator, /= */
OptModAssign, /* operator, %= */
OptAndAssign, /* operator, &= */
OptOrAssign, /* operator, |= */
OptXorAssign, /* operator, ^= */
OptLsfAssign, /* operator, <<= */
OptRsfAssign, /* operator, >>= */
/* binary operator */
OptAdd, /* operator, + */
OptSub, /* operator, - */
OptMul, /* operator, * */
OptDiv, /* operator, / */
OptMod, /* operator, % */
OptLT, /* operator, < */
OptLE, /* operator, <= */
OptEQ, /* operator, == */
OptNE, /* operator, != */
OptGT, /* operator, > */
OptGE, /* operator, >= */
OptBitAnd, /* operatoe, & */
OptBitOr, /* operatoe, | */
OptBitXor, /* operatoe, ^ */
OptShiftL, /* operatoe, << */
OptShiftR, /* operatoe, >> */
OptConnect, /* operator, .. */
OptAnd, /* operator, && */
OptOr, /* operator, || */
/* unary operator */
OptNot, /* operator, ! */
OptFlip, /* operator, ~ */
/* postfix operator or bracket */
OptLBK, /* operator, ( bracket */
OptRBK, /* operator, ) bracket */
OptLSB, /* operator, [ square bracket */
OptRSB, /* operator, ] square bracket */
OptLBR, /* operator, { brace */
OptRBR, /* operator, } brace */
OptDot, /* operator, . dot */
/* other symbol */
OptComma, /* operator, , */
OptSemic, /* operator, ; */
OptColon, /* operator, : */
OptQuestion, /* operator, ? */
OptArrow, /* operator, -> */
/* keyword */
KeyIf, /* keyword if */
KeyElif, /* keyword elif */
KeyElse, /* keyword else */
KeyWhile, /* keyword while */
KeyFor, /* keyword for */
KeyDef, /* keyword def */
KeyEnd, /* keyword end */
KeyClass, /* keyword class */
KeyBreak, /* keyword break */
KeyContinue, /* keyword continue */
KeyReturn, /* keyword return */
KeyTrue, /* keyword true */
KeyFalse, /* keyword false */
KeyNil, /* keyword nil */
KeyVar, /* keyword var */
KeyDo, /* keyword do */
KeyImport, /* keyword import */
KeyAs, /* keyword as */
KeyTry, /* keyword try */
KeyExcept, /* keyword except */
KeyRaise /* keyword raise */
} btokentype;
struct blexerreader {
const char *s;
size_t len;
void *data;
breader readf;
};
struct blexerbuf {
char *s;
size_t len, size;
};
typedef struct btoken {
btokentype type;
union {
bstring *s;
bint i;
breal r;
} u;
} btoken;
typedef struct blexer {
const char *fname;
btoken token;
int linenumber;
int lastline;
btokentype cacheType;
struct blexerbuf buf;
struct blexerreader reader;
bmap *strtab;
bvm *vm;
int cursor;
} blexer;
void be_lexer_init(blexer *lexer, bvm *vm,
const char *fname, breader reader, void *data);
void be_lexer_deinit(blexer *lexer);
void be_lexerror(blexer *lexer, const char *msg);
int be_lexer_scan_next(blexer *lexer);
bstring* be_lexer_newstr(blexer *lexer, const char *str);
const char *be_token2str(bvm *vm, btoken *token);
const char* be_tokentype2str(btokentype type);
#endif

View File

@ -0,0 +1,25 @@
/********************************************************************
** 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_libs.h"
extern void be_load_baselib(bvm *vm);
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);
void be_loadlibs(bvm *vm)
{
be_load_baselib(vm);
#if !BE_USE_PRECOMPILED_OBJECT
be_load_listlib(vm);
be_load_maplib(vm);
be_load_rangelib(vm);
be_load_filelib(vm);
#endif
}

View File

@ -0,0 +1,15 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_LIBS_H
#define BE_LIBS_H
#include "berry.h"
void be_loadlibs(bvm *vm);
#endif

View File

@ -0,0 +1,207 @@
/********************************************************************
** 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_list.h"
#include "be_mem.h"
#include "be_gc.h"
#include "be_vm.h"
#include "be_vector.h"
#include "be_exec.h"
#include <string.h>
#define datasize(size) ((size) * sizeof(bvalue))
blist* be_list_new(bvm *vm)
{
bgcobject *gco = be_gcnew(vm, BE_LIST, blist);
blist *list = cast_list(gco);
if (list) {
list->count = 0;
list->capacity = 2;
var_setlist(vm->top, list);
be_incrtop(vm);
list->data = be_malloc(vm, datasize(list->capacity));
be_stackpop(vm, 1);
}
return list;
}
void be_list_delete(bvm *vm, blist *list)
{
be_free(vm, list->data, datasize(list->capacity));
be_free(vm, list, sizeof(blist));
}
blist* be_list_copy(bvm *vm, blist *original)
{
bgcobject *gco = be_gcnew(vm, BE_LIST, blist);
blist *list = cast_list(gco);
if (list) {
size_t size = datasize(original->capacity);
list->count = original->count;
list->capacity = original->capacity;
var_setlist(vm->top, list);
be_incrtop(vm);
list->data = be_malloc(vm, size);
be_stackpop(vm, 1);
memcpy(list->data, original->data, size);
}
return list;
}
bvalue* be_list_index(blist *list, int index)
{
if (index < 0) {
index = list->count + index;
}
if (index < 0 || index >= list->count) {
return NULL;
}
return be_list_at(list, index);
}
bvalue* be_list_push(bvm *vm, blist *list, bvalue *value)
{
bvalue *slot;
if (list->count >= list->capacity) {
int newcap = be_nextsize(list->capacity);
list->data = be_realloc(vm, list->data,
datasize(list->capacity), datasize(newcap));
list->capacity = newcap;
}
slot = list->data + list->count++;
if (value != NULL) {
*slot = *value;
}
return slot;
}
bvalue* be_list_insert(bvm *vm, blist *list, int index, bvalue *value)
{
int i;
bvalue *data;
if (index < 0) {
index = list->count + index;
}
if (index < 0 || index > list->count) {
return NULL;
}
if (list->count >= list->capacity) {
int newcap = be_nextsize(list->capacity);
list->data = be_realloc(vm, list->data,
datasize(list->capacity), datasize(newcap));
list->capacity = newcap;
}
data = list->data;
for (i = list->count++; i > index; --i) {
data[i] = data[i - 1];
}
data = list->data + index;
if (value != NULL) {
*data = *value;
}
return data;
}
int be_list_remove(bvm *vm, blist *list, int index)
{
int i;
bvalue *data;
(void)vm;
if (index < 0) {
index = list->count + index;
}
if (index < 0 || index >= list->count) {
return bfalse;
}
data = list->data;
list->count--;
for (i = index; i < list->count; ++i) {
data[i] = data[i + 1];
}
return btrue;
}
void be_list_resize(bvm *vm, blist *list, int count)
{
if (count != list->count) {
int newcap = be_nextsize(count);
if (newcap > list->capacity) {
bvalue *v, *end;
list->data = be_realloc(vm, list->data,
datasize(list->capacity), datasize(newcap));
list->capacity = newcap;
v = list->data + list->count;
end = list->data + count;
while (v < end) {
var_setnil(v++);
}
}
list->count = count;
}
}
void be_list_merge(bvm *vm, blist *list, const blist *other)
{
int dst_len = list->count;
int src_len = other->count;
int length = src_len + dst_len;
if (length != 0) {
int newcap = be_nextsize(length);
if (newcap > list->capacity) {
list->data = be_realloc(vm, list->data,
datasize(list->capacity), datasize(newcap));
list->capacity = newcap;
}
memcpy(list->data + dst_len, other->data, src_len * sizeof(bvalue));
list->count = length;
}
}
void be_list_reverse(blist *list)
{
bvalue *left = list->data;
bvalue *right = left + list->count - 1;
for (; left < right; ++left, --right) {
bvalue temp = *left;
*left = *right;
*right = temp;
}
}
void be_list_pool_init(bvm *vm, blist *list)
{
bvalue *head;
be_list_resize(vm, list, 0);
head = be_list_push(vm, list, NULL);
var_setint(head, 0);
}
int be_list_pool_alloc(bvm *vm, blist *list, bvalue *src)
{
bvalue *head = be_list_data(list), *node;
int id = var_toidx(head); /* get the first free node */
if (id) {
node = head + id;
head->v.i = var_toint(node); /* link the next free node to head */
} else {
id = be_list_count(list);
node = be_list_push(vm, list, NULL);
}
*node = *src;
return id;
}
void be_list_pool_free(blist *list, int id)
{
bvalue *head = be_list_data(list);
bvalue *node = head + id;
be_assert(id > 0 && id < list->count);
/* insert a new free node to head */
*node = *head;
head->v.i = id;
}

View File

@ -0,0 +1,39 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_LIST_H
#define BE_LIST_H
#include "be_object.h"
struct blist {
bcommon_header;
bgcobject *gray; /* for gc gray list */
int count, capacity;
bvalue *data;
};
#define be_list_data(list) ((list)->data)
#define be_list_count(list) ((list)->count)
#define be_list_at(list, index) ((list)->data + index)
#define be_list_end(list) ((list)->data + (list)->count)
blist* be_list_new(bvm *vm);
void be_list_delete(bvm *vm, blist *list);
blist* be_list_copy(bvm *vm, blist *original);
bvalue* be_list_index(blist *list, int index);
bvalue* be_list_push(bvm *vm, blist *list, bvalue *value);
bvalue* be_list_insert(bvm *vm, blist *list, int index, bvalue *value);
int be_list_remove(bvm *vm, blist *list, int index);
void be_list_resize(bvm *vm, blist *list, int count);
void be_list_merge(bvm *vm, blist *list, const blist *other);
void be_list_reverse(blist *list);
void be_list_pool_init(bvm *vm, blist *list);
int be_list_pool_alloc(bvm *vm, blist *list, bvalue *src);
void be_list_pool_free(blist *list, int id);
#endif

View File

@ -0,0 +1,468 @@
/********************************************************************
** 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_string.h"
#include "be_strlib.h"
#include "be_list.h"
#include "be_func.h"
#include "be_exec.h"
#include "be_vm.h"
#include <string.h>
#define list_check_data(vm, argc) \
if (!be_islist(vm, -1) || be_top(vm) - 1 < argc) { \
be_return_nil(vm); \
}
#define list_check_ref(vm) \
if (be_refcontains(vm, 1)) { \
be_pushstring(vm, "[...]"); \
be_return(vm); \
}
static void list_getindex(bvm *vm, int index)
{
if (!be_getindex(vm, index)) {
be_raise(vm, "index_error", "list index out of range");
}
}
static int m_init(bvm *vm)
{
int i, argc = be_top(vm);
if (argc > 1 && be_islist(vm, 2)) {
be_pushvalue(vm, 2);
be_setmember(vm, 1, ".p");
} else {
be_newlist(vm);
be_setmember(vm, 1, ".p");
for (i = 2; i <= argc; ++i) {
be_pushvalue(vm, i);
be_data_push(vm, -2);
be_pop(vm, 1);
}
}
be_return_nil(vm);
}
static void push_element(bvm *vm)
{
be_toescape(vm, -1, 'x'); /* escape string */
be_strconcat(vm, -3);
be_pop(vm, 1);
}
static int m_tostring(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
list_check_ref(vm);
be_refpush(vm, 1);
be_pushstring(vm, "[");
be_pushiter(vm, -2);
while (be_iter_hasnext(vm, -3)) {
be_iter_next(vm, -3);
push_element(vm);
if (be_iter_hasnext(vm, -3)) {
be_pushstring(vm, ", ");
be_strconcat(vm, -3);
be_pop(vm, 1);
}
}
be_pop(vm, 1); /* pop iterator */
be_pushstring(vm, "]");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_refpop(vm);
be_return(vm);
}
static int m_push(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 2);
be_pushvalue(vm, 2);
be_data_push(vm, -2);
be_return_nil(vm);
}
static int m_pop(bvm *vm)
{
int argc = be_top(vm);
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
if (argc >= 2) {
be_pushvalue(vm, 2);
} else {
be_pushint(vm, -1);
}
list_getindex(vm, -2);
be_pushvalue(vm, -2);
be_data_remove(vm, -4);
be_pop(vm, 1);
be_return(vm);
}
static int m_insert(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 3);
be_pushvalue(vm, 2);
be_pushvalue(vm, 3);
be_data_insert(vm, -3);
be_return_nil(vm);
}
static int m_remove(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 2);
be_pushvalue(vm, 2);
be_data_remove(vm, -2);
be_return_nil(vm);
}
static int item_range(bvm *vm)
{
bint lower, upper;
bint size = be_data_size(vm, -1); /* get source list size */
/* get index range */
be_getmember(vm, 2, "__lower__");
lower = be_toint(vm, -1);
be_pop(vm, 1);
be_getmember(vm, 2, "__upper__");
upper = be_toint(vm, -1);
be_pop(vm, 1);
/* protection scope */
upper = upper < size ? upper : size - 1;
lower = lower < 0 ? 0 : lower;
/* construction result list instance */
be_newobject(vm, "list"); /* result list */
be_getmember(vm, 1, ".p"); /* source list */
/* copy elements */
for (; lower <= upper; ++lower) {
be_pushint(vm, lower);
list_getindex(vm, -2);
be_data_push(vm, -4);
be_pop(vm, 2);
}
be_pop(vm, 2);
be_return(vm);
}
static int item_list(bvm *vm)
{
int i, srcsize, idxsize;
be_getmember(vm, 2, ".p"); /* get index list */
srcsize = be_data_size(vm, -2); /* get source list size */
idxsize = be_data_size(vm, -1); /* get index list size */
/* construction result list instance */
be_newobject(vm, "list"); /* result list */
be_getmember(vm, 1, ".p"); /* source list */
/* copy elements */
for (i = 0; i < idxsize; ++i) {
be_pushint(vm, i);
be_getindex(vm, -5);
if (be_isint(vm, -1)) {
int idx = be_toindex(vm, -1);
if (idx >= 0 && idx < srcsize) {
be_getindex(vm, -3);
} else {
be_pushnil(vm);
}
} else {
be_pushnil(vm);
}
be_data_push(vm, -5);
be_pop(vm, 3);
}
be_pop(vm, 2);
be_return(vm);
}
static int m_item(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 2);
if (be_isint(vm, 2)) {
be_pushvalue(vm, 2);
list_getindex(vm, -2);
be_return(vm);
}
if (be_isinstance(vm, 2)) {
const char *cname = be_classname(vm, 2);
if (!strcmp(cname, "range")) {
return item_range(vm);
}
if (!strcmp(cname, "list")) {
return item_list(vm);
}
}
be_raise(vm, "index_error", "list index out of range");
be_return_nil(vm);
}
static int m_setitem(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 3);
be_pushvalue(vm, 2);
be_pushvalue(vm, 3);
if (!be_setindex(vm, -3)) {
be_raise(vm, "index_error", "list assignment index out of range");
}
be_return_nil(vm);
}
static int m_size(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
be_pushint(vm, be_data_size(vm, -1));
be_return(vm);
}
static int m_resize(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 2);
be_pushvalue(vm, 2);
be_data_resize(vm, -2);
be_return_nil(vm);
}
static int m_clear(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
be_pushint(vm, 0);
be_data_resize(vm, -2);
be_return_nil(vm);
}
static int iter_closure(bvm *vm)
{
/* for better performance, we operate the upvalues
* directly without using by the stack. */
bntvclos *func = var_toobj(vm->cf->func);
bvalue *uv0 = be_ntvclos_upval(func, 0)->value; /* list value */
bvalue *uv1 = be_ntvclos_upval(func, 1)->value; /* iter value */
bvalue *next = cast(bvalue*, var_toobj(uv1)) + 1;
blist *list = var_toobj(uv0);
if (next >= be_list_end(list)) {
be_stop_iteration(vm);
}
var_toobj(uv1) = next; /* set upvale[1] (iter value) */
/* push next value to top */
var_setval(vm->top, next);
be_incrtop(vm);
be_return(vm);
}
static int m_iter(bvm *vm)
{
be_pushntvclosure(vm, iter_closure, 2);
be_getmember(vm, 1, ".p");
be_setupval(vm, -2, 0);
be_pushiter(vm, -1);
be_setupval(vm, -3, 1);
be_pop(vm, 2);
be_return(vm);
}
static int m_connect(bvm *vm)
{
int argc = be_top(vm);
if (argc >= 2) {
be_getmember(vm, 1, ".p");
be_pushvalue(vm, 2);
be_data_push(vm, -2);
be_pop(vm, argc + 1);
}
be_return(vm); /* return self */
}
static int m_merge(bvm *vm)
{
int argc = be_top(vm);
if (argc >= 2) {
be_getmember(vm, 1, ".p");
be_getmember(vm, 2, ".p");
if (!be_islist(vm, -1)) {
be_raise(vm, "type_error", "operand must be a list");
}
be_data_merge(vm, -2);
be_pop(vm, argc + 1);
}
be_return(vm); /* return self */
}
static void connect(bvm *vm, bvalue *begin, bvalue *end)
{
size_t l0 = be_strlen(vm, -1), len = l0;
char *buf, *p;
bvalue *it;
for (it = begin; it < end; ++it) {
len += str_len(var_tostr(it));
}
buf = be_pushbuffer(vm, len);
memcpy(buf, be_tostring(vm, -2), l0);
p = buf + l0;
for (it = begin; it < end; ++it) {
bstring *s = var_tostr(it);
size_t l = str_len(s);
memcpy(p, str(s), l);
p += l;
}
be_pushstring(vm, buf);
be_moveto(vm, -1, -3);
be_pop(vm, 2);
}
static void list_concat(bvm *vm, blist *list)
{
bvalue *it, *begin = be_list_data(list);
bvalue *end = be_list_end(list);
be_pushstring(vm, ""); /* push a empty string */
for (it = begin; it < end;) {
for (; it < end && var_isstr(it); ++it);
connect(vm, begin, it); /* connect string list */
if (it < end) {
/* connect other value */
var_setval(vm->top, it);
be_incrtop(vm);
be_val2str(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
begin = ++it;
}
}
}
static int m_concat(bvm *vm)
{
bvalue *value;
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
value = be_indexof(vm, -1);
list_concat(vm, var_toobj(value));
be_return(vm);
}
static int m_reverse(bvm *vm)
{
int top = be_top(vm);
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
be_data_reverse(vm, -1);
be_pop(vm, top);
be_return(vm);
}
static int m_copy(bvm *vm)
{
be_getmember(vm, 1, ".p");
list_check_data(vm, 1);
be_getbuiltin(vm, "list");
be_copy(vm, -2);
be_call(vm, 1);
be_pop(vm, 1);
be_return(vm);
}
static int list_equal(bvm *vm, bbool iseq)
{
int i, j, res;
bbool (*eqfunc)(bvm*) = iseq ? be_iseq : be_isneq;
be_getmember(vm, 1, ".p");
be_getmember(vm, 2, ".p");
i = be_data_size(vm, -2);
j = be_data_size(vm, -1);
if (i == j) {
res = iseq;
for (i = 0; res == iseq && i < j; ++i) {
be_pushint(vm, i);
be_getindex(vm, -3);
be_pushint(vm, i);
be_getindex(vm, -4);
be_remove(vm, -2);
res = eqfunc(vm);
be_pop(vm, 3);
}
} else {
res = !iseq;
}
be_pushbool(vm, res);
be_return(vm);
}
static int m_equal(bvm *vm)
{
return list_equal(vm, btrue);
}
static int m_nequal(bvm *vm)
{
return list_equal(vm, bfalse);
}
#if !BE_USE_PRECOMPILED_OBJECT
void be_load_listlib(bvm *vm)
{
static const bnfuncinfo members[] = {
{ ".p", NULL },
{ "init", m_init },
{ "tostring", m_tostring },
{ "push", m_push },
{ "pop", m_pop },
{ "insert", m_insert },
{ "remove", m_remove },
{ "item", m_item },
{ "setitem", m_setitem },
{ "size", m_size },
{ "resize", m_resize },
{ "clear", m_clear },
{ "iter", m_iter },
{ "concat", m_concat },
{ "reverse", m_reverse },
{ "copy", m_copy },
{ "..", m_connect },
{ "+", m_merge },
{ "==", m_equal },
{ "!=", m_nequal },
{ NULL, NULL }
};
be_regclass(vm, "list", members);
}
#else
/* @const_object_info_begin
class be_class_list (scope: global, name: list) {
.p, var
init, func(m_init)
tostring, func(m_tostring)
push, func(m_push)
pop, func(m_pop)
insert, func(m_insert)
remove, func(m_remove)
item, func(m_item)
setitem, func(m_setitem)
size, func(m_size)
resize, func(m_resize)
clear, func(m_clear)
iter, func(m_iter)
concat, func(m_concat)
reverse, func(m_reverse)
copy, func(m_copy)
.., func(m_connect)
+, func(m_merge)
==, func(m_equal)
!=, func(m_nequal)
}
@const_object_info_end */
#include "../generate/be_fixed_be_class_list.h"
#endif

View File

@ -0,0 +1,347 @@
/********************************************************************
** 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_map.h"
#include "be_string.h"
#include "be_vector.h"
#include "be_class.h"
#include "be_mem.h"
#include "be_gc.h"
#include "be_vm.h"
#include "be_exec.h"
#include <string.h>
#define key(node) (&(node)->key)
#define value(node) (&(node)->value)
#define isnil(node) var_isnil(key(node))
#define setnil(node) var_setnil(key(node))
#define hash2slot(m, h) ((m)->slots + (h) % (m)->size)
#define hashcode(_v) _hashcode(vm, (_v)->type, (_v)->v)
#define keytype(key) ((signed char)(key)->type)
#define next(node) ((node)->key.next)
#define pos2slot(map, n) ((n) != LASTNODE ? ((map)->slots + (n)) : NULL)
#define pos(map, node) ((int)((node) - (map)->slots))
#define setkey(node, _v) { (node)->key.type = (bbyte)(_v)->type; \
(node)->key.v = (_v)->v; }
#define datasize(size) ((size) * sizeof(bmapnode))
#define LASTNODE ((1 << 24) - 1)
static int map_nextsize(int size)
{
be_assert(size < LASTNODE);
if (size < LASTNODE) {
return be_nextsize(size);
}
return LASTNODE + 1;
}
static uint32_t hashptr(void *p)
{
uintptr_t i = (uintptr_t)p;
return (uint32_t)((i ^ (i >> 16)) & 0xFFFFFFFF);
}
#if BE_USE_SINGLE_FLOAT
#define hashreal(v) ((uint32_t)((v).i))
#else
static uint32_t hashreal(union bvaldata v)
{
union { breal r; uint32_t i[2]; } u;
u.r = v.r;
return u.i[0] ^ u.i[1];
}
#endif
#if BE_USE_OVERLOAD_HASH
static uint32_t hashins(bvm *vm, binstance *obj)
{
int type = be_instance_member(vm, obj, str_literal(vm, "hash"), vm->top);
if (basetype(type) == BE_FUNCTION) {
bvalue *top = vm->top;
var_setinstance(top + 1, obj);
vm->top += 2;
be_dofunc(vm, top, 1); /* call method 'item' */
vm->top -= 2;
if (!var_isint(vm->top)) { /* check the return value */
const char *name = str(be_instance_name(obj));
be_raise(vm, "runtime_error", be_pushfstring(vm,
"the value of `%s::hash()` is not a 'int'",
strlen(name) ? name : "<anonymous>"));
}
return (uint32_t)var_toint(vm->top);
}
return hashptr(obj);
}
#endif
static uint32_t _hashcode(bvm *vm, int type, union bvaldata v)
{
(void)vm;
switch (type) {
case BE_NIL: return 0;
case BE_BOOL: return (uint32_t)v.b;
case BE_INT: return (uint32_t)v.i;
case BE_REAL: return hashreal(v);
case BE_STRING: return be_strhash(v.s);
#if BE_USE_OVERLOAD_HASH
case BE_INSTANCE: return hashins(vm, v.p);
#endif
default: return hashptr(v.p);
}
}
static int eqnode(bvm *vm, bmapnode *node, bvalue *key, uint32_t hash)
{
(void)vm;
if (!var_isnil(key)) {
bmapkey *k = key(node);
#if BE_USE_OVERLOAD_HASH
if (var_isinstance(key)) {
bvalue kv;
kv.type = k->type;
kv.v = k->v;
return be_vm_iseq(vm, key, &kv);
}
#endif
if(keytype(k) == key->type && hashcode(k) == hash) {
switch (key->type) {
case BE_INT: return var_toint(key) == var_toint(k);
case BE_REAL: return var_toreal(key) == var_toreal(k);
case BE_STRING: return be_eqstr(var_tostr(key), var_tostr(k));
default: return var_toobj(key) == var_toobj(k);
}
}
}
return 0;
}
static bmapnode* findprev(bmap *map, bmapnode *list, bmapnode *slot)
{
int n, pos = pos(map, slot);
bmapnode *prev = list;
for (;;) {
n = next(prev);
if (n == pos || n == LASTNODE) {
break;
}
prev = map->slots + n;
}
return n == pos ? prev : NULL;
}
static bmapnode* nextfree(bmap *map)
{
bmapnode *base = map->slots;
while (map->lastfree >= base) {
if (isnil(map->lastfree)) {
return map->lastfree;
}
--map->lastfree;
}
return NULL;
}
static bmapnode* insert(bvm *vm, bmap *map, bvalue *key, uint32_t hash)
{
bmapnode *slot = hash2slot(map, hash);
if (isnil(slot)) { /* empty slot */
setkey(slot, key);
next(slot) = LASTNODE;
} else {
uint32_t h = hashcode(key(slot)); /* get the hashcode of the exist node */
bmapnode *mainslot = hash2slot(map, h); /* get the main-slot */
bmapnode *new = nextfree(map); /* get a free slot */
if (mainslot == slot) { /* old is main slot */
/* insert in first */
setkey(new, key);
next(new) = next(slot);
next(slot) = pos(map, new);
slot = new;
} else {
bmapnode *prev = findprev(map, mainslot, slot);
next(prev) = pos(map, new); /* link the previous node */
*new = *slot; /* copy to new slot */
setkey(slot, key);
next(slot) = LASTNODE;
}
}
return slot;
}
static bmapnode* find(bvm *vm, bmap *map, bvalue *key, uint32_t hash)
{
bmapnode *slot = hash2slot(map, hash);
if (isnil(slot)) {
return NULL;
}
while (!eqnode(vm, slot, key, hash)) {
int n = next(slot);
if (n == LASTNODE) {
return NULL;
}
slot = map->slots + n;
}
return slot;
}
static void resize(bvm *vm, bmap *map, int size)
{
int i, oldsize;
bmapnode *slots, *oldslots;
if (size < map->count) {
return;
}
oldsize = map->size;
oldslots = map->slots;
slots = be_malloc(vm, datasize(size));
for (i = 0; i < size; ++i) {
setnil(slots + i);
}
map->size = size;
map->slots = slots;
map->lastfree = slots + size - 1;
/* rehash */
for (i = 0; i < oldsize; ++i) {
bmapnode *node = oldslots + i;
if (!isnil(node)) {
bvalue v;
bmapnode *newslot;
v.type = node->key.type;
v.v = node->key.v;
newslot = insert(vm, map, &v, hashcode(&v));
newslot->value = node->value;
}
}
be_free(vm, oldslots, datasize(oldsize));
}
bmap* be_map_new(bvm *vm)
{
bgcobject *gco = be_gcnew(vm, BE_MAP, bmap);
bmap *map = cast_map(gco);
if (map) {
map->size = 0;
map->count = 0;
map->slots = NULL;
var_setmap(vm->top, map);
be_incrtop(vm);
resize(vm, map, 2);
be_stackpop(vm, 1);
}
return map;
}
void be_map_delete(bvm *vm, bmap *map)
{
be_free(vm, map->slots, datasize(map->size));
be_free(vm, map, sizeof(bmap));
}
bvalue* be_map_find(bvm *vm, bmap *map, bvalue *key)
{
bmapnode *entry = find(vm, map, key, hashcode(key));
return entry ? value(entry) : NULL;
}
bvalue* be_map_insert(bvm *vm, bmap *map, bvalue *key, bvalue *value)
{
uint32_t hash = hashcode(key);
bmapnode *entry = find(vm, map, key, hash);
if (!entry) { /* new entry */
if (map->count >= map->size) {
resize(vm, map, map_nextsize(map->size));
}
entry = insert(vm, map, key, hash);
++map->count;
}
if (value) {
entry->value = *value;
}
return value(entry);
}
int be_map_remove(bvm *vm, bmap *map, bvalue *key)
{
uint32_t hash = hashcode(key);
bmapnode *slot = hash2slot(map, hash); /* main slot */
if (eqnode(vm, slot, key, hash)) { /* first node */
bmapnode *next = pos2slot(map, next(slot));
if (next) { /* has next */
*slot = *next; /* first: copy the second node to the slot */
slot = next; /* second: set the second node to nil (empty) */
}
} else { /* the node will be remove is not first-node */
bmapnode *prev = slot;
for (;;) { /* find the previous node */
int n = next(prev);
slot = pos2slot(map, n);
if (slot == NULL) { /* node not found */
return bfalse;
}
if (eqnode(vm, slot, key, hash)) {
break;
}
prev = slot;
}
/* link the list */
next(prev) = next(slot);
}
/* set to nil */
setnil(slot);
/* add to lastfree */
if (map->lastfree < slot) {
map->lastfree = slot;
}
--map->count;
return btrue;
}
bvalue* be_map_findstr(bvm *vm, bmap *map, bstring *key)
{
bvalue v;
var_setstr(&v, key);
return be_map_find(vm, map, &v);
}
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);
}
void be_map_removestr(bvm *vm, bmap *map, bstring *key)
{
bvalue v;
var_setstr(&v, key);
be_map_remove(vm, map, &v);
}
bmapnode* be_map_next(bmap *map, bmapiter *iter)
{
bmapnode *end = map->slots + map->size;
*iter = *iter ? *iter + 1 : map->slots;
while (*iter < end && isnil(*iter)) {
++(*iter);
}
return *iter < end ? *iter : NULL;
}
bmapnode* be_map_val2node(bvalue *value)
{
return (bmapnode *)((size_t)value - sizeof(bmapkey));
}
void be_map_release(bvm *vm, bmap *map)
{
(void)vm;
resize(vm, map, map->count ? map->count : 1);
}

View File

@ -0,0 +1,61 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_MAP_H
#define BE_MAP_H
#include "be_object.h"
typedef struct bmapkey {
union bvaldata v;
uint32_t type:8;
uint32_t next:24;
} bmapkey;
typedef struct bmapnode {
bmapkey key;
bvalue value;
} bmapnode;
struct bmap {
bcommon_header;
bgcobject *gray; /* for gc gray list */
bmapnode *slots;
bmapnode *lastfree;
int size;
int count;
#ifdef __cplusplus
BE_CONSTEXPR bmap(bmapnode *s, int n) :
next(0), type(BE_MAP), marked(GC_CONST), gray(0),
slots(s), lastfree(0), size(n), count(n) {}
#endif
};
typedef bmapnode *bmapiter;
#define be_map_iter() NULL
#define be_map_count(map) ((map)->count)
#define be_map_size(map) (map->size)
#define be_map_key2value(dst, node) do { \
(dst)->type = (node)->key.type; \
(dst)->v = (node)->key.v; \
} while (0);
bmap* be_map_new(bvm *vm);
void be_map_delete(bvm *vm, bmap *map);
bvalue* be_map_find(bvm *vm, bmap *map, bvalue *key);
bvalue* be_map_insert(bvm *vm, bmap *map, bvalue *key, bvalue *value);
int be_map_remove(bvm *vm, bmap *map, bvalue *key);
bvalue* be_map_findstr(bvm *vm, bmap *map, bstring *key);
bvalue* be_map_insertstr(bvm *vm, bmap *map, bstring *key, bvalue *value);
void be_map_removestr(bvm *vm, bmap *map, bstring *key);
bmapnode* be_map_next(bmap *map, bmapiter *iter);
bmapnode* be_map_val2node(bvalue *value);
void be_map_release(bvm *vm, bmap *map);
#endif

View File

@ -0,0 +1,209 @@
/********************************************************************
** 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_func.h"
#include "be_exec.h"
#include "be_map.h"
#include "be_vm.h"
#define map_check_data(vm, argc) \
if (!be_ismap(vm, -1) || be_top(vm) - 1 < argc) { \
be_return_nil(vm); \
}
#define map_check_ref(vm) \
if (be_refcontains(vm, 1)) { \
be_pushstring(vm, "{...}"); \
be_return(vm); \
}
static int m_init(bvm *vm)
{
if (be_top(vm) > 1 && be_ismap(vm, 2)) {
be_pushvalue(vm, 2);
be_setmember(vm, 1, ".p");
} else {
be_newmap(vm);
be_setmember(vm, 1, ".p");
}
be_return_nil(vm);
}
static void push_key(bvm *vm)
{
be_toescape(vm, -2, 'x'); /* escape string */
be_pushvalue(vm, -2); /* push to top */
be_strconcat(vm, -5);
be_pop(vm, 1);
}
static void push_value(bvm *vm)
{
be_toescape(vm, -1, 'x'); /* escape string */
be_strconcat(vm, -4);
be_pop(vm, 2);
if (be_iter_hasnext(vm, -3)) {
be_pushstring(vm, ", ");
be_strconcat(vm, -3);
be_pop(vm, 1);
}
}
static int m_tostring(bvm *vm)
{
be_getmember(vm, 1, ".p");
map_check_data(vm, 1);
map_check_ref(vm);
be_refpush(vm, 1);
be_pushstring(vm, "{");
be_pushiter(vm, -2); /* map iterator use 1 register */
while (be_iter_hasnext(vm, -3)) {
be_iter_next(vm, -3);
push_key(vm); /* key.tostring() */
be_pushstring(vm, ": "); /* add ': ' */
be_strconcat(vm, -5);
be_pop(vm, 1);
push_value(vm); /* value.tostring() */
}
be_pop(vm, 1); /* pop iterator */
be_pushstring(vm, "}");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_refpop(vm);
be_return(vm);
}
static int m_remove(bvm *vm)
{
be_getmember(vm, 1, ".p");
map_check_data(vm, 2);
be_pushvalue(vm, 2);
be_data_remove(vm, -2);
be_return_nil(vm);
}
static int m_item(bvm *vm)
{
be_getmember(vm, 1, ".p");
map_check_data(vm, 2);
be_pushvalue(vm, 2);
if (!be_getindex(vm, -2)) {
be_raise(vm, "key_error", be_tostring(vm, 2));
}
be_return(vm);
}
static int m_setitem(bvm *vm)
{
be_getmember(vm, 1, ".p");
map_check_data(vm, 3);
be_pushvalue(vm, 2);
be_pushvalue(vm, 3);
be_setindex(vm, -3);
be_return_nil(vm);
}
static int m_find(bvm *vm)
{
int argc = be_top(vm);
be_getmember(vm, 1, ".p");
map_check_data(vm, 2);
be_pushvalue(vm, 2);
/* not find and has default value */
if (!be_getindex(vm, -2) && argc >= 3) {
be_pushvalue(vm, 3);
}
be_return(vm);
}
static int m_insert(bvm *vm)
{
bbool res;
be_getmember(vm, 1, ".p");
map_check_data(vm, 3);
be_pushvalue(vm, 2);
be_pushvalue(vm, 3);
res = be_data_insert(vm, -3);
be_pushbool(vm, res);
be_return(vm);
}
static int m_size(bvm *vm)
{
be_getmember(vm, 1, ".p");
map_check_data(vm, 1);
be_pushint(vm, be_data_size(vm, -1));
be_return(vm);
}
static int iter_closure(bvm *vm)
{
/* for better performance, we operate the upvalues
* directly without using by the stack. */
bntvclos *func = var_toobj(vm->cf->func);
bvalue *uv0 = be_ntvclos_upval(func, 0)->value; /* list value */
bvalue *uv1 = be_ntvclos_upval(func, 1)->value; /* iter value */
bmapiter iter = var_toobj(uv1);
bmapnode *next = be_map_next(var_toobj(uv0), &iter);
if (next == NULL) {
be_stop_iteration(vm);
be_return_nil(vm); /* will not be executed */
}
var_setobj(uv1, BE_COMPTR, iter); /* set upvale[1] (iter value) */
/* push next value to top */
var_setval(vm->top, &next->value);
be_incrtop(vm);
be_return(vm);
}
static int m_iter(bvm *vm)
{
be_pushntvclosure(vm, iter_closure, 2);
be_getmember(vm, 1, ".p");
be_setupval(vm, -2, 0);
be_pushiter(vm, -1);
be_setupval(vm, -3, 1);
be_pop(vm, 2);
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
void be_load_maplib(bvm *vm)
{
static const bnfuncinfo members[] = {
{ ".p", NULL },
{ "init", m_init },
{ "tostring", m_tostring },
{ "remove", m_remove },
{ "item", m_item },
{ "setitem", m_setitem },
{ "find", m_find },
{ "size", m_size },
{ "insert", m_insert },
{ "iter", m_iter },
{ NULL, NULL }
};
be_regclass(vm, "map", members);
}
#else
/* @const_object_info_begin
class be_class_map (scope: global, name: map) {
.p, var
init, func(m_init)
tostring, func(m_tostring)
remove, func(m_remove)
item, func(m_item)
setitem, func(m_setitem)
find, func(m_find)
size, func(m_size)
insert, func(m_insert)
iter, func(m_iter)
}
@const_object_info_end */
#include "../generate/be_fixed_be_class_map.h"
#endif

View File

@ -0,0 +1,322 @@
/********************************************************************
** 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 <math.h>
#include <limits.h>
#include <stdlib.h>
#if BE_USE_MATH_MODULE
#ifdef M_PI
#undef M_PI
#endif
#define M_PI 3.141592653589793238462643383279
#if BE_INTGER_TYPE == 0 /* int */
#define M_IMAX INT_MAX
#define M_IMIN INT_MIN
#elif BE_INTGER_TYPE == 1 /* long */
#define M_IMAX LONG_MAX
#define M_IMIN LONG_MIN
#else /* int64_t (long long) */
#define M_IMAX LLONG_MAX
#define M_IMIN LLONG_MIN
#endif
#if BE_USE_SINGLE_FLOAT
#define mathfunc(func) func##f
#else
#define mathfunc(func) func
#endif
static int m_abs(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(fabs)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_ceil(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(ceil)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_floor(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(floor)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_sin(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(sin)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_cos(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(cos)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_tan(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(tan)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_asin(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(asin)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_acos(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(acos)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_atan(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(atan)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_sinh(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(sinh)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_cosh(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(cosh)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_tanh(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(tanh)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_sqrt(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(sqrt)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_exp(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(exp)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_log(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(log)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_log10(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, mathfunc(log10)(x));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_deg(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, x * (breal)(180.0 / M_PI));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_rad(bvm *vm)
{
if (be_top(vm) >= 1 && be_isnumber(vm, 1)) {
breal x = be_toreal(vm, 1);
be_pushreal(vm, x * (breal)(M_PI / 180.0));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_pow(bvm *vm)
{
if (be_top(vm) >= 2 && be_isnumber(vm, 1) && be_isnumber(vm, 2)) {
breal x = be_toreal(vm, 1);
breal y = be_toreal(vm, 2);
be_pushreal(vm, mathfunc(pow)(x, y));
} else {
be_pushreal(vm, (breal)0.0);
}
be_return(vm);
}
static int m_srand(bvm *vm)
{
if (be_top(vm) >= 1 && be_isint(vm, 1)) {
srand((unsigned int)be_toint(vm, 1));
}
be_return_nil(vm);
}
static int m_rand(bvm *vm)
{
be_pushint(vm, rand());
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(math) {
be_native_module_function("abs", m_abs),
be_native_module_function("ceil", m_ceil),
be_native_module_function("floor", m_floor),
be_native_module_function("sin", m_sin),
be_native_module_function("cos", m_cos),
be_native_module_function("tan", m_tan),
be_native_module_function("asin", m_asin),
be_native_module_function("acos", m_acos),
be_native_module_function("atan", m_atan),
be_native_module_function("sinh", m_sinh),
be_native_module_function("cosh", m_cosh),
be_native_module_function("tanh", m_tanh),
be_native_module_function("sqrt", m_sqrt),
be_native_module_function("exp", m_exp),
be_native_module_function("log", m_log),
be_native_module_function("log10", m_log10),
be_native_module_function("deg", m_deg),
be_native_module_function("rad", m_rad),
be_native_module_function("pow", m_pow),
be_native_module_function("srand", m_srand),
be_native_module_function("rand", m_rand),
be_native_module_real("pi", M_PI),
be_native_module_int("imax", M_IMAX),
be_native_module_int("imin", M_IMIN),
};
be_define_native_module(math, NULL);
#else
/* @const_object_info_begin
module math (scope: global, depend: BE_USE_MATH_MODULE) {
abs, func(m_abs)
ceil, func(m_ceil)
floor, func(m_floor)
sin, func(m_sin)
cos, func(m_cos)
tan, func(m_tan)
asin, func(m_asin)
acos, func(m_acos)
atan, func(m_atan)
sinh, func(m_sinh)
cosh, func(m_cosh)
tanh, func(m_tanh)
sqrt, func(m_sqrt)
exp, func(m_exp)
log, func(m_log)
log10, func(m_log10)
deg, func(m_deg)
rad, func(m_rad)
pow, func(m_pow)
srand, func(m_srand)
rand, func(m_rand)
pi, real(M_PI)
imax, int(M_IMAX)
imin, int(M_IMIN)
}
@const_object_info_end */
#include "../generate/be_fixed_math.h"
#endif
#endif /* BE_USE_MATH_MODULE */

View File

@ -0,0 +1,74 @@
/********************************************************************
** 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_mem.h"
#include "be_exec.h"
#include "be_vm.h"
#include "be_gc.h"
#include <stdlib.h>
#define GC_ALLOC (1 << 2) /* GC in alloc */
#ifdef BE_EXPLICIT_MALLOC
#define malloc BE_EXPLICIT_MALLOC
#endif
#ifdef BE_EXPLICIT_FREE
#define free BE_EXPLICIT_FREE
#endif
#ifdef BE_EXPLICIT_REALLOC
#define realloc BE_EXPLICIT_REALLOC
#endif
BERRY_API void* be_os_malloc(size_t size)
{
return malloc(size);
}
BERRY_API void be_os_free(void *ptr)
{
free(ptr);
}
BERRY_API void* be_os_realloc(void *ptr, size_t size)
{
return realloc(ptr, size);
}
static void* _realloc(void *ptr, size_t old_size, size_t new_size)
{
if (old_size == new_size) { /* the block unchanged */
return ptr;
}
if (ptr && new_size) { /* realloc block */
return realloc(ptr, new_size);
}
if (new_size) { /* alloc a new block */
be_assert(ptr == NULL && old_size == 0);
return malloc(new_size);
}
be_assert(new_size == 0);
free(ptr);
return NULL;
}
BERRY_API void* be_realloc(bvm *vm, void *ptr, size_t old_size, size_t new_size)
{
void *block = _realloc(ptr, old_size, new_size);
if (!block && new_size) { /* allocation failure */
vm->gc.status |= GC_ALLOC;
be_gc_collect(vm); /* try to allocate again after GC */
vm->gc.status &= ~GC_ALLOC;
block = _realloc(ptr, old_size, new_size);
if (!block) { /* lack of heap space */
be_throw(vm, BE_MALLOC_FAIL);
}
}
vm->gc.usage = vm->gc.usage + new_size - old_size; /* update allocated count */
return block;
}

View File

@ -0,0 +1,29 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_MEM_H
#define BE_MEM_H
#include "berry.h"
#ifdef __cplusplus
extern "C" {
#endif
#define be_malloc(vm, size) be_realloc((vm), NULL, 0, (size))
#define be_free(vm, ptr, size) be_realloc((vm), (ptr), (size), 0)
BERRY_API void* be_os_malloc(size_t size);
BERRY_API void be_os_free(void *ptr);
BERRY_API void* be_os_realloc(void *ptr, size_t size);
BERRY_API void* be_realloc(bvm *vm, void *ptr, size_t old_size, size_t new_size);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,406 @@
/********************************************************************
** 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_module.h"
#include "be_string.h"
#include "be_strlib.h"
#include "be_list.h"
#include "be_exec.h"
#include "be_map.h"
#include "be_gc.h"
#include "be_mem.h"
#include "be_vm.h"
#include <string.h>
/* the maximum suffix length */
#define SUFFIX_LEN 5 /* length of (.be .bec .so .dll) + 1 */
/* check POSIX platforms */
#if defined(__linux__) || \
defined(__APPLE__) || defined(__unix__) || defined(__CYGWIN__)
#define __POSIX_OS__
#endif
#if BE_USE_SHARED_LIB
#if defined(__POSIX_OS__)
#define DLL_SUFFIX ".so"
#elif defined(_WIN32)
#define DLL_SUFFIX ".dll"
#else
#define DLL_SUFFIX ""
#warning module: unsuppord OS
#endif
#endif
extern BERRY_LOCAL const bntvmodule* const be_module_table[];
static bmodule* native_module(bvm *vm, const bntvmodule *nm, bvalue *dst);
static const bntvmodule* find_native(bstring *path)
{
const bntvmodule *module;
const bntvmodule* const *node = be_module_table;
for (; (module = *node) != NULL; ++node) {
if (!strcmp(module->name, str(path))) {
return module;
}
}
return NULL;
}
static void insert_attrs(bvm *vm, bmap *table, const bntvmodule *nm)
{
size_t i;
for (i = 0; i < nm->size; ++i) {
const bntvmodobj *node = nm->attrs + i;
bstring *name = be_newstr(vm, node->name);
bvalue *v = be_map_insertstr(vm, table, name, NULL);
be_assert(node->type <= BE_CMODULE);
switch (node->type) {
case BE_CNIL:
var_setnil(v);
break;
case BE_CINT:
var_setint(v, node->u.i);
break;
case BE_CREAL:
var_setreal(v, node->u.r);
break;
case BE_CBOOL:
var_setbool(v, node->u.b);
break;
case BE_CFUNCTION:
var_setntvfunc(v, node->u.f);
break;
case BE_CSTRING:
var_setstr(v, be_newstr(vm, node->u.s));
break;
case BE_CMODULE:
native_module(vm, node->u.o, v);
break;
default: /* error */
break;
}
}
}
static bmodule* new_module(bvm *vm, const bntvmodule *nm)
{
bgcobject *gco = be_gcnew(vm, BE_MODULE, bmodule);
bmodule *obj = cast_module(gco);
if (obj) {
var_setmodule(vm->top, obj);
be_incrtop(vm);
obj->info.native = nm;
obj->table = NULL; /* gc protection */
obj->table = be_map_new(vm);
insert_attrs(vm, obj->table, nm);
be_map_release(vm, obj->table); /* clear space */
be_stackpop(vm, 1);
}
return obj;
}
static bmodule* native_module(bvm *vm, const bntvmodule *nm, bvalue *dst)
{
if (nm) {
bmodule *obj;
if (nm->module) {
obj = (bmodule *)nm->module;
} else { /* new module */
obj = new_module(vm, nm);
}
if (obj && dst) {
var_setmodule(dst, obj);
}
return obj;
}
return NULL;
}
static char* fixpath(bvm *vm, bstring *path, size_t *size)
{
char *buffer;
const char *split, *base;
bvalue *func = vm->cf->func;
bclosure *cl = var_toobj(func);
be_assert(var_isclosure(func));
base = str(cl->proto->source); /* get the source file path */
split = be_splitpath(base);
*size = split - base + (size_t)str_len(path) + SUFFIX_LEN;
buffer = be_malloc(vm, *size);
strncpy(buffer, base, split - base);
strcpy(buffer + (split - base), str(path));
return buffer;
}
static char* conpath(bvm *vm, bstring *path1, bstring *path2, size_t *size)
{
char *buffer;
int len1 = str_len(path1);
*size = (size_t)len1 + (size_t)str_len(path2) + 1 + SUFFIX_LEN;
buffer = be_malloc(vm, *size);
strcpy(buffer, str(path1));
buffer[len1] = '/';
strcpy(buffer + len1 + 1, str(path2));
return buffer;
}
static int open_script(bvm *vm, char *path)
{
int res = be_loadmodule(vm, path);
if (res == BE_OK)
be_call(vm, 0);
return res;
}
#if BE_USE_SHARED_LIB
static int open_dllib(bvm *vm, char *path)
{
int res = be_loadlib(vm, path);
if (res == BE_OK)
be_call(vm, 0);
return res;
}
#endif
static int open_libfile(bvm *vm, char *path, size_t size)
{
int res, idx = 0;
const char *sfxs[] = { "", ".bec", ".be" };
do {
strcpy(path + size - SUFFIX_LEN, sfxs[idx]);
res = open_script(vm, path);
} while (idx++ < 2 && res == BE_IO_ERROR);
if (res == BE_IO_ERROR) {
#if BE_USE_SHARED_LIB
strcpy(path + size - SUFFIX_LEN, DLL_SUFFIX);
res = open_dllib(vm, path);
#endif
}
be_free(vm, path, size);
return res;
}
static int load_path(bvm *vm, bstring *path, bstring *mod)
{
size_t size;
char *fullpath = conpath(vm, path, mod, &size);
return open_libfile(vm, fullpath, size);
}
static int load_cwd(bvm *vm, bstring *path)
{
size_t size;
char *fullpath = fixpath(vm, path, &size);
return open_libfile(vm, fullpath, size);
}
static int load_package(bvm *vm, bstring *path)
{
int res = load_cwd(vm, path); /* load from current directory */
if (res == BE_IO_ERROR && vm->module.path) {
blist *list = vm->module.path;
bvalue *v = be_list_end(list) - 1;
bvalue *first = be_list_data(list);
for (; res == BE_IO_ERROR && v >= first; v--) {
if (var_isstr(v)) {
res = load_path(vm, var_tostr(v), path);
}
}
}
return res;
}
static int load_native(bvm *vm, bstring *path)
{
const bntvmodule *nm = find_native(path);
bmodule *mod = native_module(vm, nm, NULL);
if (mod != NULL) {
/* the pointer vm->top may be changed */
var_setmodule(vm->top, mod);
be_incrtop(vm);
return BE_OK;
}
return BE_IO_ERROR;
}
static bvalue* load_cached(bvm *vm, bstring *path)
{
bvalue *v = NULL;
if (vm->module.loaded) {
v = be_map_findstr(vm, vm->module.loaded, path);
if (v) {
*vm->top = *v;
be_incrtop(vm);
}
}
return v;
}
static void cache_module(bvm *vm, bstring *name)
{
bvalue *v;
if (vm->module.loaded == NULL) {
vm->module.loaded = be_map_new(vm);
}
v = be_map_insertstr(vm, vm->module.loaded, name, NULL);
*v = vm->top[-1];
}
/* load module to vm->top */
int be_module_load(bvm *vm, bstring *path)
{
int res = BE_OK;
if (!load_cached(vm, path)) {
res = load_native(vm, path);
if (res == BE_IO_ERROR)
res = load_package(vm, path);
if (res == BE_OK)
cache_module(vm, path);
}
return res;
}
bmodule* be_module_new(bvm *vm)
{
bgcobject *gco = be_gcnew(vm, BE_MODULE, bmodule);
bmodule *obj = cast_module(gco);
if (obj) {
var_setmodule(vm->top, obj);
be_incrtop(vm);
obj->info.native = NULL;
obj->table = NULL; /* gc protection */
obj->table = be_map_new(vm);
be_stackpop(vm, 1);
}
return obj;
}
void be_module_delete(bvm *vm, bmodule *module)
{
be_free(vm, module, sizeof(bmodule));
}
bvalue* be_module_attr(bvm *vm, bmodule *module, bstring *attr)
{
return be_map_findstr(vm, module->table, attr);
}
bvalue* be_module_bind(bvm *vm, bmodule *module, bstring *attr)
{
bmap *attrs = module->table;
if (!gc_isconst(attrs)) {
bvalue *v = be_map_findstr(vm, attrs, attr);
if (v == NULL) {
v = be_map_insertstr(vm, attrs, attr, NULL);
}
return v;
}
return NULL;
}
const char* be_module_name(bmodule *module)
{
if (gc_isconst(module)) {
return module->info.name;
}
if (gc_exmark(module) & BE_MODULE_NAME) {
return str(module->info.sname);
}
if (module->info.native) {
return module->info.native->name;
}
return NULL;
}
bbool be_module_setname(bmodule *module, bstring *name)
{
if (!gc_isconst(module)) {
module->info.sname = name;
gc_setexmark(module, BE_MODULE_NAME);
return btrue;
}
return bfalse;
}
static blist* pathlist(bvm *vm)
{
if (!vm->module.path) {
vm->module.path = be_list_new(vm);
}
return vm->module.path;
}
/* push the path list to the top */
BERRY_API void be_module_path(bvm *vm)
{
blist *list = pathlist(vm);
bvalue *reg = be_incrtop(vm);
var_setlist(reg, list);
}
BERRY_API void be_module_path_set(bvm *vm, const char *path)
{
blist *list = pathlist(vm);
bvalue *value = be_list_push(vm, list, NULL);
var_setnil(value);
var_setstr(value, be_newstr(vm, path))
}
/* shared library support */
#if BE_USE_SHARED_LIB
#if defined(__POSIX_OS__)
#include <dlfcn.h>
#if defined(__GNUC__)
#define cast_func(f) (__extension__(bntvfunc)(f))
#else
#define cast_func(f) ((bntvfunc)(f))
#endif
/* load shared library */
BERRY_API int be_loadlib(bvm *vm, const char *path)
{
void *handle = dlopen(path, RTLD_LAZY);
bntvfunc func = cast_func(dlsym(handle, "berry_export"));
if (func == NULL) {
return BE_IO_ERROR;
}
be_pushntvfunction(vm, func);
return BE_OK;
}
#elif defined(_WIN32)
#include<wtypes.h>
#include <winbase.h>
BERRY_API int be_loadlib(bvm *vm, const char *path)
{
HINSTANCE handle = LoadLibrary(path);
if (handle) {
union {
FARPROC proc;
bntvfunc func;
} u;
u.proc = GetProcAddress(handle, "berry_export");
if (u.func != NULL) {
be_pushntvfunction(vm, u.func);
return BE_OK;
}
}
return BE_IO_ERROR;
}
#else
BERRY_API int be_loadlib(bvm *vm, const char *path)
{
(void)vm, (void)path;
return BE_IO_ERROR;
}
#endif
#endif /* BE_USE_SHARED_LIB */

View File

@ -0,0 +1,42 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_MODULE_H
#define BE_MODULE_H
#include "be_object.h"
#define BE_MODULE_NAME 1
typedef struct bmodule {
bcommon_header;
bmap *table;
union infodata {
const bntvmodule *native;
const char *name;
const bstring *sname;
#ifdef __cplusplus
BE_CONSTEXPR infodata(const char *name) : name(name) {}
#endif
} info;
bgcobject *gray; /* for gc gray list */
#ifdef __cplusplus
BE_CONSTEXPR bmodule(bmap *tab, const char *name) :
next(0), type(BE_MODULE), marked(GC_CONST),
table(tab), info(infodata(name)), gray(0) {}
#endif
} bmodule;
bmodule* be_module_new(bvm *vm);
void be_module_delete(bvm *vm, bmodule *module);
int be_module_load(bvm *vm, bstring *path);
bvalue* be_module_attr(bvm *vm, bmodule *module, bstring *attr);
bvalue* be_module_bind(bvm *vm, bmodule *module, bstring *attr);
const char* be_module_name(bmodule *module);
bbool be_module_setname(bmodule *module, bstring *name);
#endif

View File

@ -0,0 +1,71 @@
/********************************************************************
** 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_exec.h"
#include "be_mem.h"
#include "be_gc.h"
#include "be_vm.h"
#define cast_comobj(o) gc_cast(o, BE_COMOBJ, bcommomobj)
const char* be_vtype2str(bvalue *v)
{
switch(var_type(v)) {
case BE_NIL: return "nil";
case BE_INT: return "int";
case BE_REAL: return "real";
case BE_BOOL: return "bool";
case BE_CLOSURE: case BE_NTVCLOS:
case BE_NTVFUNC: return "function";
case BE_PROTO: return "proto";
case BE_CLASS: return "class";
case BE_STRING: return "string";
case BE_LIST: return "list";
case BE_MAP: return "map";
case BE_INSTANCE: return "instance";
case BE_MODULE: return "module";
default: return "invalid type";
}
}
bvalue* be_indexof(bvm *vm, int idx)
{
if (idx > 0) { /* absolute index */
be_assert(vm->reg + idx <= vm->top);
return vm->reg + idx - 1;
}
/* relative index */
be_assert(vm->top + idx >= vm->reg);
return vm->top + idx;
}
BERRY_API void be_newcomobj(bvm *vm, void *data, bntvfunc destory)
{
bcommomobj *obj;
bgcobject *gco = be_gcnew(vm, BE_COMOBJ, bcommomobj);
if ((obj = cast_comobj(gco)) != NULL) {
bvalue* top = be_incrtop(vm);
obj->data = data;
obj->destory = destory;
var_setobj(top, BE_COMOBJ, obj);
}
}
void be_commonobj_delete(bvm *vm, bgcobject *obj)
{
bcommomobj *co = cast_comobj(obj);
if (co) {
if (co->destory && co->data) {
be_pushntvfunction(vm, co->destory);
be_pushcomptr(vm, co->data);
be_call(vm, 1);
be_pop(vm, 2);
}
be_free(vm, co, sizeof(bcommomobj));
}
}

View File

@ -0,0 +1,241 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_OBJECT_H
#define BE_OBJECT_H
#include "berry.h"
/* basic types, do not change value */
#define BE_NONE (-1) /* unknow type */
#define BE_COMPTR (-2) /* common pointer */
#define BE_NIL 0
#define BE_INT 1
#define BE_REAL 2
#define BE_BOOL 3
#define BE_FUNCTION 4
#define BE_STRING 5
#define BE_CLASS 6
#define BE_INSTANCE 7
#define BE_PROTO 8
#define BE_LIST 9
#define BE_MAP 10
#define BE_MODULE 11
#define BE_COMOBJ 12 /* common object */
#define BE_NTVFUNC ((0 << 5) | BE_FUNCTION)
#define BE_CLOSURE ((1 << 5) | BE_FUNCTION)
#define BE_NTVCLOS ((2 << 5) | BE_FUNCTION)
#define array_count(a) (sizeof(a) / sizeof((a)[0]))
#define bcommon_header \
struct bgcobject *next; \
bbyte type; \
bbyte marked
#define bstring_header \
bcommon_header; \
bbyte extra; \
bbyte slen
typedef struct bgcobject {
bcommon_header;
} bgcobject;
typedef struct bclosure bclosure;
typedef struct bntvclos bntvclos;
typedef struct bclass bclass;
typedef struct binstance binstance;
typedef struct blist blist;
typedef struct bmap bmap;
typedef struct bupval bupval;
typedef uint32_t binstruction;
typedef struct bstring {
bstring_header;
} bstring;
/* the definition of the vector and stack data structures.
* in fact, the stack is implemented by vector. */
typedef struct bvector {
int capacity; /* the number of elements that the vector can store */
int size; /* the size of each element (bytes) */
int count; /* number of elements of the vector */
void *data; /* the data block pointer, if vector is empty,
it will point to the first element */
void *end; /* pointer to the last element, if the vector is empty,
the end pointer will be smaller than the data pointer */
} bvector, bstack;
/* berry value data union, a berry value is always described
* by the data structure contained in the bvaldata union. */
union bvaldata {
bbool b; /* boolean */
breal r; /* real number */
bint i; /* integer number */
void *p; /* object pointer */
const void *c; /* const object pointer */
bstring *s; /* string pointer */
bgcobject *gc; /* GC object */
bntvfunc nf; /* native C function */
#ifdef __cplusplus
BE_CONSTEXPR bvaldata(bbool v) : b(v) {}
BE_CONSTEXPR bvaldata(breal v) : r(v) {}
BE_CONSTEXPR bvaldata(bint v) : i(v) {}
BE_CONSTEXPR bvaldata(void *v) : p(v) {}
BE_CONSTEXPR bvaldata(const void *v) : c(v) {}
BE_CONSTEXPR bvaldata(bntvfunc v) : nf(v) {}
#endif
};
/* berry value. for simple types, the value of the data is stored,
* while the complex type stores a reference to the data. */
typedef struct bvalue {
union bvaldata v; /* the value data */
int type; /* the value type */
} bvalue;
typedef struct {
#if BE_DEBUG_VAR_INFO
bstring *name;
#endif
bbyte instack;
bbyte idx;
} bupvaldesc;
typedef struct {
#if BE_DEBUG_RUNTIME_INFO > 1
uint16_t linenumber;
uint16_t endpc;
#else
int linenumber;
int endpc;
#endif
} blineinfo;
typedef struct {
bstring *name;
#if BE_DEBUG_RUNTIME_INFO > 1
uint16_t beginpc;
uint16_t endpc;
#else
int beginpc;
int endpc;
#endif
} bvarinfo;
typedef struct bproto {
bcommon_header;
bbyte nstack; /* number of stack size by this function */
bbyte nupvals; /* upvalue count */
bbyte argc; /* argument count */
bbyte varg; /* variable argument position + 1 */
bgcobject *gray; /* for gc gray list */
bupvaldesc *upvals;
bvalue *ktab; /* constants table */
struct bproto **ptab; /* proto table */
binstruction *code; /* instructions sequence */
bstring *name; /* function name */
int codesize; /* code size */
int nconst; /* constants count */
int nproto; /* proto count */
bstring *source; /* source file name */
#if BE_DEBUG_RUNTIME_INFO /* debug information */
blineinfo *lineinfo;
int nlineinfo;
#endif
#if BE_DEBUG_VAR_INFO
bvarinfo *varinfo;
int nvarinfo;
#endif
} bproto;
/* berry closure */
struct bclosure {
bcommon_header;
bbyte nupvals;
bgcobject *gray; /* for gc gray list */
bproto *proto;
bupval *upvals[1];
};
/* C native function or closure */
struct bntvclos {
bcommon_header;
bbyte nupvals;
bgcobject *gray; /* for gc gray list */
bntvfunc f;
};
/* common object */
typedef struct {
bcommon_header;
void *data;
bntvfunc destory;
} bcommomobj;
typedef const char* (*breader)(void*, size_t*);
#define cast(_T, _v) ((_T)(_v))
#define cast_int(_v) cast(int, _v)
#define cast_bool(_v) cast(bbool, _v)
#define basetype(_t) ((_t) & 0x1F)
#define var_type(_v) ((_v)->type)
#define var_basetype(_v) basetype((_v)->type)
#define var_istype(_v, _t) (var_type(_v) == _t)
#define var_settype(_v, _t) ((_v)->type = _t)
#define var_setobj(_v, _t, _o) { (_v)->v.p = _o; var_settype(_v, _t); }
#define var_isnil(_v) var_istype(_v, BE_NIL)
#define var_isbool(_v) var_istype(_v, BE_BOOL)
#define var_isint(_v) var_istype(_v, BE_INT)
#define var_isreal(_v) var_istype(_v, BE_REAL)
#define var_isstr(_v) var_istype(_v, BE_STRING)
#define var_isclosure(_v) var_istype(_v, BE_CLOSURE)
#define var_isntvclos(_v) var_istype(_v, BE_NTVCLOS)
#define var_isntvfunc(_v) var_istype(_v, BE_NTVFUNC)
#define var_isfunction(_v) (var_basetype(_v) == BE_FUNCTION)
#define var_isproto(_v) var_istype(_v, BE_PROTO)
#define var_isclass(_v) var_istype(_v, BE_CLASS)
#define var_isinstance(_v) var_istype(_v, BE_INSTANCE)
#define var_islist(_v) var_istype(_v, BE_LIST)
#define var_ismap(_v) var_istype(_v, BE_MAP)
#define var_ismodule(_v) var_istype(_v, BE_MODULE)
#define var_isnumber(_v) (var_isint(_v) || var_isreal(_v))
#define var_setnil(_v) var_settype(_v, BE_NIL)
#define var_setval(_v, _s) (*(_v) = *(_s))
#define var_setbool(_v, _b) { var_settype(_v, BE_BOOL); (_v)->v.b = (bbool)(_b); }
#define var_setint(_v, _i) { var_settype(_v, BE_INT); (_v)->v.i = (_i); }
#define var_setreal(_v, _r) { var_settype(_v, BE_REAL); (_v)->v.r = (_r); }
#define var_setstr(_v, _s) var_setobj(_v, BE_STRING, _s)
#define var_setinstance(_v, _o) var_setobj(_v, BE_INSTANCE, _o)
#define var_setclass(_v, _o) var_setobj(_v, BE_CLASS, _o)
#define var_setclosure(_v, _o) var_setobj(_v, BE_CLOSURE, _o)
#define var_setntvclos(_v, _o) var_setobj(_v, BE_NTVCLOS, _o)
#define var_setntvfunc(_v, _o) { (_v)->v.nf = (_o); var_settype(_v, BE_NTVFUNC); }
#define var_setlist(_v, _o) var_setobj(_v, BE_LIST, _o)
#define var_setmap(_v, _o) var_setobj(_v, BE_MAP, _o)
#define var_setmodule(_v, _o) var_setobj(_v, BE_MODULE, _o)
#define var_setproto(_v, _o) var_setobj(_v, BE_PROTO, _o)
#define var_tobool(_v) ((_v)->v.b)
#define var_toint(_v) ((_v)->v.i)
#define var_toreal(_v) ((_v)->v.r)
#define var_tostr(_v) ((_v)->v.s)
#define var_togc(_v) ((_v)->v.gc)
#define var_toobj(_v) ((_v)->v.p)
#define var_tontvfunc(_v) ((_v)->v.nf)
#define var_toidx(_v) cast_int(var_toint(_v))
const char* be_vtype2str(bvalue *v);
bvalue* be_indexof(bvm *vm, int idx);
void be_commonobj_delete(bvm *vm, bgcobject *obj);
#endif

View File

@ -0,0 +1,55 @@
/********************************************************************
** 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
********************************************************************/
/* define opcode, don't change order */
/* opcode parameters description */
OPCODE(ADD), /* A, B, C | R(A) <- RK(B) + RK(C) */
OPCODE(SUB), /* A, B, C | R(A) <- RK(B) - RK(C) */
OPCODE(MUL), /* A, B, C | R(A) <- RK(B) * RK(C) */
OPCODE(DIV), /* A, B, C | R(A) <- RK(B) / RK(C) */
OPCODE(MOD), /* A, B, C | R(A) <- RK(B) % RK(C) */
OPCODE(LT), /* A, B, C | R(A) <- RK(B) < RK(C) */
OPCODE(LE), /* A, B, C | R(A) <- RK(B) <= RK(C) */
OPCODE(EQ), /* A, B, C | R(A) <- RK(B) == RK(C) */
OPCODE(NE), /* A, B, C | R(A) <- RK(B) != RK(C) */
OPCODE(GT), /* A, B, C | R(A) <- RK(B) > RK(C) */
OPCODE(GE), /* A, B, C | R(A) <- RK(B) >= RK(C) */
OPCODE(AND), /* A, B, C | R(A) <- RK(B) & RK(C) */
OPCODE(OR), /* A, B, C | R(A) <- RK(B) | RK(C) */
OPCODE(XOR), /* A, B, C | R(A) <- RK(B) ^ RK(C) */
OPCODE(SHL), /* A, B, C | R(A) <- RK(B) << RK(C) */
OPCODE(SHR), /* A, B, C | R(A) <- RK(B) >> RK(C) */
OPCODE(CONNECT), /* A, B, C | R(A) <- connect(RK(B), RK(C)) */
OPCODE(NEG), /* A, B | R(A) <- -RK(B) */
OPCODE(FLIP), /* A, B | R(A) <- ~RK(B) */
OPCODE(LDNIL), /* A | R(A) <- nil */
OPCODE(LDBOOL), /* A, B, C | R(A) <- cast_bool(B), if(C): pc++ */
OPCODE(LDINT), /* A, sBx | R(A) <- sBx */
OPCODE(LDCONST), /* A, Bx | R(A) <- K(Bx) */
OPCODE(MOVE), /* A, B, C | R(A) <- RK(B) */
OPCODE(GETGBL), /* A, Bx | R(A) <- GLOBAL(Bx) */
OPCODE(SETGBL), /* A, Bx | R(A) -> GLOBAL(Bx) */
OPCODE(GETUPV), /* A, Bx | R(A) <- UPVALUE(Bx)*/
OPCODE(SETUPV), /* A, Bx | R(A) -> UPVALUE(Bx)*/
OPCODE(JMP), /* sBx | pc <- pc + sBx */
OPCODE(JMPT), /* A, sBx | if(R(A)): pc <- pc + sBx */
OPCODE(JMPF), /* A, sBx | if(not R(A)): pc <- pc + sBx */
OPCODE(CALL), /* A, B | CALL(R(A), B) */
OPCODE(RET), /* A, B | if (R(A)) R(-1) <- RK(B) else R(-1) <- nil */
OPCODE(CLOSURE), /* A, Bx | R(A) <- CLOSURE(proto_table[Bx])*/
OPCODE(GETMBR), /* A, B, C | R(A) <- RK(B).RK(C) */
OPCODE(GETMET), /* A, B, C | R(A) <- RK(B).RK(C), R(A+1) <- RK(B) */
OPCODE(SETMBR), /* A, B, C | R(A).RK(B) <- RK(C) */
OPCODE(GETIDX), /* A, B, C | R(A) <- RK(B)[RK(C)] */
OPCODE(SETIDX), /* A, B, C | R(A)[RK(B)] <- RK(C) */
OPCODE(SETSUPER), /* A, B | class:R(A) set super with class:RK(B) */
OPCODE(CLOSE), /* A | close upvalues */
OPCODE(IMPORT), /* A, B, C | IF (A == C) import module name as RK(B) to RK(A), ELSE from module RK(C) import name as RK(B) to RK(A) */
OPCODE(EXBLK), /* A, Bx | ... */
OPCODE(CATCH), /* A, B, C | ... */
OPCODE(RAISE), /* A, B, C | ... */
OPCODE(CLASS) /* Bx | init class in K[Bx] */

View File

@ -0,0 +1,271 @@
/********************************************************************
** 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_strlib.h"
#include "be_mem.h"
#include "be_sys.h"
#include <string.h>
#include <stdlib.h>
#define FNAME_BUF_SIZE 512
#if BE_USE_OS_MODULE
#if !BE_USE_FILE_SYSTEM
#error the dependent macro BE_USE_FILE_SYSTEM must be enabled
#endif
static int m_getcwd(bvm *vm)
{
char *buf = be_malloc(vm, FNAME_BUF_SIZE);
if (be_getcwd(buf, FNAME_BUF_SIZE)) {
be_pushstring(vm, buf);
} else {
be_pushstring(vm, "");
}
be_free(vm, buf, FNAME_BUF_SIZE);
be_return(vm);
}
static int m_chdir(bvm *vm)
{
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
int res = be_chdir(be_tostring(vm, 1));
be_pushbool(vm, res == 0);
}
be_return(vm);
}
static int m_mkdir(bvm *vm)
{
int res = 1;
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
res = be_mkdir(be_tostring(vm, 1));
}
be_pushbool(vm, res == 0);
be_return(vm);
}
static int m_remove(bvm *vm)
{
int res = 1;
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
res = be_unlink(be_tostring(vm, 1));
}
be_pushbool(vm, res == 0);
be_return(vm);
}
static int m_listdir(bvm *vm)
{
int res;
bdirinfo info;
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
res = be_dirfirst(&info, be_tostring(vm, 1));
} else {
res = be_dirfirst(&info, ".");
}
be_newobject(vm, "list");
while (res == 0) {
const char *fn = info.name;
if (strcmp(fn, ".") && strcmp(fn, "..")) {
be_pushstring(vm, fn);
be_data_push(vm, -2);
be_pop(vm, 1);
}
res = be_dirnext(&info);
}
be_dirclose(&info);
be_pop(vm, 1);
be_return(vm);
}
static int m_system(bvm *vm)
{
int res = -1, i, argc = be_top(vm);
if (argc > 0) {
be_tostring(vm, 1);
be_pushstring(vm, " ");
for (i = 2; i <= argc; ++i) {
be_strconcat(vm, 1); /* add " " */
be_tostring(vm, i);
be_pushvalue(vm, i);
be_strconcat(vm, 1); /* concat arg */
be_pop(vm, 1);
}
be_pop(vm, argc);
res = system(be_tostring(vm, 1));
}
be_pushint(vm, res);
be_return(vm);
}
static int m_exit(bvm *vm)
{
int status = 0;
if (be_top(vm)) {
if (be_isint(vm, 1)) {
status = be_toindex(vm, 1); /* get the exit code */
} else if (be_isbool(vm, 1)) {
status = be_tobool(vm, 1) - 1; /* true: 0, false: -1 */
} else {
status = -1;
}
}
be_exit(vm, status);
be_return_nil(vm);
}
static int m_path_isdir(bvm *vm)
{
const char *path = NULL;
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
path = be_tostring(vm, 1);
}
be_pushbool(vm, be_isdir(path));
be_return(vm);
}
static int m_path_isfile(bvm *vm)
{
const char *path = NULL;
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
path = be_tostring(vm, 1);
}
be_pushbool(vm, be_isfile(path));
be_return(vm);
}
static int m_path_split(bvm *vm)
{
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
const char *path = be_tostring(vm, 1);
const char *split = be_splitpath(path);
size_t len = split - path;
if (split > path + 1 && split[-1] == '/') {
const char *p = split - 1;
for (; p >= path && *p == '/'; --p);
if (p >= path) {
len = p - path + 1;
}
}
be_getbuiltin(vm, "list");
be_pushnstring(vm, path, len);
be_pushstring(vm, split);
be_call(vm, 2);
be_return(vm);
}
be_return_nil(vm);
}
static int m_path_splitext(bvm *vm)
{
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
const char *path = be_tostring(vm, 1);
const char *split = be_splitname(path);
be_getbuiltin(vm, "list");
be_pushnstring(vm, path, split - path);
be_pushstring(vm, split);
be_call(vm, 2);
be_return(vm);
}
be_return_nil(vm);
}
static int m_path_exists(bvm *vm)
{
const char *path = NULL;
if (be_top(vm) >= 1 && be_isstring(vm, 1)) {
path = be_tostring(vm, 1);
}
be_pushbool(vm, be_isexist(path));
be_return(vm);
}
static int m_path_join(bvm *vm)
{
char *buf, *p;
int i, len = 0, argc = be_top(vm);
for (i = 1; i <= argc; ++i) {
if (be_isstring(vm, i)) {
len += be_strlen(vm, i) + 1;
} else {
be_raise(vm, "type_error", "arguments must be string");
}
}
buf = p = be_malloc(vm, (size_t)len + 1);
for (i = 1; i <= argc; ++i) {
int l = be_strlen(vm, i);
const char *s = be_tostring(vm, i);
if (s[0] == '/') {
p = buf;
}
strcpy(p, s);
p += l;
if (l && s[l - 1] != '/' && i != argc) {
*p++ = '/';
}
}
be_pushnstring(vm, buf, p - buf);
be_free(vm, buf, (size_t)len + 1);
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(path) {
be_native_module_function("isdir", m_path_isdir),
be_native_module_function("isfile", m_path_isfile),
be_native_module_function("exists", m_path_exists),
be_native_module_function("split", m_path_split),
be_native_module_function("splitext", m_path_splitext),
be_native_module_function("join", m_path_join)
};
static be_define_native_module(path, NULL);
be_native_module_attr_table(os) {
be_native_module_function("getcwd", m_getcwd),
be_native_module_function("chdir", m_chdir),
be_native_module_function("mkdir", m_mkdir),
be_native_module_function("remove", m_remove),
be_native_module_function("listdir", m_listdir),
be_native_module_function("system", m_system),
be_native_module_function("exit", m_exit),
be_native_module_module("path", be_native_module(path))
};
be_define_native_module(os, NULL);
#else
/* @const_object_info_begin
module path (scope: local, file: os_path, depend: BE_USE_OS_MODULE) {
isdir, func(m_path_isdir)
isfile, func(m_path_isfile)
exists, func(m_path_exists)
split, func(m_path_split)
splitext, func(m_path_splitext)
join, func(m_path_join)
}
@const_object_info_end */
#include "../generate/be_fixed_os_path.h"
/* @const_object_info_begin
module os (scope: global, depend: BE_USE_OS_MODULE) {
getcwd, func(m_getcwd)
chdir, func(m_chdir)
mkdir, func(m_mkdir)
remove, func(m_remove)
listdir, func(m_listdir)
system, func(m_system)
exit, func(m_exit)
path, module(m_libpath)
}
@const_object_info_end */
#include "../generate/be_fixed_os.h"
#endif
#endif /* BE_USE_OS_MODULE */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_PARSER_H
#define BE_PARSER_H
#include "be_object.h"
#include "be_string.h"
typedef enum {
ETVOID,
ETNIL,
ETBOOL,
ETREAL,
ETINT,
ETSTRING,
ETPROTO,
ETCONST,
ETLOCAL,
ETGLOBAL,
ETUPVAL,
ETMEMBER,
ETINDEX,
ETREG
} exptype_t;
typedef struct {
union {
struct { /* for suffix */
unsigned int idx:9; /* suffix RK index */
unsigned int obj:9; /* object RK index */
unsigned int tt:5; /* object type */
} ss;
breal r; /* for ETREAL */
bint i; /* for ETINT */
bstring *s; /* for ETSTRING */
bproto *p; /* for ETPROTO */
int idx; /* variable index */
} v;
int t; /* patch list of 'exit when true' */
int f; /* patch list of 'exit when false' */
bbyte not; /* not mark */
bbyte type;
} bexpdesc;
typedef struct bblockinfo {
struct bblockinfo *prev;
bbyte nactlocals; /* number of active local variables */
bbyte type; /* block type mask */
bbyte hasupval; /* has upvalue mark */
int breaklist; /* break list */
int beginpc; /* begin pc */
int continuelist; /* continue list */
} bblockinfo;
typedef struct bfuncinfo {
struct bfuncinfo *prev; /* outer function */
bproto *proto; /* the function prototype */
bblockinfo *binfo; /* block information */
struct blexer *lexer; /* the lexer pointer */
blist *local; /* local variable */
bmap *upval; /* upvalue variable */
bvector code; /* code vector */
bvector kvec; /* constants table */
bvector pvec; /* proto table */
#if BE_DEBUG_RUNTIME_INFO /* debug information */
bvector linevec;
#endif
#if BE_DEBUG_VAR_INFO
bvector varvec;
#endif
int pc; /* program count */
bbyte freereg; /* first free register */
bbyte flags; /* some flages */
} bfuncinfo;
/* code block type definitions */
#define BLOCK_LOOP 1
#define BLOCK_EXCEPT 2
bclosure *be_parser_source(bvm *vm,
const char *fname, breader reader, void *data, bbool islocal);
#endif

View File

@ -0,0 +1,123 @@
/********************************************************************
** 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_func.h"
#include "be_vm.h"
static int m_init(bvm *vm)
{
be_pushvalue(vm, 2);
be_setmember(vm, 1, "__lower__");
be_pop(vm, 1);
be_pushvalue(vm, 3);
be_setmember(vm, 1, "__upper__");
be_return_nil(vm);
}
static int m_tostring(bvm *vm)
{
be_pushstring(vm, "(");
be_getmember(vm, 1, "__lower__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, "..");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_getmember(vm, 1, "__upper__");
be_tostring(vm, -1);
be_strconcat(vm, -2);
be_pop(vm, 1);
be_pushstring(vm, ")");
be_strconcat(vm, -2);
be_pop(vm, 1);
be_return(vm);
}
static int m_upper(bvm *vm)
{
be_getmember(vm, 1, "__upper__");
be_return(vm);
}
static int m_lower(bvm *vm)
{
be_getmember(vm, 1, "__lower__");
be_return(vm);
}
static int m_setrange(bvm *vm)
{
be_pushvalue(vm, 2);
be_setmember(vm, 1, "__lower__");
be_pop(vm, 1);
be_pushvalue(vm, 3);
be_setmember(vm, 1, "__upper__");
be_return_nil(vm);
}
static int iter_closure(bvm *vm)
{
/* for better performance, we operate the upvalues
* directly without using by the stack. */
bntvclos *func = var_toobj(vm->cf->func);
bvalue *uv0 = be_ntvclos_upval(func, 0)->value;
bvalue *uv1 = be_ntvclos_upval(func, 1)->value;
bint lower = var_toint(uv0); /* upvalue[0] => lower */
bint upper = var_toint(uv1); /* upvalue[1] => upper */
if (lower > upper) {
be_stop_iteration(vm);
}
var_toint(uv0) = lower + 1; /* set upvale[0] */
be_pushint(vm, lower); /* push the return value */
be_return(vm);
}
static int m_iter(bvm *vm)
{
be_pushntvclosure(vm, iter_closure, 2);
be_getmember(vm, 1, "__lower__");
be_setupval(vm, -2, 0);
be_pop(vm, 1);
be_getmember(vm, 1, "__upper__");
be_setupval(vm, -2, 1);
be_pop(vm, 1);
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
void be_load_rangelib(bvm *vm)
{
static const bnfuncinfo members[] = {
{ "__lower__", NULL },
{ "__upper__", NULL },
{ "init", m_init },
{ "tostring", m_tostring },
{ "lower", m_lower },
{ "upper", m_upper },
{ "setrange", m_setrange },
{ "iter", m_iter },
{ NULL, NULL }
};
be_regclass(vm, "range", members);
}
#else
/* @const_object_info_begin
class be_class_range (scope: global, name: range) {
__lower__, var
__upper__, var
init, func(m_init)
tostring, func(m_tostring)
lower, func(m_lower)
upper, func(m_upper)
setrange, func(m_setrange)
iter, func(m_iter)
}
@const_object_info_end */
#include "../generate/be_fixed_be_class_range.h"
#endif

View File

@ -0,0 +1,106 @@
/********************************************************************
** 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 "berry.h"
#include "be_repl.h"
#include <string.h>
#define safecall(func, ...) if (func) { func(__VA_ARGS__); }
#if BE_USE_SCRIPT_COMPILER
static int try_return(bvm *vm, const char *line)
{
int res, idx;
line = be_pushfstring(vm, "return (%s)", line);
idx = be_absindex(vm, -1); /* get the source text absolute index */
res = be_loadbuffer(vm, "stdin", line, strlen(line)); /* compile line */
be_remove(vm, idx); /* remove source string */
return res;
}
static bbool is_multline(bvm *vm)
{
const char *msg = be_tostring(vm, -1);
size_t len = strlen(msg);
if (len > 5) { /* multi-line text if the error message is 'EOS' at the end */
return !strcmp(msg + len - 5, "'EOS'");
}
return bfalse;
}
static int compile(bvm *vm, char *line, breadline getl, bfreeline freel)
{
int res = try_return(vm, line);
if (be_getexcept(vm, res) == BE_SYNTAX_ERROR) {
be_pop(vm, 2); /* pop exception values */
be_pushstring(vm, line);
safecall(freel, line); /* free line buffer */
for (;;) {
const char *src = be_tostring(vm, -1); /* get source code */
int idx = be_absindex(vm, -1); /* get the source text absolute index */
/* compile source line */
res = be_loadbuffer(vm, "stdin", src, strlen(src));
if (!res || !is_multline(vm)) {
be_remove(vm, idx); /* remove source code */
return res;
}
be_pop(vm, 2); /* pop exception values */
line = getl(">> "); /* read a new input line */
be_pushfstring(vm, "\n%s", line);
safecall(freel, line); /* free line buffer */
be_strconcat(vm, -2);
be_pop(vm, 1); /* pop new line */
}
} else {
safecall(freel, line); /* free line buffer */
}
return res;
}
static int call_script(bvm *vm)
{
int res = be_pcall(vm, 0); /* call the main function */
switch (res) {
case BE_OK: /* execution succeed */
if (!be_isnil(vm, -1)) { /* print return value when it's not nil */
be_dumpvalue(vm, -1);
}
be_pop(vm, 1); /* pop the result value */
break;
case BE_EXCEPTION: /* vm run error */
be_dumpexcept(vm);
be_pop(vm, 1); /* pop the function value */
break;
default: /* BE_EXIT or BE_MALLOC_FAIL */
return res;
}
return 0;
}
BERRY_API int be_repl(bvm *vm, breadline getline, bfreeline freeline)
{
char *line;
be_assert(getline != NULL);
while ((line = getline("> ")) != NULL) {
int res = compile(vm, line, getline, freeline);
if (res == BE_MALLOC_FAIL)
return BE_MALLOC_FAIL;
if (res) {
be_dumpexcept(vm);
} else { /* compiled successfully */
res = call_script(vm);
if (res) {
return res == BE_EXIT ? be_toindex(vm, -1) : res;
}
}
}
be_writenewline();
return 0;
}
#endif

View File

@ -0,0 +1,26 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_REPL_H
#define BE_REPL_H
#include "berry.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef char* (*breadline)(const char *prompt);
typedef void (*bfreeline)(char *ptr);
BERRY_API int be_repl(bvm *vm, breadline getline, bfreeline freeline);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,279 @@
/********************************************************************
** 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_string.h"
#include "be_vm.h"
#include "be_mem.h"
#include "be_constobj.h"
#include <string.h>
#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 be_define_const_str(_name, _s, _hash, _extra, _len, _next) \
BERRY_LOCAL const bcstring be_const_str_##_name = { \
.next = (bgcobject *)_next, \
.type = BE_STRING, \
.marked = GC_CONST, \
.extra = _extra, \
.slen = _len, \
.hash = _hash, \
.s = _s \
}
/* const string table */
struct bconststrtab {
const bstring* const *table;
int count; /* string count */
int size;
};
#if BE_USE_PRECOMPILED_OBJECT
#include "../generate/be_const_strtab_def.h"
#endif
int be_eqstr(bstring *s1, bstring *s2)
{
int slen;
if (s1 == s2) { /* short string or the same string */
return 1;
}
slen = s1->slen;
/* long string */
if (slen == 255 && slen == s2->slen) {
blstring *ls1 = cast(blstring*, s1);
blstring *ls2 = cast(blstring*, s2);
return ls1->llen == ls2->llen && !strcmp(lstr(ls1), lstr(ls2));
}
return 0;
}
static void resize(bvm *vm, int size)
{
int i;
struct bstringtable *tab = &vm->strtab;
if (size > tab->size) {
tab->table = be_realloc(vm, tab->table,
tab->size * sizeof(bstring*), size * sizeof(bstring*));
for (i = tab->size; i < size; ++i) {
tab->table[i] = NULL;
}
}
for (i = 0; i < tab->size; ++i) { /* rehash */
bstring *p = tab->table[i];
tab->table[i] = NULL;
while (p) { /* for each node in the list */
bstring *hnext = next(p);
uint32_t hash = be_strhash(p) & (size - 1);
p->next = cast(void*, tab->table[hash]);
tab->table[hash] = p;
p = hnext;
}
}
if (size < tab->size) {
for (i = size; i < tab->size; ++i) {
tab->table[i] = NULL;
}
tab->table = be_realloc(vm, tab->table,
tab->size * sizeof(bstring*), size * sizeof(bstring*));
}
tab->size = size;
}
static void free_sstring(bvm *vm, bstring *str)
{
be_free(vm, str, sizeof(bsstring) + str->slen + 1);
}
/* FNV-1a Hash */
static uint32_t str_hash(const char *str, size_t len)
{
uint32_t hash = 2166136261u;
be_assert(str || len);
while (len--) {
hash = (hash ^ (unsigned char)*str++) * 16777619u;
}
return hash;
}
void be_string_init(bvm *vm)
{
resize(vm, 8);
#if !BE_USE_PRECOMPILED_OBJECT
/* the destructor name deinit needs to exist all the time, to ensure
* that it does not need to be created when the heap is exhausted. */
be_gc_fix(vm, cast(bgcobject*, str_literal(vm, "deinit")));
#endif
/* be_const_str_deinit --> for precompiled */
}
void be_string_deleteall(bvm *vm)
{
int i;
struct bstringtable *tab = &vm->strtab;
for (i = 0; i < tab->size; ++i) {
bstring *node = tab->table[i];
while (node) {
bstring *next = next(node);
free_sstring(vm, node);
node = next;
}
}
be_free(vm, tab->table, tab->size * sizeof(bstring*));
}
static bstring* createstrobj(bvm *vm, size_t len, int islong)
{
size_t size = (islong ? sizeof(blstring)
: sizeof(bsstring)) + len + 1;
bgcobject *gco = be_gc_newstr(vm, size, islong);
bstring *s = cast_str(gco);
if (s) {
s->slen = islong ? 255 : (bbyte)len;
char *str = cast(char *, islong ? lstr(s) : sstr(s));
str[len] = '\0';
}
return s;
}
#if BE_USE_PRECOMPILED_OBJECT
static bstring* find_conststr(const char *str, size_t len)
{
const struct bconststrtab *tab = &m_const_string_table;
uint32_t hash = str_hash(str, len);
bcstring *s = (bcstring*)tab->table[hash % tab->size];
for (; s != NULL; s = next(s)) {
if (len == s->slen && !strncmp(str, s->s, len)) {
return (bstring*)s;
}
}
return NULL;
}
#endif
static bstring* newshortstr(bvm *vm, const char *str, size_t len)
{
bstring *s;
int size = vm->strtab.size;
uint32_t hash = str_hash(str, len);
bstring **list = vm->strtab.table + (hash & (size - 1));
for (s = *list; s != NULL; s = next(s)) {
if (len == s->slen && !strncmp(str, sstr(s), len)) {
return s;
}
}
s = createstrobj(vm, len, 0);
if (s) {
memcpy(cast(char *, sstr(s)), str, len);
s->extra = 0;
s->next = cast(void*, *list);
#if BE_USE_STR_HASH_CACHE
cast(bsstring*, s)->hash = hash;
#endif
*list = s;
vm->strtab.count++;
if (vm->strtab.count > size << 2) {
resize(vm, size << 1);
}
}
return s;
}
bstring* be_newlongstr(bvm *vm, const char *str, size_t len)
{
bstring *s;
blstring *ls;
s = createstrobj(vm, len, 1);
ls = cast(blstring*, s);
s->extra = 0;
ls->llen = cast_int(len);
if (str) { /* if the argument 'str' is NULL, we just allocate space */
memcpy(cast(char *, lstr(s)), str, len);
}
return s;
}
bstring* be_newstr(bvm *vm, const char *str)
{
return be_newstrn(vm, str, strlen(str));
}
bstring *be_newstrn(bvm *vm, const char *str, size_t len)
{
if (len <= SHORT_STR_MAX_LEN) {
#if BE_USE_PRECOMPILED_OBJECT
bstring *s = find_conststr(str, len);
return s ? s : newshortstr(vm, str, len);
#else
return newshortstr(vm, str, len);
#endif
}
return be_newlongstr(vm, str, len); /* long string */
}
void be_gcstrtab(bvm *vm)
{
struct bstringtable *tab = &vm->strtab;
int size = tab->size, i;
for (i = 0; i < size; ++i) {
bstring **list = tab->table + i;
bstring *prev = NULL, *node, *next;
for (node = *list; node; node = next) {
next = next(node);
if (!gc_isfixed(node) && gc_iswhite(node)) {
free_sstring(vm, node);
tab->count--;
if (prev) { /* link list */
prev->next = cast(void*, next);
} else {
*list = next;
}
} else {
prev = node;
gc_setwhite(node);
}
}
}
if (tab->count < size >> 2 && size > 8) {
resize(vm, size >> 1);
}
}
uint32_t be_strhash(const bstring *s)
{
if (gc_isconst(s)) {
return cast(bcstring*, s)->hash;
}
#if BE_USE_STR_HASH_CACHE
if (s->slen != 255) {
return cast(bsstring*, s)->hash;
}
#endif
return str_hash(str(s), str_len(s));
}
const char* be_str2cstr(const bstring *s)
{
be_assert(cast_str(s) != NULL);
if (gc_isconst(s)) {
return cstr(s);
}
if (s->slen == 255) {
return lstr(s);
}
return sstr(s);
}
void be_str_setextra(bstring *s, int extra)
{
if (!gc_isconst(s)) {
s->extra = cast(bbyte, extra);
}
}

View File

@ -0,0 +1,57 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_STRING_H
#define BE_STRING_H
#include "be_object.h"
#define SHORT_STR_MAX_LEN 64
typedef struct {
bstring_header;
#if BE_USE_STR_HASH_CACHE
uint32_t hash;
#endif
/* char s[]; */
} bsstring;
typedef struct {
bstring str;
int llen;
/* char s[]; */
} blstring;
typedef struct {
bstring_header;
uint32_t hash;
const char *s;
} bcstring;
#define str_len(_s) \
((_s)->slen == 255 ? cast(blstring*, _s)->llen : (_s)->slen)
#define str(_s) be_str2cstr(_s)
#define str_extra(_s) ((_s)->extra)
#define str_literal(_vm, _s) be_newstrn((_vm), (_s), sizeof(_s) - 1)
#if BE_USE_PRECOMPILED_OBJECT
#include "../generate/be_const_strtab.h"
#endif
void be_string_init(bvm *vm);
void be_string_deleteall(bvm *vm);
int be_eqstr(bstring *s1, bstring *s2);
bstring* be_newstr(bvm *vm, const char *str);
bstring* be_newstrn(bvm *vm, const char *str, size_t len);
bstring* be_newlongstr(bvm *vm, const char *str, size_t len);
void be_gcstrtab(bvm *vm);
uint32_t be_strhash(const bstring *s);
const char* be_str2cstr(const bstring *s);
void be_str_setextra(bstring *s, int extra);
#endif

View File

@ -0,0 +1,738 @@
/********************************************************************
** 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_strlib.h"
#include "be_string.h"
#include "be_vm.h"
#include "be_class.h"
#include "be_module.h"
#include "be_exec.h"
#include "be_mem.h"
#include <string.h>
#include <stdio.h>
#define is_space(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
#define is_digit(c) ((c) >= '0' && (c) <= '9')
#define skip_space(s) while (is_space(*(s))) { ++(s); }
typedef bint (*str_opfunc)(const char*, const char*, bint, bint);
bstring* be_strcat(bvm *vm, bstring *s1, bstring *s2)
{
size_t len = (size_t)str_len(s1) + str_len(s2);
if (len <= SHORT_STR_MAX_LEN) {
char buf[SHORT_STR_MAX_LEN + 1];
strcpy(buf, str(s1));
strncat(buf, str(s2), len);
return be_newstrn(vm, buf, len);
} else { /* long string */
bstring *s = be_newstrn(vm, NULL, len);
char *sbuf = (char*)str(s);
strcpy(sbuf, str(s1));
strcpy(sbuf + str_len(s1), str(s2));
return s;
}
}
int be_strcmp(bstring *s1, bstring *s2)
{
if (be_eqstr(s1, s2)) {
return 0;
}
return strcmp(str(s1), str(s2));
}
bstring* be_num2str(bvm *vm, bvalue *v)
{
char buf[25];
if (var_isint(v)) {
sprintf(buf, BE_INT_FORMAT, var_toint(v));
} else if (var_isreal(v)) {
sprintf(buf, "%g", var_toreal(v));
} else {
sprintf(buf, "(nan)");
}
return be_newstr(vm, buf);
}
static void module2str(char *buf, bvalue *v)
{
const char *name = be_module_name(cast(bmodule*, var_toobj(v)));
if (name) {
sprintf(buf, "<module: %s>", name);
} else {
sprintf(buf, "<module: %p>", var_toobj(v));
}
}
static bstring* sim2str(bvm *vm, bvalue *v)
{
char sbuf[64]; /* BUG: memory overflow */
switch (var_type(v)) {
case BE_NIL:
strcpy(sbuf, "nil");
break;
case BE_BOOL:
strcpy(sbuf, var_tobool(v) ? "true" : "false");
break;
case BE_INT:
sprintf(sbuf, BE_INT_FORMAT, var_toint(v));
break;
case BE_REAL:
sprintf(sbuf, "%g", var_toreal(v));
break;
case BE_CLOSURE: case BE_NTVCLOS: case BE_NTVFUNC:
sprintf(sbuf, "<function: %p>", var_toobj(v));
break;
case BE_CLASS:
sprintf(sbuf, "<class: %s>",
str(be_class_name(cast(bclass*, var_toobj(v)))));
break;
case BE_MODULE:
module2str(sbuf, v);
break;
default:
strcpy(sbuf, "(unknow value)");
break;
}
return be_newstr(vm, sbuf);
}
static bstring* ins2str(bvm *vm, int idx)
{
bstring *s = str_literal(vm, "tostring");
binstance *obj = var_toobj(vm->reg + idx);
/* get method 'tostring' */
int type = be_instance_member(vm, obj, s, vm->top);
be_incrtop(vm); /* push the obj::tostring to stack */
if (basetype(type) != BE_FUNCTION) {
bstring *name = be_class_name(be_instance_class(obj));
char *sbuf = be_malloc(vm, (size_t)str_len(name) + 16);
sprintf(sbuf, "<instance: %s()>", str(name));
be_stackpop(vm, 1); /* pop the obj::tostring */
s = be_newstr(vm, sbuf);
be_free(vm, sbuf, (size_t)str_len(name) + 16);
} else {
*vm->top = vm->reg[idx];
be_dofunc(vm, vm->top - 1, 1);
be_stackpop(vm, 1); /* pop the obj::tostring */
if (!var_isstr(vm->top)) { /* check the return value */
const char *name = str(be_instance_name(obj));
be_raise(vm, "runtime_error", be_pushfstring(vm,
"the value of `%s::tostring()` is not a 'string'",
strlen(name) ? name : "<anonymous>"));
}
s = var_tostr(vm->top);
}
return s;
}
void be_val2str(bvm *vm, int index)
{
bstring *s;
int idx = be_absindex(vm, index) - 1;
bvalue *v = vm->reg + idx;
if (var_isstr(v)) return; /* do nothing */
s = var_isinstance(v) ? ins2str(vm, idx) : sim2str(vm, v);
v = vm->reg + idx; /* the stack may change */
var_setstr(v, s);
}
static void pushstr(bvm *vm, const char *s, size_t len)
{
/* to create a string and then update the top pointer,
* otherwise the GC may crash due to uninitialized values.
**/
bstring *str = be_newstrn(vm, s, len);
bvalue *reg = be_incrtop(vm);
var_setstr(reg, str);
}
static const char* concat2(bvm *vm)
{
bvalue *dst = vm->top - 2;
bstring *s1 = var_tostr(dst);
bstring *s2 = var_tostr(dst + 1);
bstring *s = be_strcat(vm, s1, s2);
be_assert(var_isstr(vm->top - 2) && var_isstr(vm->top - 1));
dst = vm->top - 2; /* the stack may change */
var_setstr(dst, s);
--vm->top;
return str(s);
}
const char* be_pushvfstr(bvm *vm, const char *format, va_list arg)
{
pushstr(vm, "", 0);
for (;;) {
const char *p = strchr(format, '%');
if (p == NULL) {
break;
}
pushstr(vm, format, p - format);
concat2(vm);
switch (p[1]) {
case 's': {
const char *s = va_arg(arg, char*);
if (s == NULL) {
s = "(null)";
}
pushstr(vm, s, strlen(s));
break;
}
case 'd': {
bstring *s;
bvalue *v = be_incrtop(vm);
var_setint(v, va_arg(arg, int));
s = be_num2str(vm, v);
var_setstr(v, s);
break;
}
case 'f': case 'g': {
bstring *s;
bvalue *v = be_incrtop(vm);
var_setreal(v, cast(breal, va_arg(arg, double)));
s = be_num2str(vm, v);
var_setstr(v, s);
break;
}
case 'c': {
char c = cast(char, va_arg(arg, int));
pushstr(vm, &c, 1);
break;
}
case '%': {
pushstr(vm, "%", 1);
break;
}
case 'p': {
char buf[2 * sizeof(void*) + 4];
sprintf(buf, "%p", va_arg(arg, void*));
pushstr(vm, buf, strlen(buf));
break;
}
default:
pushstr(vm, "(unknow)", 8);
break;
}
concat2(vm);
format = p + 2;
}
pushstr(vm, format, strlen(format));
return concat2(vm);
}
/*******************************************************************
* the function be_str2int():
* >>-+------------+--+-----+----digits----><
* '-whitespace-' +- + -+
* '- - -'
*******************************************************************/
BERRY_API bint be_str2int(const char *str, const char **endstr)
{
int c, sign;
bint sum = 0;
skip_space(str);
sign = c = *str++;
if (c == '+' || c == '-') {
c = *str++;
}
while (is_digit(c)) {
sum = sum * 10 + c - '0';
c = *str++;
}
if (endstr) {
*endstr = str - 1;
}
return sign == '-' ? -sum : sum;
}
/*******************************************************************
* the function be_str2real():
* >>-+------------+--+-----+--+-digits--+---+--+--------+-+------->
* '-whitespace-' +- + -+ | '-.-' '-digits-' |
* '- - -' '-.--digits-----------------'
*
* >--+------------------------+----------------------------------><
* '-+-e-+--+-----+--digits-'
* '-E-' +- + -+
* '- - -'
*******************************************************************/
BERRY_API breal be_str2real(const char *str, const char **endstr)
{
int c, sign;
breal sum = 0, deci = 0, point = (breal)0.1;
skip_space(str);
sign = c = *str++;
if (c == '+' || c == '-') {
c = *str++;
}
while (is_digit(c)) {
sum = sum * 10 + c - '0';
c = *str++;
}
if (c == '.') {
c = *str++;
while (is_digit(c)) {
deci = deci + ((breal)c - '0') * point;
point *= (breal)0.1;
c = *str++;
}
}
sum = sum + deci;
if (c == 'e' || c == 'E') {
int e = 0;
breal ratio = (c = *str++) == '-' ? (breal)0.1 : 10;
if (c == '+' || c == '-') {
c = *str++;
}
while (is_digit(c)) {
e = e * 10 + c - '0';
c = *str++;
}
while (e--) {
sum *= ratio;
}
}
if (endstr) {
*endstr = str - 1;
}
return sign == '-' ? -sum : sum;
}
/* convert a string to a number (integer or real).
* 1. skip \s*[\+\-]?\d*
* 2. matched [.eE]? yes: real, no: integer.
**/
BERRY_API const char *be_str2num(bvm *vm, const char *str)
{
const char *sout;
bint c, vint = be_str2int(str, &sout);
c = *sout;
if (c == '.' || c == 'e' || c == 'E') {
be_pushreal(vm, be_str2real(str, &sout));
} else {
be_pushint(vm, vint);
}
return sout;
}
/* string subscript operation */
bstring* be_strindex(bvm *vm, bstring *str, bvalue *idx)
{
if (var_isint(idx)) {
int pos = var_toidx(idx);
if (pos < str_len(str)) {
return be_newstrn(vm, str(str) + pos, 1);
}
be_raise(vm, "index_error", "string index out of range");
}
be_raise(vm, "index_error", "string indices must be integers");
return NULL;
}
const char* be_splitpath(const char *path)
{
const char *p;
for (p = path - 1; *path != '\0'; ++path) {
if (*path == '/') {
p = path;
}
}
return p + 1; /* return the file name pointer */
}
const char* be_splitname(const char *path)
{
const char *p, *q, *end = path + strlen(path);
for (p = end; *p != '.' && p > path; --p); /* skip [^\.] */
for (q = p; *q == '.' && q > path; --q); /* skip \. */
if ((q == path && *q == '.') || *q == '/') {
return end;
}
return p;
}
static unsigned escape_length(const char *s, int quote)
{
unsigned c, len = 0, step = quote == '"' ? 5 : 3;
for (; (c = *s) != '\0'; ++s) {
switch (c) {
case '\\': case '\n': case '\r': case '\t':
len += 1;
break;
default:
if (c < 0x20)
len += step;
else if (c == (unsigned)quote)
len += 1;
break;
}
}
return len;
}
static unsigned eschex(unsigned num)
{
return num <= 9 ? '0' + num : 'a' + num - 10;
}
/* escape as Berry or JSON */
static char* escape(char *q, unsigned c, int quote)
{
int json = quote == '"';
switch (c) {
case '\\': *q++ = '\\'; *q = '\\'; break;
case '\n': *q++ = '\\'; *q = 'n'; break;
case '\r': *q++ = '\\'; *q = 'r'; break;
case '\t': *q++ = '\\'; *q = 't'; break;
default:
if (c < 0x20) { /* other characters are escaped using '\uxxxx' */
*q++ = '\\';
if (json) {
*q++ = 'u'; *q++ = '0'; *q++ = '0';
*q++ = (char)eschex(c >> 4);
*q = (char)eschex(c & 0x0f);
} else {
*q++ = 'x';
*q++ = (char)eschex(c >> 4);
*q = (char)eschex(c & 0x0f);
}
} else { /* quotes and unescaped characters */
if (c == (unsigned)quote)
*q++ = '\\';
*q = (char)c;
}
break;
}
return q;
}
static void toescape(bvm *vm, int index, int quote)
{
char *buf, *q;
const char *p, *s = be_tostring(vm, index);
size_t len = (size_t)be_strlen(vm, index);
len += escape_length(s, quote) + 2; /* escape length + quote mark */
buf = q = be_pushbuffer(vm, len);
*q++ = (char)quote; /* add first quote */
/* generate escape string */
for (p = s; *p != '\0'; ++p, ++q) {
q = escape(q, *p, quote);
}
*q = (char)quote; /* add last quote */
be_pushnstring(vm, buf, len); /* make escape string from buffer */
be_moveto(vm, -1, index);
be_pop(vm, 2); /* remove buffer & top string */
}
BERRY_API const char* be_toescape(bvm *vm, int index, int mode)
{
if (be_isstring(vm, index)) {
index = be_absindex(vm, index);
toescape(vm, index, mode == 'u' ? '"' : '\'');
}
return be_tostring(vm, index);
}
#if BE_USE_STRING_MODULE
#define MAX_FORMAT_MODE 32
#define FLAGES "+- #0"
static const char* skip2dig(const char *s)
{
if (is_digit(*s)) {
++s;
}
if (is_digit(*s)) {
++s;
}
return s;
}
static const char* get_mode(const char *str, char *buf)
{
const char *p = str;
while (*p && strchr(FLAGES, *p)) { /* skip flags */
++p;
}
p = skip2dig(p); /* skip width (2 digits at most) */
if (*p == '.') {
p = skip2dig(++p); /* skip width (2 digits at most) */
}
*(buf++) = '%';
strncpy(buf, str, p - str + 1);
buf[p - str + 1] = '\0';
return p;
}
static void mode_fixlen(char *mode, const char *lenmode)
{
size_t l = strlen(mode), lm = strlen(lenmode);
char spec = mode[l - 1];
strcpy(mode + l - 1, lenmode);
mode[l + lm - 1] = spec;
mode[l + lm] = '\0';
}
static int str_format(bvm *vm)
{
int top = be_top(vm);
if (top > 0 && be_isstring(vm, 1)) {
int index = 2;
const char *format = be_tostring(vm, 1);
pushstr(vm, "", 0);
for (;;) {
char mode[MAX_FORMAT_MODE];
char buf[128];
const char *p = strchr(format, '%');
if (p == NULL) {
break;
}
pushstr(vm, format, p - format);
concat2(vm);
p = get_mode(p + 1, mode);
buf[0] = '\0';
if (index > top) {
be_raise(vm, "runtime_error", be_pushfstring(vm,
"bad argument #%d to 'format': no value", index));
}
switch (*p) {
case 'd': case 'i': case 'o':
case 'u': case 'x': case 'X':
if (be_isint(vm, index)) {
mode_fixlen(mode, BE_INT_FMTLEN);
sprintf(buf, mode, be_toint(vm, index));
}
be_pushstring(vm, buf);
break;
case 'e': case 'E':
case 'f': case 'g': case 'G':
if (be_isnumber(vm, index)) {
sprintf(buf, mode, be_toreal(vm, index));
}
be_pushstring(vm, buf);
break;
case 'c':
if (be_isint(vm, index)) {
sprintf(buf, "%c", (int)be_toint(vm, index));
}
be_pushstring(vm, buf);
break;
case 's': {
const char *s = be_tostring(vm, index);
int len = be_strlen(vm, 2);
if (len > 100 && strlen(mode) == 2) {
be_pushvalue(vm, index);
} else {
sprintf(buf, mode, s);
be_pushstring(vm, buf);
}
break;
}
default: /* error */
be_raise(vm, "runtime_error", be_pushfstring(vm,
"invalid option '%%%c' to 'format'", *p));
break;
}
concat2(vm);
format = p + 1;
++index;
}
pushstr(vm, format, strlen(format));
concat2(vm);
be_return(vm);
}
be_return_nil(vm);
}
/* string.op(s1, s2, begin=0, end=length(s2)) */
static bint str_operation(bvm *vm, str_opfunc func, bint error)
{
int top = be_top(vm);
/* check the number and type of arguments */
if (top >= 2 && be_isstring(vm, 1) && be_isstring(vm, 2)) {
/* get the operation string and its length */
int len1 = be_strlen(vm, 1);
int len2 = be_strlen(vm, 2);
const char *s1 = be_tostring(vm, 1);
const char *s2 = be_tostring(vm, 2);
/* get begin and end indexes (may use default values) */
bint begin = top >= 3 && be_isint(vm, 3) ? be_toint(vm, 3) : 0;
bint end = top >= 4 && be_isint(vm, 4) ? be_toint(vm, 4) : len1;
/* basic range check:
* 1. begin position must be greater than 0 and
* less than the length of the source string.
* 2. the length of the pattern string cannot be
* less than the matching range (end - begin).
**/
if (begin >= 0 && begin <= len1 && end - begin >= len2) {
/* call the operation function */
return func(s1, s2, begin, end - len2);
}
}
return error; /* returns the default error value */
}
static bint _sfind(const char *s1, const char *s2, bint begin, bint end)
{
const char *res = strstr(s1 + begin, s2);
if (res) {
bint pos = (bint)(res - s1);
return pos <= end ? pos : -1;
}
return -1;
}
static int str_find(bvm *vm)
{
be_pushint(vm, str_operation(vm, _sfind, -1));
be_return(vm);
}
static bint _scount(const char *s1, const char *s2, bint begin, bint end)
{
bint count = 0;
const char *res = s1 + begin, *send = s1 + end;
while ((res = strstr(res, s2)) != NULL && res <= send) {
count += 1;
res += 1;
}
return count;
}
static int str_count(bvm *vm)
{
be_pushint(vm, str_operation(vm, _scount, 0));
be_return(vm);
}
static bbool _split_string(bvm *vm, int top)
{
if (be_isstring(vm, 2)) {
const char *res;
int len1 = be_strlen(vm, 1);
int len2 = be_strlen(vm, 2);
const char *s1 = be_tostring(vm, 1);
const char *s2 = be_tostring(vm, 2);
bint count = len2 /* match when the pattern string is not empty */
? top >= 3 && be_isint(vm, 3) ? be_toint(vm, 3) : len1
: 0; /* cannot match empty pattern string */
while (count-- && (res = strstr(s1, s2)) != NULL) {
be_pushnstring(vm, s1, res - s1);
be_data_push(vm, -2);
be_pop(vm, 1);
s1 = res + len2;
}
be_pushstring(vm, s1);
be_data_push(vm, -2);
be_pop(vm, 1);
return btrue;
}
return bfalse;
}
static bbool _split_index(bvm *vm)
{
if (be_isint(vm, 2)) {
int len = be_strlen(vm, 1), idx = be_toindex(vm, 2);
const char *s = be_tostring(vm, 1);
idx = idx > len ? len : idx < -len ? -len : idx;
if (idx < 0) {
idx += len;
}
be_pushnstring(vm, s, idx);
be_data_push(vm, -2);
be_pop(vm, 1);
be_pushnstring(vm, s + idx, (size_t)len - idx);
be_data_push(vm, -2);
be_pop(vm, 1);
return btrue;
}
return bfalse;
}
static int str_split(bvm *vm)
{
int top = be_top(vm);
be_newobject(vm, "list");
if (top >= 2 && be_isstring(vm, 1)) {
if (!_split_index(vm))
_split_string(vm, top);
}
be_pop(vm, 1);
be_return(vm);
}
static int str_i2hex(bvm *vm)
{
int top = be_top(vm);
if (top && be_isint(vm, 1)) {
bint value = be_toint(vm, 1);
char fmt[10] = { "%" BE_INT_FMTLEN "X" }, buf[18];
if (top >= 2 && be_isint(vm, 2)) {
bint num = be_toint(vm, 2);
if (num > 0 && num <= 16) {
sprintf(fmt, "%%.%d" BE_INT_FMTLEN "X", (int)num);
}
}
sprintf(buf, fmt, value);
be_pushstring(vm, buf);
be_return(vm);
}
be_return_nil(vm);
}
static int str_byte(bvm *vm)
{
if (be_top(vm) && be_isstring(vm, 1)) {
const bbyte *s = (const bbyte *)be_tostring(vm, 1);
be_pushint(vm, *s);
be_return(vm);
}
be_return_nil(vm);
}
static int str_char(bvm *vm)
{
if (be_top(vm) && be_isint(vm, 1)) {
char c = be_toint(vm, 1) & 0xFF;
be_pushnstring(vm, &c, 1);
be_return(vm);
}
be_return_nil(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(string) {
be_native_module_function("format", str_format),
be_native_module_function("count", str_count),
be_native_module_function("split", str_split),
be_native_module_function("find", str_find),
be_native_module_function("hex", str_i2hex),
be_native_module_function("byte", str_byte),
be_native_module_function("char", str_char)
};
be_define_native_module(string, NULL);
#else
/* @const_object_info_begin
module string (scope: global, depend: BE_USE_STRING_MODULE) {
format, func(str_format)
count, func(str_count)
split, func(str_split)
find, func(str_find)
hex, func(str_i2hex)
byte, func(str_byte)
char, func(str_char)
}
@const_object_info_end */
#include "../generate/be_fixed_string.h"
#endif
#endif /* BE_USE_STRING_MODULE */

View File

@ -0,0 +1,31 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_STRLIB_H
#define BE_STRLIB_H
#include "be_object.h"
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
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);
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);
bstring* be_strindex(bvm *vm, bstring *str, bvalue *idx);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,48 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_SYS_H
#define BE_SYS_H
#include <stddef.h>
/* directory information for directory traversal */
typedef struct {
void *dir;
void *file;
const char *name;
} bdirinfo;
#ifdef __cplusplus
extern "C" {
#endif
void* be_fopen(const char *filename, const char *modes);
int be_fclose(void *hfile);
size_t be_fwrite(void *hfile, const void *buffer, size_t length);
size_t be_fread(void *hfile, void *buffer, size_t length);
char* be_fgets(void *hfile, void *buffer, int size);
int be_fseek(void *hfile, long offset);
long int be_ftell(void *hfile);
long int be_fflush(void *hfile);
size_t be_fsize(void *hfile);
int be_isdir(const char *path);
int be_isfile(const char *path);
int be_isexist(const char *path);
char* be_getcwd(char *buf, size_t size);
int be_chdir(const char *path);
int be_mkdir(const char *path);
int be_unlink(const char *filename);
int be_dirfirst(bdirinfo *info, const char *path);
int be_dirnext(bdirinfo *info);
int be_dirclose(bdirinfo *info);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,36 @@
/********************************************************************
** 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"
#if BE_USE_SYS_MODULE
static int m_path(bvm *vm)
{
be_getbuiltin(vm, "list");
be_module_path(vm);
be_call(vm, 1);
be_pop(vm, 1);
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(sys){
be_native_module_function("path", m_path)
};
be_define_native_module(sys, NULL);
#else
/* @const_object_info_begin
module sys (scope: global, depend: BE_USE_SYS_MODULE) {
path, func(m_path)
}
@const_object_info_end */
#include "../generate/be_fixed_sys.h"
#endif
#endif /* BE_USE_SYS_MODULE */

View File

@ -0,0 +1,71 @@
/********************************************************************
** 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 "berry.h"
#include <time.h>
#if BE_USE_TIME_MODULE
static int m_time(bvm *vm)
{
be_pushint(vm, (bint)time(NULL));
be_return(vm);
}
static void time_insert(bvm *vm, const char *key, int value)
{
be_pushstring(vm, key);
be_pushint(vm, value);
be_data_insert(vm, -3);
be_pop(vm, 2);
}
static int m_dump(bvm *vm)
{
if (be_top(vm) >= 1 && be_isint(vm, 1)) {
time_t ts = be_toint(vm, 1);
struct tm *t = localtime(&ts);
be_newobject(vm, "map");
time_insert(vm, "year", t->tm_year + 1900);
time_insert(vm, "month", t->tm_mon + 1);
time_insert(vm, "day", t->tm_mday);
time_insert(vm, "hour", t->tm_hour);
time_insert(vm, "min", t->tm_min);
time_insert(vm, "sec", t->tm_sec);
time_insert(vm, "weekday", t->tm_wday);
be_pop(vm, 1);
be_return(vm);
}
be_return_nil(vm);
}
static int m_clock(bvm *vm)
{
be_pushreal(vm, clock() / (breal)CLOCKS_PER_SEC);
be_return(vm);
}
#if !BE_USE_PRECOMPILED_OBJECT
be_native_module_attr_table(time) {
be_native_module_function("time", m_time),
be_native_module_function("dump", m_dump),
be_native_module_function("clock", m_clock)
};
be_define_native_module(time, NULL);
#else
/* @const_object_info_begin
module time (scope: global, depend: BE_USE_TIME_MODULE) {
time, func(m_time)
dump, func(m_dump)
clock, func(m_clock)
}
@const_object_info_end */
#include "../generate/be_fixed_time.h"
#endif
#endif /* BE_USE_TIME_MODULE */

View File

@ -0,0 +1,142 @@
/********************************************************************
** 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_var.h"
#include "be_vm.h"
#include "be_vector.h"
#include "be_string.h"
#include "be_map.h"
#include "be_gc.h"
#define global(vm) ((vm)->gbldesc.global)
#define builtin(vm) ((vm)->gbldesc.builtin)
void be_globalvar_init(bvm *vm)
{
global(vm).vtab = be_map_new(vm);
be_gc_fix(vm, gc_object(global(vm).vtab));
be_vector_init(vm, &global(vm).vlist, sizeof(bvalue));
#if !BE_USE_PRECOMPILED_OBJECT
builtin(vm).vtab = be_map_new(vm);
be_vector_init(vm, &builtin(vm).vlist, sizeof(bvalue));
be_gc_fix(vm, gc_object(builtin(vm).vtab));
#endif
}
void be_globalvar_deinit(bvm *vm)
{
global(vm).vtab = NULL;
be_vector_delete(vm, &global(vm).vlist);
#if !BE_USE_PRECOMPILED_OBJECT
builtin(vm).vtab = NULL;
be_vector_delete(vm, &builtin(vm).vlist);
#endif
}
static int global_find(bvm *vm, bstring *name)
{
bvalue *res = be_map_findstr(vm, global(vm).vtab, name);
if (res) {
return var_toidx(res) + be_builtin_count(vm);
}
return -1; /* not found */
}
int be_global_find(bvm *vm, bstring *name)
{
int res = global_find(vm, name);
return res != -1 ? res : be_builtin_find(vm, name);
}
static int global_new_anonymous(bvm *vm)
{
int idx = be_global_count(vm);
/* allocate space for new variables */
be_vector_resize(vm, &global(vm).vlist, idx + 1);
/* set the new variable to nil */
var_setnil((bvalue *)global(vm).vlist.end);
return idx;
}
int be_global_new(bvm *vm, bstring *name)
{
int idx = global_find(vm, name);
if (idx == -1) {
bvalue *desc;
idx = global_new_anonymous(vm);
desc = be_map_insertstr(vm, global(vm).vtab, name, NULL);
var_setint(desc, idx);
idx += be_builtin_count(vm);
}
return idx;
}
bvalue* be_global_var(bvm *vm, int index)
{
int bcnt = be_builtin_count(vm);
if (index < bcnt) {
return be_vector_at(&builtin(vm).vlist, index);
}
index -= bcnt;
return be_vector_at(&global(vm).vlist, index);
}
void be_global_release_space(bvm *vm)
{
be_map_release(vm, global(vm).vtab);
be_vector_release(vm, &global(vm).vlist);
}
int be_builtin_find(bvm *vm, bstring *name)
{
bvalue *res = be_map_findstr(vm, builtin(vm).vtab, name);
if (res) {
return var_toidx(res);
}
return -1; /* not found */
}
bstring* be_builtin_name(bvm *vm, int index)
{
bmap *map = builtin(vm).vtab;
bmapnode *end, *node = map->slots;
for (end = node + map->size; node < end; ++node) {
if (var_isstr(&node->key) && node->value.v.i == index) {
return node->key.v.s;
}
}
return NULL;
}
#if !BE_USE_PRECOMPILED_OBJECT
int be_builtin_new(bvm *vm, bstring *name)
{
int idx = be_builtin_find(vm, name);
if (idx == -1) {
bvalue *desc;
idx = be_map_count(builtin(vm).vtab);
desc = be_map_insertstr(vm, builtin(vm).vtab, name, NULL);
var_setint(desc, idx);
be_vector_resize(vm, &builtin(vm).vlist, idx + 1);
/* set the new variable to nil */
var_setnil((bvalue*)(builtin(vm).vlist.end));
}
return idx;
}
void be_bulitin_release_space(bvm *vm)
{
be_map_release(vm, builtin(vm).vtab);
be_vector_release(vm, &builtin(vm).vlist);
}
#else
void be_const_builtin_set(bvm *vm, const bmap *map, const bvector *vec)
{
builtin(vm).vtab = cast(bmap*, map);
builtin(vm).vlist = *vec;
}
#endif

View File

@ -0,0 +1,31 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_VAR_H
#define BE_VAR_H
#include "be_object.h"
#define be_global_count(vm) \
be_vector_count(&(vm)->gbldesc.global.vlist)
#define be_builtin_count(vm) \
be_vector_count(&(vm)->gbldesc.builtin.vlist)
void be_globalvar_init(bvm *vm);
void be_globalvar_deinit(bvm *vm);
int be_global_find(bvm *vm, bstring *name);
int be_global_new(bvm *vm, bstring *name);
bvalue* be_global_var(bvm *vm, int index);
void be_global_release_space(bvm *vm);
int be_builtin_find(bvm *vm, bstring *name);
bstring* be_builtin_name(bvm *vm, int index);
int be_builtin_new(bvm *vm, bstring *name);
void be_bulitin_release_space(bvm *vm);
void be_const_builtin_set(bvm *vm, const bmap *map, const bvector *vec);
#endif

View File

@ -0,0 +1,153 @@
/********************************************************************
** 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_vector.h"
#include "be_mem.h"
#include <string.h>
/* initialize a vector, the vector structure itself is usually allocated
* on the stack, and the data is allocated from the heap.
**/
void be_vector_init(bvm *vm, bvector *vector, int size)
{
vector->capacity = 2; /* the default capacity */
vector->size = size;
vector->count = 0;
vector->data = be_malloc(vm, (size_t)vector->capacity * size);
vector->end = (char*)vector->data - size;
memset(vector->data, 0, (size_t)vector->capacity * size);
}
void be_vector_delete(bvm *vm, bvector *vector)
{
be_free(vm, vector->data, (size_t)vector->capacity * vector->size);
}
void* be_vector_at(bvector *vector, int index)
{
return (char*)vector->data + (size_t)index * vector->size;
}
void be_vector_push(bvm *vm, bvector *vector, void *data)
{
size_t size = vector->size;
size_t capacity = vector->capacity;
size_t count = vector->count++;
if (count >= capacity) {
int newcap = be_nextsize(vector->capacity);
vector->data = be_realloc(vm,
vector->data, vector->capacity * size, newcap * size);
vector->end = (char*)vector->data + count * size;
vector->capacity = newcap;
} else {
vector->end = (char*)vector->end + size;
}
if (data != NULL) {
memcpy(vector->end, data, size);
}
}
/* clear the expanded portion if the memory expands */
void be_vector_push_c(bvm *vm, bvector *vector, void *data)
{
int capacity = vector->capacity + 1;
be_vector_push(vm, vector, data);
if (vector->capacity > capacity) {
size_t size = ((size_t)vector->capacity - capacity) * vector->size;
memset(be_vector_at(vector, capacity), 0, size);
}
}
void be_vector_remove_end(bvector *vector)
{
be_assert(vector->count > 0);
vector->count--;
vector->end = (char*)vector->end - vector->size;
}
void be_vector_resize(bvm *vm, bvector *vector, int count)
{
size_t size = vector->size;
be_assert(count >= 0);
if (count != be_vector_count(vector)) {
int newcap = be_nextsize(count);
if (newcap > vector->capacity) { /* extended capacity */
vector->data = be_realloc(vm,
vector->data, vector->capacity * size, newcap * size);
vector->capacity = newcap;
}
vector->count = count;
vector->end = (char*)vector->data + size * ((size_t)count - 1);
}
}
void be_vector_clear(bvector *vector)
{
vector->count = 0;
vector->end = (char*)vector->data - vector->size;
}
/* free not used */
void* be_vector_release(bvm *vm, bvector *vector)
{
size_t size = vector->size;
int count = be_vector_count(vector);
if (count == 0) {
be_free(vm, vector->data, vector->capacity * size);
vector->capacity = 0;
vector->data = NULL;
vector->end = NULL;
} else if (count < vector->capacity) {
vector->data = be_realloc(vm,
vector->data, vector->capacity * size, count * size);
vector->end = (char*)vector->data + ((size_t)count - 1) * size;
vector->capacity = count;
}
return vector->data;
}
/* use binary search to find the vector capacity between 0-1024 */
static int binary_search(int value)
{
static const uint16_t tab[] = {
0, 2, 4, 6, 8, 10, 12, 14, 16,
20, 24, 28, 32, 40, 48, 64, 96, 128,
192, 256, 384, 512, 768, 1024
};
const uint16_t *low = tab;
const uint16_t *high = tab + array_count(tab) - 1;
while (low <= high) {
const uint16_t *mid = low + ((high - low) >> 1);
if (*mid == value) {
return mid[1];
}
if (*mid < value) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return *low;
}
static int nextpow(int value)
{
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return value + 1;
}
int be_nextsize(int size)
{
if (size < 1024) {
return binary_search(size);
}
return nextpow(size);
}

View File

@ -0,0 +1,43 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_VECTOR_H
#define BE_VECTOR_H
#include "be_object.h"
/* =============================== defines =============================== */
#define be_vector_data(vector) ((vector)->data)
#define be_vector_first(vector) ((vector)->data)
#define be_vector_isend(vector, item) ((item) > (vector)->end)
#define be_vector_isempty(vector) (!(vector)->count)
#define be_vector_end(vector) ((vector)->end)
#define be_vector_count(vector) ((vector)->count)
#define be_vector_capacity(vector) ((vector)->capacity)
#define be_stack_init(vm, stack, size) be_vector_init(vm, stack, size)
#define be_stack_delete(vm, stack) be_vector_delete(vm, stack)
#define be_stack_clear(stack) be_vector_clear(stack)
#define be_stack_push(vm, stack, data) be_vector_push(vm, stack, data)
#define be_stack_pop(stack) be_vector_remove_end(stack)
#define be_stack_top(stack) be_vector_end(stack)
#define be_stack_base(stack) be_vector_first(stack)
#define be_stack_count(stack) be_vector_count(stack)
#define be_stack_isempty(stack) be_vector_isempty(stack)
/* ========================== function extern ========================== */
void be_vector_init(bvm *vm, bvector *vector, int size);
void be_vector_delete(bvm *vm, bvector *vector);
void* be_vector_at(bvector *vector, int index);
void be_vector_push(bvm *vm, bvector *vector, void *data);
void be_vector_push_c(bvm *vm, bvector *vector, void *data);
void be_vector_remove_end(bvector *vector);
void be_vector_resize(bvm *vm, bvector *vector, int count);
void be_vector_clear(bvector *vector);
void* be_vector_release(bvm *vm, bvector *vector);
int be_nextsize(int value);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,108 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BE_VM_H
#define BE_VM_H
#include "be_object.h"
typedef struct {
struct {
bmap *vtab; /* global variable index table */
bvector vlist; /* global variable list */
} global;
struct {
bmap *vtab; /* built-in variable index table */
bvector vlist; /* built-in variable list */
} builtin;
} bglobaldesc;
typedef struct {
bvalue *func; /* function register pointer */
bvalue *top; /* top register pointer */
bvalue *reg; /* base register pointer */
binstruction *ip; /* instruction pointer (only berry-function) */
#if BE_USE_DEBUG_HOOK
blineinfo *lineinfo;
#endif
int status;
} bcallframe;
struct bgc {
bgcobject *list; /* the GC-object list */
bgcobject *gray; /* the gray object list */
bgcobject *fixed; /* the fixed objecct list */
size_t usage; /* the count of bytes currently allocated */
size_t threshold; /* he threshold of allocation for the next GC */
bbyte steprate; /* the rate of increase in the distribution between two GCs (percentage) */
bbyte status;
};
struct bstringtable {
bstring **table;
int count; /* string count */
int size;
};
struct bmoduledesc {
bmap *loaded; /* loaded module map */
blist *path; /* module load path list */
};
typedef struct {
bvalue func;
binstruction *ip;
} bcallsnapshot;
struct bupval {
bvalue* value;
union {
bvalue value;
struct bupval* next;
} u;
int refcnt;
};
struct bvm {
bglobaldesc gbldesc; /* global description */
bvalue *stack; /* stack space */
bvalue *stacktop; /* stack top register */
bupval *upvalist; /* open upvalue list */
bstack callstack; /* function call stack */
bstack exceptstack; /* exception stack */
bcallframe *cf; /* function call frame */
bvalue *reg; /* function base register */
bvalue *top; /* function top register */
binstruction *ip; /* function instruction pointer */
struct blongjmp *errjmp; /* error jump point */
bstack refstack; /* object reference stack */
struct bmoduledesc module; /* module description */
struct bstringtable strtab; /* short string table */
bstack tracestack; /* call state trace-stack */
bmap *ntvclass; /* native class table */
blist *registry; /* registry list */
struct bgc gc;
#if BE_USE_DEBUG_HOOK
bvalue hook;
bbyte hookmask;
#endif
};
#define NONE_FLAG 0
#define BASE_FRAME (1 << 0)
#define PRIM_FUNC (1 << 1)
void be_dofunc(bvm *vm, bvalue *v, int argc);
bbool be_value2bool(bvm *vm, bvalue *v);
bbool be_vm_iseq(bvm *vm, bvalue *a, bvalue *b);
bbool be_vm_isneq(bvm *vm, bvalue *a, bvalue *b);
bbool be_vm_islt(bvm *vm, bvalue *a, bvalue *b);
bbool be_vm_isle(bvm *vm, bvalue *a, bvalue *b);
bbool be_vm_isgt(bvm *vm, bvalue *a, bvalue *b);
bbool be_vm_isge(bvm *vm, bvalue *a, bvalue *b);
#endif

View File

@ -0,0 +1,437 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BERRY_H
#define BERRY_H
#include <stddef.h>
#include <stdint.h>
#include "berry_conf.h"
#ifdef __cplusplus
extern "C" {
#endif
/* do not modify the version number! */
#define BERRY_VERSION "0.1.10"
#if BE_STACK_TOTAL_MAX < BE_STACK_FREE_MIN * 2
#error "The value of the macro BE_STACK_TOTAL_MAX is too small."
#endif
/* basic type definition */
#if BE_INTGER_TYPE == 0
#define BE_INTEGER int
#define BE_INT_FMTLEN ""
#elif BE_INTGER_TYPE == 1
#define BE_INTEGER long
#define BE_INT_FMTLEN "l"
#elif BE_INTGER_TYPE == 2
#ifdef _WIN32
#define BE_INTEGER __int64
#define BE_INT_FMTLEN "I64"
#else
#define BE_INTEGER long long
#define BE_INT_FMTLEN "ll"
#endif
#else
#error "Unsupported integer type for `bint`."
#endif
#define BE_INT_FORMAT "%" BE_INT_FMTLEN "d"
typedef uint8_t bbyte;
typedef BE_INTEGER bint;
#if BE_USE_SINGLE_FLOAT != 0
typedef float breal;
#else
typedef double breal;
#endif
/* boolean values definition */
#ifndef __cplusplus
#define bbool _Bool
#define bfalse 0
#define btrue 1
#else
#define bbool bool
#define bfalse false
#define btrue true
#endif
/* error code definition */
enum berrorcode {
BE_OK = 0,
BE_EXIT,
BE_MALLOC_FAIL,
BE_EXCEPTION,
BE_SYNTAX_ERROR,
BE_EXEC_ERROR,
BE_IO_ERROR
};
/* native-module member type specifier */
#define BE_CNIL 0
#define BE_CINT 1
#define BE_CREAL 2
#define BE_CBOOL 3
#define BE_CFUNCTION 4
#define BE_CSTRING 5
#define BE_CMODULE 6
/* API function mark */
#if defined(_WIN32) || defined(__CYGWIN__) /* in Windows */
#if defined(BERRY_MODULE) /* berry extension module */
#define BERRY_API __declspec(dllimport)
#else /* berry core */
#define BERRY_API __declspec(dllexport)
#endif
#else /* other platforms */
#define BERRY_API extern
#endif
/* only linux */
#if defined(_WIN32) || defined(__CYGWIN__) /* in Windows */
#define BERRY_LOCAL
#elif defined(__GNUC__) /* in GCC */
#define BERRY_LOCAL __attribute__ ((visibility ("hidden")))
#else /* other platforms */
#define BERRY_LOCAL
#endif
#ifdef __cplusplus
#ifdef __cpp_constexpr
#define BE_CONSTEXPR constexpr
#else
#define BE_CONSTEXPR
#endif
#endif
#ifdef __cplusplus
#define BE_EXPORT_VARIABLE extern "C"
#else
#define BE_EXPORT_VARIABLE
#endif
typedef struct bvm bvm; /* virtual machine structure */
typedef int (*bntvfunc)(bvm*); /* native function pointer */
/* native function information */
typedef struct {
const char *name;
bntvfunc function;
} bnfuncinfo;
/* native module object node */
typedef struct bntvmodobj {
const char *name;
int type;
union value {
bint i;
breal r;
bbool b;
bntvfunc f;
const char *s;
const void *o;
#ifdef __cplusplus
BE_CONSTEXPR value(bint v) : i(v) {}
BE_CONSTEXPR value(breal v) : r(v) {}
BE_CONSTEXPR value(bbool v) : b(v) {}
BE_CONSTEXPR value(bntvfunc v) : f(v) {}
BE_CONSTEXPR value(const char *v) : s(v) {}
BE_CONSTEXPR value(const void *v) : o(v) {}
#endif
} u;
#ifdef __cplusplus
BE_CONSTEXPR bntvmodobj(const char *name) :
name(name), type(BE_CNIL), u(bint(0)) {}
BE_CONSTEXPR bntvmodobj(const char *name, bint v) :
name(name), type(BE_CINT), u(v) {}
BE_CONSTEXPR bntvmodobj(const char *name, breal v) :
name(name), type(BE_CREAL), u(v) {}
BE_CONSTEXPR bntvmodobj(const char *name, bbool v) :
name(name), type(BE_CBOOL), u(v) {}
BE_CONSTEXPR bntvmodobj(const char *name, bntvfunc v) :
name(name), type(BE_CFUNCTION), u(v) {}
BE_CONSTEXPR bntvmodobj(const char *name, const char *v) :
name(name), type(BE_CSTRING), u(v) {}
BE_CONSTEXPR bntvmodobj(const char *name, int _tpye, const void *v) :
name(name), type(_tpye), u(v) {}
#endif
} bntvmodobj;
/* native module object */
typedef struct bntvmodule {
const char *name; /* native module name */
const bntvmodobj *attrs; /* native module attributes */
size_t size; /* native module attribute count */
const struct bmodule *module; /* const module object */
bntvfunc init; /* initialization function */
} bntvmodule;
/* native module node definition macro */
#ifndef __cplusplus
#define be_native_module_nil(_name) \
{ .name = (_name), .type = BE_CNIL, .u.i = 0 }
#define be_native_module_int(_name, _v) \
{ .name = (_name), .type = BE_CINT, .u.i = (bint)(_v) }
#define be_native_module_real(_name, _v) \
{ .name = (_name), .type = BE_CREAL, .u.r = (breal)(_v) }
#define be_native_module_bool(_name, _b) \
{ .name = (_name), .type = BE_CBOOL, .u.b = (bbool)(_b) }
#define be_native_module_function(_name, _f) \
{ .name = (_name), .type = BE_CFUNCTION, .u.f = (_f) }
#define be_native_module_str(_name, _s) \
{ .name = (_name), .type = BE_CSTRING, .u.s = (_s) }
#define be_native_module_module(_name, _m) \
{ .name = (_name), .type = BE_CMODULE, .u.o = &(_m) }
#else
#define be_native_module_nil(_name) \
bntvmodobj(_name)
#define be_native_module_int(_name, _v) \
bntvmodobj(_name, bint(_v))
#define be_native_module_real(_name, _v) \
bntvmodobj(_name, breal(_v))
#define be_native_module_bool(_name, _b) \
bntvmodobj(_name, bbool(_b))
#define be_native_module_function(_name, _f) \
bntvmodobj(_name, _f)
#define be_native_module_str(_name, _s) \
bntvmodobj(_name, _s)
#define be_native_module_module(_name, _m) \
bntvmodobj(_name, BE_CMODULE, &(_m))
#endif
#define be_native_module_attr_table(name) \
static const bntvmodobj name##_attrs[] =
#define be_native_module(name) be_native_module_##name
/* native module declaration macro */
#define be_extern_native_module(name) \
extern const bntvmodule be_native_module(name)
/* native module definition macro */
#ifndef __cplusplus
#define be_define_native_module(_name, _init) \
const bntvmodule be_native_module(_name) = { \
.name = #_name, \
.attrs = _name##_attrs, \
.size = sizeof(_name##_attrs) \
/ sizeof(_name##_attrs[0]), \
.module = NULL, \
.init = _init \
}
#else
#define be_define_native_module(_name, _init) \
const bntvmodule be_native_module(_name) = { \
#_name, _name##_attrs, \
sizeof(_name##_attrs) \
/ sizeof(_name##_attrs[0]), \
0, _init \
}
#endif
/* debug hook typedefs */
#define BE_HOOK_LINE 1
#define BE_HOOK_CALL 2
#define BE_HOOK_RET 4
#define BE_HOOK_EXCEPT 8
typedef struct bhookinfo {
int type; /* current hook type */
int line; /* current linenumber */
const char *source; /* source path information */
const char *func_name; /* current function name */
void *data; /* user extended data */
} bhookinfo;
typedef void(*bntvhook)(bvm *vm, bhookinfo *info);
/* the default assert definition */
#if !BE_DEBUG
#if defined(be_assert)
#undef be_assert
#endif
#define be_assert(expr) ((void)0)
#endif
/* FFI functions */
#define be_writestring(s) be_writebuffer((s), strlen(s))
#define be_writenewline() be_writebuffer("\n", 1)
#define be_return(vm) return be_returnvalue(vm)
#define be_return_nil(vm) return be_returnnilvalue(vm)
#define be_loadfile(vm, name) be_loadmode((vm), (name), 0)
#define be_loadmodule(vm, name) be_loadmode((vm), (name), 1)
#define be_loadstring(vm, str) \
be_loadbuffer((vm), "string", (str), strlen(str))
#define be_dostring(vm, s) \
(be_loadstring((vm), (s)) || be_pcall((vm), 0))
BERRY_API bint be_str2int(const char *str, const char **endstr);
BERRY_API breal be_str2real(const char *str, const char **endstr);
BERRY_API const char *be_str2num(bvm *vm, const char *str);
BERRY_API int be_top(bvm *vm);
BERRY_API const char *be_typename(bvm *vm, int index);
BERRY_API const char *be_classname(bvm *vm, int index);
BERRY_API bbool be_classof(bvm *vm, int index);
BERRY_API int be_strlen(bvm *vm, int index);
BERRY_API void be_strconcat(bvm *vm, int index);
BERRY_API void be_pop(bvm *vm, int n);
BERRY_API void be_remove(bvm *vm, int index);
BERRY_API int be_absindex(bvm *vm, int index);
BERRY_API bbool be_isnil(bvm *vm, int index);
BERRY_API bbool be_isbool(bvm *vm, int index);
BERRY_API bbool be_isint(bvm *vm, int index);
BERRY_API bbool be_isreal(bvm *vm, int index);
BERRY_API bbool be_isnumber(bvm *vm, int index);
BERRY_API bbool be_isstring(bvm *vm, int index);
BERRY_API bbool be_isclosure(bvm *vm, int index);
BERRY_API bbool be_isntvclos(bvm *vm, int index);
BERRY_API bbool be_isfunction(bvm *vm, int index);
BERRY_API bbool be_isproto(bvm *vm, int index);
BERRY_API bbool be_isclass(bvm *vm, int index);
BERRY_API bbool be_isinstance(bvm *vm, int index);
BERRY_API bbool be_islist(bvm *vm, int index);
BERRY_API bbool be_ismap(bvm *vm, int index);
BERRY_API bbool be_iscomptr(bvm *vm, int index);
BERRY_API bbool be_iscomobj(bvm *vm, int index);
BERRY_API bbool be_isderived(bvm *vm, int index);
BERRY_API bint be_toint(bvm *vm, int index);
BERRY_API breal be_toreal(bvm *vm, int index);
BERRY_API int be_toindex(bvm *vm, int index);
BERRY_API bbool be_tobool(bvm *vm, int index);
BERRY_API const char* be_tostring(bvm *vm, int index);
BERRY_API const char* be_toescape(bvm *vm, int index, int mode);
BERRY_API void* be_tocomptr(bvm* vm, int index);
BERRY_API void be_moveto(bvm *vm, int from, int to);
BERRY_API void be_pushnil(bvm *vm);
BERRY_API void be_pushbool(bvm *vm, int b);
BERRY_API void be_pushint(bvm *vm, bint i);
BERRY_API void be_pushreal(bvm *vm, breal r);
BERRY_API void be_pushstring(bvm *vm, const char *str);
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_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);
BERRY_API void be_pushcomptr(bvm *vm, void *ptr);
BERRY_API bbool be_pushiter(bvm *vm, int index);
BERRY_API void be_newlist(bvm *vm);
BERRY_API void be_newmap(bvm *vm);
BERRY_API void be_newmodule(bvm *vm);
BERRY_API void be_newcomobj(bvm *vm, void *data, bntvfunc destory);
BERRY_API void be_newobject(bvm *vm, const char *name);
BERRY_API bbool be_copy(bvm *vm, int index);
BERRY_API bbool be_setname(bvm *vm, int index, const char *name);
BERRY_API bbool be_getglobal(bvm *vm, const char *name);
BERRY_API void be_setglobal(bvm *vm, const char *name);
BERRY_API bbool be_getbuiltin(bvm *vm, const char *name);
BERRY_API bbool be_setmember(bvm *vm, int index, const char *k);
BERRY_API bbool be_getmember(bvm *vm, int index, const char *k);
BERRY_API bbool be_getmethod(bvm *vm, int index, const char *k);
BERRY_API bbool be_getindex(bvm *vm, int index);
BERRY_API bbool be_setindex(bvm *vm, int index);
BERRY_API void be_getupval(bvm *vm, int index, int pos);
BERRY_API bbool be_setupval(bvm *vm, int index, int pos);
BERRY_API bbool be_setsuper(bvm *vm, int index);
BERRY_API void be_getsuper(bvm *vm, int index);
BERRY_API int be_data_size(bvm *vm, int index);
BERRY_API void be_data_push(bvm *vm, int index);
BERRY_API bbool be_data_insert(bvm *vm, int index);
BERRY_API bbool be_data_remove(bvm *vm, int index);
BERRY_API bbool be_data_merge(bvm *vm, int index);
BERRY_API void be_data_resize(bvm *vm, int index);
BERRY_API void be_data_reverse(bvm *vm, int index);
BERRY_API int be_iter_next(bvm *vm, int index);
BERRY_API bbool be_iter_hasnext(bvm *vm, int index);
BERRY_API bbool be_refcontains(bvm *vm, int index);
BERRY_API void be_refpush(bvm *vm, int index);
BERRY_API void be_refpop(bvm *vm);
BERRY_API void be_stack_require(bvm *vm, int count);
/* relop operation APIs */
BERRY_API bbool be_iseq(bvm *vm);
BERRY_API bbool be_isneq(bvm *vm);
BERRY_API bbool be_islt(bvm *vm);
BERRY_API bbool be_isle(bvm *vm);
BERRY_API bbool be_isgt(bvm *vm);
BERRY_API bbool be_isge(bvm *vm);
/* Function call/return APIs */
BERRY_API int be_returnvalue(bvm *vm);
BERRY_API int be_returnnilvalue(bvm *vm);
BERRY_API void be_call(bvm *vm, int argc);
BERRY_API int be_pcall(bvm *vm, int argc);
BERRY_API void be_exit(bvm *vm, int status);
/* exception APIs */
BERRY_API void be_raise(bvm *vm, const char *except, const char *msg);
BERRY_API int be_getexcept(bvm *vm, int code);
BERRY_API void be_dumpvalue(bvm *vm, int index);
BERRY_API void be_dumpexcept(bvm *vm);
BERRY_API void be_stop_iteration(bvm *vm);
BERRY_API void be_regfunc(bvm *vm, const char *name, bntvfunc f);
BERRY_API void be_regclass(bvm *vm, const char *name, const bnfuncinfo *lib);
/* VM management APIs */
BERRY_API bvm* be_vm_new(void);
BERRY_API void be_vm_delete(bvm *vm);
/* code load APIs */
BERRY_API int be_loadbuffer(bvm *vm,
const char *name, const char *buffer, size_t length);
BERRY_API int be_loadmode(bvm *vm, const char *name, bbool islocal);
BERRY_API int be_loadlib(bvm *vm, const char *path);
BERRY_API int be_savecode(bvm *vm, const char *name);
/* module path list APIs */
BERRY_API void be_module_path(bvm *vm);
BERRY_API void be_module_path_set(bvm *vm, const char *path);
/* registry operation */
BERRY_API int be_register(bvm *vm, int index);
BERRY_API void be_unregister(bvm *vm, int id);
BERRY_API void be_getregister(bvm *vm, int id);
/* debug APIs */
BERRY_API void be_sethook(bvm *vm, const char *mask);
BERRY_API void be_setntvhook(bvm *vm, bntvhook hook, void *data, int mask);
/* basic character IO APIs */
BERRY_API void be_writebuffer(const char *buffer, size_t length);
BERRY_API char* be_readstring(char *buffer, size_t size);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1 @@
#include "port/berry_conf.h"

View File

@ -0,0 +1,57 @@
/********************************************************************
** 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 "berry.h"
/* this file contains the declaration of the module table. */
/* default modules declare */
be_extern_native_module(string);
be_extern_native_module(json);
be_extern_native_module(math);
be_extern_native_module(time);
be_extern_native_module(os);
be_extern_native_module(sys);
be_extern_native_module(debug);
be_extern_native_module(gc);
/* user-defined modules declare start */
/* user-defined modules declare end */
/* module list declaration */
BERRY_LOCAL const bntvmodule* const be_module_table[] = {
/* default modules register */
#if BE_USE_STRING_MODULE
&be_native_module(string),
#endif
#if BE_USE_JSON_MODULE
&be_native_module(json),
#endif
#if BE_USE_MATH_MODULE
&be_native_module(math),
#endif
#if BE_USE_TIME_MODULE
&be_native_module(time),
#endif
#if BE_USE_OS_MODULE
&be_native_module(os),
#endif
#if BE_USE_SYS_MODULE
&be_native_module(sys),
#endif
#if BE_USE_DEBUG_MODULE
&be_native_module(debug),
#endif
#if BE_USE_GC_MODULE
&be_native_module(gc),
#endif
/* user-defined modules register start */
/* user-defined modules register end */
NULL /* do not remove */
};

View File

@ -0,0 +1,362 @@
/********************************************************************
** 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 "berry.h"
#include "be_mem.h"
#include "be_sys.h"
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
/* this file contains configuration for the file system. */
/* standard input and output */
extern "C" {
void serial(const char *sp) {
char s[200];
snprintf_P(s, sizeof(s), "%s", sp);
Serial.printf(s);
Serial.flush();
}
void serial3(const char *sp, uint32_t a, uint32_t b, uint32_t c) {
char s[200];
snprintf_P(s, sizeof(s), "%s 0x%08X 0x%08X 0x%08X\n", sp, a, b, c);
Serial.printf(s);
Serial.flush();
}
void serial2s1(const char *sp, const char * a, const char * b, uint32_t c) {
char s[200];
snprintf_P(s, sizeof(s), "%s '%s' '%s' 0x%08X\n", sp, a, b, c);
Serial.printf(s);
Serial.flush();
}
// int strncmp_PP(const char * str1P, const char * str2P, size_t size)
// {
// int result = 0;
// while (size > 0)
// {
// char ch1 = pgm_read_byte(str1P++);
// char ch2 = pgm_read_byte(str2P++);
// result = ch1 - ch2;
// if (result != 0 || ch2 == '\0')
// {
// break;
// }
// size--;
// }
// return result;
// }
}
BERRY_API void be_writebuffer(const char *buffer, size_t length)
{
Serial.write(buffer, length);
// be_fwrite(stdout, buffer, length);
}
BERRY_API char* be_readstring(char *buffer, size_t size)
{
return 0;
// return be_fgets(stdin, buffer, (int)size);
}
/* use the standard library implementation file API. */
void* be_fopen(const char *filename, const char *modes)
{
return nullptr;
// return fopen(filename, modes);
}
int be_fclose(void *hfile)
{
return 0;
// return fclose(hfile);
}
size_t be_fwrite(void *hfile, const void *buffer, size_t length)
{
return 0;
// return fwrite(buffer, 1, length, hfile);
}
size_t be_fread(void *hfile, void *buffer, size_t length)
{
return 0;
// return fread(buffer, 1, length, hfile);
}
char* be_fgets(void *hfile, void *buffer, int size)
{
return nullptr;
// return fgets(buffer, size, hfile);
}
int be_fseek(void *hfile, long offset)
{
return 0;
// return fseek(hfile, offset, SEEK_SET);
}
long int be_ftell(void *hfile)
{
return 0;
// return ftell(hfile);
}
long int be_fflush(void *hfile)
{
return 0;
// return fflush(hfile);
}
size_t be_fsize(void *hfile)
{
// long int size, offset = be_ftell(hfile);
// fseek(hfile, 0L, SEEK_END);
// size = ftell(hfile);
// fseek(hfile, offset, SEEK_SET);
// return size;
return 0;
}
// #if BE_USE_FILE_SYSTEM
// #if defined(USE_FATFS) /* FatFs */
// int be_isdir(const char *path)
// {
// FILINFO fno;
// FRESULT fr = f_stat(path, &fno);
// return fr == FR_OK && fno.fattrib & AM_DIR;
// }
// int be_isfile(const char *path)
// {
// FILINFO fno;
// FRESULT fr = f_stat(path, &fno);
// return fr == FR_OK && !(fno.fattrib & AM_DIR);
// }
// int be_isexist(const char *path)
// {
// FILINFO fno;
// return f_stat(path, &fno) == FR_OK;
// }
// char* be_getcwd(char *buf, size_t size)
// {
// FRESULT fr = f_getcwd(buf, (UINT)size);
// return fr == FR_OK ? buf : NULL;
// }
// int be_chdir(const char *path)
// {
// return f_chdir(path);
// }
// int be_mkdir(const char *path)
// {
// return f_mkdir(path);
// }
// int be_unlink(const char *filename)
// {
// return f_unlink(filename);
// }
// int be_dirfirst(bdirinfo *info, const char *path)
// {
// info->dir = be_os_malloc(sizeof(DIR));
// info->file = be_os_malloc(sizeof(FILINFO));
// if (info->dir && info->file) {
// FRESULT fr = f_opendir(info->dir, path);
// return fr == FR_OK ? be_dirnext(info) : 1;
// }
// be_os_free(info->dir);
// be_os_free(info->file);
// info->dir = NULL;
// info->file = NULL;
// return 1;
// }
// int be_dirnext(bdirinfo *info)
// {
// FRESULT fr = f_readdir(info->dir, info->file);
// info->name = ((FILINFO *)info->file)->fname;
// return fr != FR_OK || *info->name == '\0';
// }
// int be_dirclose(bdirinfo *info)
// {
// if (info->dir) {
// int res = f_closedir(info->dir) != FR_OK;
// be_os_free(info->dir);
// be_os_free(info->file);
// return res;
// }
// return 1;
// }
// #elif defined(_MSC_VER) /* MSVC*/
// #include <windows.h>
// #include <direct.h>
// #include <io.h>
// int be_isdir(const char *path)
// {
// DWORD type = GetFileAttributes(path);
// return type != INVALID_FILE_ATTRIBUTES
// && (type & FILE_ATTRIBUTE_DIRECTORY) != 0;
// }
// int be_isfile(const char *path)
// {
// DWORD type = GetFileAttributes(path);
// return type != INVALID_FILE_ATTRIBUTES
// && (type & FILE_ATTRIBUTE_DIRECTORY) == 0;
// }
// int be_isexist(const char *path)
// {
// return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES;
// }
// char* be_getcwd(char *buf, size_t size)
// {
// return _getcwd(buf, (int)size);
// }
// int be_chdir(const char *path)
// {
// return _chdir(path);
// }
// int be_mkdir(const char *path)
// {
// return _mkdir(path);
// }
// int be_unlink(const char *filename)
// {
// return remove(filename);
// }
// int be_dirfirst(bdirinfo *info, const char *path)
// {
// char *buf = be_os_malloc(strlen(path) + 3);
// info->file = be_os_malloc(sizeof(struct _finddata_t));
// info->dir = NULL;
// if (buf && info->file) {
// struct _finddata_t *cfile = info->file;
// strcat(strcpy(buf, path), "/*");
// info->dir = (void *)_findfirst(buf, cfile);
// info->name = cfile->name;
// be_os_free(buf);
// return (intptr_t)info->dir == -1;
// }
// be_os_free(buf);
// return 1;
// }
// int be_dirnext(bdirinfo *info)
// {
// struct _finddata_t *cfile = info->file;
// int res = _findnext((intptr_t)info->dir, cfile) != 0;
// info->name = cfile->name;
// return res;
// }
// int be_dirclose(bdirinfo *info)
// {
// be_os_free(info->file);
// return _findclose((intptr_t)info->dir) != 0;
// }
// #else /* must be POSIX */
// #include <dirent.h>
// #include <unistd.h>
// #include <sys/stat.h>
// int be_isdir(const char *path)
// {
// struct stat path_stat;
// int res = stat(path, &path_stat);
// return res == 0 && S_ISDIR(path_stat.st_mode);
// }
// int be_isfile(const char *path)
// {
// struct stat path_stat;
// int res = stat(path, &path_stat);
// return res == 0 && !S_ISDIR(path_stat.st_mode);
// }
// int be_isexist(const char *path)
// {
// struct stat path_stat;
// return stat(path, &path_stat) == 0;
// }
// char* be_getcwd(char *buf, size_t size)
// {
// return getcwd(buf, size);
// }
// int be_chdir(const char *path)
// {
// return chdir(path);
// }
// int be_mkdir(const char *path)
// {
// #ifdef _WIN32
// return mkdir(path);
// #else
// return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
// #endif
// }
// int be_unlink(const char *filename)
// {
// return remove(filename);
// }
// int be_dirfirst(bdirinfo *info, const char *path)
// {
// info->dir = opendir(path);
// if (info->dir) {
// return be_dirnext(info);
// }
// return 1;
// }
// int be_dirnext(bdirinfo *info)
// {
// struct dirent *file;
// info->file = file = readdir(info->dir);
// if (file) {
// info->name = file->d_name;
// return 0;
// }
// return 1;
// }
// int be_dirclose(bdirinfo *info)
// {
// return closedir(info->dir) != 0;
// }
// #endif /* POSIX */
// #endif /* BE_USE_OS_MODULE || BE_USE_FILE_SYSTEM */

View File

@ -0,0 +1,180 @@
/********************************************************************
** 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
********************************************************************/
#ifndef BERRY_CONF_H
#define BERRY_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
extern void serial(const char *sp);
extern void serial3(const char *sp, uint32_t a, uint32_t b, uint32_t c);
extern void serial2s1(const char *sp, const char *a, const char *b, uint32_t c);
extern int strncmp_PP(const char * str1P, const char * str2P, size_t size);
#ifdef __cplusplus
}
#endif
#include <assert.h>
/* Macro: BE_DEBUG
* Berry interpreter debug switch.
* Default: 0
**/
#ifndef BE_DEBUG
#define BE_DEBUG 0
#endif
/* Macro: BE_LONGLONG_INT
* Select integer length.
* If the value is 0, use an integer of type int, use a long
* integer type when the value is 1, and use a long long integer
* type when the value is 2.
* Default: 2
*/
#define BE_INTGER_TYPE 1
/* Macro: BE_USE_SINGLE_FLOAT
* Select floating point precision.
* Use double-precision floating-point numbers when the value
* is 0 (default), otherwise use single-precision floating-point
* numbers.
* Default: 0
**/
#define BE_USE_SINGLE_FLOAT 1
/* Macro: BE_USE_PRECOMPILED_OBJECT
* Use precompiled objects to avoid creating these objects at
* runtime. Enable this macro can greatly optimize RAM usage.
* Default: 1
**/
#define BE_USE_PRECOMPILED_OBJECT 1
/* Macro: BE_DEBUG_RUNTIME_INFO
* Set runtime error debugging information.
* 0: unable to output source file and line number at runtime.
* 1: output source file and line number information at runtime.
* 2: the information use uint16_t type (save space).
* Default: 1
**/
#define BE_DEBUG_RUNTIME_INFO 0
/* Macro: BE_DEBUG_VAR_INFO
* Set variable debugging tracking information.
* 0: disable variable debugging tracking information at runtime.
* 1: enable variable debugging tracking information at runtime.
* Default: 1
**/
#define BE_DEBUG_VAR_INFO 0
/* Macro: BE_STACK_TOTAL_MAX
* Set the maximum total stack size.
* Default: 20000
**/
#define BE_STACK_TOTAL_MAX 4000
/* Macro: BE_STACK_FREE_MIN
* Set the minimum free count of the stack. The stack idles will
* be checked when a function is called, and the stack will be
* expanded if the number of free is less than BE_STACK_FREE_MIN.
* Default: 10
**/
#define BE_STACK_FREE_MIN 10
/* Macro: BE_STACK_FREE_MIN
* The short string will hold the hash value when the value is
* true. It may be faster but requires more RAM.
* Default: 0
**/
#define BE_USE_STR_HASH_CACHE 0
/* Macro: BE_USE_FILE_SYSTEM
* The file system interface will be used when this macro is true
* or when using the OS module. Otherwise the file system interface
* will not be used.
* Default: 0
**/
#define BE_USE_FILE_SYSTEM 0
/* Macro: BE_USE_SCRIPT_COMPILER
* Enable compiler when BE_USE_SCRIPT_COMPILER is not 0, otherwise
* disable the compiler.
* Default: 1
**/
#define BE_USE_SCRIPT_COMPILER 1
/* Macro: BE_USE_BYTECODE_SAVER
* Enable save bytecode to file when BE_USE_BYTECODE_SAVER is not 0,
* otherwise disable the feature.
* Default: 1
**/
#define BE_USE_BYTECODE_SAVER 0
/* Macro: BE_USE_BYTECODE_LOADER
* Enable load bytecode from file when BE_USE_BYTECODE_LOADER is not 0,
* otherwise disable the feature.
* Default: 1
**/
#define BE_USE_BYTECODE_LOADER 0
/* Macro: BE_USE_SHARED_LIB
* Enable shared library when BE_USE_SHARED_LIB is not 0,
* otherwise disable the feature.
* Default: 1
**/
#define BE_USE_SHARED_LIB 0
/* Macro: BE_USE_OVERLOAD_HASH
* Allows instances to overload hash methods for use in the
* built-in Map class. Disable this feature to crop the code
* size.
* Default: 1
**/
#define BE_USE_OVERLOAD_HASH 1
/* Macro: BE_USE_DEBUG_HOOK
* Berry debug hook switch.
* Default: 0
**/
#define BE_USE_DEBUG_HOOK 0
/* Macro: BE_USE_XXX_MODULE
* These macros control whether the related module is compiled.
* When they are true, they will enable related modules. At this
* point you can use the import statement to import the module.
* They will not compile related modules when they are false.
**/
#define BE_USE_STRING_MODULE 1
#define BE_USE_JSON_MODULE 1
#define BE_USE_MATH_MODULE 1
#define BE_USE_TIME_MODULE 0
#define BE_USE_OS_MODULE 0
#define BE_USE_SYS_MODULE 0
#define BE_USE_DEBUG_MODULE 0
#define BE_USE_GC_MODULE 0
/* Macro: BE_EXPLICIT_XXX
* If these macros are defined, the corresponding function will
* use the version defined by these macros. These macro definitions
* are not required.
* The default is to use the functions in the standard library.
**/
#define BE_EXPLICIT_ABORT abort
#define BE_EXPLICIT_EXIT exit
#define BE_EXPLICIT_MALLOC malloc
#define BE_EXPLICIT_FREE free
#define BE_EXPLICIT_REALLOC realloc
/* Macro: be_assert
* Berry debug assertion. Only enabled when BE_DEBUG is active.
* Default: use the assert() function of the standard library.
**/
#define be_assert(expr) assert(expr)
#endif

View File

@ -673,6 +673,12 @@
#define D_CMND_PING "Ping"
#define D_JSON_PING "Ping"
// Commands xdrv_52_berry.ino - Berry scripting language
#define D_PRFX_BR "Br"
#define D_CMND_BR_RUN ""
#define D_CMND_BR_RESET "Reset"
#define D_BR_NOT_STARTED "Berry not started"
// Commands xsns_02_analog.ino
#define D_CMND_ADCPARAM "AdcParam"
@ -716,6 +722,7 @@
#define D_LOG_WIFI "WIF: " // Wifi
#define D_LOG_ZIGBEE "ZIG: " // Zigbee
#define D_LOG_TCP "TCP: " // TCP bridge
#define D_LOG_BERRY "BRY: " // Berry scripting language
/********************************************************************************************/

546
tasmota/xdrv_52_berry.ino Normal file
View File

@ -0,0 +1,546 @@
/*
xdrv_52_berry.ino - Berry scripting language
Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_BERRY
// #ifdef ESP32
#define XDRV_52 52
#include <berry.h>
#include <csetjmp>
const size_t BERRY_STACK = 4096; // size for the alternate stack for continuation
const char kBrCommands[] PROGMEM = D_PRFX_BR "|" // prefix
D_CMND_BR_RUN "|" D_CMND_BR_RESET
;
void (* const BerryCommand[])(void) PROGMEM = {
CmndBrRun, CmndBrReset,
};
/*********************************************************************************************\
* Async mode for Berry VM
*
* We enhance the berry language with `yield()` and `wait(ms)` functions.
* When called, the VM is frozen and control is given back to Tasmota. Then Tasmota
* at next tick or when the time is reached, resumes the VM.
*
* This is based on coroutines scheme, similar to the contiuation stack of ESP8266.
* The basic concept is that Tasmota records a longjump target including current stack position
* and return address.
* The Berry VM is then called with an alternate stack so that we can switch from both stacks
* and keep the callchain intact.
*
* High level view:
* - Tasmota records a return vector with `setjmp`
* - Tasmota changes replaces the native stack with an alternate stack pre-allocated on the heap
* - Tasmota calls the Berry VM with `be_pcall`
* - During the flow of Berry VM, the user code calls `yield()` or `wait(ms)`
* - Corresponding native function is called (still on alternate stack)
* - Native function records VM resume target with `setjmp`
* - and gives back function to Tasmota via `longjmp`.
* Note: `longjmp` restores at the same time the native stack.
*
* Note: trampoline functions relies on global variables, since stack variable don't work anymore
* when replacing stack.
*
\*********************************************************************************************/
class BerrySupport {
public:
bvm *vm = nullptr; // berry vm
#ifdef USE_BERRY_ASYNC
// Alternate stack for the Berry VM
uint8_t *stack_alloc = nullptr; // stack malloc address
uint8_t *stack = nullptr; // alternate stack for continuation (top of stack)
// longjmp vectors to yield from Tasmota to VM and reverse
bool ta_cont_ok = false; // is the Tasmota continuation address valid?
bool vm_cont_ok = false; // is the VM continuation address valid?
jmp_buf ta_cont; // continuation structure for the longjump back to Tasmota
jmp_buf vm_cont;
// used by trampoline to call be_pcall()
#endif // USE_BERRY_ASYNC
const char *fname = nullptr; // name of berry function to call
int32_t fret = 0;
};
BerrySupport berry;
//
// Sanity Check for be_top()
//
// Checks that the Berry stack is empty, if not print a Warning and empty it
//
void checkBeTop(void) {
int32_t top = be_top(berry.vm);
if (top != 0) {
be_pop(berry.vm, top); // TODO should not be there
AddLog(LOG_LEVEL_ERROR, D_LOG_BERRY "Error be_top is non zero=%d", top);
}
}
/*********************************************************************************************\
* Native functions mapped to Berry functions
*
\*********************************************************************************************/
// Berry: `log(string) -> nil`
// Logs the string at LOG_LEVEL_INFO (loglevel=2)
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);
AddLog(LOG_LEVEL_INFO, D_LOG_BERRY "LOG: %s", msg);
be_return(vm); // Return
}
be_return_nil(vm); // Return nil when something goes wrong
}
// Berry: `getFreeHeap() -> int`
// ESP object
int32_t l_getFreeHeap(bvm *vm) {
be_pushint(vm, ESP.getFreeHeap());
be_return(vm);
}
// Berry: `printStack() -> nul`
// print stack pointer
// int32_t l_printStack(bvm *vm) {
// int r = 0;
// AddLog(LOG_LEVEL_INFO, PSTR("Trampo: stack = 0x%08X"), &r);
// be_return(vm);
// }
// Yield
int32_t l_yield(bvm *vm) {
#ifdef USE_BERRY_ASYNC
if (berry.ta_cont_ok) { // if no ta_cont address, then ignore
if (setjmp(berry.vm_cont) == 0) { // record the current state
berry.vm_cont_ok = true;
longjmp(berry.ta_cont, -1); // give back control to Tasmota
}
}
berry.vm_cont_ok = false; // from now, berry.vm_cont is no more valid
#endif // USE_BERRY_ASYNC
be_return(vm);
}
// be_native_module_attr_table(esp) {
// be_native_module_function("getFreeHeap", l_getFreeHeap),
// };
// be_define_native_module(math, nullptr);
/*********************************************************************************************\
* Handlers for Berry calls and async
*
\*********************************************************************************************/
// // call a function (if exists) of type void -> void
// void callBerryFunctionVoid_berry(const char * fname) {
// berry.fret = 0;
// callBerryFunctionVoid(berry.fname);
// }
// call a function (if exists) of type void -> void
void callBerryFunctionVoid(const char * fname) {
if (nullptr == berry.vm) { return; }
checkBeTop();
be_getglobal(berry.vm, fname);
if (!be_isnil(berry.vm, -1)) {
// AddLog(LOG_LEVEL_DEBUG, D_LOG_BERRY "Calling '%s'", fname);
be_pcall(berry.vm, 0);
be_pop(berry.vm, 1); // remove function object
} else {
// AddLog(LOG_LEVEL_DEBUG, D_LOG_BERRY "Function '%s' not found", fname);
be_pop(berry.vm, 1); // remove nil object
}
checkBeTop();
}
void test_input(void) {
int i = 0;
AddLog(LOG_LEVEL_INFO, "test_input stack = 0x%08X", &i);
callBerryFunctionVoid("noop");
}
int be_pcall_with_alt_stack() {
berry.fret = be_pcall(berry.vm, 0);
return berry.fret;
}
void printStack(void) {
int r = 0;
AddLog(LOG_LEVEL_INFO, PSTR("Trampo: stack = 0x%08X"), &r);
}
#ifdef USE_BERRY_ASYNC
int32_t callTrampoline(void *func) {
// Tasmota stack active
// ----------------------------------
static int r;
berry.vm_cont_ok = false;
if ((r = setjmp(berry.ta_cont)) == 0) { // capture registers
// Tasmota stack active
// ----------------------------------
// on the first run, we call back ourselves with the alternate stack
// we clone the return vector and change the stack pointer
static jmp_buf trampo;
memmove(trampo, berry.ta_cont, sizeof(berry.ta_cont));
#if defined(ESP8266) || defined(ESP32)
trampo[1] = (int32_t) berry.stack; // change stack
#else
#error "Need CPU specific code for setting alternate stack"
#endif
longjmp(trampo, (int)func);
// this part is unreachable (longjmp does not return)
} else if (r == -1) {
// Tasmota stack active
// ----------------------------------
// the call has completed normally, and `yield` was not called
berry.ta_cont_ok = false;
AddLog(LOG_LEVEL_INFO, "Trampo: old stack restored");
// printStack();
} else {
// WARNING
// ALTERNATE stack active
// - DON'T USE ANY LOCAL VARIABLE HERE
// -----------------------------------
// r contains the address of the function to call
// AddLog(LOG_LEVEL_INFO, "Trampo: new stack reg");
// printStack();
berry.ta_cont_ok = true; // Berry can call back Tasmota thread
callBerryFunctionVoid("noop");
AddLog(LOG_LEVEL_INFO, "Trampo: after callBerryFunctionVoid");
// printStack();
longjmp(berry.ta_cont, -1);
// this part is unreachable (longjmp does not return)
// which protects us from accidentally using the alternate stack
// in regular code
}
// Tasmota stack active
// ----------------------------------
}
#endif // USE_BERRY_ASYNC
// void fake_callBerryFunctionVoid(const char * fname, jmp_buf * env) {
// (void) setjmp(env);
// }
// void call_callBerryFunctionVoid(const char * fname, jmp_buf * ret_env, ) {
// callBerryFunctionVoid(fname);
// longjump(env, 1);
// }
/*********************************************************************************************\
* Handlers for Berry calls and async
*
\*********************************************************************************************/
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\") "
// - def l_getFreeHeap() return 1234 end
// - def l_log(m) print(m) end
// Simulate Tasmota module
"class Tasmota "
"def getFreeHeap() return l_getFreeHeap() end "
// "def log(m) return l_log(m) end "
"end "
"tasmota = Tasmota() "
"n = 1;"
"def every_second() n = n + 1; if (n % 100 == 10) log('foobar '+str(n)+' free_heap = '+str(tasmota.getFreeHeap())) end end; "
;
/*********************************************************************************************\
* VM Init
\*********************************************************************************************/
void BrReset(void) {
// clean previous VM if any
if (berry.vm != nullptr) {
be_vm_delete(berry.vm);
berry.vm = nullptr;
#ifdef USE_BERRY_ASYNC
berry.ta_cont_ok = false; // is the Tasmota continuation address valid?
berry.vm_cont_ok = false; // is the VM continuation address valid?
#endif // USE_BERRY_ASYNC
}
int32_t ret_code1, ret_code2;
bool berry_init_ok = false;
do {
#ifdef USE_BERRY_ASYNC
berry.stack_alloc = (uint8_t*) malloc(BERRY_STACK); // alternate stack
berry.stack = berry.stack_alloc + BERRY_STACK; // top of stack
#endif // USE_BERRY_ASYNC
uint32_t heap_before = ESP.getFreeHeap();
berry.vm = be_vm_new(); /* create a virtual machine instance */
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_BERRY "Berry VM created, RAM consumed=%u (Heap=%u)"), heap_before - ESP.getFreeHeap(), ESP.getFreeHeap());
// Register functions
be_regfunc(berry.vm, "log", l_logInfo);
be_regfunc(berry.vm, "l_getFreeHeap", l_getFreeHeap);
// be_regfunc(berry.vm, "printStack", l_printStack);
be_regfunc(berry.vm, "yield", l_yield);
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_BERRY "Berry function registered, RAM consumed=%u (Heap=%u)"), heap_before - ESP.getFreeHeap(), ESP.getFreeHeap());
ret_code1 = be_loadstring(berry.vm, berry_prog);
if (ret_code1 != 0) {
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_BERRY "ERROR: be_loadstring [%s] %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1));
be_pop(berry.vm, 2);
break;
}
ret_code2 = be_pcall(berry.vm, 0);
if (ret_code1 != 0) {
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_BERRY "ERROR: be_pcall [%s] %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1));
be_pop(berry.vm, 2);
break;
}
be_pop(berry.vm, 1);
// AddLog(LOG_LEVEL_INFO, PSTR("Get function"));
// AddLog(LOG_LEVEL_INFO, PSTR("BE_TOP = %d"), be_top(berry.vm));
// AddLog(LOG_LEVEL_INFO, PSTR("Get function"));
// be_getglobal(vm, PSTR("func"));
// be_pushint(vm, 3);
// be_pcall(vm, 1);
// be_pop(vm, 2);
// // AddLog(LOG_LEVEL_INFO, PSTR("BE_TOP = %d"), be_top(vm));
// be_getglobal(vm, "testreal");
// AddLog(LOG_LEVEL_INFO, PSTR("is_nil -1 = %d"), be_isnil(vm, -1));
// be_pcall(vm, 0);
// // AddLog(LOG_LEVEL_INFO, PSTR("is_nil -1 = %d"), be_isnil(vm, -1));
// AddLog(LOG_LEVEL_INFO, PSTR("to_string -1 = %s"), be_tostring(vm, -1));
// be_pop(vm, 1);
// AddLog(LOG_LEVEL_INFO, PSTR("BE_TOP = %d"), be_top(vm));
// try a non-existant function
// be_getglobal(vm, "doesnotexist");
// AddLog(LOG_LEVEL_INFO, PSTR("is_nil -1 = %d"), be_isnil(vm, -1));
// AddLog(LOG_LEVEL_INFO, PSTR("BE_TOP = %d"), be_top(vm));
// be_pop(vm, 1);
// Try
// callBerryFunctionVoid("noop");
// callBerryFunctionVoid("noop2");
// test_input();
/////////////////////////////////
// callTrampoline(nullptr);
// // Try coroutines
// int jmp_val;
// if ((jmp_val=setjmp(berry.ta_cont)) == 0) {
// AddLog(LOG_LEVEL_INFO, "vm return address = 0x%08X", berry.ta_cont[0]);
// AddLog(LOG_LEVEL_INFO, "vm stack address = 0x%08X", berry.ta_cont[1]);
// callTrampoline(nullptr);
// // // call routine
// // jmp_buf trampoline_env;
// // fake_callBerryFunctionVoid("noop", &tasmota_env);
// // trampoline_env[0] = call_callBerryFunctionVoid
// } else {
// AddLog(LOG_LEVEL_INFO, "vm return address = 0x%08X", berry.ta_cont[0]);
// // we get back control
// }
#ifdef USE_BERRY_ASYNC
if (berry.vm_cont_ok) {
printStack();
AddLog(LOG_LEVEL_INFO, "Trampo: we need to complete vm exec 1");
if (setjmp(berry.ta_cont) == 0) {
berry.ta_cont_ok = true;
berry.vm_cont_ok = false;
AddLog(LOG_LEVEL_INFO, "Trampo: call exec 1");
longjmp(berry.vm_cont, 1);
}
berry.ta_cont_ok = false;
AddLog(LOG_LEVEL_INFO, "Trampo: returned from exec 1");
}
printStack();
if (berry.vm_cont_ok) {
printStack();
AddLog(LOG_LEVEL_INFO, "Trampo: we need to complete vm exec 2");
if (setjmp(berry.ta_cont) == 0) {
berry.ta_cont_ok = true;
berry.vm_cont_ok = false;
AddLog(LOG_LEVEL_INFO, "Trampo: call exec 2");
longjmp(berry.vm_cont, 1);
}
berry.ta_cont_ok = false;
AddLog(LOG_LEVEL_INFO, "Trampo: returned from exec 2");
}
printStack();
#endif // USE_BERRY_ASYNC
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_BERRY "Berry initialized, RAM consumed=%u (Heap=%u)"), heap_before - ESP.getFreeHeap(), ESP.getFreeHeap());
// AddLog(LOG_LEVEL_INFO, PSTR("Delete Berry VM"));
// be_vm_delete(vm);
// AddLog(LOG_LEVEL_INFO, PSTR("After Berry"));
berry_init_ok = true;
} while (0);
if (!berry_init_ok) {
// free resources
if (berry.vm != nullptr) {
be_vm_delete(berry.vm);
berry.vm = nullptr;
}
#ifdef USE_BERRY_ASYNC
if (berry.stack_alloc != nullptr) {
free(berry.stack_alloc);
berry.stack_alloc = nullptr;
}
#endif // USE_BERRY_ASYNC
}
}
/*********************************************************************************************\
* Tasmota Commands
\*********************************************************************************************/
//
// Command `BrRun`
//
void CmndBrRun(void) {
int32_t ret_code;
const char * ret_type, * ret_val;
if (berry.vm == nullptr) { ResponseCmndChar_P(PSTR(D_BR_NOT_STARTED)); return; }
char br_cmd[XdrvMailbox.data_len+12];
// encapsulate into a function, copied from `be_repl.c` / `try_return()`
snprintf_P(br_cmd, sizeof(br_cmd), "return (%s)", XdrvMailbox.data);
checkBeTop();
do {
// First try with the `return ()` wrapper
ret_code = be_loadbuffer(berry.vm, "input", br_cmd, strlen(br_cmd));
if (be_getexcept(berry.vm, ret_code) == BE_SYNTAX_ERROR) {
be_pop(berry.vm, 2); // remove exception values
// if fails, try the direct command
ret_code = be_loadbuffer(berry.vm, "input", XdrvMailbox.data, strlen(XdrvMailbox.data));
}
if (0 != ret_code) break;
ret_code = be_pcall(berry.vm, 0); // execute code
} while (0);
if (0 == ret_code) {
// code taken from REPL, look first at top, and if nil, look at return value
if (be_isnil(berry.vm, 0)) {
ret_val = be_tostring(berry.vm, -1);
} else {
ret_val = be_tostring(berry.vm, 0);
}
Response_P(PSTR("%s"), ret_val);
be_pop(berry.vm, 1);
} else {
Response_P(PSTR("[%s] %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1));
be_pop(berry.vm, 2);
}
checkBeTop();
}
//
// Command `BrReset`
//
void CmndBrReset(void) {
if (berry.vm == nullptr) { ResponseCmndChar_P(PSTR(D_BR_NOT_STARTED)); return; }
BrReset();
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv52(uint8_t function)
{
bool result = false;
switch (function) {
//case FUNC_PRE_INIT:
case FUNC_INIT:
BrReset();
break;
case FUNC_EVERY_100_MSECOND:
// callBerryFunctionVoid("every_100ms");
// ScripterEvery100ms();
break;
case FUNC_EVERY_SECOND:
// callBerryFunctionVoid("every_second");
// ScriptEverySecond();
break;
case FUNC_COMMAND:
result = DecodeCommand(kBrCommands, BerryCommand);
break;
case FUNC_SET_POWER:
break;
case FUNC_RULES_PROCESS:
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_BUTTON:
break;
case FUNC_WEB_ADD_MAIN_BUTTON:
break;
case FUNC_WEB_ADD_HANDLER:
// Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration);
// Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration);
// Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);}, script_upload_start);
// Webserver->on("/exs", HTTP_GET, ScriptExecuteUploadSuccess);
break;
#endif // USE_WEBSERVER
case FUNC_SAVE_BEFORE_RESTART:
// if (bitRead(Settings.rule_enabled, 0)) {
// Run_Scripter(">R", 2, 0);
// Scripter_save_pvars();
// }
break;
case FUNC_MQTT_DATA:
break;
case FUNC_WEB_SENSOR:
break;
case FUNC_JSON_APPEND:
break;
case FUNC_BUTTON_PRESSED:
break;
case FUNC_LOOP:
break;
}
return result;
}
// #endif // ESP32
#endif // USE_BERRY