diff --git a/CHANGELOG.md b/CHANGELOG.md index cf78a1df8..fc723c450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/LICENSE b/lib/lib_div/Berry-0.1.10/LICENSE new file mode 100644 index 000000000..609969272 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/LICENSE @@ -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. diff --git a/lib/lib_div/Berry-0.1.10/generate/be_const_strtab.h b/lib/lib_div/Berry-0.1.10/generate/be_const_strtab.h new file mode 100644 index 000000000..df695ac4a --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_const_strtab.h @@ -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; diff --git a/lib/lib_div/Berry-0.1.10/generate/be_const_strtab_def.h b/lib/lib_div/Berry-0.1.10/generate/be_const_strtab_def.h new file mode 100644 index 000000000..f3503a6f5 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_const_strtab_def.h @@ -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 +}; diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_list.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_list.h new file mode 100644 index 000000000..64eaac2ac --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_list.h @@ -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 +); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_map.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_map.h new file mode 100644 index 000000000..eebf1b658 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_map.h @@ -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 +); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_range.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_range.h new file mode 100644 index 000000000..fc55c6304 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_be_class_range.h @@ -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 +); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_debug.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_debug.h new file mode 100644 index 000000000..4139381d9 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_debug.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_gc.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_gc.h new file mode 100644 index 000000000..388427f71 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_gc.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_json.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_json.h new file mode 100644 index 000000000..ab4b0254c --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_json.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_m_builtin.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_m_builtin.h new file mode 100644 index 000000000..4b6d13344 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_m_builtin.h @@ -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 +); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_math.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_math.h new file mode 100644 index 000000000..028e6e006 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_math.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_os.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_os.h new file mode 100644 index 000000000..25022b87d --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_os.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_os_path.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_os_path.h new file mode 100644 index 000000000..fc4f5edbc --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_os_path.h @@ -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" +); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_string.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_string.h new file mode 100644 index 000000000..c2da8b838 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_string.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_sys.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_sys.h new file mode 100644 index 000000000..5fb85ceb0 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_sys.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/generate/be_fixed_time.h b/lib/lib_div/Berry-0.1.10/generate/be_fixed_time.h new file mode 100644 index 000000000..7c10c5fb9 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/generate/be_fixed_time.h @@ -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); diff --git a/lib/lib_div/Berry-0.1.10/library.properties b/lib/lib_div/Berry-0.1.10/library.properties new file mode 100644 index 000000000..23cdd9946 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/library.properties @@ -0,0 +1,7 @@ +name=Berry +version=0.1.10 +author=Guan Wenliang , +maintainer=Stephan Hadinger +sentence=Berry scripting language for Tasmota32 +paragraph=Berry is a ultra-lightweight dynamically typed embedded scripting language. +architectures=esp32 diff --git a/lib/lib_div/Berry-0.1.10/src/be_api.c b/lib/lib_div/Berry-0.1.10/src/be_api.c new file mode 100644 index 000000000..ad1a0dfb0 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_api.c @@ -0,0 +1,1129 @@ +/******************************************************************** +** 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_vm.h" +#include "be_func.h" +#include "be_class.h" +#include "be_string.h" +#include "be_vector.h" +#include "be_var.h" +#include "be_list.h" +#include "be_map.h" +#include "be_parser.h" +#include "be_debug.h" +#include "be_exec.h" +#include "be_strlib.h" +#include "be_module.h" +#include "be_gc.h" +#include + +#define retreg(vm) ((vm)->cf->func) + +static void class_init(bvm *vm, bclass *c, const bnfuncinfo *lib) +{ + if (lib) { + while (lib->name) { + bstring *s = be_newstr(vm, lib->name); + if (lib->function) { /* method */ + be_prim_method_bind(vm, c, s, lib->function); + } else { + be_member_bind(vm, c, s); /* member */ + } + ++lib; + } + be_map_release(vm, c->members); /* clear space */ + } +} + +static bclass* class_auto_make(bvm *vm, bstring *name, const bnfuncinfo *lib) +{ + bvalue key, *res; + var_setobj(&key, BE_COMPTR, (void*)lib); + if (vm->ntvclass == NULL) { + vm->ntvclass = be_map_new(vm); + } + res = be_map_find(vm, vm->ntvclass, &key); + if (res == NULL || !var_isclass(res)) { + bclass *c; + /* insert class to native class table */ + res = be_map_insert(vm, vm->ntvclass, &key, NULL); + var_setnil(res); /* must be initialized to ensure correct GC */ + c = be_newclass(vm, name, NULL); + var_setclass(res, c); + class_init(vm, c, lib); /* bind members */ + return c; + } + return var_toobj(res); +} + +BERRY_API void be_regfunc(bvm *vm, const char *name, bntvfunc f) +{ + bvalue *var; + bstring *s = be_newstr(vm, name); +#if !BE_USE_PRECOMPILED_OBJECT + int idx = be_builtin_find(vm, s); + be_assert(idx == -1); + if (idx == -1) { /* new function */ + idx = be_builtin_new(vm, s); +#else + int idx = be_global_find(vm, s); + be_assert(idx < be_builtin_count(vm)); + if (idx < be_builtin_count(vm)) { /* new function */ + idx = be_global_new(vm, s); +#endif + var = be_global_var(vm, idx); + var_setntvfunc(var, f); + } /* error case, do nothing */ +} + +BERRY_API void be_regclass(bvm *vm, const char *name, const bnfuncinfo *lib) +{ + bvalue *var; + bstring *s = be_newstr(vm, name); +#if !BE_USE_PRECOMPILED_OBJECT + int idx = be_builtin_find(vm, s); + be_assert(idx == -1); + if (idx == -1) { /* new function */ + idx = be_builtin_new(vm, s); +#else + int idx = be_global_find(vm, s); + be_assert(idx < be_builtin_count(vm)); + if (idx < be_builtin_count(vm)) { /* new function */ + idx = be_global_new(vm, s); +#endif + var = be_global_var(vm, idx); + var_setclass(var, class_auto_make(vm, s, lib)); + } /* error case, do nothing */ +} + +BERRY_API int be_top(bvm *vm) +{ + return cast_int(vm->top - vm->reg); +} + +BERRY_API void be_pop(bvm *vm, int n) +{ + be_assert(n <= vm->top - vm->reg); + be_stackpop(vm, n); +} + +BERRY_API int be_absindex(bvm *vm, int index) +{ + if (index > 0) { + return index; + } + be_assert(vm->reg <= vm->top + index); + return cast_int(vm->top + index - vm->reg + 1); +} + +BERRY_API bbool be_isnil(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isnil(v); +} + +BERRY_API bbool be_isbool(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isbool(v); +} + +BERRY_API bbool be_isint(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isint(v); +} + +BERRY_API bbool be_isreal(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isreal(v); +} + +BERRY_API bbool be_isnumber(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isnumber(v); +} + +BERRY_API bbool be_isstring(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isstr(v); +} + +BERRY_API bbool be_isclosure(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isclosure(v); +} + +BERRY_API bbool be_isntvclos(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isntvclos(v); +} + +BERRY_API bbool be_isfunction(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isfunction(v); +} + +BERRY_API bbool be_isproto(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isproto(v); +} + +BERRY_API bbool be_isclass(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isclass(v); +} + +BERRY_API bbool be_isinstance(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_isinstance(v); +} + +BERRY_API bbool be_islist(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_islist(v); +} + +BERRY_API bbool be_ismap(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_ismap(v); +} + +BERRY_API bbool be_iscomptr(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_istype(v, BE_COMPTR); +} + +BERRY_API bbool be_iscomobj(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_istype(v, BE_COMOBJ); +} + +BERRY_API bint be_toint(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_toint(v); +} + +BERRY_API breal be_toreal(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_isreal(v)) { + return var_toreal(v); + } + if (var_isint(v)) { + return cast(breal, var_toint(v)); + } + return cast(breal, 0.0); +} + +BERRY_API int be_toindex(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return var_toidx(v); +} + +BERRY_API bbool be_tobool(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return be_value2bool(vm, v); +} + +BERRY_API const char* be_tostring(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (!var_isstr(v)) { + be_val2str(vm, index); + v = be_indexof(vm, index); + } + return str(var_tostr(v)); +} + +BERRY_API void* be_tocomptr(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_istype(v, BE_COMPTR)) { + return var_toobj(v); + } + if (var_istype(v, BE_COMOBJ)) { + bcommomobj *obj = var_toobj(v); + return obj->data; + } + return NULL; +} + +BERRY_API void be_moveto(bvm *vm, int from, int to) +{ + bvalue *src = be_indexof(vm, from); + bvalue *dst = be_indexof(vm, to); + var_setval(dst, src); +} + +BERRY_API void be_pushnil(bvm *vm) +{ + bvalue *reg = be_incrtop(vm); + var_setnil(reg); +} + +BERRY_API void be_pushbool(bvm *vm, int b) +{ + bvalue *reg = be_incrtop(vm); + var_setbool(reg, b != bfalse); +} + +BERRY_API void be_pushint(bvm *vm, bint i) +{ + bvalue *reg = be_incrtop(vm); + var_setint(reg, i); +} + +BERRY_API void be_pushreal(bvm *vm, breal r) +{ + bvalue *reg = be_incrtop(vm); + var_setreal(reg, r); +} + +BERRY_API void be_pushstring(bvm *vm, const char *str) +{ + /* to create a string and then push the top registor, + * otherwise the GC may crash due to uninitialized values. + **/ + bstring *s = be_newstr(vm, str); + bvalue *reg = be_incrtop(vm); + be_assert(reg < vm->stacktop); + var_setstr(reg, s); +} + +BERRY_API void be_pushnstring(bvm *vm, const char *str, size_t n) +{ + /* to create a string and then push the top registor, + * otherwise the GC may crash due to uninitialized values. + **/ + bstring *s = be_newstrn(vm, str, n); + bvalue *reg = be_incrtop(vm); + var_setstr(reg, s); +} + +BERRY_API const char* be_pushfstring(bvm *vm, const char *format, ...) +{ + const char* s; + va_list arg_ptr; + va_start(arg_ptr, format); + s = be_pushvfstr(vm, format, arg_ptr); + va_end(arg_ptr); + return s; +} + +BERRY_API void* be_pushbuffer(bvm *vm, size_t size) +{ + bstring *s = be_newlongstr(vm, NULL, size); + bvalue *reg = be_incrtop(vm); + var_setstr(reg, s); + return (void*)str(s); +} + +BERRY_API void be_pushvalue(bvm *vm, int index) +{ + bvalue *reg = vm->top; + var_setval(reg, be_indexof(vm, index)); + be_incrtop(vm); +} + +BERRY_API void be_pushntvclosure(bvm *vm, bntvfunc f, int nupvals) +{ + /* to create a native closure and then push the top registor, + * otherwise the GC may crash due to uninitialized values. + **/ + bntvclos *cl = be_newntvclosure(vm, f, nupvals); + bvalue *top = be_incrtop(vm); + var_setntvclos(top, cl); +} + +BERRY_API void be_pushntvfunction(bvm *vm, bntvfunc f) +{ + bvalue *top = be_incrtop(vm); + var_setntvfunc(top, f); +} + +BERRY_API void be_pushclass(bvm *vm, const char *name, const bnfuncinfo *lib) +{ + bclass *c; + bstring *s = be_newstr(vm, name); + bvalue *dst = be_incrtop(vm); + var_setstr(dst, s); + c = class_auto_make(vm, s, lib); + var_setclass(vm->top - 1, c); +} + +BERRY_API void be_pushcomptr(bvm *vm, void *ptr) +{ + bvalue *top = be_incrtop(vm); + var_setobj(top, BE_COMPTR, ptr); +} + +BERRY_API void be_remove(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + bvalue *top = --vm->top; + while (v < top) { + *v = v[1]; + ++v; + } +} + +BERRY_API void be_strconcat(bvm *vm, int index) +{ + bstring *s; + bvalue *dst = be_indexof(vm, index); + bvalue *src = be_indexof(vm, -1); + be_assert(var_isstr(src) && var_isstr(dst)); + s = be_strcat(vm, var_tostr(dst), var_tostr(src)); + var_setstr(dst, s); +} + +BERRY_API bbool be_setsuper(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + bvalue *top = be_indexof(vm, -1); + if (var_isclass(v) && var_isclass(top)) { + bclass *c = var_toobj(v); + if (!gc_isconst(c)) { + bclass *super = var_toobj(top); + be_class_setsuper(c, super); + return btrue; + } + } + return bfalse; +} + +BERRY_API void be_getsuper(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + bvalue *top = be_incrtop(vm); + if (var_isclass(v)) { + bclass *c = var_toobj(v); + c = be_class_super(c); + if (c) { + var_setclass(top, c); + return; + } + } else if (var_isinstance(v)) { + binstance *o = var_toobj(v); + o = be_instance_super(o); + if (o) { + var_setinstance(top, o); + return; + } + } + var_setnil(top); +} + +static bclass* _getclass(bvalue *v) +{ + if (var_isinstance(v)) { + binstance *ins = var_toobj(v); + return be_instance_class(ins); + } + return var_isclass(v) ? var_toobj(v) : NULL; +} + +BERRY_API bbool be_isderived(bvm *vm, int index) +{ + bclass *sup = _getclass(be_indexof(vm, -1)); + if (sup) { + bclass *c = _getclass(be_indexof(vm, index)); + while (c && c != sup) + c = be_class_super(c); + return c != NULL; + } + return bfalse; +} + +BERRY_API const char *be_typename(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + return be_vtype2str(v); +} + +BERRY_API const char *be_classname(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_isclass(v)) { + bclass *c = var_toobj(v); + return str(be_class_name(c)); + } + if (var_isinstance(v)) { + binstance *i = var_toobj(v); + return str(be_instance_name(i)); + } + return NULL; +} + +BERRY_API bbool be_classof(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_isinstance(v)) { + bvalue *top = be_incrtop(vm); + binstance *ins = var_toobj(v); + var_setclass(top, be_instance_class(ins)); + return btrue; + } + return bfalse; +} + +BERRY_API int be_strlen(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_isstr(v)) { + return str_len(var_tostr(v)); + } + return 0; +} + +BERRY_API void be_newlist(bvm *vm) +{ + blist *list = be_list_new(vm); + bvalue *top = be_incrtop(vm); + var_setlist(top, list); +} + +BERRY_API void be_newmap(bvm *vm) +{ + bmap *map = be_map_new(vm); + bvalue *top = be_incrtop(vm); + var_setobj(top, BE_MAP, map); +} + +BERRY_API void be_newmodule(bvm *vm) +{ + bmodule *mod = be_module_new(vm); + bvalue *top = be_incrtop(vm); + var_setobj(top, BE_MODULE, mod); +} + +BERRY_API void be_newobject(bvm *vm, const char *name) +{ + be_getbuiltin(vm, name); + be_call(vm, 0); + be_getmember(vm, -1, ".p"); +} + +BERRY_API bbool be_setname(bvm *vm, int index, const char *name) +{ + bvalue *v = be_indexof(vm, index); + if (var_ismodule(v)) { + bmodule *module = var_toobj(v); + return be_module_setname(module, be_newstr(vm, name)); + } + return bfalse; +} + +BERRY_API bbool be_getglobal(bvm *vm, const char *name) +{ + int idx = be_global_find(vm, be_newstr(vm, name)); + bvalue *top = be_incrtop(vm); + if (idx > -1) { + *top = *be_global_var(vm, idx); + return btrue; + } + var_setnil(top); + return bfalse; +} + +BERRY_API void be_setglobal(bvm *vm, const char *name) +{ + int idx; + bstring *s = be_newstr(vm, name); + bvalue *v = be_incrtop(vm); + var_setstr(v, s); + idx = be_global_new(vm, s); + v = be_global_var(vm, idx); + *v = *be_indexof(vm, -2); + be_stackpop(vm, 1); +} + +BERRY_API bbool be_getbuiltin(bvm *vm, const char *name) +{ + int idx = be_builtin_find(vm, be_newstr(vm, name)); + bvalue *top = be_incrtop(vm); + if (idx > -1) { + *top = *be_global_var(vm, idx); + return btrue; + } + var_setnil(top); + return bfalse; +} + +BERRY_API bbool be_setmember(bvm *vm, int index, const char *k) +{ + int res = BE_NIL; + bvalue *o = be_indexof(vm, index); + if (var_isinstance(o)) { + bstring *key = be_newstr(vm, k); + bvalue *v = be_indexof(vm, -1); + binstance *obj = var_toobj(o); + res = be_instance_setmember(vm, obj, key, v); + } else if (var_ismodule(o)) { + bstring *key = be_newstr(vm, k); + bmodule *mod = var_toobj(o); + bvalue *v = be_module_bind(vm, mod, key); + if (v) { + *v = *be_indexof(vm, -1); + return btrue; + } + } + return res != BE_NIL; +} + +BERRY_API bbool be_copy(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + bvalue *top = be_incrtop(vm); + if (var_type(v) == BE_LIST) { + blist *list = be_list_copy(vm, var_toobj(v)); + var_setlist(top, list) + return btrue; + } + var_setnil(top); + return bfalse; +} + +static int ins_member(bvm *vm, int index, const char *k) +{ + int type = BE_NIL; + bvalue *o = be_indexof(vm, index); + bvalue *top = be_incrtop(vm); + var_setnil(top); + if (var_isinstance(o)) { + binstance *obj = var_toobj(o); + type = be_instance_member(vm, obj, be_newstr(vm, k), top); + } + return type; +} + +BERRY_API bbool be_getmember(bvm *vm, int index, const char *k) +{ + return ins_member(vm, index, k) != BE_NIL; +} + +BERRY_API bbool be_getmethod(bvm *vm, int index, const char *k) +{ + return basetype(ins_member(vm, index, k)) == BE_FUNCTION; +} + +BERRY_API bbool be_getindex(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + bvalue *k = be_indexof(vm, -1); + bvalue *dst = be_incrtop(vm); + switch (var_type(o)) { + case BE_LIST: + if (var_isint(k)) { + blist *list = cast(blist*, var_toobj(o)); + int idx = var_toidx(k); + bvalue *src = be_list_index(list, idx); + if (src) { + var_setval(dst, src); + return btrue; + } + } + break; + case BE_MAP: + if (!var_isnil(k)) { + bmap *map = cast(bmap*, var_toobj(o)); + bvalue *src = be_map_find(vm, map, k); + if (src) { + var_setval(dst, src); + return btrue; + } + } + break; + default: + break; + } + var_setnil(dst); + return bfalse; +} + +static bvalue* list_setindex(blist *list, bvalue *key) +{ + int idx = var_toidx(key); + if (idx < be_list_count(list)) { + return be_list_at(list, idx); + } + return NULL; +} + +BERRY_API bbool be_setindex(bvm *vm, int index) +{ + bvalue *dst = NULL; + bvalue *o = be_indexof(vm, index); + bvalue *k = be_indexof(vm, -2); + bvalue *v = be_indexof(vm, -1); + switch (var_type(o)) { + case BE_LIST: + if (var_isint(k)) { + blist *list = var_toobj(o); + dst = list_setindex(list, k); + } + break; + case BE_MAP: + if (!var_isnil(k)) { + bmap *map = var_toobj(o); + dst = be_map_insert(vm, map, k, NULL); + } + break; + default: + break; + } + if (dst) { + var_setval(dst, v); + return btrue; + } + return bfalse; +} + +BERRY_API void be_getupval(bvm *vm, int index, int pos) +{ + bvalue *f = index ? be_indexof(vm, index) : vm->cf->func; + bvalue *uv, *top = be_incrtop(vm); + be_assert(var_istype(f, BE_NTVCLOS)); + if (var_istype(f, BE_NTVCLOS)) { + bntvclos *nf = var_toobj(f); + be_assert(pos >= 0 && pos < nf->nupvals); + uv = be_ntvclos_upval(nf, pos)->value; + var_setval(top, uv); + } else { + var_setnil(top); + } +} + +BERRY_API bbool be_setupval(bvm *vm, int index, int pos) +{ + bvalue *f = index ? be_indexof(vm, index) : vm->cf->func; + bvalue *uv, *v = be_indexof(vm, -1); + be_assert(var_istype(f, BE_NTVCLOS)); + if (var_istype(f, BE_NTVCLOS)) { + bntvclos *nf = var_toobj(f); + be_assert(pos >= 0 && pos < nf->nupvals); + uv = be_ntvclos_upval(nf, pos)->value; + var_setval(uv, v); + return btrue; + } + return bfalse; +} + +BERRY_API int be_data_size(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_islist(v)) { + blist *list = var_toobj(v); + return be_list_count(list); + } else if (var_ismap(v)) { + bmap *map = cast(bmap*, var_toobj(v)); + return be_map_count(map); + } + return -1; +} + +BERRY_API void be_data_push(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + bvalue *v = be_indexof(vm, -1); + if (var_islist(o)) { + blist *list = var_toobj(o); + be_list_push(vm, list, v); + } +} + +BERRY_API bbool be_data_insert(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + bvalue *k = be_indexof(vm, -2); + bvalue *v = be_indexof(vm, -1); + switch (var_type(o)) { + case BE_MAP: + if (!var_isnil(k)) { + bmap *map = cast(bmap*, var_toobj(o)); + bvalue *dst = be_map_find(vm, map, k); + if (dst == NULL) { + return be_map_insert(vm, map, k, v) != NULL; + } + } + break; + case BE_LIST: + if (var_isint(k)) { + blist *list = cast(blist*, var_toobj(o)); + return be_list_insert(vm, list, var_toidx(k), v) != NULL; + } + break; + default: + break; + } + return bfalse; +} + +BERRY_API bbool be_data_remove(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + bvalue *k = be_indexof(vm, -1); + switch (var_type(o)) { + case BE_MAP: + if (!var_isnil(k)) { + bmap *map = cast(bmap*, var_toobj(o)); + return be_map_remove(vm, map, k); + } + break; + case BE_LIST: + if (var_isint(k)) { + blist *list = cast(blist*, var_toobj(o)); + return be_list_remove(vm, list, var_toidx(k)); + } + break; + default: + break; + } + return bfalse; +} + +BERRY_API bbool be_data_merge(bvm *vm, int index) +{ + bvalue *a = be_indexof(vm, index); + bvalue *b = be_indexof(vm, -1); + if (var_islist(a) && var_islist(b)) { + blist *dst = var_toobj(a), *src = var_toobj(b); + be_list_merge(vm, dst, src); + return btrue; + } + return bfalse; +} + +BERRY_API void be_data_resize(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + bvalue *v = be_indexof(vm, -1); + if (var_islist(o)) { + blist *list = var_toobj(o); + if (var_isint(v)) { + be_list_resize(vm, list, var_toidx(v)); + } + } +} + +BERRY_API void be_data_reverse(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_type(v) == BE_LIST) { + be_list_reverse(var_toobj(v)); + } +} + +BERRY_API bbool be_pushiter(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + if (var_ismap(v)) { + bvalue *iter = be_incrtop(vm); + var_setobj(iter, BE_COMPTR, NULL); + return btrue; + } else if (var_islist(v)) { + blist *list = var_toobj(v); + bvalue *iter = be_incrtop(vm); + var_setobj(iter, BE_COMPTR, be_list_data(list) - 1); + return btrue; + } + return bfalse; +} + +static int list_next(bvm *vm) +{ + bvalue *iter = be_indexof(vm, -1); + bvalue *next, *dst = be_incrtop(vm); + next = cast(bvalue*, var_toobj(iter)) + 1; + var_setobj(iter, BE_COMPTR, next); + var_setval(dst, next); + return 1; +} + +static bbool list_hasnext(bvm *vm, bvalue *v) +{ + bvalue *next; + bvalue *iter = be_indexof(vm, -1); + blist *obj = var_toobj(v); + next = cast(bvalue*, var_toobj(iter)) + 1; + return next >= be_list_data(obj) && next < be_list_end(obj); +} + +static int map_next(bvm *vm, bvalue *v) +{ + bmapiter iter; + bmapnode *entry; + bvalue *dst = vm->top; + bvalue *itvar = be_indexof(vm, -1); + iter = var_toobj(itvar); + entry = be_map_next(var_toobj(v), &iter); + var_setobj(itvar, BE_COMPTR, iter); + if (entry) { + be_map_key2value(dst, entry); + var_setval(dst + 1, &entry->value); + vm->top += 2; + return 2; + } + return 0; +} + +static bbool map_hasnext(bvm *vm, bvalue *v) +{ + bvalue *node = be_indexof(vm, -1); + bmapiter iter = var_toobj(node); + return be_map_next(var_toobj(v), &iter) != NULL; +} + +BERRY_API int be_iter_next(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + if (var_islist(o)) { + return list_next(vm); + } else if (var_ismap(o)) { + return map_next(vm, o); + } + return 0; +} + +BERRY_API bbool be_iter_hasnext(bvm *vm, int index) +{ + bvalue *o = be_indexof(vm, index); + if (var_islist(o)) { + return list_hasnext(vm, o); + } else if (var_ismap(o)) { + return map_hasnext(vm, o); + } + return bfalse; +} + +BERRY_API bbool be_refcontains(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + binstance **ref = be_stack_base(&vm->refstack); + binstance **top = be_stack_top(&vm->refstack); + binstance *ins = var_toobj(v); + be_assert(var_isinstance(v)); + if (ref) { + while (ref <= top && *ref != ins) { + ++ref; + } + return ref <= top; + } + return bfalse; +} + +BERRY_API void be_refpush(bvm *vm, int index) +{ + bvalue *v = be_indexof(vm, index); + binstance *ins = var_toobj(v); + be_assert(var_isinstance(v)); + be_stack_push(vm, &vm->refstack, &ins); +} + +BERRY_API void be_refpop(bvm *vm) +{ + be_stack_pop(&vm->refstack); + if (be_stack_isempty(&vm->refstack)) { + be_vector_release(vm, &vm->refstack); + } +} + +BERRY_API int be_returnvalue(bvm *vm) +{ + bvalue *src = vm->top - 1; + bvalue *ret = retreg(vm); + var_setval(ret, src); + return 0; +} + +BERRY_API int be_returnnilvalue(bvm *vm) +{ + bvalue *ret = retreg(vm); + var_setnil(ret); + return 0; +} + +BERRY_API void be_call(bvm *vm, int argc) +{ + bvalue *fval = vm->top - argc - 1; + be_dofunc(vm, fval, argc); +} + +BERRY_API int be_pcall(bvm *vm, int argc) +{ + bvalue *f = vm->top - argc - 1; + return be_protectedcall(vm, f, argc); +} + +BERRY_API void be_raise(bvm *vm, const char *except, const char *msg) +{ + be_pushstring(vm, except); + if (msg) { + be_pushstring(vm, msg); + } else { + be_pushnil(vm); + } + be_pop(vm, 2); + be_save_stacktrace(vm); + be_throw(vm, BE_EXCEPTION); +} + +BERRY_API void be_stop_iteration(bvm *vm) +{ + be_raise(vm, "stop_iteration", NULL); +} + +BERRY_API int be_getexcept(bvm *vm, int code) +{ + if (code == BE_EXCEPTION) { + if (be_isstring(vm, -2)) { + const char *except = be_tostring(vm, -2); + if (!strcmp(except, "syntax_error")) { + return BE_SYNTAX_ERROR; + } + if (!strcmp(except, "io_error")) { + return BE_IO_ERROR; + } + } + return BE_EXEC_ERROR; + } + return code; +} + +static int _dvfunc(bvm *vm, bbool esc) +{ + const char* s = esc ? + be_toescape(vm, 1, 'x') : be_tostring(vm, 1); + be_writestring(s); + be_return_nil(vm); +} + +static int _dumpesc(bvm *vm) +{ + return _dvfunc(vm, btrue); +} + +static int _dumpdir(bvm *vm) +{ + return _dvfunc(vm, bfalse); +} + +static int dump_value(bvm *vm, int index, bbool esc) +{ + int res, top = be_top(vm) + 1; + index = be_absindex(vm, index); + be_pushntvfunction(vm, esc ? _dumpesc : _dumpdir); + be_pushvalue(vm, index); + res = be_pcall(vm, 1); /* using index to store result */ + be_remove(vm, top); /* remove '_dumpvalue' function */ + be_remove(vm, top); /* remove the value */ + if (res == BE_EXCEPTION) { + be_dumpexcept(vm); + } + return res; +} + +BERRY_API void be_dumpvalue(bvm *vm, int index) +{ + if (dump_value(vm, index, btrue) == BE_OK) { + be_writenewline(); + } +} + +BERRY_API void be_dumpexcept(bvm *vm) +{ + do { + /* print exception value */ + if (dump_value(vm, -2, bfalse)) break; + be_writestring(": "); + /* print exception argument */ + if (dump_value(vm, -1, bfalse)) break; + be_writenewline(); + /* print stack traceback */ + be_tracestack(vm); + } while (0); + be_pop(vm, 2); /* pop the exception value & argument */ +} + +BERRY_API bbool be_iseq(bvm *vm) +{ + be_assert(vm->reg + 2 <= vm->top); + return be_vm_iseq(vm, vm->top - 2, vm->top - 1); +} + +BERRY_API bbool be_isneq(bvm *vm) +{ + be_assert(vm->reg + 2 <= vm->top); + return be_vm_isneq(vm, vm->top - 2, vm->top - 1); +} + +BERRY_API bbool be_islt(bvm *vm) +{ + be_assert(vm->reg + 2 <= vm->top); + return be_vm_islt(vm, vm->top - 2, vm->top - 1); +} + +BERRY_API bbool be_isle(bvm *vm) +{ + be_assert(vm->reg + 2 <= vm->top); + return be_vm_isle(vm, vm->top - 2, vm->top - 1); +} + +BERRY_API bbool be_isgt(bvm *vm) +{ + be_assert(vm->reg + 2 <= vm->top); + return be_vm_isgt(vm, vm->top - 2, vm->top - 1); +} + +BERRY_API bbool be_isge(bvm *vm) +{ + be_assert(vm->reg + 2 <= vm->top); + return be_vm_isge(vm, vm->top - 2, vm->top - 1); +} + +BERRY_API int be_register(bvm *vm, int index) +{ + bvalue *v; + if (!vm->registry) { + vm->registry = be_list_new(vm); + be_list_pool_init(vm, vm->registry); + } + be_assert(vm->registry != NULL); + v = be_indexof(vm, index); + return be_list_pool_alloc(vm, vm->registry, v); +} + +BERRY_API void be_unregister(bvm *vm, int id) +{ + be_assert(vm->registry != NULL); + be_list_pool_free(vm->registry, id); +} + +BERRY_API void be_getregister(bvm *vm, int id) +{ + blist *reg = vm->registry; + be_assert(reg && id > 0 && id < be_list_count(reg)); + var_setval(vm->top, be_list_at(reg, id)); + be_incrtop(vm); +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_baselib.c b/lib/lib_div/Berry-0.1.10/src/be_baselib.c new file mode 100644 index 000000000..f0d7e0147 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_baselib.c @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_bytecode.c b/lib/lib_div/Berry-0.1.10/src/be_bytecode.c new file mode 100644 index 000000000..b8242d326 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_bytecode.c @@ -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 + +#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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_bytecode.h b/lib/lib_div/Berry-0.1.10/src/be_bytecode.h new file mode 100644 index 000000000..08affe1de --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_bytecode.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_class.c b/lib/lib_div/Berry-0.1.10/src/be_class.c new file mode 100644 index 000000000..71e42c787 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_class.c @@ -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; +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_class.h b/lib/lib_div/Berry-0.1.10/src/be_class.h new file mode 100644 index 000000000..ac2e68c79 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_class.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_code.c b/lib/lib_div/Berry-0.1.10/src/be_code.c new file mode 100644 index 000000000..68865190c --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_code.c @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_code.h b/lib/lib_div/Berry-0.1.10/src/be_code.h new file mode 100644 index 000000000..8dfc21501 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_code.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_constobj.h b/lib/lib_div/Berry-0.1.10/src/be_constobj.h new file mode 100644 index 000000000..6b9e8d737 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_constobj.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_debug.c b/lib/lib_div/Berry-0.1.10/src/be_debug.c new file mode 100644 index 000000000..b50a27b02 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_debug.c @@ -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 +#include + +#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(":"); + } +#else + (void)proto; (void)ip; + be_writestring(":"); +#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: 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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_debug.h b/lib/lib_div/Berry-0.1.10/src/be_debug.h new file mode 100644 index 000000000..13ec28936 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_debug.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_debuglib.c b/lib/lib_div/Berry-0.1.10/src/be_debuglib.c new file mode 100644 index 000000000..97123916b --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_debuglib.c @@ -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 + +#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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_decoder.h b/lib/lib_div/Berry-0.1.10/src/be_decoder.h new file mode 100644 index 000000000..354de5665 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_decoder.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_exec.c b/lib/lib_div/Berry-0.1.10/src/be_exec.c new file mode 100644 index 000000000..4bca65877 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_exec.c @@ -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 + +#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; + } + } +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_exec.h b/lib/lib_div/Berry-0.1.10/src/be_exec.h new file mode 100644 index 000000000..ffb85fa87 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_exec.h @@ -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 + +/* 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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_filelib.c b/lib/lib_div/Berry-0.1.10/src/be_filelib.c new file mode 100644 index 000000000..2a489555a --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_filelib.c @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_func.c b/lib/lib_div/Berry-0.1.10/src/be_func.c new file mode 100644 index 000000000..a4e210271 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_func.c @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_func.h b/lib/lib_div/Berry-0.1.10/src/be_func.h new file mode 100644 index 000000000..a9cdaa127 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_func.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_gc.c b/lib/lib_div/Berry-0.1.10/src/be_gc.c new file mode 100644 index 000000000..58a00f73a --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_gc.c @@ -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); +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_gc.h b/lib/lib_div/Berry-0.1.10/src/be_gc.h new file mode 100644 index 000000000..16ca7e766 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_gc.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_gclib.c b/lib/lib_div/Berry-0.1.10/src/be_gclib.c new file mode 100644 index 000000000..9ca9eab50 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_gclib.c @@ -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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_jsonlib.c b/lib/lib_div/Berry-0.1.10/src/be_jsonlib.c new file mode 100644 index 000000000..3c7e6da5a --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_jsonlib.c @@ -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 + +#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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_lexer.c b/lib/lib_div/Berry-0.1.10/src/be_lexer.c new file mode 100644 index 000000000..eea82109d --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_lexer.c @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_lexer.h b/lib/lib_div/Berry-0.1.10/src/be_lexer.h new file mode 100644 index 000000000..09090db6b --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_lexer.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_libs.c b/lib/lib_div/Berry-0.1.10/src/be_libs.c new file mode 100644 index 000000000..bf95f5dad --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_libs.c @@ -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 +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_libs.h b/lib/lib_div/Berry-0.1.10/src/be_libs.h new file mode 100644 index 000000000..a8014790b --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_libs.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_list.c b/lib/lib_div/Berry-0.1.10/src/be_list.c new file mode 100644 index 000000000..7d696d736 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_list.c @@ -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 + +#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; +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_list.h b/lib/lib_div/Berry-0.1.10/src/be_list.h new file mode 100644 index 000000000..9f5745725 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_list.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_listlib.c b/lib/lib_div/Berry-0.1.10/src/be_listlib.c new file mode 100644 index 000000000..a46c89e21 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_listlib.c @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_map.c b/lib/lib_div/Berry-0.1.10/src/be_map.c new file mode 100644 index 000000000..4a7d072ca --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_map.c @@ -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 + +#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 : "")); + } + 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); +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_map.h b/lib/lib_div/Berry-0.1.10/src/be_map.h new file mode 100644 index 000000000..d632f0d0b --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_map.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_maplib.c b/lib/lib_div/Berry-0.1.10/src/be_maplib.c new file mode 100644 index 000000000..2d8446edd --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_maplib.c @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_mathlib.c b/lib/lib_div/Berry-0.1.10/src/be_mathlib.c new file mode 100644 index 000000000..4ee24b1cf --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_mathlib.c @@ -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 +#include +#include + +#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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_mem.c b/lib/lib_div/Berry-0.1.10/src/be_mem.c new file mode 100644 index 000000000..7047f14e5 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_mem.c @@ -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 + +#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; +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_mem.h b/lib/lib_div/Berry-0.1.10/src/be_mem.h new file mode 100644 index 000000000..88d305862 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_mem.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_module.c b/lib/lib_div/Berry-0.1.10/src/be_module.c new file mode 100644 index 000000000..fcda03173 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_module.c @@ -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 + +/* 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 + +#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 +#include + +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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_module.h b/lib/lib_div/Berry-0.1.10/src/be_module.h new file mode 100644 index 000000000..1a95df59b --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_module.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_object.c b/lib/lib_div/Berry-0.1.10/src/be_object.c new file mode 100644 index 000000000..a0da81939 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_object.c @@ -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)); + } +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_object.h b/lib/lib_div/Berry-0.1.10/src/be_object.h new file mode 100644 index 000000000..d465138ec --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_object.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_opcodes.h b/lib/lib_div/Berry-0.1.10/src/be_opcodes.h new file mode 100644 index 000000000..562bd4b31 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_opcodes.h @@ -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] */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_oslib.c b/lib/lib_div/Berry-0.1.10/src/be_oslib.c new file mode 100644 index 000000000..1d4f029c2 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_oslib.c @@ -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 +#include + +#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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_parser.c b/lib/lib_div/Berry-0.1.10/src/be_parser.c new file mode 100644 index 000000000..1839e9392 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_parser.c @@ -0,0 +1,1528 @@ +/******************************************************************** +** 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_parser.h" +#include "be_lexer.h" +#include "be_vector.h" +#include "be_mem.h" +#include "be_vm.h" +#include "be_map.h" +#include "be_list.h" +#include "be_var.h" +#include "be_code.h" +#include "be_string.h" +#include "be_func.h" +#include "be_class.h" +#include "be_decoder.h" +#include "be_debug.h" +#include "be_exec.h" + +#define OP_NOT_BINARY TokenNone +#define OP_NOT_UNARY TokenNone +#define OP_NOT_ASSIGN TokenNone +#define UNARY_OP_PRIO 3 +#define ASSIGN_OP_PRIO 16 + +#define FUNC_METHOD 1 +#define FUNC_ANONYMOUS 2 + +/* get binary operator priority */ +#define binary_op_prio(op) (binary_op_prio_tab[cast_int(op) - OptAdd]) + +#define scan_next_token(parser) (be_lexer_scan_next(&(parser)->lexer)) +#define next_token(parser) ((parser)->lexer.token) +#define next_type(parser) (next_token(parser).type) +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define token2str(parser) be_token2str((parser)->vm, &next_token(parser)) +#define funcname(parser) ((parser)->islocal ? "loader" : "main") + +#define upval_index(v) ((v) & 0xFF) +#define upval_target(v) ((bbyte)(((v) >> 8) & 0xFF)) +#define upval_instack(v) ((bbyte)(((v) >> 16) != 0)) +#define upval_desc(i, t, s) (((i) & 0xFF) | (((t) & 0xFF) << 8) \ + | (((s) != 0) << 16)) + +#define match_id(parser, s) ((s) = _match_id(parser)) + +#define parser_newstr(p, str) be_lexer_newstr(&(p)->lexer, (str)) + +#define parser_error(p, msg) be_lexerror(&(p)->lexer, msg) + +#define push_error(parser, ...) \ + parser_error(parser, be_pushfstring(parser->vm, __VA_ARGS__)) + +typedef struct { + blexer lexer; + bvm *vm; + bfuncinfo *finfo; + bclosure *cl; + bbyte islocal; +} bparser; + +#if BE_USE_SCRIPT_COMPILER + +static void stmtlist(bparser *parser); +static void block(bparser *parser, int type); +static void expr(bparser *parser, bexpdesc *e); +static void sub_expr(bparser *parser, bexpdesc *e, int prio); + +static const bbyte binary_op_prio_tab[] = { + 5, 5, 4, 4, 4, /* + - * / % */ + 11, 11, 12, 12, 11, 11, /* < <= == != > >= */ + 7, 9, 8, 6, 6, 10, 13, 14 /* & | ^ << >> .. && || */ +}; + +static void match_token(bparser *parser, btokentype type) +{ + if (next_type(parser) != type) { /* error when token is no match */ + btoken *token = &next_token(parser); + const char *s1 = be_tokentype2str(type); + const char *s2 = be_token2str(parser->vm, token); + push_error(parser, "expected '%s' before '%s'", s1, s2); + } + scan_next_token(parser); /* skip this token */ +} + +static void match_notoken(bparser *parser, btokentype type) +{ + if (next_type(parser) == type) { /* error when token is match */ + push_error(parser, + "expected statement before '%s'", token2str(parser)); + } +} + +static void check_symbol(bparser *parser, bexpdesc *e) +{ + if (e->type == ETVOID && e->v.s == NULL) { /* error when token is not a symbol */ + push_error(parser, + "unexpected symbol near '%s'", token2str(parser)); + } +} + +static void check_var(bparser *parser, bexpdesc *e) +{ + check_symbol(parser, e); /* check the token is a symbol */ + if (e->type == ETVOID) { /* error when symbol is undefined */ + int line = parser->lexer.linenumber; + parser->lexer.linenumber = parser->lexer.lastline; + push_error(parser, + "'%s' undeclared (first use in this function)", str(e->v.s)); + parser->lexer.linenumber = line; + } +} + +static int match_skip(bparser *parser, btokentype type) +{ + if (next_type(parser) == type) { /* match */ + scan_next_token(parser); /* skip this token */ + return btrue; + } + return bfalse; +} + +static bstring* _match_id(bparser *parser) +{ + if (next_type(parser) == TokenId) { + bstring *str = next_token(parser).u.s; + scan_next_token(parser); /* skip ID */ + return str; + } + return NULL; +} + +#if BE_DEBUG_VAR_INFO + +void begin_varinfo(bparser *parser, bstring *name) +{ + bvarinfo *var; + bfuncinfo *finfo = parser->finfo; + be_vector_push_c(parser->vm, &finfo->varvec, NULL); + var = be_vector_end(&finfo->varvec); + var->name = name; + var->beginpc = finfo->pc; + var->endpc = 0; /* */ + finfo->proto->varinfo = be_vector_data(&finfo->varvec); + finfo->proto->nvarinfo = be_vector_capacity(&finfo->varvec); +} + +void end_varinfo(bparser *parser, int beginpc) +{ + bfuncinfo *finfo = parser->finfo; + bblockinfo *binfo = finfo->binfo; + bvarinfo *it = be_vector_data(&finfo->varvec); + bvarinfo *end = be_vector_end(&finfo->varvec); + if (beginpc == -1) /* use block->beginpc by default */ + beginpc = binfo->beginpc; + /* skip the variable of the previous blocks */ + for (; it->beginpc < beginpc; ++it); + for (; it <= end; ++it) { + if (!it->endpc) /* write to endpc only once */ + it->endpc = finfo->pc; + } +} + +#else + +#define begin_varinfo(parser, name) +#define end_varinfo(parser, beginpc) (void)(beginpc) + +#endif + +static void begin_block(bfuncinfo *finfo, bblockinfo *binfo, int type) +{ + binfo->prev = finfo->binfo; + finfo->binfo = binfo; + binfo->type = (bbyte)type; + binfo->hasupval = 0; + binfo->beginpc = finfo->pc; + binfo->nactlocals = (bbyte)be_list_count(finfo->local); + if (type & BLOCK_LOOP) { + binfo->breaklist = NO_JUMP; + binfo->continuelist = NO_JUMP; + } +} + +static void end_block_ex(bparser *parser, int beginpc) +{ + bfuncinfo *finfo = parser->finfo; + bblockinfo *binfo = finfo->binfo; + be_code_close(finfo, 0); /* close upvalues */ + if (binfo->type & BLOCK_LOOP) { + be_code_jumpto(finfo, binfo->beginpc); + be_code_patchjump(finfo, binfo->breaklist); + be_code_patchlist(finfo, binfo->continuelist, binfo->beginpc); + } + end_varinfo(parser, beginpc); + be_list_resize(parser->vm, finfo->local, binfo->nactlocals); + finfo->freereg = binfo->nactlocals; + finfo->binfo = binfo->prev; +} + +static void end_block(bparser *parser) +{ + end_block_ex(parser, -1); +} + +static bstring* parser_source(bparser *parser) +{ + if (parser->finfo) { + return parser->finfo->proto->source; + } + return be_newstr(parser->vm, parser->lexer.fname); +} + +static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo) +{ + bvm *vm = parser->vm; + bproto *proto = be_newproto(vm); + var_setproto(vm->top, proto); + be_stackpush(vm); + be_vector_init(vm, &finfo->code, sizeof(binstruction)); + proto->code = be_vector_data(&finfo->code); + proto->codesize = be_vector_capacity(&finfo->code); + be_vector_init(vm, &finfo->kvec, sizeof(bvalue)); + proto->ktab = be_vector_data(&finfo->kvec); + proto->nconst = be_vector_capacity(&finfo->kvec); + be_vector_init(vm, &finfo->pvec, sizeof(bproto*)); + proto->ptab = be_vector_data(&finfo->pvec); + proto->nproto = be_vector_capacity(&finfo->pvec); + proto->source = parser_source(parser); + finfo->local = be_list_new(vm); + var_setlist(vm->top, finfo->local); + be_stackpush(vm); + finfo->upval = be_map_new(vm); + var_setmap(vm->top, finfo->upval); + be_stackpush(vm); + finfo->prev = parser->finfo; + finfo->lexer = &parser->lexer; + finfo->proto = proto; + finfo->freereg = 0; + finfo->binfo = NULL; + finfo->pc = 0; + finfo->flags = 0; + parser->finfo = finfo; +#if BE_DEBUG_RUNTIME_INFO + be_vector_init(vm, &finfo->linevec, sizeof(blineinfo)); + proto->lineinfo = be_vector_data(&finfo->linevec); + proto->nlineinfo = be_vector_capacity(&finfo->linevec); +#endif +#if BE_DEBUG_VAR_INFO + be_vector_init(vm, &finfo->varvec, sizeof(bvarinfo)); + proto->varinfo = be_vector_data(&finfo->varvec); + proto->nvarinfo = be_vector_capacity(&finfo->varvec); +#endif + begin_block(finfo, binfo, 0); +} + +static void setupvals(bfuncinfo *finfo) +{ + bproto *proto = finfo->proto; + int nupvals = be_map_count(finfo->upval); + if (nupvals) { + bmapnode *node; + bmap *map = finfo->upval; + bmapiter iter = be_map_iter(); + bupvaldesc *upvals = be_malloc( + finfo->lexer->vm, sizeof(bupvaldesc) * nupvals); + while ((node = be_map_next(map, &iter)) != NULL) { + uint32_t v = (uint32_t)var_toint(&node->value); + bupvaldesc *uv = upvals + upval_index(v); + uv->idx = upval_target(v); + uv->instack = upval_instack(v); +#if BE_DEBUG_VAR_INFO + uv->name = var_tostr(&node->key); +#endif + } + proto->upvals = upvals; + proto->nupvals = (bbyte)nupvals; + } +} + +static void end_func(bparser *parser) +{ + bvm *vm = parser->vm; + bfuncinfo *finfo = parser->finfo; + bproto *proto = finfo->proto; + + be_code_ret(finfo, NULL); /* append a return to last code */ + end_block(parser); + setupvals(finfo); + proto->code = be_vector_release(vm, &finfo->code); + proto->codesize = finfo->pc; + proto->ktab = be_vector_release(vm, &finfo->kvec); + proto->nconst = be_vector_count(&finfo->kvec); + proto->ptab = be_vector_release(vm, &finfo->pvec); + proto->nproto = be_vector_count(&finfo->pvec); +#if BE_DEBUG_RUNTIME_INFO + proto->lineinfo = be_vector_release(vm, &finfo->linevec); + proto->nlineinfo = be_vector_count(&finfo->linevec); +#endif +#if BE_DEBUG_VAR_INFO + proto->varinfo = be_vector_release(vm, &finfo->varvec); + proto->nvarinfo = be_vector_count(&finfo->varvec); +#endif + parser->finfo = parser->finfo->prev; + be_stackpop(vm, 2); /* pop upval and local */ +} + +static btokentype get_binop(bparser *parser) +{ + btokentype op = next_type(parser); + if (op >= OptAdd && op <= OptOr) { + return op; + } + return OP_NOT_BINARY; +} + +static btokentype get_unary_op(bparser *parser) +{ + btokentype op = next_type(parser); + if (op == OptSub || op == OptNot || op == OptFlip) { + return op; /* operator 'negative' 'not' and 'flip' */ + } + return OP_NOT_UNARY; +} + +static btokentype get_assign_op(bparser *parser) +{ + btokentype op = next_type(parser); + if (op >= OptAssign && op <= OptRsfAssign) { + return op; + } + return OP_NOT_ASSIGN; +} + +static void init_exp(bexpdesc *e, exptype_t type, bint i) +{ + e->type = (bbyte)type; + e->t = NO_JUMP; + e->f = NO_JUMP; + e->not = 0; + e->v.s = NULL; + e->v.i = i; +} + +static int find_localvar(bfuncinfo *finfo, bstring *s, int begin) +{ + int i, count = be_list_count(finfo->local); + bvalue *var = be_list_data(finfo->local); + for (i = count - 1; i >= begin; --i) { + if (be_eqstr(var[i].v.s, s)) { + return i; + } + } + return -1; /* not found */ +} + +static int new_localvar(bparser *parser, bstring *name) +{ + bfuncinfo *finfo = parser->finfo; + int reg = find_localvar(finfo, name, finfo->binfo->nactlocals); + if (reg == -1) { + bvalue *var; + reg = be_list_count(finfo->local); /* new local index */ + var = be_list_push(parser->vm, finfo->local, NULL); + var_setstr(var, name); + if (reg >= finfo->freereg) { + be_code_allocregs(finfo, 1); /* use a register */ + } + begin_varinfo(parser, name); + } + return reg; +} + +static int find_upval(bfuncinfo *finfo, bstring *s) +{ + bvm *vm = finfo->lexer->vm; + bvalue *desc = be_map_findstr(vm, finfo->upval, s); + if (desc) { + return upval_index(desc->v.i); + } + return -1; +} + +static void mark_upval(bfuncinfo *finfo, int level) +{ + bblockinfo *binfo = finfo->prev->binfo; + while (binfo->nactlocals > level) { + binfo = binfo->prev; + } + binfo->hasupval = 1; +} + +static int new_upval(bvm *vm, bfuncinfo *finfo, bstring *name, bexpdesc *var) +{ + int index; + bvalue *desc; + int target = var->v.idx; + int instack = var->type == ETLOCAL; + if (instack) { /* mark upvalue's block */ + mark_upval(finfo, target); + } + index = be_map_count(finfo->upval); + if (index >= 255) { + be_lexerror(finfo->lexer, be_pushfstring(vm, + "too many upvalues (in '%s')", str(name))); + } + desc = be_map_insertstr(vm, finfo->upval, name, NULL); + var_setint(desc, upval_desc(index, target, instack)); + return index; +} + +static void new_var(bparser *parser, bstring *name, bexpdesc *var) +{ + bfuncinfo *finfo = parser->finfo; + if (finfo->prev || finfo->binfo->prev || parser->islocal) { + init_exp(var, ETLOCAL, 0); + var->v.idx = new_localvar(parser, name); + } else { + init_exp(var, ETGLOBAL, 0); + var->v.idx = be_global_new(parser->vm, name); + if (var->v.idx > (int)IBx_MASK) { + push_error(parser, + "too many global variables (in '%s')", str(name)); + } + } +} + +static int singlevaraux(bvm *vm, bfuncinfo *finfo, bstring *s, bexpdesc *var) +{ + if (finfo == NULL) { + return ETVOID; + } else { + int idx = find_localvar(finfo, s, 0); + if (idx >= 0) { /* local variable */ + init_exp(var, ETLOCAL, 0); + var->v.idx = idx; + return ETLOCAL; + } else { + idx = find_upval(finfo, s); + if (idx < 0) { + /* find the previous scope */ + int res = singlevaraux(vm, finfo->prev, s, var); + if (res == ETUPVAL || res == ETLOCAL) { + idx = new_upval(vm, finfo, s, var); /* new upvalue */ + } else if (be_global_find(vm, s) >= 0) { + return ETGLOBAL; /* global variable */ + } else { + return ETVOID; /* unknow (new variable or error) */ + } + } + init_exp(var, ETUPVAL, idx); + return ETUPVAL; + } + } +} + +static void singlevar(bparser *parser, bexpdesc *var) +{ + bstring *varname = next_token(parser).u.s; + int type = singlevaraux(parser->vm, parser->finfo, varname, var); + switch (type) { + case ETVOID: + init_exp(var, ETVOID, 0); + var->v.s = varname; + break; + case ETGLOBAL: + init_exp(var, ETGLOBAL, 0); + var->v.idx = be_global_find(parser->vm, varname); + break; + default: + break; + } +} + +static void func_varlist(bparser *parser) +{ + bexpdesc v; + bstring *str; + /* '(' [ID {',' ID}] ')' */ + match_token(parser, OptLBK); /* skip '(' */ + if (match_id(parser, str) != NULL) { + new_var(parser, str, &v); /* new variable */ + while (match_skip(parser, OptComma)) { /* ',' */ + str = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + /* new local variable */ + if (find_localvar(parser->finfo, str, 0) == -1) { + new_var(parser, str, &v); + } else { + push_error(parser, "redefinition of '%s'", str(str)); + } + } + } + match_token(parser, OptRBK); /* skip ')' */ + parser->finfo->proto->argc = parser->finfo->freereg; +} + +static bproto* funcbody(bparser *parser, bstring *name, int type) +{ + bfuncinfo finfo; + bblockinfo binfo; + + /* '(' varlist ')' block 'end' */ + begin_func(parser, &finfo, &binfo); + finfo.proto->name = name; + if (type & FUNC_METHOD) { + new_localvar(parser, parser_newstr(parser, "self")); + } + func_varlist(parser); + stmtlist(parser); + end_func(parser); + match_token(parser, KeyEnd); /* skip 'end' */ + return finfo.proto; +} + +/* anonymous function */ +static void anon_func(bparser *parser, bexpdesc *e) +{ + bproto *proto; + bstring *name = parser_newstr(parser, ""); + /* 'def' ID '(' varlist ')' block 'end' */ + scan_next_token(parser); /* skip 'def' */ + proto = funcbody(parser, name, FUNC_ANONYMOUS); + init_exp(e, ETPROTO, be_code_proto(parser->finfo, proto)); + be_stackpop(parser->vm, 1); +} + +static void lambda_varlist(bparser *parser) +{ + bexpdesc v; + bstring *str; + /* [ID {',' ID}] | {ID}] */ + if (match_id(parser, str) != NULL) { + bbool comma; + new_var(parser, str, &v); /* new variable */ + comma = next_type(parser) == OptComma; + while (next_type(parser) != OptArrow) { + if (comma) { + match_token(parser, OptComma); /* match and skip ',' */ + } + str = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + /* new local variable */ + if (find_localvar(parser->finfo, str, 0) == -1) { + new_var(parser, str, &v); + } else { + push_error(parser, "redefinition of '%s'", str(str)); + } + } + } + match_token(parser, OptArrow); /* skip '->' */ + parser->finfo->proto->argc = parser->finfo->freereg; +} + +/* lambda expression */ +static void lambda_expr(bparser *parser, bexpdesc *e) +{ + bexpdesc e1; + bfuncinfo finfo; + bblockinfo binfo; + bstring *name = parser_newstr(parser, ""); + /* '/' ID {[',' ID]} '->' expr */ + scan_next_token(parser); /* skip '/' */ + begin_func(parser, &finfo, &binfo); + finfo.proto->name = name; + lambda_varlist(parser); + expr(parser, &e1); + check_var(parser, &e1); + be_code_ret(parser->finfo, &e1); + end_func(parser); + init_exp(e, ETPROTO, be_code_proto(parser->finfo, finfo.proto)); + be_stackpop(parser->vm, 1); +} + +static void new_primtype(bparser *parser, const char *type, bexpdesc *e) +{ + int idx; + bvm *vm = parser->vm; + bfuncinfo *finfo = parser->finfo; + + scan_next_token(parser); + idx = be_builtin_find(vm, parser_newstr(parser, type)); + init_exp(e, ETGLOBAL, idx); + idx = be_code_nextreg(finfo, e); + be_code_call(finfo, idx, 0); + e->type = ETLOCAL; +} + +static void list_nextmember(bparser *parser, bexpdesc *l) +{ + bexpdesc e, v = *l; + bfuncinfo *finfo = parser->finfo; + expr(parser, &e); /* value */ + check_var(parser, &e); + be_code_binop(finfo, OptConnect, &v, &e); + be_code_freeregs(finfo, 1); +} + +static void map_nextmember(bparser *parser, bexpdesc *l) +{ + bexpdesc e, v = *l; + bfuncinfo *finfo = parser->finfo; + expr(parser, &e); /* key */ + check_var(parser, &e); + be_code_index(finfo, &v, &e); + match_token(parser, OptColon); /* ':' */ + expr(parser, &e); /* value */ + check_var(parser, &e); + be_code_setvar(finfo, &v, &e); +} + +static void list_expr(bparser *parser, bexpdesc *e) +{ + /* '[' {expr ','} [expr] ']' */ + new_primtype(parser, "list", e); /* new list */ + while (next_type(parser) != OptRSB) { + list_nextmember(parser, e); + if (!match_skip(parser, OptComma)) { /* ',' */ + break; + } + } + e->type = ETREG; + match_token(parser, OptRSB); /* skip ']' */ +} + +static void map_expr(bparser *parser, bexpdesc *e) +{ + /* '{' {expr ':' expr ','} [expr ':' expr] '}' */ + new_primtype(parser, "map", e); /* new map */ + while (next_type(parser) != OptRBR) { + map_nextmember(parser, e); + if (!match_skip(parser, OptComma)) { /* ',' */ + break; + } + } + e->type = ETREG; + match_token(parser, OptRBR); /* skip '}' */ +} + +static int exprlist(bparser *parser, bexpdesc *e) +{ + bfuncinfo *finfo = parser->finfo; + int n = 1; + /* expr { ',' expr } */ + expr(parser, e); + check_var(parser, e); + be_code_nextreg(finfo, e); + while (match_skip(parser, OptComma)) { /* ',' */ + expr(parser, e); + check_var(parser, e); + be_code_nextreg(finfo, e); + ++n; + } + return n; +} + +static void call_expr(bparser *parser, bexpdesc *e) +{ + bexpdesc args; + bfuncinfo *finfo = parser->finfo; + int argc = 0, base; + int ismember = e->type == ETMEMBER; + + /* func '(' [exprlist] ')' */ + check_var(parser, e); + /* code function index to next register */ + if (ismember) { + base = be_code_getmethod(finfo, e); + } else { + base = be_code_nextreg(finfo, e); + } + scan_next_token(parser); /* skip '(' */ + if (next_type(parser) != OptRBK) { + argc = exprlist(parser, &args); + } + match_token(parser, OptRBK); /* skip ')' */ + argc += ismember; + be_code_call(finfo, base, argc); + if (e->type != ETREG) { + e->type = ETREG; + e->v.idx = base; + } +} + +static void member_expr(bparser *parser, bexpdesc *e) +{ + bstring *str; + /* . ID */ + check_var(parser, e); + scan_next_token(parser); /* skip '.' */ + if (match_id(parser, str) != NULL) { + bexpdesc key; + init_exp(&key, ETSTRING, 0); + key.v.s = str; + be_code_member(parser->finfo, e, &key); + } else { + push_error(parser, "invalid syntax near '%s'", + be_token2str(parser->vm, &next_token(parser))); + } +} + +static void index_expr(bparser *parser, bexpdesc *e) +{ + bexpdesc e1; + /* [expr] */ + check_var(parser, e); + scan_next_token(parser); /* skip '[' */ + expr(parser, &e1); + check_var(parser, &e1); + be_code_index(parser->finfo, e, &e1); + match_token(parser, OptRSB); /* skip ']' */ +} + +static void simple_expr(bparser *parser, bexpdesc *e) +{ + switch (next_type(parser)) { + case TokenInteger: + init_exp(e, ETINT, next_token(parser).u.i); + break; + case TokenReal: + init_exp(e, ETREAL, 0); + e->v.r = next_token(parser).u.r; + break; + case TokenString: + init_exp(e, ETSTRING, 0); + e->v.s = next_token(parser).u.s; + break; + case TokenId: + singlevar(parser, e); + break; + case KeyTrue: + init_exp(e, ETBOOL, 1); + break; + case KeyFalse: + init_exp(e, ETBOOL, 0); + break; + case KeyNil: + init_exp(e, ETNIL, 0); + break; + default: /* unknow expr */ + return; + } + scan_next_token(parser); +} + +static void primary_expr(bparser *parser, bexpdesc *e) +{ + switch (next_type(parser)) { + case OptLBK: /* '(' expr ')' */ + scan_next_token(parser); /* skip '(' */ + /* sub_expr() is more efficient because there is no need to initialize e. */ + sub_expr(parser, e, ASSIGN_OP_PRIO); + check_var(parser, e); + match_token(parser, OptRBK); /* skip ')' */ + break; + case OptLSB: /* list */ + list_expr(parser, e); + break; + case OptLBR: /* map */ + map_expr(parser, e); + break; + case KeyDef: /* anonymous function */ + anon_func(parser, e); + break; + case OptDiv: /* lambda expression */ + lambda_expr(parser, e); + break; + default: /* simple expr */ + simple_expr(parser, e); + break; + } +} + +static void suffix_expr(bparser *parser, bexpdesc *e) +{ + primary_expr(parser, e); + for (;;) { + switch (next_type(parser)) { + case OptLBK: /* '(' function call */ + call_expr(parser, e); + break; + case OptDot: /* '.' member */ + member_expr(parser, e); + break; + case OptLSB: /* '[' index */ + index_expr(parser, e); + break; + default: + return; + } + } +} + +static void suffix_alloc_reg(bparser *parser, bexpdesc *l) +{ + bfuncinfo *finfo = parser->finfo; + bbool suffix = l->type == ETINDEX || l->type == ETMEMBER; + /* in the suffix expression, if the object is a temporary + * variable (l->v.ss.tt == ETREG), it needs to be cached. */ + if (suffix && l->v.ss.tt == ETREG) { + be_code_allocregs(finfo, 1); + } +} + +/* compound assignment */ +static void compound_assign(bparser *parser, int op, bexpdesc *l, bexpdesc *r) +{ + if (op != OptAssign) { /* check left variable */ + check_var(parser, l); + /* cache the register of the object when continuously assigning */ + suffix_alloc_reg(parser, l); + } + expr(parser, r); /* right expression */ + check_var(parser, r); + if (op != OptAssign) { /* compound assignment */ + bexpdesc e = *l; + op = op < OptAndAssign ? op - OptAddAssign + OptAdd + : op - OptAndAssign + OptBitAnd; + be_code_binop(parser->finfo, op, &e, r); /* coding operation */ + *r = e; + } +} + +static int check_newvar(bparser *parser, bexpdesc *e) +{ + if (e->type == ETGLOBAL) { + if (e->v.idx < be_builtin_count(parser->vm)) { + e->v.s = be_builtin_name(parser->vm, e->v.idx); + return btrue; + } + return bfalse; + } + return e->type == ETVOID; +} + +static void assign_expr(bparser *parser) +{ + bexpdesc e; + btokentype op; + int line = parser->lexer.linenumber; + expr(parser, &e); /* left expression */ + check_symbol(parser, &e); + op = get_assign_op(parser); + if (op != OP_NOT_ASSIGN) { /* assign operator */ + bexpdesc e1; + scan_next_token(parser); + compound_assign(parser, op, &e, &e1); + if (check_newvar(parser, &e)) { /* new variable */ + new_var(parser, e.v.s, &e); + } + if (be_code_setvar(parser->finfo, &e, &e1)) { + parser->lexer.linenumber = line; + parser_error(parser, + "try to assign constant expressions."); + } + } else if (e.type >= ETMEMBER) { + bfuncinfo *finfo = parser->finfo; + /* these expressions occupy a register and need to be freed */ + finfo->freereg = (bbyte)be_list_count(finfo->local); + } else if (e.type == ETVOID) { /* not assign expression */ + /* undeclared symbol */ + parser->lexer.linenumber = line; + check_var(parser, &e); + } +} + +/* conditional expression */ +static void cond_expr(bparser *parser, bexpdesc *e) +{ + /* expr '?' expr ':' expr */ + if (next_type(parser) == OptQuestion) { + int jf, jl = NO_JUMP; /* jump list */ + bfuncinfo *finfo = parser->finfo; + scan_next_token(parser); /* skip '?' */ + be_code_jumpbool(finfo, e, bfalse); /* go if true */ + jf = e->f; + expr(parser, e); + check_var(parser, e); + be_code_nextreg(finfo, e); + be_code_freeregs(finfo, 1); + be_code_conjump(finfo, &jl, be_code_jump(finfo)); /* connect jump */ + be_code_patchjump(finfo, jf); + match_token(parser, OptColon); /* match and skip ':' */ + expr(parser, e); + check_var(parser, e); + e->v.idx = be_code_nextreg(finfo, e); + be_code_patchjump(finfo, jl); + e->type = ETREG; + } +} + +/* binary operator: + - * / % && || < <= == != > >= + * unary operator: + - ! + */ +static void sub_expr(bparser *parser, bexpdesc *e, int prio) +{ + bfuncinfo *finfo = parser->finfo; + btokentype op = get_unary_op(parser); + if (op != OP_NOT_UNARY) { + int line, res; + scan_next_token(parser); + line = parser->lexer.linenumber; + sub_expr(parser, e, UNARY_OP_PRIO); + check_var(parser, e); + res = be_code_unop(finfo, op, e); + if (res) { /* encode unary op */ + parser->lexer.linenumber = line; + push_error(parser, "wrong type argument to unary '%s'", + res == 1 ? "negative" : "bit-flip"); + } + } else { + suffix_expr(parser, e); + } + op = get_binop(parser); + while (op != OP_NOT_BINARY && prio > binary_op_prio(op)) { + bexpdesc e2; + check_var(parser, e); + scan_next_token(parser); + be_code_prebinop(finfo, op, e); /* and or */ + init_exp(&e2, ETVOID, 0); + sub_expr(parser, &e2, binary_op_prio(op)); + check_var(parser, &e2); + be_code_binop(finfo, op, e, &e2); /* encode binary op */ + op = get_binop(parser); + } + if (prio == ASSIGN_OP_PRIO) { + cond_expr(parser, e); + } +} + +static void expr(bparser *parser, bexpdesc *e) +{ + init_exp(e, ETVOID, 0); + sub_expr(parser, e, ASSIGN_OP_PRIO); +} + +static void expr_stmt(bparser *parser) +{ + assign_expr(parser); +} + +static int block_follow(bparser *parser) +{ + switch (next_type(parser)) { + case KeyElse: case KeyElif: + case KeyEnd: case KeyExcept: + case TokenEOS: + return 0; + default: + return 1; + } +} + +static int cond_stmt(bparser *parser) +{ + bexpdesc e; + /* expr */ + match_notoken(parser, OptRBK); + expr(parser, &e); + check_var(parser, &e); + be_code_jumpbool(parser->finfo, &e, bfalse); /* go if true */ + return e.f; +} + +static void condition_block(bparser *parser, int *jmp) +{ + bfuncinfo *finfo = parser->finfo; + int br = cond_stmt(parser); + block(parser, 0); + if (next_type(parser) == KeyElif + || next_type(parser) == KeyElse) { + be_code_conjump(finfo, jmp, be_code_jump(finfo)); /* connect jump */ + } + be_code_patchjump(finfo, br); +} + +static void if_stmt(bparser *parser) +{ + int jl = NO_JUMP; /* jump list */ + /* IF expr block {ELSEIF expr block}, [ELSE block], end */ + scan_next_token(parser); /* skip 'if' */ + condition_block(parser, &jl); + while (match_skip(parser, KeyElif)) { /* 'elif' */ + condition_block(parser, &jl); + } + if (match_skip(parser, KeyElse)) { /* 'else' */ + block(parser, 0); + } + match_token(parser, KeyEnd); /* skip end */ + be_code_patchjump(parser->finfo, jl); +} + +static void do_stmt(bparser *parser) +{ + /* DO block END */ + scan_next_token(parser); /* skip 'do' */ + block(parser, 0); + match_token(parser, KeyEnd); /* skip 'end' */ +} + +static void while_stmt(bparser *parser) +{ + int brk; + bblockinfo binfo; + bfuncinfo *finfo = parser->finfo; + /* WHILE expr block END */ + scan_next_token(parser); /* skip 'while' */ + begin_block(parser->finfo, &binfo, BLOCK_LOOP); + brk = cond_stmt(parser); + stmtlist(parser); + end_block(parser); + be_code_patchjump(finfo, brk); + match_token(parser, KeyEnd); /* skip 'end' */ +} + +static bstring* for_itvar(bparser *parser) +{ + bstring *str; + if (match_id(parser, str) == NULL) { + push_error(parser, + "missing iteration variable before '%s'", + token2str(parser)); + } + return str; +} + +static void for_init(bparser *parser, bexpdesc *v) +{ + bexpdesc e; + bstring *s; + bfuncinfo *finfo = parser->finfo; + /* .it = __iterator__(expr) */ + s = parser_newstr(parser, "__iterator__"); + init_exp(&e, ETGLOBAL, be_builtin_find(parser->vm, s)); + be_code_nextreg(finfo, &e); /* code function '__iterator__' */ + expr(parser, v); + check_var(parser, v); + be_code_nextreg(finfo, v); + be_code_call(finfo, e.v.idx, 1); /* call __iterator__(expr) */ + be_code_freeregs(finfo, 1); /* free register of __iterator__ */ + s = parser_newstr(parser, ".it"); + init_exp(v, ETLOCAL, new_localvar(parser, s)); +} + +static void for_iter(bparser *parser, bstring *var, bexpdesc *it) +{ + bexpdesc e; + bfuncinfo *finfo = parser->finfo; + /* reset the jump head PC of the for loop body */ + finfo->binfo->beginpc = finfo->pc; + /* itvar = .it() */ + init_exp(&e, ETLOCAL, new_localvar(parser, var)); /* new itvar */ + be_code_setvar(finfo, &e, it); /* code function to variable '.it' */ + be_code_call(finfo, e.v.idx, 0); /* itvar <- call .it() */ + stmtlist(parser); +} + +static void for_leave(bparser *parser, int jcatch, int beginpc) +{ + bexpdesc e; + bfuncinfo *finfo = parser->finfo; + int jbrk = finfo->binfo->breaklist; + init_exp(&e, ETSTRING, 0); + e.v.s = parser_newstr(parser, "stop_iteration"); + end_block_ex(parser, beginpc); /* leave except & loop block */ + if (jbrk != NO_JUMP) { /* has `break` statement in iteration block */ + be_code_exblk(finfo, 1); + jbrk = be_code_jump(finfo); + } + be_code_conjump(finfo, &jcatch, finfo->pc); + be_code_catch(finfo, be_code_nextreg(finfo, &e), 1, 0, NULL); + be_code_raise(finfo, NULL, NULL); + be_code_conjump(finfo, &jbrk, finfo->pc); + be_code_freeregs(finfo, 1); +} + +/* approximate equivalent script code: + * .it = __iter__(expr) + * try + * while (1) + * itvar = .it() + * stmtlist + * end + * except ('stop_iteration') + * end + * */ +static void for_stmt(bparser *parser) +{ + bstring *var; + bexpdesc iter; + bblockinfo binfo; + int jcatch, beginpc = parser->finfo->pc; + /* FOR ID : expr block END */ + scan_next_token(parser); /* skip 'for' */ + begin_block(parser->finfo, &binfo, BLOCK_EXCEPT | BLOCK_LOOP); + var = for_itvar(parser); + match_token(parser, OptColon); /* skip ':' */ + for_init(parser, &iter); + jcatch = be_code_exblk(parser->finfo, 0); + for_iter(parser, var, &iter); + for_leave(parser, jcatch, beginpc); + match_token(parser, KeyEnd); /* skip 'end' */ +} + +static bblockinfo* break_block(bparser *parser) +{ + int try_depth = 0; /* count of exception catch blocks */ + bblockinfo *binfo = parser->finfo->binfo; + /* BREAK | CONTINUE */ + scan_next_token(parser); /* skip 'break' or 'continue' */ + while (binfo && !(binfo->type & BLOCK_LOOP)) { + if (binfo->type & BLOCK_EXCEPT) { + ++try_depth; /* leave the exception catch block */ + } + binfo = binfo->prev; + } + if (binfo && try_depth) { /* exception catch blocks that needs to leave */ + be_code_exblk(parser->finfo, try_depth); + } + return binfo; +} + +static void break_stmt(bparser *parser) +{ + bfuncinfo *f = parser->finfo; + bblockinfo *binfo = break_block(parser); + if (binfo != NULL) { /* connect jump */ + be_code_conjump(f, &binfo->breaklist, be_code_jump(f)); + } else { + parser_error(parser, "break not loop"); + } +} + +static void continue_stmt(bparser *parser) +{ + bfuncinfo *f = parser->finfo; + bblockinfo *b = break_block(parser); + if (b != NULL) { /* connect jump */ + be_code_conjump(f, &b->continuelist, be_code_jump(f)); + } else { + parser_error(parser, "continue not loop"); + } +} + +static bbool isoverloadable(btokentype type) +{ + return (type >= OptAdd && type <= OptConnect) /* overloaded binary operator */ + || type == OptFlip || type == OptLBK; /* '~' and '()' operator */ +} + +static bstring* func_name(bparser* parser, bexpdesc* e, int ismethod) +{ + btokentype type = next_type(parser); + if (type == TokenId) { + bstring *name = next_token(parser).u.s; + if (!ismethod) { + new_var(parser, name, e); /* new variable */ + } + scan_next_token(parser); /* skip name */ + return name; + } else if (ismethod && isoverloadable(type)) { + scan_next_token(parser); /* skip token */ + /* '-*' negative operator */ + if (type == OptSub && next_type(parser) == OptMul) { + scan_next_token(parser); /* skip '*' */ + return parser_newstr(parser, "-*"); + } + /* '()' call operator */ + if (type == OptLBK && next_type(parser) == OptRBK) { + scan_next_token(parser); /* skip ')' */ + return parser_newstr(parser, "()"); + } + return parser_newstr(parser, be_tokentype2str(type)); + } + push_error(parser, + "the token '%s' is not a valid function name.", + token2str(parser)); + return NULL; +} + +static void def_stmt(bparser *parser) +{ + bexpdesc e; + bproto *proto; + bfuncinfo *finfo = parser->finfo; + /* 'def' ID '(' varlist ')' block 'end' */ + scan_next_token(parser); /* skip 'def' */ + proto = funcbody(parser, func_name(parser, &e, 0), 0); + be_code_closure(finfo, &e, be_code_proto(finfo, proto)); + be_stackpop(parser->vm, 1); +} + +static void return_stmt(bparser *parser) +{ + bexpdesc e; + /* 'return' expr */ + scan_next_token(parser); /* skip 'return' */ + expr(parser, &e); + if (e.v.s) { /* expression is not empty */ + check_var(parser, &e); + } + be_code_ret(parser->finfo, &e); +} + +static void check_class_attr(bparser *parser, bclass *c, bstring *attr) +{ + if (be_class_attribute(parser->vm, c, attr) != BE_NIL) { + push_error(parser, + "redefinition of the attribute '%s'", str(attr)); + } +} + +static void classvar_stmt(bparser *parser, bclass *c) +{ + bstring *name; + /* 'var' ID {',' ID} */ + scan_next_token(parser); /* skip 'var' */ + if (match_id(parser, name) != NULL) { + check_class_attr(parser, c, name); + be_member_bind(parser->vm, c, name); + while (match_skip(parser, OptComma)) { /* ',' */ + if (match_id(parser, name) != NULL) { + check_class_attr(parser, c, name); + be_member_bind(parser->vm, c, name); + } else { + parser_error(parser, "class var error"); + } + } + } else { + parser_error(parser, "class var error"); + } +} + +static void classdef_stmt(bparser *parser, bclass *c) +{ + bexpdesc e; + bstring *name; + bproto *proto; + /* 'def' ID '(' varlist ')' block 'end' */ + scan_next_token(parser); /* skip 'def' */ + name = func_name(parser, &e, 1); + check_class_attr(parser, c, name); + proto = funcbody(parser, name, FUNC_METHOD); + be_method_bind(parser->vm, c, proto->name, proto); + be_stackpop(parser->vm, 1); +} + +static void class_inherit(bparser *parser, bexpdesc *e) +{ + if (next_type(parser) == OptColon) { /* ':' */ + bexpdesc e1; + scan_next_token(parser); /* skip ':' */ + expr(parser, &e1); + check_var(parser, &e1); + be_code_setsuper(parser->finfo, e, &e1); + } +} + +static void class_block(bparser *parser, bclass *c) +{ + /* { [;] } */ + while (block_follow(parser)) { + switch (next_type(parser)) { + case KeyVar: classvar_stmt(parser, c); break; + case KeyDef: classdef_stmt(parser, c); break; + case OptSemic: scan_next_token(parser); break; + default: push_error(parser, + "unexpected token '%s'", token2str(parser)); + } + } +} + +static void class_stmt(bparser *parser) +{ + bstring *name; + /* 'class' ID [':' ID] class_block 'end' */ + scan_next_token(parser); /* skip 'class' */ + if (match_id(parser, name) != NULL) { + bexpdesc e; + bclass *c = be_newclass(parser->vm, name, NULL); + new_var(parser, name, &e); + be_code_class(parser->finfo, &e, c); + class_inherit(parser, &e); + class_block(parser, c); + be_class_compress(parser->vm, c); /* compress class size */ + match_token(parser, KeyEnd); /* skip 'end' */ + } else { + parser_error(parser, "class name error"); + } +} + +static void import_stmt(bparser *parser) +{ + bstring *name; /* variable name */ + bexpdesc m, v; + /* 'import' (ID (['as' ID] | {',' ID}) | STRING 'as' ID ) */ + scan_next_token(parser); /* skip 'import' */ + init_exp(&m, ETSTRING, 0); + m.v.s = name = next_token(parser).u.s; + if (next_type(parser) == TokenString) { /* STRING 'as' ID */ + scan_next_token(parser); /* skip the module path */ + match_token(parser, KeyAs); /* match and skip 'as' */ + name = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + } else { /* ID (['as' ID] | {',' ID}) */ + match_token(parser, TokenId); /* match and skip ID */ + if (match_skip(parser, KeyAs)) { /* 'as' */ + name = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + } else { /* {',' ID} */ + while (match_skip(parser, OptComma)) { /* ',' */ + new_var(parser, name, &v); + be_code_import(parser->finfo, &m, &v); /* code import */ + init_exp(&m, ETSTRING, 0); /* scanning for next node */ + m.v.s = name = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + } + } + } + new_var(parser, name, &v); + be_code_import(parser->finfo, &m, &v); +} + +static void var_field(bparser *parser) +{ + /* ID ['=' expr] */ + bexpdesc e1, e2; + bstring *name; + name = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + if (match_skip(parser, OptAssign)) { /* '=' */ + expr(parser, &e2); + check_var(parser, &e2); + } else { + init_exp(&e2, ETNIL, 0); + } + new_var(parser, name, &e1); /* new variable */ + be_code_setvar(parser->finfo, &e1, &e2); +} + +static void var_stmt(bparser *parser) +{ + /* 'var' ID ['=' expr] {',' ID ['=' expr]} */ + scan_next_token(parser); /* skip 'var' */ + var_field(parser); + while (match_skip(parser, OptComma)) { /* ',' */ + var_field(parser); + } +} + +static int except_case_list(bparser *parser, int *base) +{ + int idx; + bexpdesc e; + bfuncinfo *finfo = parser->finfo; + /* expr {',' expr} | '..' */ + if (match_skip(parser, OptConnect)) { /* '..' */ + *base = finfo->freereg; + return 0; + } + expr(parser, &e); /* first exception expression */ + check_var(parser, &e); + *base = idx = be_code_nextreg(finfo, &e); + while (match_skip(parser, OptComma)) { /* ',' */ + expr(parser, &e); + check_var(parser, &e); + idx = be_code_nextreg(finfo, &e); + } + idx = idx - *base + 1; /* count of exception expression */ + be_code_freeregs(finfo, idx); + return idx; +} + +static int except_var_list(bparser *parser, int base) +{ + bexpdesc v; + (void)base; /* unused variable (no debugging) */ + /* [as ID [, ID]] */ + if (match_skip(parser, KeyAs)) { /* 'as' */ + bstring *name = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + new_var(parser, name, &v); /* new local variable */ + be_assert(v.type == ETLOCAL && v.v.idx == base); + if (match_skip(parser, OptComma)) { /* match and skip ',' */ + name = next_token(parser).u.s; + match_token(parser, TokenId); /* match and skip ID */ + new_var(parser, name, &v); /* new local variable */ + be_assert(v.type == ETLOCAL && v.v.idx == base + 1); + return 2; + } + return 1; + } + return 0; +} + +static void except_block(bparser *parser, int *jmp, int *jbrk) +{ + int base = 0; /* the first register of the catch opcode */ + int ecnt = 0; /* exception cases count */ + int vcnt = 0; /* exception variable count */ + bblockinfo binfo; + bfuncinfo *finfo = parser->finfo; + /* 'except' (expr {',' expr} | '..') ['as' ID [',' ID]] */ + match_token(parser, KeyExcept); /* skip 'except' */ + begin_block(finfo, &binfo, 0); /* begin catch block */ + /* link from the previous except failure point */ + be_code_conjump(finfo, jmp, finfo->pc); + /* (expr {',' expr} | '..') ['as' ID [',' ID]] */ + ecnt = except_case_list(parser, &base); + vcnt = except_var_list(parser, base); + be_code_catch(finfo, base, ecnt, vcnt, jmp); + stmtlist(parser); + be_code_conjump(finfo, jbrk, be_code_jump(finfo)); + end_block(parser); /* leave catch block */ +} + +static void try_stmt(bparser *parser) +{ + int jcatch, jbrk; + /* 'try' block 'except' except_stmt block 'end' */ + scan_next_token(parser); /* skip 'try' */ + jcatch = be_code_exblk(parser->finfo, 0); + block(parser, BLOCK_EXCEPT); + be_code_exblk(parser->finfo, 1); + jbrk = be_code_jump(parser->finfo); + except_block(parser, &jcatch, &jbrk); + while (next_type(parser) == KeyExcept) { + except_block(parser, &jcatch, &jbrk); + } + be_code_patchjump(parser->finfo, jcatch); + be_code_raise(parser->finfo, NULL, NULL); + be_code_patchjump(parser->finfo, jbrk); + match_token(parser, KeyEnd); /* skip 'end' */ +} + +static void throw_stmt(bparser *parser) +{ + bexpdesc e1, e2; + /* 'raise' expr */ + scan_next_token(parser); /* skip 'raise' */ + expr(parser, &e1); + check_var(parser, &e1); + if (match_skip(parser, OptComma)) { + expr(parser, &e2); + check_var(parser, &e2); + be_code_raise(parser->finfo, &e1, &e2); + } else { + be_code_raise(parser->finfo, &e1, NULL); + } +} + +static void statement(bparser *parser) +{ + switch (next_type(parser)) { + case KeyIf: if_stmt(parser); break; + case KeyWhile: while_stmt(parser); break; + case KeyFor: for_stmt(parser); break; + case KeyDo: do_stmt(parser); break; + case KeyBreak: break_stmt(parser); break; + case KeyContinue: continue_stmt(parser); break; + case KeyDef: def_stmt(parser); break; + case KeyClass: class_stmt(parser); break; + case KeyReturn: return_stmt(parser); break; + case KeyImport: import_stmt(parser); break; + case KeyVar: var_stmt(parser); break; + case KeyTry: try_stmt(parser); break; + case KeyRaise: throw_stmt(parser); break; + case OptSemic: scan_next_token(parser); break; /* empty statement */ + default: expr_stmt(parser); break; + } + be_assert(parser->finfo->freereg == be_list_count(parser->finfo->local)); +} + +static void stmtlist(bparser *parser) +{ + while (block_follow(parser)) { + statement(parser); + } +} + +static void block(bparser *parser, int type) +{ + bblockinfo binfo; + begin_block(parser->finfo, &binfo, type); + stmtlist(parser); + end_block(parser); +} + +static void mainfunc(bparser *parser, bclosure *cl) +{ + bblockinfo binfo; + bfuncinfo finfo; + begin_func(parser, &finfo, &binfo); + finfo.proto->argc = 0; /* args */ + finfo.proto->name = be_newstr(parser->vm, funcname(parser)); + cl->proto = finfo.proto; + be_remove(parser->vm, -3); /* pop proto from stack */ + stmtlist(parser); + end_func(parser); + match_token(parser, TokenEOS); /* skip EOS */ +} + +bclosure* be_parser_source(bvm *vm, + const char *fname, breader reader, void *data, bbool islocal) +{ + bparser parser; + bclosure *cl = be_newclosure(vm, 0); + parser.vm = vm; + parser.finfo = NULL; + parser.cl = cl; + parser.islocal = (bbyte)islocal; + var_setclosure(vm->top, cl); + be_stackpush(vm); + be_lexer_init(&parser.lexer, vm, fname, reader, data); + scan_next_token(&parser); /* scan first token */ + mainfunc(&parser, cl); + be_lexer_deinit(&parser.lexer); + be_global_release_space(vm); /* clear global space */ + be_stackpop(vm, 1); + scan_next_token(&parser); /* clear lexer */ + return cl; +} + +#endif diff --git a/lib/lib_div/Berry-0.1.10/src/be_parser.h b/lib/lib_div/Berry-0.1.10/src/be_parser.h new file mode 100644 index 000000000..47c9f6f48 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_parser.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_rangelib.c b/lib/lib_div/Berry-0.1.10/src/be_rangelib.c new file mode 100644 index 000000000..3682ae3ce --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_rangelib.c @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_repl.c b/lib/lib_div/Berry-0.1.10/src/be_repl.c new file mode 100644 index 000000000..17b766df7 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_repl.c @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_repl.h b/lib/lib_div/Berry-0.1.10/src/be_repl.h new file mode 100644 index 000000000..7d27e7850 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_repl.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_string.c b/lib/lib_div/Berry-0.1.10/src/be_string.c new file mode 100644 index 000000000..db7dcaebf --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_string.c @@ -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 + +#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); + } +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_string.h b/lib/lib_div/Berry-0.1.10/src/be_string.h new file mode 100644 index 000000000..51b3fd016 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_string.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 +********************************************************************/ +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_strlib.c b/lib/lib_div/Berry-0.1.10/src/be_strlib.c new file mode 100644 index 000000000..f4bda16c4 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_strlib.c @@ -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 +#include + +#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, "", name); + } else { + sprintf(buf, "", 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, "", var_toobj(v)); + break; + case BE_CLASS: + sprintf(sbuf, "", + 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, "", 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 : "")); + } + 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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_strlib.h b/lib/lib_div/Berry-0.1.10/src/be_strlib.h new file mode 100644 index 000000000..6d0e8ebb6 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_strlib.h @@ -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 + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_sys.h b/lib/lib_div/Berry-0.1.10/src/be_sys.h new file mode 100644 index 000000000..79a8b3c69 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_sys.h @@ -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 + +/* 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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_syslib.c b/lib/lib_div/Berry-0.1.10/src/be_syslib.c new file mode 100644 index 000000000..650db845c --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_syslib.c @@ -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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_timelib.c b/lib/lib_div/Berry-0.1.10/src/be_timelib.c new file mode 100644 index 000000000..7b779aa36 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_timelib.c @@ -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 + +#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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/be_var.c b/lib/lib_div/Berry-0.1.10/src/be_var.c new file mode 100644 index 000000000..ebc70ecea --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_var.c @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_var.h b/lib/lib_div/Berry-0.1.10/src/be_var.h new file mode 100644 index 000000000..6b9908a6b --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_var.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_vector.c b/lib/lib_div/Berry-0.1.10/src/be_vector.c new file mode 100644 index 000000000..b73479601 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_vector.c @@ -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 + +/* 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); +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_vector.h b/lib/lib_div/Berry-0.1.10/src/be_vector.h new file mode 100644 index 000000000..15f399cc6 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_vector.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/be_vm.c b/lib/lib_div/Berry-0.1.10/src/be_vm.c new file mode 100644 index 000000000..06b80cb7f --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_vm.c @@ -0,0 +1,1053 @@ +/******************************************************************** +** 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_vm.h" +#include "be_decoder.h" +#include "be_string.h" +#include "be_strlib.h" +#include "be_class.h" +#include "be_func.h" +#include "be_vector.h" +#include "be_map.h" +#include "be_module.h" +#include "be_mem.h" +#include "be_var.h" +#include "be_gc.h" +#include "be_exec.h" +#include "be_debug.h" +#include "be_libs.h" +#include + +#define NOT_METHOD BE_NONE + +#define vm_error(vm, except, ...) \ + be_raise(vm, except, be_pushfstring(vm, __VA_ARGS__)) + +#define RA() (reg + IGET_RA(ins)) +#define RKB() ((isKB(ins) ? ktab : reg) + KR2idx(IGET_RKB(ins))) +#define RKC() ((isKC(ins) ? ktab : reg) + KR2idx(IGET_RKC(ins))) + +#define var2cl(_v) cast(bclosure*, var_toobj(_v)) +#define var2real(_v) (var_isreal(_v) ? (_v)->v.r : (breal)(_v)->v.i) +#define val2bool(v) ((v) ? btrue : bfalse) +#define ibinop(op, a, b) ((a)->v.i op (b)->v.i) + +#if BE_USE_DEBUG_HOOK + #define DEBUG_HOOK() \ + if (vm->hookmask & BE_HOOK_LINE) { \ + do_linehook(vm); \ + reg = vm->reg; \ + } +#else + #define DEBUG_HOOK() +#endif + +#define vm_exec_loop() \ + loop: \ + DEBUG_HOOK(); \ + switch (IGET_OP(ins = *vm->ip++)) + +#define opcase(opcode) case OP_##opcode +#define dispatch() goto loop + +#define equal_rule(op, iseq) \ + bbool res; \ + if (var_isint(a) && var_isint(b)) { \ + res = ibinop(op, a, b); \ + } else if (var_isnumber(a) && var_isnumber(b)) { \ + res = var2real(a) op var2real(b); \ + } else if (var_isinstance(a)) { \ + res = object_eqop(vm, #op, iseq, a, b); \ + } else if (var_type(a) == var_type(b)) { /* same types */ \ + if (var_isnil(a)) { /* nil op nil */ \ + res = 1 op 1; \ + } else if (var_isbool(a)) { /* bool op bool */ \ + res = var_tobool(a) op var_tobool(b); \ + } else if (var_isstr(a)) { /* string op string */ \ + res = 1 op be_eqstr(a->v.s, b->v.s); \ + } else if (var_isclass(a) || var_isfunction(a)) { \ + res = var_toobj(a) op var_toobj(b); \ + } else { \ + binop_error(vm, #op, a, b); \ + res = bfalse; /* will not be executed */ \ + } \ + } else { /* different types */ \ + res = 1 op 0; \ + } \ + return res + +#define relop_rule(op) \ + bbool res; \ + if (var_isint(a) && var_isint(b)) { \ + res = ibinop(op, a, b); \ + } else if (var_isnumber(a) && var_isnumber(b)) { \ + res = var2real(a) op var2real(b); \ + } else if (var_isstr(a) && var_isstr(b)) { \ + bstring *s1 = var_tostr(a), *s2 = var_tostr(b); \ + res = be_strcmp(s1, s2) op 0; \ + } else if (var_isinstance(a)) { \ + binstance *obj = var_toobj(a); \ + object_binop(vm, #op, *a, *b); \ + check_bool(vm, obj, #op); \ + res = var_tobool(vm->top); \ + } else { \ + binop_error(vm, #op, a, b); \ + res = bfalse; /* will not be executed */ \ + } \ + return res + +#define bitwise_block(op) \ + bvalue *dst = RA(), *a = RKB(), *b = RKC(); \ + if (var_isint(a) && var_isint(b)) { \ + var_setint(dst, ibinop(op, a, b)); \ + } else if (var_isinstance(a)) { \ + ins_binop(vm, #op, ins); \ + } else { \ + binop_error(vm, #op, a, b); \ + } + +#define push_native(_vm, _f, _ns, _t) { \ + precall(_vm, _f, _ns, _t); \ + _vm->cf->status = PRIM_FUNC; \ +} + +static void attribute_error(bvm *vm, const char *t, bvalue *b, bvalue *c) +{ + const char *attr = var_isstr(c) ? str(var_tostr(c)) : be_vtype2str(c); + vm_error(vm, "attribute_error", + "'%s' value has no %s '%s'", be_vtype2str(b), t, attr); +} + +static void binop_error(bvm *vm, const char *op, bvalue *a, bvalue *b) +{ + vm_error(vm, "type_error", + "unsupported operand type(s) for %s: '%s' and '%s'", + op, be_vtype2str(a), be_vtype2str(b)); +} + +static void unop_error(bvm *vm, const char *op, bvalue *a) +{ + vm_error(vm, "type_error", + "unsupported operand type(s) for %s: '%s'", + op, be_vtype2str(a)); +} + +static void call_error(bvm *vm, bvalue *v) +{ + vm_error(vm, "type_error", + "'%s' value is not callable", be_vtype2str(v)); +} + +static void check_bool(bvm *vm, binstance *obj, const char *method) +{ + if (!var_isbool(vm->top)) { + const char *name = str(be_instance_name(obj)); + vm_error(vm, "type_error", + "`%s::%s` return value error, the expected type is 'bool'", + strlen(name) ? name : "", method); + } +} + +#if BE_USE_DEBUG_HOOK +static void do_linehook(bvm *vm) +{ + bcallframe *cf = vm->cf; + bclosure *cl = var_toobj(cf->func); + int pc = cast_int(vm->ip - cl->proto->code); + if (!pc || pc > cf->lineinfo->endpc) { + while (pc > cf->lineinfo->endpc) + cf->lineinfo++; + be_callhook(vm, BE_HOOK_LINE); + } else { + blineinfo *linfo = cf->lineinfo; + blineinfo *base = cl->proto->lineinfo; + while (linfo > base && pc <= linfo[-1].endpc) + linfo--; + if (cf->lineinfo != linfo) { + cf->lineinfo = linfo; + be_callhook(vm, BE_HOOK_LINE); + } + } +} +#endif + +static void precall(bvm *vm, bvalue *func, int nstack, int mode) +{ + bcallframe *cf; + int expan = nstack + BE_STACK_FREE_MIN; + if (vm->stacktop < func + expan) { + size_t fpos = func - vm->stack; + be_stack_expansion(vm, expan); + func = vm->stack + fpos; + } + be_stack_push(vm, &vm->callstack, NULL); + cf = be_stack_top(&vm->callstack); + cf->func = func - mode; + cf->top = vm->top; + cf->reg = vm->reg; + vm->reg = func + 1; + vm->top = vm->reg + nstack; + vm->cf = cf; +} + +static void push_closure(bvm *vm, bvalue *func, int nstack, int mode) +{ + bclosure *cl = var_toobj(func); + precall(vm, func, nstack, mode); + vm->cf->ip = vm->ip; + vm->cf->status = NONE_FLAG; + vm->ip = cl->proto->code; +#if BE_USE_DEBUG_HOOK + vm->cf->lineinfo = cl->proto->lineinfo; + be_callhook(vm, BE_HOOK_CALL); +#endif +} + +static void ret_native(bvm *vm) +{ + bcallframe *_cf = vm->cf; + vm->reg = _cf->reg; + vm->top = _cf->top; + be_stack_pop(&vm->callstack); + vm->cf = be_stack_top(&vm->callstack); +} + +static bbool obj2bool(bvm *vm, bvalue *var) +{ + binstance *obj = var_toobj(var); + bstring *tobool = str_literal(vm, "tobool"); + /* get operator method */ + if (be_instance_member(vm, obj, tobool, vm->top)) { + vm->top[1] = *var; /* move self to argv[0] */ + be_dofunc(vm, vm->top, 1); /* call method 'tobool' */ + /* check the return value */ + check_bool(vm, obj, "tobool"); + return var_tobool(vm->top); + } + return btrue; +} + +bbool be_value2bool(bvm *vm, bvalue *v) +{ + switch (var_basetype(v)) { + case BE_NIL: + return bfalse; + case BE_BOOL: + return var_tobool(v); + case BE_INT: + return val2bool(v->v.i); + case BE_REAL: + return val2bool(v->v.r); + case BE_INSTANCE: + return obj2bool(vm, v); + default: + return btrue; + } +} + +static void obj_method(bvm *vm, bvalue *o, bstring *attr, bvalue *dst) +{ + binstance *obj = var_toobj(o); + int type = be_instance_member(vm, obj, attr, dst); + if (basetype(type) != BE_FUNCTION) { + vm_error(vm, "attribute_error", + "the '%s' object has no method '%s'", + str(be_instance_name(obj)), str(attr)); + } +} + +static int obj_attribute(bvm *vm, bvalue *o, bstring *attr, bvalue *dst) +{ + binstance *obj = var_toobj(o); + int type = be_instance_member(vm, obj, attr, dst); + if (basetype(type) == BE_NIL) { + vm_error(vm, "attribute_error", + "the '%s' object has no attribute '%s'", + str(be_instance_name(obj)), str(attr)); + } + return type; +} + +static bbool object_eqop(bvm *vm, + const char *op, bbool iseq, bvalue *a, bvalue *b) +{ + binstance *o = var_toobj(a); + bvalue self = *a, other = *b; + bbool isself = var_isinstance(b) && o == var_toobj(b); + /* first, try to call the overloaded operator of the object */ + int type = be_instance_member(vm, o, be_newstr(vm, op), vm->top); + if (basetype(type) == BE_FUNCTION) { /* call method */ + bvalue *top = vm->top; + top[1] = self; /* move self to argv[0] */ + top[2] = other; /* move other to argv[1] */ + be_incrtop(vm); /* prevent collection results */ + be_dofunc(vm, top, 2); /* call method 'item' */ + be_stackpop(vm, 1); + check_bool(vm, o, op); /* check return value */ + return var_tobool(vm->top); /* copy result to dst */ + } + /* the default equal operation rule */ + return iseq == isself; /* check object self */ +} + +static void object_binop(bvm *vm, const char *op, bvalue self, bvalue other) +{ + bvalue *top = vm->top; + /* get operator method (possible GC) */ + obj_method(vm, &self, be_newstr(vm, op), vm->top); + top[1] = self; /* move self to argv[0] */ + top[2] = other; /* move other to argv[1] */ + be_incrtop(vm); /* prevent collection results */ + be_dofunc(vm, top, 2); /* call method 'item' */ + be_stackpop(vm, 1); +} + +#define ins_binop(vm, op, ins) { \ + object_binop(vm, op, *RKB(), *RKC()); \ + reg = vm->reg; \ + *RA() = *vm->top; /* copy result to dst */ \ +} + +static void ins_unop(bvm *vm, const char *op, bvalue self) +{ + bvalue *top = vm->top; + /* get operator method (possible GC) */ + obj_method(vm, &self, be_newstr(vm, op), vm->top); + top[1] = self; /* move self to argv[0] */ + be_dofunc(vm, top, 1); /* call method 'item' */ +} + +bbool be_vm_iseq(bvm *vm, bvalue *a, bvalue *b) +{ + equal_rule(==, btrue); +} + +bbool be_vm_isneq(bvm *vm, bvalue *a, bvalue *b) +{ + equal_rule(!=, bfalse); +} + +bbool be_vm_islt(bvm *vm, bvalue *a, bvalue *b) +{ + relop_rule(<); +} + +bbool be_vm_isle(bvm *vm, bvalue *a, bvalue *b) +{ + relop_rule(<=); +} + +bbool be_vm_isgt(bvm *vm, bvalue *a, bvalue *b) +{ + relop_rule(>); +} + +bbool be_vm_isge(bvm *vm, bvalue *a, bvalue *b) +{ + relop_rule(>=); +} + +static void make_range(bvm *vm, bvalue lower, bvalue upper) +{ + /* get method 'item' (possible GC) */ + int idx = be_builtin_find(vm, str_literal(vm, "range")); + bvalue *top = vm->top; + top[0] = *be_global_var(vm, idx); + top[1] = lower; /* move lower to argv[0] */ + top[2] = upper; /* move upper to argv[1] */ + vm->top += 3; /* prevent collection results */ + be_dofunc(vm, top, 2); /* call method 'item' */ + vm->top -= 3; +} + +static void connect_str(bvm *vm, bstring *a, bvalue *b) +{ + bstring *s; + if (var_isstr(b)) { + s = be_strcat(vm, a, var_tostr(b)); + var_setstr(vm->top, s); + } else { + *vm->top++ = *b; + be_val2str(vm, -1); + b = vm->top - 1; + s = be_strcat(vm, a, var_tostr(b)); + var_setstr(b, s); + vm->top -= 1; + } +} + +BERRY_API bvm* be_vm_new(void) +{ + bvm *vm = be_os_malloc(sizeof(bvm)); + be_assert(vm != NULL); + memset(vm, 0, sizeof(bvm)); /* clear all members */ + be_gc_init(vm); + be_string_init(vm); + be_stack_init(vm, &vm->callstack, sizeof(bcallframe)); + be_stack_init(vm, &vm->refstack, sizeof(binstance*)); + be_stack_init(vm, &vm->exceptstack, sizeof(struct bexecptframe)); + be_stack_init(vm, &vm->tracestack, sizeof(bcallsnapshot)); + vm->stack = be_malloc(vm, sizeof(bvalue) * BE_STACK_FREE_MIN); + vm->stacktop = vm->stack + BE_STACK_FREE_MIN; + vm->reg = vm->stack; + vm->top = vm->reg; + be_globalvar_init(vm); + be_gc_setpause(vm, 1); + be_loadlibs(vm); + return vm; +} + +BERRY_API void be_vm_delete(bvm *vm) +{ + be_gc_deleteall(vm); + be_string_deleteall(vm); + be_stack_delete(vm, &vm->callstack); + be_stack_delete(vm, &vm->refstack); + be_stack_delete(vm, &vm->exceptstack); + be_stack_delete(vm, &vm->tracestack); + be_free(vm, vm->stack, (vm->stacktop - vm->stack) * sizeof(bvalue)); + be_globalvar_deinit(vm); +#if BE_USE_DEBUG_HOOK + /* free native hook */ + if (var_istype(&vm->hook, BE_COMPTR)) + be_free(vm, var_toobj(&vm->hook), sizeof(struct bhookblock)); +#endif + /* free VM structure */ + be_os_free(vm); +} + +static void vm_exec(bvm *vm) +{ + bclosure *clos; + bvalue *ktab, *reg; + binstruction ins; + vm->cf->status |= BASE_FRAME; +newframe: /* a new call frame */ + be_assert(var_isclosure(vm->cf->func)); + clos = var_toobj(vm->cf->func); + ktab = clos->proto->ktab; + reg = vm->reg; + vm_exec_loop() { + opcase(LDNIL): { + var_setnil(RA()); + dispatch(); + } + opcase(LDBOOL): { + bvalue *v = RA(); + var_setbool(v, IGET_RKB(ins)); + if (IGET_RKC(ins)) { /* skip next instruction */ + vm->ip += 1; + } + dispatch(); + } + opcase(LDINT): { + bvalue *v = RA(); + var_setint(v, IGET_sBx(ins)); + dispatch(); + } + opcase(LDCONST): { + bvalue *dst = RA(); + *dst = ktab[IGET_Bx(ins)]; + dispatch(); + } + opcase(GETGBL): { + bvalue *v = RA(); + int idx = IGET_Bx(ins); + *v = *be_global_var(vm, idx); + dispatch(); + } + opcase(SETGBL): { + bvalue *v = RA(); + int idx = IGET_Bx(ins); + *be_global_var(vm, idx) = *v; + dispatch(); + } + opcase(GETUPV): { + bvalue *v = RA(); + int idx = IGET_Bx(ins); + be_assert(*clos->upvals != NULL); + *v = *clos->upvals[idx]->value; + dispatch(); + } + opcase(SETUPV): { + bvalue *v = RA(); + int idx = IGET_Bx(ins); + be_assert(*clos->upvals != NULL); + *clos->upvals[idx]->value = *v; + dispatch(); + } + opcase(MOVE): { + bvalue *dst = RA(); + *dst = *RKB(); + dispatch(); + } + opcase(ADD): { + bvalue *dst = RA(), *a = RKB(), *b = RKC(); + if (var_isint(a) && var_isint(b)) { + var_setint(dst, ibinop(+, a, b)); + } else if (var_isnumber(a) && var_isnumber(b)) { + breal x = var2real(a), y = var2real(b); + var_setreal(dst, x + y); + } else if (var_isstr(a) && var_isstr(b)) { /* strcat */ + bstring *s = be_strcat(vm, var_tostr(a), var_tostr(b)); + reg = vm->reg; + dst = RA(); + var_setstr(dst, s); + } else if (var_isinstance(a)) { + ins_binop(vm, "+", ins); + } else { + binop_error(vm, "+", a, b); + } + dispatch(); + } + opcase(SUB): { + bvalue *dst = RA(), *a = RKB(), *b = RKC(); + if (var_isint(a) && var_isint(b)) { + var_setint(dst, ibinop(-, a, b)); + } else if (var_isnumber(a) && var_isnumber(b)) { + breal x = var2real(a), y = var2real(b); + var_setreal(dst, x - y); + } else if (var_isinstance(a)) { + ins_binop(vm, "-", ins); + } else { + binop_error(vm, "-", a, b); + } + dispatch(); + } + opcase(MUL): { + bvalue *dst = RA(), *a = RKB(), *b = RKC(); + if (var_isint(a) && var_isint(b)) { + var_setint(dst, ibinop(*, a, b)); + } else if (var_isnumber(a) && var_isnumber(b)) { + breal x = var2real(a), y = var2real(b); + var_setreal(dst, x * y); + } else if (var_isinstance(a)) { + ins_binop(vm, "*", ins); + } else { + binop_error(vm, "*", a, b); + } + dispatch(); + } + opcase(DIV): { + bvalue *dst = RA(), *a = RKB(), *b = RKC(); + if (var_isint(a) && var_isint(b)) { + bint x = var_toint(a), y = var_toint(b); + if (y == 0) { + vm_error(vm, "divzero_error", "division by zero"); + } else { + var_setint(dst, x / y); + } + } else if (var_isnumber(a) && var_isnumber(b)) { + breal x = var2real(a), y = var2real(b); + if (y == cast(breal, 0)) { + vm_error(vm, "divzero_error", "division by zero"); + } + var_setreal(dst, x / y); + } else if (var_isinstance(a)) { + ins_binop(vm, "/", ins); + } else { + binop_error(vm, "/", a, b); + } + dispatch(); + } + opcase(MOD): { + bvalue *dst = RA(), *a = RKB(), *b = RKC(); + if (var_isint(a) && var_isint(b)) { + var_setint(dst, ibinop(%, a, b)); + } else if (var_isinstance(a)) { + ins_binop(vm, "%", ins); + } else { + binop_error(vm, "%", a, b); + } + dispatch(); + } + opcase(LT): { + bbool res = be_vm_islt(vm, RKB(), RKC()); + bvalue *dst; + reg = vm->reg; + dst = RA(); + var_setbool(dst, res); + dispatch(); + } + opcase(LE): { + bbool res = be_vm_isle(vm, RKB(), RKC()); + bvalue *dst; + reg = vm->reg; + dst = RA(); + var_setbool(dst, res); + dispatch(); + } + opcase(EQ): { + bbool res = be_vm_iseq(vm, RKB(), RKC()); + bvalue *dst; + reg = vm->reg; + dst = RA(); + var_setbool(dst, res); + dispatch(); + } + opcase(NE): { + bbool res = be_vm_isneq(vm, RKB(), RKC()); + bvalue *dst; + reg = vm->reg; + dst = RA(); + var_setbool(dst, res); + dispatch(); + } + opcase(GT): { + bbool res = be_vm_isgt(vm, RKB(), RKC()); + bvalue *dst; + reg = vm->reg; + dst = RA(); + var_setbool(dst, res); + dispatch(); + } + opcase(GE): { + bbool res = be_vm_isge(vm, RKB(), RKC()); + bvalue *dst; + reg = vm->reg; + dst = RA(); + var_setbool(dst, res); + dispatch(); + } + opcase(CONNECT): { + bvalue *a = RKB(), *b = RKC(); + if (var_isint(a) && var_isint(b)) { + make_range(vm, *RKB(), *RKC()); + } else if (var_isstr(a)) { + connect_str(vm, var_tostr(a), b); + } else if (var_isinstance(a)) { + object_binop(vm, "..", *RKB(), *RKC()); + } else { + binop_error(vm, "..", RKB(), RKC()); + } + reg = vm->reg; + *RA() = *vm->top; /* copy result to R(A) */ + dispatch(); + } + opcase(AND): { + bitwise_block(&); + dispatch(); + } + opcase(OR): { + bitwise_block(|); + dispatch(); + } + opcase(XOR): { + bitwise_block(^); + dispatch(); + } + opcase(SHL): { + bitwise_block(<<); + dispatch(); + } + opcase(SHR): { + bitwise_block(>>); + dispatch(); + } + opcase(NEG): { + bvalue *dst = RA(), *a = RKB(); + if (var_isint(a)) { + var_setint(dst, -a->v.i); + } else if (var_isreal(a)) { + var_setreal(dst, -a->v.r); + } else if (var_isinstance(a)) { + ins_unop(vm, "-*", *RKB()); + reg = vm->reg; + *RA() = *vm->top; /* copy result to dst */ + } else { + unop_error(vm, "-", a); + } + dispatch(); + } + opcase(FLIP): { + bvalue *dst = RA(), *a = RKB(); + if (var_isint(a)) { + var_setint(dst, -a->v.i); + } else if (var_isinstance(a)) { + ins_unop(vm, "~", *RKB()); + reg = vm->reg; + *RA() = *vm->top; /* copy result to dst */ + } else { + unop_error(vm, "~", a); + } + dispatch(); + } + opcase(JMP): { + vm->ip += IGET_sBx(ins); + dispatch(); + } + opcase(JMPT): { + if (be_value2bool(vm, RA())) { + vm->ip += IGET_sBx(ins); + } + dispatch(); + } + opcase(JMPF): { + if (!be_value2bool(vm, RA())) { + vm->ip += IGET_sBx(ins); + } + dispatch(); + } + opcase(CLOSURE): { + bvalue *dst; + bproto *p = clos->proto->ptab[IGET_Bx(ins)]; + bclosure *cl = be_newclosure(vm, p->nupvals); + cl->proto = p; + reg = vm->reg; + dst = RA(); + var_setclosure(dst, cl); + be_initupvals(vm, cl); + dispatch(); + } + opcase(CLASS): { + bclass *c = var_toobj(ktab + IGET_Bx(ins)); + be_class_upvalue_init(vm, c); + dispatch(); + } + opcase(GETMBR): { + bvalue *a = RA(), *b = RKB(), *c = RKC(); + if (var_isinstance(b) && var_isstr(c)) { + obj_attribute(vm, b, var_tostr(c), a); + } else if (var_ismodule(b) && var_isstr(c)) { + bstring *attr = var_tostr(c); + bmodule *module = var_toobj(b); + bvalue *v = be_module_attr(vm, module, attr); + if (v) { + *a = *v; + } else { + vm_error(vm, "attribute_error", + "module '%s' has no attribute '%s'", + be_module_name(module), str(attr)); + } + } else { + attribute_error(vm, "attribute", b, c); + } + dispatch(); + } + opcase(GETMET): { + bvalue *a = RA(), *b = RKB(), *c = RKC(); + if (var_isinstance(b) && var_isstr(c)) { + bvalue self = *b; + bstring *attr = var_tostr(c); + binstance *obj = var_toobj(b); + int type = obj_attribute(vm, b, var_tostr(c), a); + if (type == MT_METHOD || type == MT_PRIMMETHOD) { + a[1] = self; + } else if (var_basetype(a) == BE_FUNCTION) { + a[1] = *a; + var_settype(a, NOT_METHOD); + } else { + vm_error(vm, "attribute_error", + "class '%s' has no method '%s'", + str(be_instance_name(obj)), str(attr)); + } + } else if (var_ismodule(b) && var_isstr(c)) { + bstring *attr = var_tostr(c); + bmodule *module = var_toobj(b); + bvalue *src = be_module_attr(vm, module, attr); + if (src) { + var_settype(a, NOT_METHOD); + a[1] = *src; + } else { + vm_error(vm, "attribute_error", + "module '%s' has no method '%s'", + be_module_name(module), str(attr)); + } + } else { + attribute_error(vm, "method", b, c); + } + dispatch(); + } + opcase(SETMBR): { + bvalue *a = RA(), *b = RKB(), *c = RKC(); + if (var_isinstance(a) && var_isstr(b)) { + binstance *obj = var_toobj(a); + bstring *attr = var_tostr(b); + if (!be_instance_setmember(vm, obj, attr, c)) { + vm_error(vm, "attribute_error", + "class '%s' cannot assign to attribute '%s'", + str(be_instance_name(obj)), str(attr)); + } + dispatch(); + } + if (var_ismodule(a) && var_isstr(b)) { + bmodule *obj = var_toobj(a); + bstring *attr = var_tostr(b); + bvalue tmp = *c; /* stack may change */ + bvalue *v = be_module_bind(vm, obj, attr); + if (v != NULL) { + *v = tmp; + dispatch(); + } + } + attribute_error(vm, "writable attribute", a, b); + dispatch(); + } + opcase(GETIDX): { + bvalue *b = RKB(), *c = RKC(); + if (var_isinstance(b)) { + bvalue *top = vm->top; + /* get method 'item' */ + obj_method(vm, b, str_literal(vm, "item"), vm->top); + top[1] = *b; /* move object to argv[0] */ + top[2] = *c; /* move key to argv[1] */ + vm->top += 3; /* prevent collection results */ + be_dofunc(vm, top, 2); /* call method 'item' */ + vm->top -= 3; + reg = vm->reg; + *RA() = *vm->top; /* copy result to R(A) */ + } else if (var_isstr(b)) { + bstring *s = be_strindex(vm, var_tostr(b), c); + reg = vm->reg; + var_setstr(RA(), s); + } else { + vm_error(vm, "type_error", + "value '%s' does not support subscriptable", + be_vtype2str(b)); + } + dispatch(); + } + opcase(SETIDX): { + bvalue *a = RA(), *b = RKB(), *c = RKC(); + if (var_isinstance(a)) { + bvalue *top = vm->top; + /* get method 'setitem' */ + obj_method(vm, a, str_literal(vm, "setitem"), vm->top); + top[1] = *a; /* move object to argv[0] */ + top[2] = *b; /* move key to argv[1] */ + top[3] = *c; /* move src to argv[2] */ + vm->top += 4; + be_dofunc(vm, top, 3); /* call method 'setitem' */ + vm->top -= 4; + reg = vm->reg; + } else { + vm_error(vm, "type_error", + "value '%s' does not support index assignment", + be_vtype2str(a)); + } + dispatch(); + } + opcase(SETSUPER): { + bvalue *a = RA(), *b = RKB(); + if (var_isclass(a) && var_isclass(b)) { + bclass *obj = var_toobj(a); + be_class_setsuper(obj, var_toobj(b)); + } else { + vm_error(vm, "type_error", + "value '%s' does not support set super", + be_vtype2str(b)); + } + dispatch(); + } + opcase(CLOSE): { + be_upvals_close(vm, RA()); + dispatch(); + } + opcase(IMPORT): { + bvalue *b = RKB(); + if (var_isstr(b)) { + bstring *name = var_tostr(b); + int res = be_module_load(vm, name); + reg = vm->reg; + switch (res) { + case BE_OK: /* find the module */ + be_stackpop(vm, 1); + *RA() = *vm->top; + break; + case BE_EXCEPTION: /* pop the exception value and message */ + be_pop(vm, 2); + be_throw(vm, BE_EXCEPTION); + break; + default: + vm_error(vm, "import_error", "module '%s' not found", str(name)); + } + } else { + vm_error(vm, "type_error", + "import '%s' does not support import", + be_vtype2str(b)); + } + dispatch(); + } + opcase(CATCH): { + bvalue *base = RA(), *top = vm->top; + int i = 0, ecnt = IGET_RKB(ins), vcnt = IGET_RKC(ins); + while (i < ecnt && !be_vm_iseq(vm, top, base + i)) { + ++i; + } + if (!ecnt || i < ecnt) { /* exception catched */ + base = RA(), top = vm->top; + for (i = 0; i < vcnt; ++i) { + *base++ = *top++; + } + vm->ip += 1; /* skip next instruction */ + } + dispatch(); + } + opcase(RAISE): { + if (IGET_RA(ins) < 2) { + bvalue *top = vm->top; + top[0] = *RKB(); /* push the exception value to top */ + if (IGET_RA(ins)) { /* has exception argument? */ + top[1] = *RKC(); /* push the exception argument to top + 1 */ + } else { + var_setnil(top + 1); + } + be_save_stacktrace(vm); + } + be_throw(vm, BE_EXCEPTION); /* throw / rethrow the exception */ + dispatch(); + } + opcase(EXBLK): { + if (!IGET_RA(ins)) { + be_except_block_setup(vm); + if (be_setjmp(vm->errjmp->b)) { + be_except_block_resume(vm); + goto newframe; + } + reg = vm->reg; + } else { + be_except_block_close(vm, IGET_Bx(ins)); + } + dispatch(); + } + opcase(CALL): { + bvalue *var = RA(); + int mode = 0, argc = IGET_RKB(ins); + recall: /* goto: instantiation class and call constructor */ + switch (var_type(var)) { + case NOT_METHOD: + var[0] = var[1]; + ++var, --argc, mode = 1; + goto recall; + case BE_CLASS: + if (be_class_newobj(vm, var_toobj(var), var, ++argc)) { + reg = vm->reg; + var = RA() + 1; /* to next register */ + goto recall; /* call constructor */ + } + break; + case BE_INSTANCE: { + bvalue *v = var + argc++, temp; + /* load the '()' method to `temp' */ + obj_method(vm, var, str_literal(vm, "()"), &temp); + for (; v >= var; --v) v[1] = v[0]; + *var = temp; + goto recall; /* call '()' method */ + } + case BE_CLOSURE: { + bvalue *v, *end; + bproto *proto = var2cl(var)->proto; + push_closure(vm, var, proto->nstack, mode); + reg = vm->reg; + v = reg + argc; + end = reg + proto->argc; + for (; v < end; ++v) { + var_setnil(v); + } + goto newframe; + } + case BE_NTVCLOS: { + bntvclos *f = var_toobj(var); + push_native(vm, var, argc, mode); + f->f(vm); /* call C primitive function */ + ret_native(vm); + break; + } + case BE_NTVFUNC: { + bntvfunc f = var_tontvfunc(var); + push_native(vm, var, argc, mode); + f(vm); /* call C primitive function */ + ret_native(vm); + break; + } + default: + call_error(vm, var); + } + reg = vm->reg; + dispatch(); + } + opcase(RET): { + bcallframe *cf; + bvalue *ret; +#if BE_USE_DEBUG_HOOK + be_callhook(vm, BE_HOOK_RET); +#endif + cf = vm->cf; + ret = vm->cf->func; + /* copy return value */ + if (IGET_RA(ins)) { + *ret = *RKB(); + } else { + var_setnil(ret); + } + vm->reg = cf->reg; + vm->top = cf->top; + vm->ip = cf->ip; + be_stack_pop(&vm->callstack); /* pop don't delete */ + if (cf->status & BASE_FRAME) { /* entrance function */ + bstack *cs = &vm->callstack; + if (!be_stack_isempty(cs)) { + vm->cf = be_stack_top(cs); + } + return; + } + vm->cf = be_stack_top(&vm->callstack); + goto newframe; + } + } +} + +static void do_closure(bvm *vm, bvalue *reg, int argc) +{ + bvalue *v, *end; + bproto *proto = var2cl(reg)->proto; + push_closure(vm, reg, proto->nstack, 0); + v = vm->reg + argc; + end = vm->reg + proto->argc; + for (; v <= end; ++v) { + var_setnil(v); + } + vm_exec(vm); +} + +static void do_ntvclos(bvm *vm, bvalue *reg, int argc) +{ + bntvclos *f = var_toobj(reg); + push_native(vm, reg, argc, 0); + f->f(vm); /* call C primitive function */ + ret_native(vm); +} + +static void do_ntvfunc(bvm *vm, bvalue *reg, int argc) +{ + bntvfunc f = var_tontvfunc(reg); + push_native(vm, reg, argc, 0); + f(vm); /* call C primitive function */ + ret_native(vm); +} + +static void do_class(bvm *vm, bvalue *reg, int argc) +{ + if (be_class_newobj(vm, var_toobj(reg), reg, ++argc)) { + be_incrtop(vm); + be_dofunc(vm, reg + 1, argc); + be_stackpop(vm, 1); + } +} + +void be_dofunc(bvm *vm, bvalue *v, int argc) +{ + be_assert(vm->reg <= v && v < vm->stacktop); + be_assert(vm->stack <= vm->reg && vm->reg < vm->stacktop); + switch (var_type(v)) { + case BE_CLASS: do_class(vm, v, argc); break; + case BE_CLOSURE: do_closure(vm, v, argc); break; + case BE_NTVCLOS: do_ntvclos(vm, v, argc); break; + case BE_NTVFUNC: do_ntvfunc(vm, v, argc); break; + default: call_error(vm, v); + } +} diff --git a/lib/lib_div/Berry-0.1.10/src/be_vm.h b/lib/lib_div/Berry-0.1.10/src/be_vm.h new file mode 100644 index 000000000..9c806fd2d --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/be_vm.h @@ -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 diff --git a/lib/lib_div/Berry-0.1.10/src/berry.h b/lib/lib_div/Berry-0.1.10/src/berry.h new file mode 100644 index 000000000..f3eb7cde6 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/berry.h @@ -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 +#include + +#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 diff --git a/lib/lib_div/Berry-0.1.10/src/berry_conf.h b/lib/lib_div/Berry-0.1.10/src/berry_conf.h new file mode 100644 index 000000000..f645c0338 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/berry_conf.h @@ -0,0 +1 @@ +#include "port/berry_conf.h" \ No newline at end of file diff --git a/lib/lib_div/Berry-0.1.10/src/port/be_modtab.c b/lib/lib_div/Berry-0.1.10/src/port/be_modtab.c new file mode 100644 index 000000000..9250e83e2 --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/port/be_modtab.c @@ -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 */ +}; diff --git a/lib/lib_div/Berry-0.1.10/src/port/be_port.cpp b/lib/lib_div/Berry-0.1.10/src/port/be_port.cpp new file mode 100644 index 000000000..7233f57dc --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/port/be_port.cpp @@ -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 +#include +#include + +/* 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 +// #include +// #include + +// 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 +// #include +// #include + +// 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 */ diff --git a/lib/lib_div/Berry-0.1.10/src/port/berry_conf.h b/lib/lib_div/Berry-0.1.10/src/port/berry_conf.h new file mode 100644 index 000000000..aee1214aa --- /dev/null +++ b/lib/lib_div/Berry-0.1.10/src/port/berry_conf.h @@ -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 + +/* 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 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 710061e51..0eb558bbe 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -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 /********************************************************************************************/ diff --git a/tasmota/xdrv_52_berry.ino b/tasmota/xdrv_52_berry.ino new file mode 100644 index 000000000..04d73bf29 --- /dev/null +++ b/tasmota/xdrv_52_berry.ino @@ -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 . +*/ + + +#ifdef USE_BERRY +// #ifdef ESP32 + +#define XDRV_52 52 + +#include +#include + +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