mirror of https://github.com/arendst/Tasmota.git
Merge pull request #10870 from s-hadinger/berry_early_1
Preview of Berry language for Tasmota32
This commit is contained in:
commit
d178b0e021
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
|
@ -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
|
||||
};
|
|
@ -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
|
||||
);
|
|
@ -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
|
||||
);
|
|
@ -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
|
||||
);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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
|
||||
);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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"
|
||||
);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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] */
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
#include "port/berry_conf.h"
|
|
@ -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 */
|
||||
};
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue