diff --git a/ChangeLog b/ChangeLog index 5563c93d..899596e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2014-09-28 + Refactor image handling to use system object + +2014-09-22 + Implemented ARRAY command to read JSON into a map variable + +2014-09-19 + Fix STR(v) support for map variables + +2014-09-12 + Add support for unary operators on array elements + Fix printing UDS vars + +2014-09-08 + Fix call FUNC with (arg1), (arg2) + Fix INKEY Backspace in FLTK + Fix FOR/NEXT using float increments + 2014-09-06 RTE command renamed THROW diff --git a/configure.ac b/configure.ac index be987b4d..6f99b745 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [0.11.15]) +AC_INIT([smallbasic], [0.11.16]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET @@ -226,6 +226,7 @@ function buildSDL() { AC_DEFINE(IMPL_DEV_READ, 1, [Implement dev_read()]) AC_DEFINE(IMPL_OSD_SOUND, 1, [Driver implements osd_sound()]) AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) + AC_DEFINE(IMPL_IMAGE, 1, [Driver implements image commands]) AC_DEFINE(OS_PREC64, 1, [64 bit variables]) AC_DEFINE(DRV_BEEP, 1, [Use the driver based beep function]) diff --git a/debian/changelog b/debian/changelog index bd30d069..5cfc45cb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +smallbasic (0.11.16) unstable; urgency=low + * Add support for unary operators on array elements + * Fix call FUNC with (arg1), (arg2) + * Fix INKEY Backspace in FLTK + * Fix FOR/NEXT using float increments + + -- Chris Warren-Smith Sat, 13 Sept 2014 09:45:25 +1000 + smallbasic (0.11.15) unstable; urgency=low * RTE renamed THROW diff --git a/samples/distro-examples/devio/google.bas b/samples/distro-examples/devio/google.bas new file mode 100644 index 00000000..0c651d13 --- /dev/null +++ b/samples/distro-examples/devio/google.bas @@ -0,0 +1,14 @@ +url = "http://ajax.googleapis.com/ajax/services/search/web?rsz=large&q=" + trim(command) + "&v=1.0" +open url as #1 +if (eof(1)) then + throw "Connection failed: " + url +fi + +dim results +tload #1, results +json = array(results) +num_results = len(json.responseData.results) +for i = 0 to num_results - 1 + print json.responseData.results(i).titleNoFormatting + print " "; json.responseData.results(i).unescapedUrl +next i diff --git a/samples/distro-examples/tests/array.bas b/samples/distro-examples/tests/array.bas index d3fb1f39..e949ef63 100644 --- a/samples/distro-examples/tests/array.bas +++ b/samples/distro-examples/tests/array.bas @@ -29,3 +29,44 @@ if ubound(m)<>3 then ?"UBOUND() ERROR" if lbound(m,2)<>-1 then ?"LBOUND() ERROR" if ubound(m,2)<>1 then ?"UBOUND() ERROR" +if (isarray(m) == false) then + throw "m is not an array" +end if + +m2 = array("[1,2,3,\"other\"]") +if (isarray(m2) == false) then + throw "m2 is not an array" +end if + +if (isnumber(m2(0)) == false) then + throw "m2(0) is not an number" +end if + +if (isstring(m2(3)) == false) then + throw "m2(3) is not an string" +end if + +m3 = array("{\"cat\":{\"name\":\"lots\"},\"other\":\"thing\",\"zz\":\"thing\"}") +if (ismap(m3) == false) then + throw "m3 is not an map" +end if + +m4 = byref m3 +if (isref(m4) == false) then + throw "m3 is not an ref" +end if + +if m4.cat.name <> "lots" then + throw "ref/map error" +end if + +dim sim +sim << 100 +sim(0) -- +sim(0) ++ +sim(0) /= 2 +if (sim(0) <> 50) then + throw "dim sim not tasty" +fi + + diff --git a/samples/distro-examples/tests/iifs.bas b/samples/distro-examples/tests/iifs.bas index 77333ed6..ecdebf8e 100644 --- a/samples/distro-examples/tests/iifs.bas +++ b/samples/distro-examples/tests/iifs.bas @@ -1,4 +1,15 @@ ' +' Test for refactoring regression +' +blue=1 +red=2 +green=3 + +if false then blue=4:red=5:green=6 + +if (1 <> blue OR red <> 2 OR green <> 3) then + throw "Inline IF error: blue=" + blue + " red=" + red + " green=" + green +endif ' normal if if 1 then:? "Normal IF - Ok":else:? "ERROR":fi @@ -25,4 +36,3 @@ goto 200 ? "label 600 - Ok" goto 210 - diff --git a/samples/distro-examples/tests/output/hash.out b/samples/distro-examples/tests/output/hash.out index cedbb133..994fdc9f 100644 --- a/samples/distro-examples/tests/output/hash.out +++ b/samples/distro-examples/tests/output/hash.out @@ -1,3 +1,3 @@ something 123 -[blah=something,other=123,100=cats] +{"100":"cats","blah":"something","other":123} diff --git a/samples/distro-examples/tests/output/ref.out b/samples/distro-examples/tests/output/ref.out index 71e51bf0..9182ef68 100644 --- a/samples/distro-examples/tests/output/ref.out +++ b/samples/distro-examples/tests/output/ref.out @@ -4,9 +4,10 @@ dog= dog 1= 1 a=b a=b a<>b a<>b -[NAME=kitchen] -[NAME=hall] -[NAME=Kitchen,FRIDGE=empty] -[NAME=toilet,OCCUPIED=-1] -[NAME=Kitchen,FRIDGE=empty] -[NAME=hall] +{"NAME":"kitchen"} +{"NAME":"hall"} +{"FRIDGE":"empty","NAME":"Kitchen"} +{"NAME":"toilet","OCCUPIED":-1} +{"FRIDGE":"empty","NAME":"Kitchen"} +{"NAME":"hall"} +toilet diff --git a/samples/distro-examples/tests/output/uds.out b/samples/distro-examples/tests/output/uds.out index 5d56218f..2546fe1e 100644 --- a/samples/distro-examples/tests/output/uds.out +++ b/samples/distro-examples/tests/output/uds.out @@ -1,2 +1,12 @@ start of test -end of test +a: +{"XCAT":"cat","XDOG":"dog","XFISH":{"BIG":"big","SMALL":"small"}} +In a: +a.XCAT=cat +a.XDOG=dog +a.XFISH={"BIG":"big","SMALL":"small"} +In a.xfish: +a.xfish.BIG=big +a.xfish.SMALL=small +3 +2 diff --git a/samples/distro-examples/tests/ref.bas b/samples/distro-examples/tests/ref.bas index dddc7e82..5f4bd82b 100644 --- a/samples/distro-examples/tests/ref.bas +++ b/samples/distro-examples/tests/ref.bas @@ -15,7 +15,7 @@ print "a<>b", iFF(a==b, "a=b", "a<>b") dim rooms sub addRoom(byref room) - rooms << @room + rooms << byref room end dim kitchen,hall,toilet @@ -40,3 +40,5 @@ print rooms(0) print rooms(1) print rooms(2) +roomref = byref rooms(0) +print roomref.name diff --git a/samples/distro-examples/tests/tau.bas b/samples/distro-examples/tests/tau.bas index 56c1de38..0189794d 100644 --- a/samples/distro-examples/tests/tau.bas +++ b/samples/distro-examples/tests/tau.bas @@ -33,7 +33,7 @@ sub build_ta end sub cerr - rte 2 + throw 2 end sub addRoom(the_thing,d) diff --git a/samples/distro-examples/tests/trycatch.bas b/samples/distro-examples/tests/trycatch.bas index d3b1684b..8099a7f9 100644 --- a/samples/distro-examples/tests/trycatch.bas +++ b/samples/distro-examples/tests/trycatch.bas @@ -40,4 +40,33 @@ end try ? "outer after try" +rem +rem Test whether the stack is unwound correctly +rem + +iter = 100 +cnt = 0 +i = 0 + +while (i < iter) + try + while (true) + if (true) + if (true) + throw "foo" + end if + end if + wend + catch err + select case err + case "foo" + cnt++ + end select + end try + i++ +wend + +if (cnt <> iter) then + print "Test failed: "; cnt; " <> "; iter +end if diff --git a/samples/distro-examples/tests/uds.bas b/samples/distro-examples/tests/uds.bas index 642889fc..9e40f2a1 100644 --- a/samples/distro-examples/tests/uds.bas +++ b/samples/distro-examples/tests/uds.bas @@ -1,5 +1,5 @@ ' -' This is a test for user-defined structures +' This is a test for the MAP variable type ' ? "start of test" @@ -87,4 +87,27 @@ if ((my_pet.x + 1) < 10) then ? "wrong !" fi -? "end of test" +a.xcat = "cat" +a.xdog = "dog" +a.xfish.big = "big" +a.xfish.small = "small" + +? "a:" +? a + +? "In a:" +for i in a + ? "a." + i + "="; a(i) +next i + +? "In a.xfish:" +for i in a.xfish + ? "a.xfish." + i + "="; a.xfish(i) +next i + +? len(a) +? len(a.xfish) + +inner.foo="bar" +m.b << inner +s = " " + m.b(0).foo diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 64ff9889..6afae666 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -12,83 +12,85 @@ endif AM_CPPFLAGS = -I$(top_builddir)/src -DPACKAGE_LIB_DIR=\""$(pkglibdir)"\" -EXTRA_DIST = \ - keywords.c \ - ../languages/chars.el.h \ - ../languages/chars.en.h \ - ../languages/keywords.el.c \ - ../languages/keywords.en.c \ - ../languages/messages.el.h \ - ../languages/messages.en.h \ - ../modules/configure.in \ - ../modules/example1.c \ - ../modules/Makefile.am \ - ../modules/mod_utils.c \ - ../modules/mod_utils.h \ - ../modules/example1.c \ - ../modules/flite/flite.c \ - ../modules/flite/Makefile \ - ../modules/flite/README.TXT \ - ../modules/flite/test.bas \ - ../modules/gdbm/gdbm.c \ - ../modules/gdbm/Makefile \ - ../modules/mysql/Makefile.am \ +EXTRA_DIST = \ + keywords.c \ + ../languages/chars.el.h \ + ../languages/chars.en.h \ + ../languages/keywords.el.c \ + ../languages/keywords.en.c \ + ../languages/messages.el.h \ + ../languages/messages.en.h \ + ../modules/configure.in \ + ../modules/example1.c \ + ../modules/Makefile.am \ + ../modules/mod_utils.c \ + ../modules/mod_utils.h \ + ../modules/example1.c \ + ../modules/flite/flite.c \ + ../modules/flite/Makefile \ + ../modules/flite/README.TXT \ + ../modules/flite/test.bas \ + ../modules/gdbm/gdbm.c \ + ../modules/gdbm/Makefile \ + ../modules/mysql/Makefile.am \ ../modules/mysql/mysql.c noinst_LIBRARIES = libsb_common.a -libsb_common_a_SOURCES = \ - bc.c bc.h \ - blib.c blib.h \ - blib_db.c \ - blib_func.c \ - blib_graph.c \ - blib_math.c matrix.c \ - blib_ui.c blib_ui.h \ - blib_math.h blib_ui.h \ - blib_sound.c \ - brun.c \ - ceval.c \ - circle.c \ - decomp.c \ - device.c device.h \ - screen.c \ - system.c \ - drvmouse.h drvsound.h \ - eval.c \ - extlib.c extlib.h \ - file.c \ - ffill.c \ - fmt.c fmt.h \ - fs_serial.c fs_serial.h \ - fs_socket_client.c fs_socket_client.h \ - fs_stream.c fs_stream.h \ - g_line.c \ - geom.c geom.h g_bmp.h \ - inet.c inet.h \ - kw.c kw.h \ - match.c match.h \ - mem.c \ - panic.c panic.h \ - pfill.c \ - plot.c \ - proc.c pproc.h \ - sberr.c sberr.h \ - scan.c scan.h \ - str.c str.h \ - tasks.c tasks.h \ - search.c search.h \ - var_hash.c var_hash.h \ - keymap.c keymap.h \ - units.c units.h \ - var.c var.h \ - vmt.c vmt.h \ - messages.h \ - osd.h \ - pdb.h \ - pmem.h \ - sbapp.h \ - smbas.h \ +libsb_common_a_SOURCES = \ + ../lib/match.c ../lib/match.h \ + ../lib/search.c ../lib/search.h \ + ../lib/jsmn.c ../lib/jsmn.h \ + ../lib/matrix.c \ + bc.c bc.h \ + blib.c blib.h \ + blib_db.c \ + blib_func.c \ + blib_graph.c \ + blib_math.c \ + blib_ui.c blib_ui.h \ + blib_math.h blib_ui.h \ + blib_sound.c \ + brun.c \ + ceval.c \ + circle.c \ + decomp.c \ + device.c device.h \ + screen.c \ + system.c \ + drvmouse.h drvsound.h \ + eval.c \ + extlib.c extlib.h \ + file.c \ + ffill.c \ + fmt.c fmt.h \ + fs_serial.c fs_serial.h \ + fs_socket_client.c fs_socket_client.h \ + fs_stream.c fs_stream.h \ + g_line.c \ + geom.c geom.h g_bmp.h \ + inet.c inet.h \ + kw.c kw.h \ + mem.c \ + panic.c panic.h \ + pfill.c \ + plot.c \ + proc.c pproc.h \ + sberr.c sberr.h \ + scan.c scan.h \ + str.c str.h \ + tasks.c tasks.h \ + var_map.c var_obj.c var_map.h \ + var_eval.c var_eval.h \ + keymap.c keymap.h \ + units.c units.h \ + var.c var.h \ + vmt.c vmt.h \ + messages.h \ + osd.h \ + pmem.h \ + sbapp.h \ + smbas.h \ sys.h diff --git a/src/common/blib.c b/src/common/blib.c index 34cb336a..7859edfc 100644 --- a/src/common/blib.c +++ b/src/common/blib.c @@ -10,7 +10,7 @@ #include "common/sys.h" #include "common/kw.h" #include "common/var.h" -#include "common/var_hash.h" +#include "common/var_map.h" #include "common/device.h" #include "common/blib.h" #include "common/pproc.h" @@ -452,21 +452,18 @@ void cmd_print(int output) { var_t var, *vuser_p; int handle = 0; - /* - * prefix - # (file) - */ + // prefix - # (file) if (output == PV_FILE) { par_getsharp(); if (prog_error) { return; } handle = par_getint(); - if (prog_error) + if (prog_error) { return; - if (code_peek() == kwTYPE_EOC || code_peek() == kwTYPE_LINE) { // There - // are - // no - // parameters + } + if (code_peek() == kwTYPE_EOC || code_peek() == kwTYPE_LINE) { + // There are no parameters if (dev_fstatus(handle)) { dev_fwrite(handle, (byte *) "\n", 1); } else { @@ -485,9 +482,7 @@ void cmd_print(int output) { } } - /* - * prefix: memory variable - */ + // prefix: memory variable if (output == PV_STRING) { if (!code_isvar()) { err_argerr(); @@ -507,9 +502,7 @@ void cmd_print(int output) { handle = (mem_t)vuser_p; } - /* - * prefix - USING - */ + // prefix - USING code = code_peek(); if (code == kwUSING) { code_skipnext(); @@ -537,9 +530,7 @@ void cmd_print(int output) { use_format = 1; } - /* - * PRINT - */ + // PRINT do { code = code_peek(); if (code == kwTYPE_SEP) { @@ -601,13 +592,14 @@ void cmd_logprint() { */ void cmd_input(int input) { byte code; - byte print_crlf = 1, next_is_const = 0; + byte print_crlf = 1; + byte next_is_const = 0; byte input_is_finished = 0; - - var_t prompt, *vuser_p = NULL; - + var_t prompt; + var_t *vuser_p = NULL; int handle = 0; - int cur_par_idx, unused_vars; + int cur_par_idx; + int unused_vars; char *inps = NULL, *inp_p; int pcount = 0, redo = 0; par_t *ptable = NULL; @@ -615,9 +607,7 @@ void cmd_input(int input) { char *p, lc; v_init(&prompt); - /* - * prefix - # (file) - */ + // prefix - # (file) if (input == PV_FILE) { par_getsharp(); if (prog_error) { @@ -637,9 +627,7 @@ void cmd_input(int input) { } } - /* - * prefix: memory variable - */ + // prefix: memory variable if (input == PV_STRING) { if (!code_isvar()) { err_argerr(); @@ -663,9 +651,7 @@ void cmd_input(int input) { handle = (mem_t) vuser_p; } - /* - * prefix: prompt - */ + // prefix: prompt if (input == PV_CONSOLE) { v_setstr(&prompt, ""); @@ -683,33 +669,30 @@ void cmd_input(int input) { v_strcat(&prompt, "? "); } } - } else { /* no prompt */ + } else { + // no prompt v_setstr(&prompt, "? "); } } else { print_crlf = 0; } - /* - * get list of parameters - */ + + // get list of parameters pcount = par_getpartable(&ptable, ",;"); if (pcount == 0) { rt_raise(ERR_INPUT_NO_VARS); } - /* - * the INPUT itself - */ + // the INPUT itself if (!prog_error) { - do { // "redo from start" + do { + // "redo from start" if (input == PV_CONSOLE) { // prompt if (prompt.v.p.ptr) { pv_write((char *) prompt.v.p.ptr, input, handle); } } - /* - * get user's input - */ + // get user's input switch (input) { case PV_CONSOLE: // console @@ -781,7 +764,7 @@ void cmd_input(int input) { v_setstr(par->var, ""); unused_vars++; } - } else { // we are continue to read + } else { // we continue to read if (par->flags & PAR_BYVAL) { // no constants are allowed err_typemismatch(); @@ -792,7 +775,7 @@ void cmd_input(int input) { next_is_const = 0; if (cur_par_idx < (pcount - 1)) { if (ptable[cur_par_idx + 1].flags & PAR_BYVAL) { - cur_par_idx++; // <-- WARNING + cur_par_idx++; // par = previous parameter // ptable[cur_par_idx] = the constant next_is_const = 1; @@ -843,9 +826,7 @@ void cmd_input(int input) { redo = 1; tmp_free(inps); #if USE_TERM_IO - /* - * standard input case - */ + // standard input case if (!os_graphics) { if (term_israw()) { fprintf(stdout, "\n\a* %s *\n", WORD_INPUT_REDO); @@ -867,15 +848,11 @@ void cmd_input(int input) { } while (redo && !prog_error); } - /* - * exit - */ + // exit if (input == PV_CONSOLE) { if (print_crlf && (prog_error == 0)) { #if USE_TERM_IO - /* - * standard input case - */ + // standard input case if (!os_graphics) { if (!term_israw()) pv_write("\n", input, handle); @@ -897,28 +874,24 @@ void cmd_input(int input) { * ON x GOTO|GOSUB ... */ void cmd_on_go() { - addr_t next_ip, expr_ip, table_ip, dest_ip; - code_t command; - byte count; + addr_t dest_ip; var_t var; - addr_t index; stknode_t node; - next_ip = code_getaddr(); + addr_t next_ip = code_getaddr(); code_skipaddr(); - command = code_getnext(); - count = code_getnext(); - table_ip = prog_ip; - expr_ip = prog_ip + (count * ADDRSZ); + code_t command = code_getnext(); + byte count = code_getnext(); + addr_t table_ip = prog_ip; + addr_t expr_ip = prog_ip + (count * ADDRSZ); v_init(&var); prog_ip = expr_ip; eval(&var); - index = (v_igetval(&var) - 1); + addr_t index = (v_igetval(&var) - 1); if (((int) index == -1) || ((int) index >= (int) count)) { - // index == -1 (0 on BASIC) || index >= count - // do nothing + // index == -1 (0 on BASIC) || index >= count do nothing command = kwNULL; prog_ip = next_ip; } else if ((int) index < 0) { @@ -951,9 +924,7 @@ void cmd_on_go() { */ void cmd_gosub() { stknode_t node; - bid_t goto_label; - - goto_label = code_getaddr(); + bid_t goto_label = code_getaddr(); node.type = kwGOSUB; node.x.vgosub.ret_ip = prog_ip; code_jump_label(goto_label); @@ -973,25 +944,22 @@ void cmd_gosub() { * cmd_param is the first UDP/F's command * * @param cmd is the type of the udp (function or procedure) + * @param target sub/func + * @param return-variable ID */ -void cmd_udp(int cmd) { +addr_t cmd_push_args(int cmd, addr_t goto_addr, addr_t rvid) { stknode_t param; - addr_t pcount = 0, rvid; - var_t *arg = NULL; - byte ready, code; addr_t ofs; - var_t var_ptr; - addr_t goto_addr; - - goto_addr = code_getaddr(); // target sub/func - rvid = code_getaddr(); // return-variable ID + addr_t pcount = 0; + var_t *arg = NULL; if (code_peek() == kwTYPE_LEVEL_BEGIN) { - code_skipnext(); // kwTYPE_LEVEL_BEGIN (which means - // left-parenthesis) + // kwTYPE_LEVEL_BEGIN (which means left-parenthesis) + code_skipnext(); if (code_peek() == kwTYPE_CALL_PTR) { // replace call address with address in first arg + var_t var_ptr; code_skipnext(); v_init(&var_ptr); eval(&var_ptr); @@ -1003,93 +971,88 @@ void cmd_udp(int cmd) { rvid = var_ptr.v.ap.v; } - ready = 0; + byte ready = 0; do { - code = code_peek(); // get next BC + byte code = code_peek(); // get next BC switch (code) { - case kwTYPE_EOC: // end of an expression (parameter) - code_skipnext(); // ignore it + case kwTYPE_EOC: // end of an expression (parameter) + code_skipnext(); // ignore it break; - case kwTYPE_SEP: // separator (comma or semi-colon) - code_skipsep(); // ignore it + case kwTYPE_SEP: // separator (comma or semi-colon) + code_skipsep(); // ignore it break; - case kwTYPE_LEVEL_END: // (right-parenthesis) which means: end of - // parameters - ready = 1; // finish flag + case kwTYPE_LEVEL_END: // (right-parenthesis) which means: end of parameters + code_skipnext(); + if (code_peek() == kwTYPE_SEP) { + // another set of arguments - foo(a+1), (b+1) + code_skipsep(); + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + code_skipnext(); + } + } else { + ready = 1; // finish flag + } break; - case kwTYPE_VAR: // the parameter is a variable - ofs = prog_ip; // keep expression's IP - - if (code_isvar()) { // this parameter is a single variable (it is - // not an - // expression) - param.type = kwTYPE_VAR; // - param.x.param.res = code_getvarptr(); // var_t pointer; the variable - // itself - param.x.param.vcheck = 0x3; // parameter can be used 'by value' or - // 'by - // reference' - code_push(¶m); // push parameter + case kwTYPE_VAR: // the parameter is a variable + ofs = prog_ip; // keep expression's IP + if (code_isvar()) { // this parameter is a single variable (it is not an expression) + param.type = kwTYPE_VAR; + param.x.param.res = code_getvarptr(); // var_t pointer; the variable itself + param.x.param.vcheck = 0x3; // parameter can be used 'by value' or 'by reference' + code_push(¶m); // push parameter pcount++; - break; // we finished with this parameter + break; // we finished with this parameter } - prog_ip = ofs; // back to the start of the expression + prog_ip = ofs; // back to the start of the expression // now we are sure, this parameter is not a single variable - // no 'break' here default: - // *** default: the parameter is an expression *** - - arg = v_new(); // create a new temporary variable; it is the - // by-val value - // 'arg' will be freed at udp's return - - eval(arg); // execute the expression and store the result - // to 'arg' + // default: the parameter is an expression + arg = v_new(); // create a new temporary variable; it is the + // by-val value 'arg' will be freed at udp's return + eval(arg); // execute the expression and store the result to 'arg' if (!prog_error) { - param.type = kwTYPE_VAR; // + param.type = kwTYPE_VAR; param.x.param.res = arg; // var_t pointer; the variable itself - param.x.param.vcheck = 1; // parameter can be used only as 'by - // value' - code_push(¶m); // push parameter + param.x.param.vcheck = 1; // parameter can be used only as 'by value' + code_push(¶m); // push parameter pcount++; - } else { // error; clean up and return + } else { // error; clean up and return v_free(arg); tmp_free(arg); return; } - } - } while (!ready); - - // / - code_skipnext(); // right-parenthesis; kwTYPE_LEVEL_END } + // store call-info - param.type = cmd; // type of call (procedure or function) - param.x.vcall.pcount = pcount; // number of parameters which passed on - // (the number of - // parameter-nodes in the stack) - param.x.vcall.ret_ip = prog_ip; // where to go after exit (caller's - // next address) - param.x.vcall.rvid = rvid; // return-variable ID + param.type = cmd; // type of call (procedure or function) + param.x.vcall.pcount = pcount; // number parameter-nodes in the stack + param.x.vcall.ret_ip = prog_ip; // where to go after exit (caller's next address) + param.x.vcall.rvid = rvid; // return-variable ID - if (rvid != INVALID_ADDR) { // if we call a function + if (rvid != INVALID_ADDR) { + // if we call a function param.x.vcall.retvar = tvar[rvid]; // store previous data of RVID - tvar[rvid] = v_new(); // create a temporary variable to store the - // function's result + tvar[rvid] = v_new(); // create a temporary variable to store the function's result // value will be restored on udp-return } param.x.vcall.task_id = -1; - code_push(¶m); // store it to stack + code_push(¶m); // store it to stack + + return goto_addr; +} - prog_ip = goto_addr; // jump to udp's code +void cmd_udp(int cmd) { + addr_t goto_addr = code_getaddr(); + addr_t rvid = code_getaddr(); + prog_ip = cmd_push_args(cmd, goto_addr, rvid); } /** @@ -1102,122 +1065,103 @@ void cmd_udp(int cmd) { */ void cmd_call_unit_udp(int cmd, int udp_tid, addr_t goto_addr, addr_t rvid) { stknode_t param; - addr_t pcount = 0; - var_t *arg = NULL; - byte ready, code; addr_t ofs; - int my_tid; - - my_tid = ctask->tid; + var_t *arg = NULL; + addr_t pcount = 0; + int my_tid = ctask->tid; if (code_peek() == kwTYPE_LEVEL_BEGIN) { - code_skipnext(); // kwTYPE_LEVEL_BEGIN (which means - // left-parenthesis) + code_skipnext(); // kwTYPE_LEVEL_BEGIN (which means left-parenthesis) - ready = 0; + byte ready = 0; do { - code = code_peek(); // get next BC + byte code = code_peek(); // get next BC switch (code) { - case kwTYPE_EOC: // end of an expression (parameter) - code_skipnext(); // ignore it + case kwTYPE_EOC: // end of an expression (parameter) + code_skipnext(); // ignore it break; - case kwTYPE_SEP: // separator (comma or semi-colon) - code_skipsep(); // ignore it + case kwTYPE_SEP: // separator (comma or semi-colon) + code_skipsep(); // ignore it break; - case kwTYPE_LEVEL_END: // (right-parenthesis) which means: end of - // parameters - ready = 1; // finish flag + case kwTYPE_LEVEL_END: // (right-parenthesis) which means: end of parameters + code_skipnext(); + if (code_peek() == kwTYPE_SEP) { + // another set of arguments - foo(a+1), (b+1) + code_skipsep(); + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + code_skipnext(); + } + } else { + ready = 1; // finish flag + } break; - case kwTYPE_VAR: // the parameter is a variable - ofs = prog_ip; // keep expression's IP + case kwTYPE_VAR: // the parameter is a variable + ofs = prog_ip; // keep expression's IP - if (code_isvar()) { // this parameter is a single variable (it is - // not an - // expression) - param.type = kwTYPE_VAR; // + if (code_isvar()) { // this parameter is a single variable (not an expression) + param.type = kwTYPE_VAR; param.x.param.res = code_getvarptr(); // var_t pointer; the variable itself - param.x.param.vcheck = 0x3; // parameter can be used 'by value' or - // 'by - // reference' + param.x.param.vcheck = 0x3; // parameter can be used 'by value' or 'by reference' activate_task(udp_tid); - code_push(¶m); // push parameter, on unit's task + code_push(¶m); // push parameter, on unit's task activate_task(my_tid); pcount++; - break; // we finished with this parameter + break; // we finished with this parameter } - prog_ip = ofs; // back to the start of the expression + prog_ip = ofs; // back to the start of the expression // now we are sure, this parameter is not a single variable - // no 'break' here default: - // *** default: the parameter is an expression *** - - arg = v_new(); // create a new temporary variable; it is the - // by-val value - // 'arg' will be freed at udp's return - - eval(arg); // execute the expression and store the result - // to 'arg' + // default: the parameter is an expression + arg = v_new(); // create a new temporary variable; it is the by-val value + // 'arg' will be freed at udp's return + eval(arg); // execute the expression and store the result to 'arg' if (!prog_error) { - param.type = kwTYPE_VAR; // + param.type = kwTYPE_VAR; param.x.param.res = arg; // var_t pointer; the variable itself - param.x.param.vcheck = 1; // parameter can be used only as 'by - // value' - + param.x.param.vcheck = 1; // parameter can be used only as 'by value' activate_task(udp_tid); code_push(¶m); // push parameter, on unit's task activate_task(my_tid); - pcount++; - } else { // error; clean up and return + } else { // error; clean up and return v_free(arg); tmp_free(arg); return; } - } - } while (!ready); - - // / - code_skipnext(); // right-parenthesis; kwTYPE_LEVEL_END } - // + if (prog_error) { return; } // store call-info - activate_task(udp_tid); - // if (prog_error) { return; } - param.type = cmd; // type of call (procedure or function) + param.type = cmd; // type of call (procedure or function) param.x.vcall.pcount = pcount; // number of parameters which passed on - // (the number of - // parameter-nodes in the stack) - param.x.vcall.ret_ip = prog_ip; // where to go after exit (caller's - // next address) - param.x.vcall.rvid = rvid; // return-variable ID + // (the number of parameter-nodes in the stack) + param.x.vcall.ret_ip = prog_ip; // where to go after exit (caller's next address) + param.x.vcall.rvid = rvid; // return-variable ID - if (rvid != INVALID_ADDR) { // if we call a function + if (rvid != INVALID_ADDR) {// if we call a function param.x.vcall.retvar = tvar[rvid]; // store previous data of RVID - tvar[rvid] = v_new(); // create a temporary variable to store the - // function's result - // value will be restored on udp-return + tvar[rvid] = v_new(); // create a temporary variable to store the + // function's result value will be restored on udp-return } param.x.vcall.task_id = my_tid; - - code_push(¶m); // store it to stack, on unit's task + code_push(¶m); // store it to stack, on unit's task prog_ip = goto_addr + ADDRSZ + 3; // jump to udp's code } @@ -1228,14 +1172,11 @@ void cmd_call_unit_udp(int cmd, int udp_tid, addr_t goto_addr, addr_t rvid) { * The global's one. */ void cmd_crvar() { - int i, count; - addr_t vid; - stknode_t node; - - count = code_getnext(); // number of variables to create + int i; + int count = code_getnext(); // number of variables to create for (i = 0; i < count; i++) { - vid = code_getaddr(); // an ID on global-variable-table is used - // because ... it is a patch :( + addr_t vid = code_getaddr(); // an ID on global-variable-table is used + stknode_t node; // store previous variable to stack // we will restore it at 'return' @@ -1260,76 +1201,64 @@ void cmd_crvar() { * 'by reference' parameters are stored as local variables in the stack (kwTYPE_BYREF) */ void cmd_param() { - int i, pcount; - bid_t vid; - byte vattr; - stknode_t ncall, *param, node; - var_t *param_var; - - code_pop(&ncall); // get caller's info-node + stknode_t ncall; + code_pop(&ncall); // get caller's info-node if ((ncall.type != kwPROC) && (ncall.type != kwFUNC)) { err_stackmess(); return; } - pcount = code_getnext(); + int pcount = code_getnext(); - if (pcount != ncall.x.vcall.pcount) { // the number of the parameters that - // are - // required by this procedure/function + if (pcount != ncall.x.vcall.pcount) { + // the number of the parameters that are required by this procedure/function // are different from the number that was passed by the caller err_parm_num(); return; } - if (pcount) { // get parameters - param = (stknode_t *) tmp_alloc(sizeof(stknode_t) * pcount); + if (pcount) { // get parameters + int i; + stknode_t *param = (stknode_t *) tmp_alloc(sizeof(stknode_t) * pcount); for (i = pcount - 1; i > -1; i--) { code_pop(¶m[i]); } - code_push(&ncall); // push call's pars (again); we will needed at - // 'return' + code_push(&ncall); // push call's pars (again); we will needed at 'return' for (i = 0; i < pcount; i++) { // check parameters one-by-one - vattr = code_getnext(); - vid = code_getaddr(); - param_var = param[i].x.param.res; + byte vattr = code_getnext(); + bid_t vid = code_getaddr(); + var_t *param_var = param[i].x.param.res; + stknode_t node; - if ((vattr & 0x80) == 0) { // UDP requires a 'by value' parameter; - // any value is - // good + if ((vattr & 0x80) == 0) { // UDP requires a 'by value' parameter node.type = kwTYPE_CRVAR; node.x.vdvar.vid = vid; node.x.vdvar.vptr = tvar[vid]; - code_push(&node); // store previous variable (with the same ID) - // to stack + code_push(&node); // store previous variable (with the same ID) to stack // assign if (param[i].x.param.vcheck == 1) { - tvar[vid] = param_var; // its already cloned by the CALL - // (expr) + tvar[vid] = param_var; // its already cloned by the CALL (expr) } else { tvar[vid] = v_clone(param_var); } - } else { // UDP requires 'by reference' parameter + } else { // UDP requires 'by reference' parameter if (param[i].x.param.vcheck == 1) { - err_parm_byref(i); // error; the parameter can be used only 'by - // value' + err_parm_byref(i); // error; the parameter can be used only 'by value' break; } else { node.type = kwBYREF; node.x.vdvar.vid = vid; node.x.vdvar.vptr = tvar[vid]; - code_push(&node); // store previous variable to stack (with the - // same ID) + code_push(&node); // store previous variable to stack (with the same ID) tvar[vid] = param_var; // use the 'var_t' } } } - tmp_free(param); } else { - code_push(&ncall); // push caller's info node + code_push(&ncall); // push caller's info node } } @@ -1340,15 +1269,13 @@ void cmd_udpret() { stknode_t node, rval; code_pop(&node); - while ((node.type != kwPROC) && (node.type != kwFUNC)) { // pop from - // stack until - // caller's node found + while ((node.type != kwPROC) && (node.type != kwFUNC)) { + // pop from stack until caller's node found if (node.type == kwTYPE_CRVAR) { // local variable - cleanup v_free(tvar[node.x.vdvar.vid]); // free local variable data tmp_free(tvar[node.x.vdvar.vid]); - tvar[node.x.vdvar.vid] = node.x.vdvar.vptr; // restore ptr (replace - // to pre-call - // variable) + tvar[node.x.vdvar.vid] = node.x.vdvar.vptr; + // restore ptr (replace to pre-call variable) } else if (node.type == kwBYREF) { // variable 'by reference' tvar[node.x.vdvar.vid] = node.x.vdvar.vptr; // restore ptr } @@ -1364,8 +1291,8 @@ void cmd_udpret() { dump_stack(); } else { // restore return value - if (node.x.vcall.rvid != (bid_t) INVALID_ADDR) { // it is a function - // store value to stack + if (node.x.vcall.rvid != (bid_t) INVALID_ADDR) { + // it is a function store value to stack rval.type = kwTYPE_RET; rval.x.vdvar.vptr = tvar[node.x.vcall.rvid]; code_push(&rval); @@ -1728,8 +1655,8 @@ void cmd_for() { var_p_t var_elem_ptr = 0; switch (array_p->type) { - case V_HASH: - var_elem_ptr = hash_elem(array_p, 0); + case V_MAP: + var_elem_ptr = map_elem(array_p, 0); break; case V_ARRAY: @@ -1819,15 +1746,15 @@ void cmd_until() { stknode_t node; code_pop(&node); - code_skipaddr(); jump_ip = code_getaddr(); // expression v_init(&var); eval(&var); - if (!v_sign(&var)) + if (!v_sign(&var)) { code_jump(jump_ip); + } v_free(&var); } @@ -1907,23 +1834,22 @@ void cmd_next() { if (check) { code_push(&node); code_jump(jump_ip); - } else + } else { code_jump(next_ip); + } } - v_free(&var_to); } else { // // FOR [EACH] v1 IN v2 // - array_p = node.x.vfor.arr_ptr; var_elem_ptr = 0; switch (array_p->type) { - case V_HASH: + case V_MAP: node.x.vfor.step_expr_ip++; // element-index - var_elem_ptr = hash_elem(array_p, node.x.vfor.step_expr_ip); + var_elem_ptr = map_elem(array_p, node.x.vfor.step_expr_ip); break; case V_ARRAY: @@ -1955,7 +1881,6 @@ void cmd_next() { } else { code_jump(next_ip); } - } // clean up @@ -1989,7 +1914,6 @@ void cmd_read() { if (prog_error) { return; } - // //// if (!prog_error) { v_free(vp); @@ -2005,14 +1929,12 @@ void cmd_read() { break; case kwTYPE_INT: prog_dp++; - vp->type = V_INT; memcpy(&vp->v.i, prog_source + prog_dp,OS_INTSZ); prog_dp += OS_INTSZ; break; case kwTYPE_NUM: prog_dp++; - vp->type = V_NUM; memcpy(&vp->v.n, &prog_source[prog_dp], OS_REALSZ); prog_dp += OS_REALSZ; @@ -2020,7 +1942,6 @@ void cmd_read() { case kwTYPE_STR: { dword len; prog_dp++; - vp->type = V_STR; memcpy(&len, prog_source + prog_dp, OS_STRLEN); prog_dp += OS_STRLEN; @@ -2036,10 +1957,9 @@ void cmd_read() { rt_raise(ERR_READ_DATA_INDEX_FMT, prog_dp); return; } - - if (prog_source[prog_dp] == kwTYPE_EOC) + if (prog_source[prog_dp] == kwTYPE_EOC) { prog_dp++; - + } } }; } while (!exitf); @@ -2049,8 +1969,7 @@ void cmd_read() { * DATA ... */ void cmd_data() { - rt_raise("CANNOT EXECUTE DATA"); // if you see it, I did something - // stupid + rt_raise("CANNOT EXECUTE DATA"); } /** @@ -2064,10 +1983,9 @@ void cmd_restore() { * RANDOMIZE [num] */ void cmd_randomize() { - byte code; long seed; - code = code_peek(); + byte code = code_peek(); switch (code) { case kwTYPE_LINE: case kwTYPE_EOC: @@ -2085,9 +2003,7 @@ void cmd_randomize() { * DELAY */ void cmd_delay() { - dword ms; - - ms = par_getint(); + dword ms = par_getint(); if (prog_error) { return; } @@ -2128,10 +2044,9 @@ void cmd_locate() { */ void cmd_pause() { int x = 0, evc; - byte code; long start, now; - code = code_peek(); + byte code = code_peek(); if (code == kwTYPE_VAR) { x = par_getint(); if (prog_error) { @@ -2221,84 +2136,6 @@ void cmd_color() { dev_settextcolor(fg, bg); } -/* - * SPLIT string, delimiters, array() - void cmd_split() - { - int count, i; - char *p, *ps, *new_text; - var_t *var_p, *elem_p; - addr_t use_ip, exit_ip = INVALID_ADDR; - char *str = NULL, *del = NULL; - - par_massget("SSP", &str, &del, &var_p); - - if ( !prog_error ) { - // is there a use keyword ? - if ( code_peek() == kwUSE ) { - code_skipnext(); - use_ip = code_getaddr(); - exit_ip = code_getaddr(); - } - else - use_ip = INVALID_ADDR; - - // - v_toarray1(var_p, 1); - - // reformat - new_text = tmp_strdup(str); - count = 0; - ps = p = new_text; - while ( *p ) { - if ( strchr(del, *p) ) { - *p = '\0'; - - // add element (ps) - if ( var_p->v.a.size <= count ) // resize array - v_resize_array(var_p, count+16); - - // store string - elem_p = v_elem(var_p, count); - v_setstr(elem_p, ps); - count ++; - - // next word - ps = p+1; - } - - p ++; - } - - if ( *ps ) { - // add the last element (ps) - if ( v_asize(var_p) <= count ) // resize array - v_resize_array(var_p, count+1); - - elem_p = v_elem(var_p, count); - v_setstr(elem_p, ps); - - count ++; - } - v_resize_array(var_p, count); // final resize - - // execute user's expression for each element - if ( use_ip != INVALID_ADDR ) { - for ( i = 0; i < v_asize(var_p) && !prog_error; i ++ ) { - elem_p = v_elem(var_p, i); - exec_usefunc(elem_p, use_ip); - } - // jmp to correct location - code_jump(exit_ip); - } - - // cleanup - pfree2(str, del); - tmp_free(new_text); - } - } - */ - void cmd_split() { cmd_wsplit(); } @@ -2330,9 +2167,7 @@ void cmd_wsplit() { } v_toarray1(var_p, 1); - /* - * reformat - */ + // reformat new_text = tmp_strdup(str); count = 0; wait_q = 0; @@ -2457,8 +2292,9 @@ void cmd_wjoin() { strcat((char *) str->v.p.ptr, (char *) e_str.v.p.ptr); v_free(&e_str); - if (i != var_p->v.p.size - 1) + if (i != var_p->v.p.size - 1) { strcat((char *) str->v.p.ptr, (char *) del.v.p.ptr); + } } // todo: realloc down or not @@ -2606,40 +2442,6 @@ int sb_qcmp(var_t * a, var_t * b, addr_t use_ip) { if (use_ip == INVALID_ADDR) { return v_compare(a, b); } else { - /* - * int r; - */ - /* - * var_t result; - */ - /* - * v_set(tvar[SYSVAR_X], a); - */ - /* - * v_set(tvar[SYSVAR_Y], b); - */ - /* - * code_jump(use_ip); - */ - /* - * // evaluate the function result left on the stack - */ - /* - * v_init(&result); - */ - /* - * eval(&result); - */ - /* - * r = v_igetval(&result); - */ - /* - * v_free(&result); - */ - /* - * return r; - */ - var_t v1, v2; int r; @@ -2696,10 +2498,6 @@ void cmd_sort() { } // NO RTE anymore... there is no meaning on this because of empty // arrays/variables (example: TLOAD "data", V:SORT V) - // else - // rt_raise("SORT: Not an array"); - - // return if (exit_ip != INVALID_ADDR) { code_jump(exit_ip); } @@ -2791,9 +2589,9 @@ void cmd_search() { void cmd_swap(void) { var_t *va, *vb, *vc; - if (code_isvar()) + if (code_isvar()) { va = code_getvarptr(); - else { + } else { err_typemismatch(); return; } @@ -2862,26 +2660,6 @@ void cmd_exprseq(void) { } } -/** - * IMAGE #handle, index, x, y [,sx,sy [,w,h]] - * Display html text - */ -void cmd_image() { - var_int_t h, i, x, y; - var_int_t sx, sy, iw, ih; - sx = sy = iw = ih = 0; - - par_getsharp(); - if (prog_error) { - return; - } - - par_massget("IIIIiiii", &h, &i, &x, &y, &sx, &sy, &iw, &ih); - if (!prog_error) { - dev_image(h, i, x, y, sx, sy, iw, ih); - } -} - /** * evaluate the select expression and then store it on the stack * syntax is: @@ -2896,9 +2674,8 @@ void cmd_image() { */ void cmd_select() { stknode_t node; - var_t *expr; - expr = v_new(); + var_t *expr = v_new(); v_init(expr); eval(expr); @@ -2914,17 +2691,14 @@ void cmd_select() { * which could either be another case line or "end select" */ void cmd_case() { - stknode_t *node; - addr_t true_ip, false_ip; var_t var_p; - - true_ip = code_getaddr(); // matching case - false_ip = code_getaddr(); // non-matching case + addr_t true_ip = code_getaddr(); // matching case + addr_t false_ip = code_getaddr(); // non-matching case v_init(&var_p); eval(&var_p); - node = code_stackpeek(); + stknode_t *node = code_stackpeek(); if (node->type != kwSELECT) { rt_raise(ERR_SYNTAX); @@ -2947,12 +2721,9 @@ void cmd_case() { * skip to cmd_end_select if a previous case was true */ void cmd_case_else() { - stknode_t *node; - addr_t true_ip, false_ip; - - true_ip = code_getaddr(); // default block - false_ip = code_getaddr(); // end-select - node = code_stackpeek(); + addr_t true_ip = code_getaddr(); // default block + addr_t false_ip = code_getaddr(); // end-select + stknode_t *node = code_stackpeek(); code_jump(node->x.vcase.flags ? false_ip : true_ip); } @@ -2998,14 +2769,31 @@ void cmd_definekey(void) { v_free(&var); } +/** + * Handler for unthrown/uncaught exceptions + */ void cmd_catch() { addr_t end_try_ip = code_getaddr(); addr_t outer_catch_ip = code_getaddr(); - // cleanup the catch address, then skip to end-try + // skip level code + prog_ip += 1; + + // skip to end-try code_jump(end_try_ip); // restore outer try/catch level prog_catch_ip = outer_catch_ip; } +/** + * Call to object method + */ +void cmd_call_vfunc() { + var_t *v_func = code_getvarptr_parens(1); + if (v_func == NULL || v_func->type != V_FUNC) { + sc_raise(ERR_NO_FUNC); + } else { + v_func->v.fn.cb(v_func->v.fn.self); + } +} diff --git a/src/common/blib.h b/src/common/blib.h index 4d9da71f..bd52fa66 100644 --- a/src/common/blib.h +++ b/src/common/blib.h @@ -72,6 +72,7 @@ void cmd_poke32(void); void cmd_bcopy(void); void cmd_calladr(void); void cmd_catch(); +void cmd_call_vfunc(); var_num_t cmd_math1(long funcCode, var_t *arg); var_int_t cmd_imath1(long funcCode, var_t *arg); @@ -167,7 +168,6 @@ void cmd_dirwalk(void); void cmd_bputc(void); void cmd_bload(void); void cmd_bsave(void); -void cmd_image(void); void cmd_definekey(void); /** diff --git a/src/common/blib_func.c b/src/common/blib_func.c index c750c6eb..dad968f9 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -225,7 +225,7 @@ void date_str2dmy(char *str, long *d, long *m, long *y) { } /* - * TIME hh:mm:ss string to ints + * TIME hh:mm:ss string to ints */ void date_str2hms(char *str, long *h, long *m, long *s) { char *p; @@ -819,7 +819,7 @@ var_int_t cmd_imath1(long funcCode, var_t *arg) { // // i|f <- FUNC (str) // -void cmd_ns1(long funcCode, var_t * arg, var_t * r) { +void cmd_ns1(long funcCode, var_t *arg, var_t *r) { IF_ERR_RETURN; if (arg->type != V_STR) { v_tostr(arg); @@ -898,7 +898,7 @@ void cmd_ns1(long funcCode, var_t * arg, var_t * r) { // // str <- FUNC (any) // -void cmd_str1(long funcCode, var_t * arg, var_t * r) { +void cmd_str1(long funcCode, var_t *arg, var_t *r) { byte *p, *wp; byte *tb; var_int_t l, i; @@ -930,15 +930,7 @@ void cmd_str1(long funcCode, var_t * arg, var_t * r) { // // str <- STR$(n) // - r->v.p.ptr = tmp_alloc(64); - if (arg->type == V_INT) { - ltostr(arg->v.i, (char *) r->v.p.ptr); - } else if (arg->type == V_NUM) { - ftostr(arg->v.n, (char *) r->v.p.ptr); - } else if (arg->type == V_STR) { - tmp_free(r->v.p.ptr); - r->v.p.ptr = (byte *) tmp_strdup((char *)arg->v.p.ptr); - } + r->v.p.ptr = v_str(arg); r->v.p.size = strlen((char *) r->v.p.ptr) + 1; break; case kwCBS: @@ -959,8 +951,7 @@ void cmd_str1(long funcCode, var_t * arg, var_t * r) { // convert BASIC-Style string to C-style string // v_tostr(arg); - IF_ERR_RETURN - ; + IF_ERR_RETURN; r->v.p.ptr = (byte *) bstrdup((char *) arg->v.p.ptr); r->v.p.size = strlen((char *) r->v.p.ptr) + 1; @@ -1217,9 +1208,9 @@ void cmd_str1(long funcCode, var_t * arg, var_t * r) { } // -// str <- FUNC (void) +// str <- FUNC (void) // -void cmd_str0(long funcCode, var_t * r) { +void cmd_str0(long funcCode, var_t *r) { word ch; char tmp[3]; struct tm tms; @@ -1235,9 +1226,8 @@ void cmd_str0(long funcCode, var_t * r) { dev_events(2); } if (dev_kbhit()) { - ch = dev_getch(); // MultiByte - dev_getchr() must return the - // extended code (2 - // bytes char) + ch = dev_getch(); + // MultiByte - dev_getchr() must return the extended code (2 bytes char) if ((ch & 0xFF00) == 0xFF00) { // extra code - hardware keys tmp[0] = '\033'; tmp[1] = ch & 0xFF; @@ -1288,10 +1278,10 @@ void cmd_str0(long funcCode, var_t * r) { tmp[1] = '\0'; }; } - v_createstr(r, tmp); - } else + } else { v_createstr(r, ""); + } break; case kwDATE: // @@ -1323,7 +1313,7 @@ void cmd_str0(long funcCode, var_t * r) { // // str <- FUNC (...) // -void cmd_strN(long funcCode, var_t * r) { +void cmd_strN(long funcCode, var_t *r) { var_t arg1; var_int_t i, count, lsrc, len, start, pc; char tmp[2], *tmp_p; @@ -1756,10 +1746,23 @@ void cmd_strN(long funcCode, var_t * r) { pfree3(s1, s2, s3); } +void cmd_is_var_type(byte type, var_t *arg1, var_t *r) { + var_t *var_p; + if (code_isvar()) { + var_p = code_getvarptr(); + } else { + eval(arg1); + var_p = arg1; + } + if (!prog_error) { + r->v.i = (var_p->type == type); + } +} + // // int <- FUNC (...) // -void cmd_intN(long funcCode, var_t * r) { +void cmd_intN(long funcCode, var_t *r) { char *s1 = NULL, *s2 = NULL, *s3 = NULL; var_int_t start; @@ -1806,19 +1809,13 @@ void cmd_intN(long funcCode, var_t * r) { } // error break; case kwISARRAY: - // - // bool <- ISARRAY(v) - // - if (code_isvar()) { - var_p = code_getvarptr(); - } else { - eval(&arg1); - var_p = &arg1; - } - - if (!prog_error) { - r->v.i = (var_p->type == V_ARRAY); - } + cmd_is_var_type(V_ARRAY, &arg1, r); + break; + case kwISMAP: + cmd_is_var_type(V_MAP, &arg1, r); + break; + case kwISREF: + cmd_is_var_type(V_REF, &arg1, r); break; case kwISSTRING: // @@ -1989,7 +1986,6 @@ void cmd_intN(long funcCode, var_t * r) { } } - // //// switch (code) { case 1: r->v.i = (r2int(rc * 255.0, 0, 255) << 16) | (r2int(gc * 255.0, 0, 255) << 8) @@ -2006,43 +2002,6 @@ void cmd_intN(long funcCode, var_t * r) { } break; - case kwIMGW: { - // image width - int h, i; - par_getsharp(); - IF_ERR_RETURN; - - h = par_getint(); - IF_ERR_RETURN; - - par_getcomma(); - IF_ERR_RETURN; - - i = par_getint(); - IF_ERR_RETURN; - r->v.i = dev_image_width(h, i); - } - break; - - case kwIMGH: { - // image height - int h, i; - par_getsharp(); - IF_ERR_RETURN; - - h = par_getint(); - IF_ERR_RETURN; - - par_getcomma(); - IF_ERR_RETURN; - - i = par_getint(); - IF_ERR_RETURN; - - r->v.i = dev_image_height(h, i); - } - break; - default: rt_raise("Unsupported built-in function call %ld, please report this bug (9)", funcCode); } @@ -2117,7 +2076,7 @@ void cmd_numN(long funcCode, var_t *r) { /* * any <- FUNC (...) */ -void cmd_genfunc(long funcCode, var_t * r) { +void cmd_genfunc(long funcCode, var_t *r) { byte code, ready, first; int count, tcount, handle, i, len, ch; addr_t ofs; @@ -2191,14 +2150,13 @@ void cmd_genfunc(long funcCode, var_t * r) { case V_STR: format_str(buf, (char *) arg.v.p.ptr, (char *) arg2.v.p.ptr); case V_INT: - if (arg2.type == V_INT - ) + if (arg2.type == V_INT) { format_num(buf, (char *) arg.v.p.ptr, arg2.v.i); + } case V_NUM: - if (arg2.type == V_NUM - ) + if (arg2.type == V_NUM) { format_num(buf, (char *) arg.v.p.ptr, arg2.v.n); - + } r->type = V_STR; r->v.p.size = strlen(buf) + 1; r->v.p.ptr = tmp_alloc(r->v.p.size); diff --git a/src/common/blib_ui.c b/src/common/blib_ui.c index ae4a0b33..d554d349 100644 --- a/src/common/blib_ui.c +++ b/src/common/blib_ui.c @@ -7,322 +7,25 @@ // // Copyright(C) 2000 Nicholas Christopoulos -#include "common/sys.h" -#include "common/var.h" -#include "common/kw.h" -#include "common/pproc.h" #include "common/blib_ui.h" -#include "common/device.h" -#include "common/smbas.h" +#include "common/sberr.h" // check if driver implements the UI api #ifndef IMPL_UI -#define UI_TITLE 1 -#define UI_FRAME 2 -#define UI_CLOSEBUTTON 4 - -// type of the node -typedef enum { - ui_button, ui_text -} ui_type_t; - -// button/prompt data -typedef struct { - char *text; - int flags; - int x, y, w, h; -} ui_button_t; - -// simple text -typedef struct { - char *text; - int flags; - int x, y, w, h; -} ui_text_t; - -// ui node -typedef struct { - ui_type_t type; - int state; - union { - ui_button_t button; - ui_text_t text; - } args; -} ui_node_t; - -#define UI_MAX_ELEMENTS 256 -static ui_node_t ui_list[UI_MAX_ELEMENTS]; -static int ui_count; -static int ui_nx, ui_ny; -static int ui_curel; - -/** - * close/reset ui data - */ -void ui_reset() { - int i; - - for (i = 0; i < ui_count; i++) { - switch (ui_list[i].type) { - case ui_button: - tmp_free(ui_list[i].args.button.text); - break; - case ui_text: - tmp_free(ui_list[i].args.text.text); - break; - } - } - - ui_curel = 0; - ui_count = 0; - ui_nx = ui_ny = 0; -} - -/** - * - */ -void ui_fix_cs2(int *x, int *y) { - if (opt_uipos) { // in chars - if (os_graphics) { - *x *= dev_textwidth("0"); - *y *= dev_textheight("0"); - } - } -} - -/** - * add a button - */ -void ui_add_button(const char *text, int x, int y) { - ui_node_t *node; - - ui_fix_cs2(&x, &y); - - if (ui_count == UI_MAX_ELEMENTS - ) - rt_raise("UI: TOO MANY ELEMENTS"); - node = &ui_list[ui_count]; - node->type = ui_button; - - // - if (x == -1) - node->args.button.x = ui_nx; - else - node->args.button.x = x; - if (y == -1) - node->args.button.y = ui_ny; - else - node->args.button.y = y; - - node->args.button.text = tmp_alloc(strlen(text) + 1); - strcpy(node->args.button.text, text); - node->args.button.flags = UI_FRAME; - - node->args.button.w = dev_textwidth(text); - node->args.button.h = dev_textheight(text); - // - ui_ny = y + node->args.button.h; - ui_count++; -} - -/** - * add a prompt - */ -void ui_add_prompt(const char *text, int x, int y) { - ui_node_t *node; - - ui_fix_cs2(&x, &y); - - if (ui_count == UI_MAX_ELEMENTS - ) - rt_raise("UI: TOO MANY ELEMENTS"); - node = &ui_list[ui_count]; - node->type = ui_button; - - // - if (x == -1) - node->args.button.x = ui_nx; - else - node->args.button.x = x; - if (y == -1) - node->args.button.y = ui_ny; - else - node->args.button.y = y; - - node->args.button.text = tmp_alloc(strlen(text) + 1); - strcpy(node->args.button.text, text); - node->args.button.flags = 0; - - node->args.button.w = dev_textwidth(text); - node->args.button.h = dev_textheight(text); - - // - ui_ny = y + node->args.button.h; - ui_count++; -} - -/** - * add a text - */ -void ui_add_text(const char *text, int x, int y) { - ui_node_t *node; - - ui_fix_cs2(&x, &y); - - if (ui_count == UI_MAX_ELEMENTS - ) - rt_raise("UI: TOO MANY ELEMENTS"); - node = &ui_list[ui_count]; - node->type = ui_text; - - // - if (x == -1) - node->args.text.x = ui_nx; - else - node->args.text.x = x; - if (y == -1) - node->args.text.y = ui_ny; - else - node->args.text.y = y; - - node->args.text.text = tmp_alloc(strlen(text) + 1); - strcpy(node->args.text.text, text); - node->args.text.flags = 0; - - node->args.text.w = dev_textwidth(text); - node->args.text.h = dev_textheight(text); - - // - ui_ny = y + node->args.text.h; - ui_count++; +void ui_reset(void) { } -/* -------------------------------------------------------------------------- */ - -/** - * draw button - */ -void ui_draw_button(ui_button_t * args) { - int x1, y1, x2, y2; - - if (!os_graphics) { - x1 = args->x - 1; - y1 = args->y - 1; - x2 = args->x + args->w; - y2 = args->y + args->h; - - dev_rect(x1, y1, x2, y2, 0); - dev_setxy(args->x, args->y, 0); - dev_print(args->text); - return; - } - - x1 = args->x - 2; - y1 = args->y - 2; - x2 = args->x + args->w; - y2 = args->y + args->h; - x1 -= 2; - x2 += 2; - - dev_setcolor(15); - dev_rect(x1, y1, x2, y2, 1); - dev_setcolor(0); - dev_rect(x1, y1, x2, y2, 0); - dev_line(x1 + 1, y2 + 1, x2 + 1, y2 + 1); - dev_line(x2 + 1, y1 + 1, x2 + 1, y2 + 1); - dev_settextcolor(0, 15); - dev_setxy(args->x, args->y, 0); - dev_print(args->text); -} - -/** - * draw text - */ -void ui_draw_text(ui_text_t * args) { - dev_setxy(args->x, args->y, 0); - dev_print(args->text); +void cmd_button(void) { + err_unsup(); } -/** - * draw form - */ -void ui_draw_all() { - int i; - - for (i = 0; i < ui_count; i++) { - switch (ui_list[i].type) { - case ui_button: - ui_draw_button(&ui_list[i].args.button); - break; - case ui_text: - ui_draw_text(&ui_list[i].args.text); - break; - } - } -} - -/** - * execute it - */ -void ui_exec() { - long int key; - - ui_draw_all(); - do { - key = dev_getch(); - switch (key) { - case -2: - case -1: - case SB_KEY_BREAK: // for FRANKLIN_EBM - return; // BREAK - case '\t': // next element - ui_curel++; - if (ui_curel >= ui_count) - ui_curel = 0; - break; - }; - } while (key != 27 && key != SB_KEY_ALT('x')); +void cmd_text(void) { + err_unsup(); } -// -// BUTTON x, y, text -// -void cmd_button() { - int32 x, y; - char *s = NULL; - - par_massget("IIS", &x, &y, &s); - if (!prog_error) - ui_add_button(s, x, y); - - // cleanup - if (s) - pfree(s); -} - -// -// TEXT x, y, var$ -// -void cmd_text() { - int32 x, y; - char *s = NULL; - - par_massget("IIS", &x, &y, &s); - if (!prog_error) - ui_add_text(s, x, y); - - // cleanup - if (s) - pfree(s); -} - -// -// DOFORM -// -void cmd_doform() { - ui_exec(); - ui_reset(); +void cmd_doform(void) { + err_unsup(); } #endif diff --git a/src/common/brun.c b/src/common/brun.c index 8e4fcddd..5c29e174 100755 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -158,6 +158,12 @@ void code_pop_and_free(stknode_t *node) { tmp_free(cur_node->x.vfor.arr_ptr); } } + break; + + case kwSELECT: + v_free(cur_node->x.vcase.var_ptr); + tmp_free(cur_node->x.vcase.var_ptr); + break; } } else { if (node) { @@ -190,219 +196,6 @@ stknode_t *code_stackpeek() { return NULL; } -/** - * Convertion multi-dim index to one-dim index - */ -addr_t getarrayidx(var_t *array, var_t **var_hash_val) { - addr_t idx = 0; - addr_t lev = 0; - addr_t m = 0; - byte code; - var_t var; - addr_t idim; - addr_t i; - - do { - v_init(&var); - eval(&var); - IF_ERR_RETURN_0; - - if (var.type == V_STR || array->type == V_HASH) { - // array element is a string or element is addressing a hash - hash_get_value(array, &var, var_hash_val); - - if (code_peek() == kwTYPE_LEVEL_END) { - code_skipnext(); - } else { - err_missing_sep(); - } - v_free(&var); - return 0; - } else { - idim = v_getint(&var); - v_free(&var); - IF_ERR_RETURN_0; - - idim = idim - array->v.a.lbound[lev]; - - m = idim; - for (i = lev + 1; i < array->v.a.maxdim; i++) { - m = m * (ABS(array->v.a.ubound[i] - array->v.a.lbound[i]) + 1); - } - idx += m; - - // skip separator - code = code_peek(); - if (code == kwTYPE_SEP) { - code_skipnext(); - if (code_getnext() != ',') { - err_syntax_error(); - } - } - // next - lev++; - } - } while (code_peek() != kwTYPE_LEVEL_END); - - if (!prog_error) { - if ((int) array->v.a.maxdim != lev) { - err_missing_sep(); - } - } - return idx; -} - -/** - * Used by code_getvarptr() to retrieve an element ptr of an array - */ -var_t *code_getvarptr_arridx(var_t *basevar_p) { - addr_t array_index; - var_t *var_p = NULL; - - if (code_peek() != kwTYPE_LEVEL_BEGIN) { - err_arrmis_lp(); - } else { - code_skipnext(); // '(' - array_index = getarrayidx(basevar_p, &var_p); - - if (var_p != NULL) { - // hash map value - return var_p; - } - - if (!prog_error) { - if ((int) array_index < basevar_p->v.a.size && (int) array_index >= 0) { - var_p = (var_t *)(basevar_p->v.a.ptr + (array_index * sizeof(var_t))); - - if (code_peek() == kwTYPE_LEVEL_END) { - code_skipnext(); // ')', ')' level - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - // there is a second array inside - if (var_p->type != V_ARRAY) { - err_varisnotarray(); - } else { - return code_getvarptr_arridx(var_p); - } - } - } else { - err_arrmis_rp(); - } - } else { - err_arridx(array_index, basevar_p->v.a.size); - } - } - } - - return var_p; -} - -/** - * resolve a composite variable reference, eg: ar.ch(0).foo - */ -var_t *code_resolve_varptr(var_t *var_p, int until_parens) { - if (var_p && var_p->type == V_REF) { - var_p = eval_ref_var(var_p); - } - if (var_p) { - switch (code_peek()) { - case kwTYPE_LEVEL_BEGIN: - if (!until_parens) { - var_p = code_resolve_varptr(code_getvarptr_arridx(var_p), until_parens); - } - break; - case kwTYPE_UDS_EL: - var_p = code_resolve_varptr(hash_resolve_fields(var_p), until_parens); - break; - } - } - return var_p; -} - -/** - * Used by code_isvar() to retrieve an element ptr of an array - */ -var_t *code_isvar_arridx(var_t *basevar_p) { - addr_t array_index; - var_t *var_p = NULL; - - if (code_peek() != kwTYPE_LEVEL_BEGIN) { - return NULL; - } else { - code_skipnext(); // '(' - array_index = getarrayidx(basevar_p, &var_p); - - if (var_p != NULL) { - // hash map value - return var_p; - } - - if (!prog_error) { - if ((int) array_index < basevar_p->v.a.size) { - var_p = (var_t *)(basevar_p->v.a.ptr + (array_index * sizeof(var_t))); - - if (code_peek() == kwTYPE_LEVEL_END) { - code_skipnext(); // ')', ')' level - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - // there is a second array inside - if (var_p->type != V_ARRAY) { - return NULL; - } else { - return code_isvar_arridx(var_p); - } - } - } else { - return NULL; - } - } else { - return NULL; - } - } - } - - return var_p; -} - -/** - * returns true if the next code is a variable. - * if the following code is an expression (no matter if the first item is a variable), - * returns false - */ -int code_isvar() { - var_t *basevar_p; - var_t *var_p = NULL; - - // store IP - addr_t cur_ip = prog_ip; - - if (code_peek() == kwTYPE_VAR) { - code_skipnext(); - var_p = basevar_p = tvar[code_getaddr()]; - switch (basevar_p->type) { - case V_HASH: - case V_ARRAY: - // variable is an array or hash - var_p = code_resolve_varptr(var_p, 0); - break; - default: - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - var_p = NULL; - } - } - } - - if (var_p) { - if (kw_check_evexit(code_peek()) || code_peek() == kwTYPE_LEVEL_END) { - // restore IP - prog_ip = cur_ip; - return 1; - } - } - - // restore IP - prog_ip = cur_ip; - return 0; -} - /** * sets the value of an integer system-variable */ @@ -908,15 +701,15 @@ static inline void bc_loop_call_proc() { // end of program prog_error = -1; break; - case kwIMAGE: - cmd_image(); - break; case kwDEFINEKEY: cmd_definekey(); break; case kwSHOWPAGE: dev_show_page(); break; + case kwTYPE_CALL_VFUNC: + cmd_call_vfunc(); + break; default: err_pcode_err(pcode); } @@ -1291,6 +1084,9 @@ void bc_loop(int isf) { rt_raise("COMMAND SEPARATOR '%c' FOUND!", prog_source[prog_ip + 1]); } else { rt_raise("PARAM COUNT ERROR @%d=%X %d", prog_ip, prog_source[prog_ip], code); + if (!opt_quiet) { + hex_dump(prog_source, prog_length); + } } } else { prog_ip++; diff --git a/src/common/ceval.c b/src/common/ceval.c index 0b802555..efdf9f3e 100644 --- a/src/common/ceval.c +++ b/src/common/ceval.c @@ -87,6 +87,7 @@ void cev_prim_var() { } else { cev_add1(kwTYPE_LEVEL_END); IP++; + cev_prim_uds(); } } } diff --git a/src/common/device.c b/src/common/device.c index d0972155..2b652d18 100644 --- a/src/common/device.c +++ b/src/common/device.c @@ -10,6 +10,7 @@ #include "common/device.h" #include "common/smbas.h" #include "common/messages.h" +#include "common/keymap.h" // add-on drivers #if defined(DRV_SOUND) @@ -698,14 +699,22 @@ void log_printf(const char *format, ...) { } #ifndef IMPL_IMAGE -void dev_image(int handle, int index, int x, int y, int sx, int sy, int w, int h) { +int dev_image_load(int handle) { + return -1; } -int dev_image_width(int handle, int index) { +int dev_image_width(int handle) { return -1; } -int dev_image_height(int handle, int index) { + +int dev_image_height(int handle) { return -1; } + +void dev_image_show(var_image *image) { +} + +void dev_image_hide(int handle) { +} #endif diff --git a/src/common/device.h b/src/common/device.h index b07ef6ab..4029c15b 100644 --- a/src/common/device.h +++ b/src/common/device.h @@ -1040,55 +1040,68 @@ char_p_t *dev_create_file_list(const char *wc, int *count); */ void dev_destroy_file_list(char_p_t *list, int count); +typedef struct var_image { + int x; + int y; + int offsetTop; + int offsetLeft; + int width; + int height; + int zIndex; + int opacity; + int handle; + int id; +} var_image; + /** - * @html + * @image * - * displays html text + * loads an image * - * @param html code - * @param title display title - * @param x window coordinates - * @param y - * @param w - * @param h + * @param file handle * */ -void dev_html(const char *html, const char *title, int x, int y, int w, int h); +int dev_image_load(int handle); /** * @image * - * displays an image from an image library + * return the width of an image in an image library * * @param file handle - * @param image index - * @param x window coordinates - * @param y * */ -void dev_image(int handle, int index, int x, int y, int sx, int sy, int w, int h); +int dev_image_width(int handle); /** - * @imagew + * @image * - * return the width of an image in an image library + * return the height of an image in an image library * * @param file handle - * @param image index * */ -int dev_image_width(int handle, int index); +int dev_image_height(int handle); /** - * @imageh + * @image * - * return the height of an image in an image library + * displays the image + * + * @param file handle + * + */ +void dev_image_show(var_image *image); + +/** + * @image + * + * hides the image * * @param file handle - * @param image index * */ -int dev_image_height(int handle, int index); +void dev_image_hide(int handle); #if defined(__cplusplus) } diff --git a/src/common/eval.c b/src/common/eval.c index 5539746a..32e2a56b 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -15,13 +15,16 @@ #include "common/blib.h" #include "common/device.h" #include "common/extlib.h" +#include "common/var_eval.h" #define IP prog_ip #define CODE(x) prog_source[(x)] #define CODE_PEEK() CODE(IP) -#define V_FREE(v) \ - if ((v) && ((v)->type == V_STR || (v)->type == V_ARRAY)) { \ - v_free((v)); \ +#define V_FREE(v) \ + if ((v) && ((v)->type == V_STR || \ + (v)->type == V_MAP || \ + (v)->type == V_ARRAY)) { \ + v_free((v)); \ } /** @@ -651,21 +654,6 @@ static inline void eval_shortc(var_t *r, addr_t addr, byte op) { } } -var_t *eval_ref_var(var_t *var_p) { - var_t *result = var_p; - while (result->type == V_REF) { - if (result->v.ref == var_p) { - // circular referance error - result = NULL; - err_ref_var(); - break; - } else { - result = result->v.ref; - } - } - return result; -} - static inline void eval_var(var_t *r, var_t *var_p) { var_t *var_deref; switch (var_p->type) { @@ -684,7 +672,7 @@ static inline void eval_var(var_t *r, var_t *var_p) { break; case V_STR: case V_ARRAY: - case V_HASH: + case V_MAP: v_set(r, var_p); break; case V_REF: @@ -892,12 +880,12 @@ static inline void eval_callf(var_t *r) { case kwLEN: case kwEMPTY: case kwISARRAY: + case kwISMAP: + case kwISREF: case kwISNUMBER: case kwISSTRING: case kwRGB: case kwRGBF: - case kwIMGW: - case kwIMGH: // int FUNC(...) V_FREE(r); if (CODE_PEEK() != kwTYPE_LEVEL_BEGIN) { @@ -1103,6 +1091,30 @@ static inline void eval_callf(var_t *r) { r->v.i = dev_freefilehandle(); break; + case kwARRAY: + V_FREE(r); + map_from_str(r); + break; + + case kwIMAGE: + V_FREE(r); + if (CODE_PEEK() != kwTYPE_LEVEL_BEGIN) { + err_missing_lp(); + } else { + IP++; + par_getsharp(); + if (!prog_error) { + int handle = par_getint(); + if (!prog_error) { + var_create_image(r, handle); + if (!prog_error && CODE_PEEK() == kwTYPE_LEVEL_END) { + IP++; + } + } + } + } + break; + default: err_bfn_err(fcode); } @@ -1298,7 +1310,9 @@ void eval(var_t *r) { return; } rt_raise("UNKNOWN ERROR. IP:%d=0x%02X", IP, code); - hex_dump(prog_source, prog_length); + if (!opt_quiet) { + hex_dump(prog_source, prog_length); + } }; // run-time error check diff --git a/src/common/file.c b/src/common/file.c index cd4765d7..00794830 100644 --- a/src/common/file.c +++ b/src/common/file.c @@ -10,7 +10,6 @@ #include "common/sys.h" #include "common/device.h" #include "common/pproc.h" -#include "common/match.h" #include "common/extlib.h" #include "common/messages.h" diff --git a/src/common/fs_serial.c b/src/common/fs_serial.c index 725d53f7..d590a065 100644 --- a/src/common/fs_serial.c +++ b/src/common/fs_serial.c @@ -10,7 +10,6 @@ #include "common/sys.h" #include "common/device.h" #include "common/pproc.h" -#include "common/match.h" #include #include diff --git a/src/common/fs_stream.c b/src/common/fs_stream.c index 1d0af4cb..b10ca536 100644 --- a/src/common/fs_stream.c +++ b/src/common/fs_stream.c @@ -10,7 +10,6 @@ #include "common/sys.h" #include "common/device.h" #include "common/pproc.h" -#include "common/match.h" #include diff --git a/src/common/hotspots.h b/src/common/hotspots.h index a18b63af..1b3ee700 100644 --- a/src/common/hotspots.h +++ b/src/common/hotspots.h @@ -7,13 +7,13 @@ // // Copyright(C) 2014 Chris Warren-Smith -#include "common/var_hash.h" +#include "common/var_map.h" +#include "common/var_eval.h" void err_evsyntax(void); void err_varisarray(void); void err_varisnotarray(void); void err_notavar(void); -var_t *code_resolve_varptr(var_t* var_p, int until_parens); /** * @ingroup var @@ -82,8 +82,8 @@ static inline var_num_t code_getnext128f() { */ static inline var_num_t v_getval(var_t *v) { switch (v ? v->type : -1) { - case V_HASH: - return hash_to_int(v); + case V_MAP: + return map_to_int(v); case V_PTR: return v->v.ap.p; case V_INT: @@ -115,8 +115,8 @@ static inline var_num_t v_getval(var_t *v) { */ static inline var_int_t v_igetval(var_t *v) { switch (v ? v->type : -1) { - case V_HASH: - return hash_to_int(v); + case V_MAP: + return map_to_int(v); case V_PTR: return v->v.ap.p; case V_INT: @@ -144,7 +144,7 @@ static inline var_int_t v_igetval(var_t *v) { * * @return the var_t* */ -static inline var_t* code_getvarptr_parens(int until_parens) { +static inline var_t *code_getvarptr_parens(int until_parens) { var_t *var_p = NULL; if (code_peek() == kwTYPE_VAR) { @@ -154,7 +154,7 @@ static inline var_t* code_getvarptr_parens(int until_parens) { var_p = code_resolve_varptr(var_p, until_parens); } else { switch (var_p->type) { - case V_HASH: + case V_MAP: case V_ARRAY: var_p = code_resolve_varptr(var_p, until_parens); break; @@ -205,9 +205,6 @@ static inline void v_init(var_t *v) { * @param v the variable */ static inline void v_free(var_t *v) { - int i; - var_t *elem; - switch (v->type) { case V_STR: if (v->v.p.ptr) { @@ -219,22 +216,20 @@ static inline void v_free(var_t *v) { case V_ARRAY: if (v->v.a.size) { if (v->v.a.ptr) { + int i; for (i = 0; i < v->v.a.size; i++) { - elem = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i)); + var_t *elem = (var_t *) (v->v.a.ptr + (sizeof(var_t) * i)); v_free(elem); } - tmp_free(v->v.a.ptr); v->v.a.ptr = NULL; v->v.a.size = 0; } } break; - case V_HASH: - hash_free(v); + case V_MAP: + map_free(v); break; } - v_init(v); } - diff --git a/src/common/keymap.h b/src/common/keymap.h index ad52a46e..edc2c3df 100644 --- a/src/common/keymap.h +++ b/src/common/keymap.h @@ -12,6 +12,10 @@ #ifndef KEYMAP_H #define KEYMAP_H +#if defined(__cplusplus) +extern "C" { +#endif + #define PCKBSIZE 256 // Keyboard codes @@ -88,6 +92,8 @@ int keymap_invoke(word key); int keymap_kbhit(); int keymap_kbpeek(); +#if defined(__cplusplus) +} #endif - +#endif diff --git a/src/common/kw.h b/src/common/kw.h index 8586f148..8b05143e 100644 --- a/src/common/kw.h +++ b/src/common/kw.h @@ -77,6 +77,7 @@ enum keyword { // line 50 kwTYPE_CALL_UDF, /* Call user defined function */ kwTYPE_CALL_UDP, /* Call user defined procedure */ kwTYPE_CALL_PTR, /* Call user defined procedure or function from address pointer */ + kwTYPE_CALL_VFUNC, /* Call virtual function */ kwTYPE_CALLEXTF, /* Call an external function */ kwTYPE_CALLEXTP, /* Call an external procedure */ kwTYPE_CRVAR, /* Create dynamic variable (PARAMETERS OR LOCALS) */ @@ -247,7 +248,6 @@ enum proc_keywords { kwEXPRSEQ, kwUNLOADLIB, kwCALLCP, - kwIMAGE, kwDEFINEKEY, kwSHOWPAGE, kwTHROW, @@ -299,6 +299,8 @@ enum func_keywords { kwISARRAY, kwISNUMBER, kwISSTRING, + kwISMAP, + kwISREF, kwATAN2, kwPOW, kwROUND, @@ -406,8 +408,8 @@ enum func_keywords { kwCBS, kwBCS, kwCALLCF, - kwIMGW, - kwIMGH, + kwARRAY, + kwIMAGE, kwNULLFUNC }; @@ -420,7 +422,7 @@ enum func_keywords { * @param table the table of codes to scan * @return non-zero on success */ -int kw_check(code_t * table, code_t code); +int kw_check(code_t *table, code_t code); /** * @ingroup sys diff --git a/src/common/pdb.h b/src/common/pdb.h deleted file mode 100644 index 939a1a57..00000000 --- a/src/common/pdb.h +++ /dev/null @@ -1,76 +0,0 @@ -// This file is part of SmallBASIC -// -// PDB for Non-PalmOS -// -// This program is distributed under the terms of the GPL v2.0 or later -// Download the GNU Public License (GPL) from www.gnu.org -// -// Copyright(C) 2000 Nicholas Christopoulos - -/** - * @defgroup utils Uitilies - */ - -#if !defined(_pdb_h) -#define _pdb_h - -#include "common/sys.h" - -/** - * @ingroup utils - * @struct pdb_record_entry - * PDB record - */ -struct pdb_record_entry { - dword localChunkID; /* offset to where record starts */ - struct { - int delete:1; - int dirty :1; - int busy :1; - int secret :1; - int category :4; - } attributes;byte uniqueID[3]; -}; - -typedef struct pdb_record_entry pdb_record_entry_t; - -#define PDB_RECORD_ENTRY_SIZE 8 - -/** - * @ingroup utils - * @struct pdb_record_list - * PDB record - */ -struct pdb_record_list { - dword next_record_list_id; - word num_records; -}; -typedef struct pdb_record_list pdb_record_list_t; - -#define PDB_RECORD_LIST_SIZE 6 - -/** - * @ingroup utils - * @struct pdb_database_hdr - * PDB file header - */ -struct pdb_database_hdr { - char name[32]; - word attributes; - word version; - dword creation_date; - dword modification_date; - dword last_backup_date; - dword modification_number; - dword app_info_id; - dword sort_info_id; - char type[4]; - char creator[4]; - dword unique_id_seed; - pdb_record_list_t record_list; -}; -typedef struct pdb_database_hdr pdb_database_hdr_t; - -#define PDB_DATABASE_HDR_SIZE 78 - -#endif diff --git a/src/common/pproc.h b/src/common/pproc.h index b427126e..c1a41d17 100644 --- a/src/common/pproc.h +++ b/src/common/pproc.h @@ -64,15 +64,6 @@ void bc_loop(int isf); */ void eval(var_t *result); -/** - * @ingroup exec - * - * resolve the variable reference - * - * @param var_t - */ -var_t *eval_ref_var(var_t *var_p); - /** * @ingroup exec * diff --git a/src/common/proc.c b/src/common/proc.c index 49f0c384..16674114 100644 --- a/src/common/proc.c +++ b/src/common/proc.c @@ -10,7 +10,6 @@ #include "common/sys.h" #include "common/pproc.h" #include "common/messages.h" -#include "common/var_hash.h" #include /* @@ -260,51 +259,6 @@ void pv_write(char *str, int method, int handle) { } } -/* - * print the array variable - */ -void pv_write_array(var_t *var, int method, int handle) { - pv_write("[", method, handle); - - if (var->v.a.maxdim == 2) { - int rows, cols; - var_t *e; - int i, j, pos; - - // NxN - rows = ABS(var->v.a.ubound[0] - var->v.a.lbound[0]) + 1; - cols = ABS(var->v.a.ubound[1] - var->v.a.lbound[1]) + 1; - - for (i = 0; i < rows; i++) { - for (j = 0; j < cols; j++) { - pos = i * cols + j; - e = (var_t *) (var->v.a.ptr + (sizeof(var_t) * pos)); - pv_writevar(e, method, handle); - if (j != cols - 1) { - pv_write(",", method, handle); // add space? - } - } - if (i != rows - 1) { - pv_write(";", method, handle); // add space? - } - } - } else { - var_t *e; - int i; - - for (i = 0; i < var->v.a.size; i++) { - e = (var_t *) (var->v.a.ptr + (sizeof(var_t) * i)); - pv_writevar(e, method, handle); - if (i != var->v.a.size - 1) { - pv_write(",", method, handle); // add space? - } - } - } - - // close array - pv_write("]", method, handle); -} - /* * just prints the value of variable 'var' */ @@ -318,8 +272,9 @@ void pv_writevar(var_t *var, int method, int handle) { case V_STR: pv_write((char *)var->v.p.ptr, method, handle); break; - case V_HASH: - hash_write(var, method, handle); + case V_ARRAY: + case V_MAP: + map_write(var, method, handle); break; case V_PTR: ltostr(var->v.ap.p, tmpsb); @@ -333,9 +288,6 @@ void pv_writevar(var_t *var, int method, int handle) { ftostr(var->v.n, tmpsb); pv_write(tmpsb, method, handle); break; - case V_ARRAY: - pv_write_array(var, method, handle); - break; case V_REF: pv_writevar(var->v.ref, method, handle); break; diff --git a/src/common/sberr.c b/src/common/sberr.c index a10c4902..0a65076f 100644 --- a/src/common/sberr.c +++ b/src/common/sberr.c @@ -243,6 +243,14 @@ void err_ref_var() { rt_raise(ERR_REF_VAR); } +void err_ref_circ_var() { + rt_raise(ERR_REF_CIRC_VAR); +} + +void err_array() { + rt_raise(MSG_ARRAY_SE); +} + /** * the DONE message */ @@ -274,21 +282,28 @@ void inf_break(int pline) { // assign error to variable or match with next expression int err_throw_catch(const char *err) { + var_t *arg; + var_t v_catch; int caught = 1; - byte code = code_peek(); - if (code != kwTYPE_EOC && code != kwTYPE_LINE) { - if (code_peek() == kwTYPE_VAR) { - var_t *arg = code_getvarptr(); - v_setstr(arg, err); - } else { - var_t v_catch; - v_init(&v_catch); - eval(&v_catch); - // catch is conditional on matching error - caught = (v_catch.type == V_STR && strstr(err, v_catch.v.p.ptr) != NULL); - v_free(&v_catch); - } + switch (code_peek()) { + case kwTYPE_VAR: + arg = code_getvarptr(); + v_setstr(arg, err); + break; + case kwTYPE_STR: + v_init(&v_catch); + eval(&v_catch); + // catch is conditional on matching error + caught = (v_catch.type == V_STR && strstr(err, v_catch.v.p.ptr) != NULL); + v_free(&v_catch); + break; + case kwTYPE_EOC: + case kwTYPE_LINE: + break; + default: + rt_raise(ERR_INVALID_CATCH); + break; } return caught; } @@ -303,14 +318,22 @@ void err_throw_str(const char *err) { code_getaddr(); // restore outer level prog_catch_ip = code_getaddr(); + // get the stack level + byte level = code_getnext(); caught = err_throw_catch(err); while (!caught && prog_catch_ip != INVALID_ADDR) { code_jump(prog_catch_ip + 1); code_getaddr(); prog_catch_ip = code_getaddr(); + level = code_getnext(); caught = err_throw_catch(err); } + + // cleanup the stack + while (prog_stack_count > level) { + code_pop_and_free(NULL); + } } if (!caught) { prog_error = 0x80; @@ -333,17 +356,19 @@ void err_throw(const char *fmt, ...) { // throw user error void cmd_throw() { - var_t v_throw; - v_init(&v_throw); - const char *err = ""; - byte code = code_peek(); - if (code != kwTYPE_EOC && code != kwTYPE_LINE) { - eval(&v_throw); - if (v_throw.type == V_STR) { - err = v_throw.v.p.ptr; + if (!gsb_last_error) { + var_t v_throw; + v_init(&v_throw); + const char *err = ""; + byte code = code_peek(); + if (code != kwTYPE_EOC && code != kwTYPE_LINE) { + eval(&v_throw); + if (v_throw.type == V_STR) { + err = v_throw.v.p.ptr; + } } + err_throw_str(err); + v_free(&v_throw); } - err_throw_str(err); - v_free(&v_throw); } diff --git a/src/common/sberr.h b/src/common/sberr.h index fb86648d..954f7e89 100644 --- a/src/common/sberr.h +++ b/src/common/sberr.h @@ -68,6 +68,8 @@ void err_notavar(void); void err_run_err(const char *file); void err_invkw(addr_t addr, byte code); void err_ref_var(); +void err_ref_circ_var(); +void err_array(); #define err_type_mismatch() err_typemismatch() #define err_syntax_error() err_syntax() diff --git a/src/common/scan.c b/src/common/scan.c index af3a5792..786f8bc7 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -900,12 +900,12 @@ char *comp_prev_char(const char *root, const char *ptr) { } /** - * get next word - * if buffer's len is zero, then the next element is not a word + * get next word + * if buffer's len is zero, then the next element is not a word * - * @param text the source - * @param dest the buffer to store the result - * @return pointer of text to the next element + * @param text the source + * @param dest the buffer to store the result + * @return pointer of text to the next element */ const char *comp_next_word(const char *text, char *dest) { const char *p = text; @@ -1053,8 +1053,8 @@ void comp_expression(char *expr, byte no_parser) { // special case for INPUT if (idx == kwINPUTF) { if (*comp_next_char(ptr) != '(') { - idx = -1; // INPUT is SPECIAL SEPARATOR (OPEN...FOR - // INPUT...) + // INPUT is SPECIAL SEPARATOR (OPEN...FOR INPUT...) + idx = -1; } } @@ -1072,8 +1072,7 @@ void comp_expression(char *expr, byte no_parser) { bc_add_code(&bc, kwTYPE_LEVEL_BEGIN); bc_add_code(&bc, kwTYPE_CALL_PTR); // next is address - // skip next ( since we already added - // kwTYPE_LEVEL_BEGIN + // skip next '(' since we already added kwTYPE_LEVEL_BEGIN // to allow kwTYPE_CALL_PTR to be the next code char *par = comp_next_char(ptr); if (*par == '(') { @@ -1161,11 +1160,11 @@ void comp_expression(char *expr, byte no_parser) { } comp_add_variable(&bc, comp_bc_name); } - } // kw - } // extf - } // opr - } // sp. sep - } // check sep + } + } + } + } + } addr_opr = 0; // end isalpha block } else if (*ptr == ',' || *ptr == ';' || *ptr == '#') { @@ -1643,9 +1642,10 @@ char *comp_array_uds_field(char *p, bc_t *bc) { /* * array's args */ -void comp_array_params(char *src) { +char *comp_array_params(char *src, char exitChar) { char *p = src; - char *ss = NULL, *se = NULL; + char *ss = NULL; + char *se = NULL; int level = 0; while (*p) { @@ -1678,19 +1678,21 @@ void comp_array_params(char *src) { if (*(p + 1) == '.') { p = comp_array_uds_field(p + 2, &comp_prog); } - } // lev = 0 + } break; }; - p++; + if (*p == exitChar) { + p++; + break; + } } - - // if (level > 0) { sc_raise(MSG_ARRAY_MIS_RP); } else if (level < 0) { sc_raise(MSG_ARRAY_MIS_LP); } + return p; } /* @@ -1773,786 +1775,872 @@ void bc_store_exports(const char *slist) { tmp_free(newlist); } -/* - * PASS1: scan source line - */ -void comp_text_line(char *text) { - char *p; - char *lb_end; - char *last_cmd; - long idx; - int sharp, ladd, linc, ldec, decl = 0, vattr; - int leqop; - char pname[SB_KEYWORD_SIZE + 1]; - char vname[SB_KEYWORD_SIZE + 1]; - - if (comp_error) { - return; - } - str_alltrim(text); - p = text; - - // EOL - if (*p == ':') { - p++; - comp_text_line(p); - return; - } - // remark - if (*p == '\'' || *p == '#') { - return; - } - // empty line - if (*p == '\0') { - return; +void comp_get_unary(const char *p, int *ladd, int *linc, int *ldec, int *leqop) { + *ladd = (strncmp(p, "<<", 2) == 0); + *linc = (strncmp(p, "++", 2) == 0); + *ldec = (strncmp(p, "--", 2) == 0); + if (p[1] == '=' && strchr("-+/\\*^%&|", p[0])) { + *leqop = p[0]; + } else { + *leqop = 0; } +} - lb_end = p = (char *)comp_next_word(text, comp_bc_name); - last_cmd = p; - p = get_param_sect(p, ":", comp_bc_parm); +void comp_text_line_let(long idx, int ladd, int linc, int ldec, int leqop) { + char *p; + char *parms = comp_bc_parm; + char *array_index = NULL; + int v_func = 0; - // check old style labels - if (is_all_digits(comp_bc_name)) { - str_alltrim(comp_bc_name); - idx = comp_label_getID(comp_bc_name); - comp_label_setip(idx); - if (comp_error) { - return; - } - // continue - last_cmd = p = (char *)comp_next_word(lb_end, comp_bc_name); - if (strlen(comp_bc_name) == 0) { - if (!p) { - return; + if (parms[0] == '(') { + int level = 0; + p = parms; + while (*p) { + switch(*p) { + case '(': + level++; + break; + case ')': + level--; + break; + case '.': + // advance beyond UDS element + p++; + while (*p == '_' || isalnum(*p)) { + p++; + } + p--; + break; } - if (*p == '\0') { - return; + p++; + if (level == 0 && *p != '(' && *p != '.') { + break; } } - p = get_param_sect(p, ":", comp_bc_parm); - } - // what's this ? - idx = comp_is_keyword(comp_bc_name); - if (idx == kwREM) { - return; // remarks... return - } - if (idx == -1) { - idx = comp_is_proc(comp_bc_name); - if (idx != -1) { - if (idx == kwCALLCP) { - bc_add_code(&comp_prog, kwTYPE_CALL_UDP); - bc_add_addr(&comp_prog, idx); // place holder - bc_add_addr(&comp_prog, 0); // return-variable ID - bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); - // allow cmd_udp to find the initial var-ptr arg - bc_add_code(&comp_prog, kwTYPE_CALL_PTR); - char *next = trim_empty_parentheses(comp_bc_parm); - comp_expression(next, 0); - bc_add_code(&comp_prog, kwTYPE_LEVEL_END); - } else { - // simple buildin procedure - // there is no need to check it more... - // save it and return (go to next) - bc_add_pcode(&comp_prog, idx); - char *next = trim_empty_parentheses(comp_bc_parm); - comp_expression(next, 0); - } - - if (*p == ':') { // command separator - bc_eoc(&comp_prog); - p++; - comp_text_line(p); + if (level == 0) { + p = comp_next_char(p); + if (*p != '=') { + // array(n) unary-operator + int len = (p - parms) + 1; + array_index = tmp_alloc(len); + strncpy(array_index, parms, len); + array_index[len - 1] = '\0'; + + // store plain operator in comp_bc_parm + strcpy(comp_bc_parm, p); + comp_get_unary(comp_bc_parm, &ladd, &linc, &ldec, &leqop); } - return; } } - if (idx == kwLET) { // old-style keyword LET - char *p; - idx = -1; + if (idx == kwCONST) { + // const a=10: b=10 p = (char *)comp_next_word(comp_bc_parm, comp_bc_name); - strcpy(comp_bc_parm, p); - } else if (idx == kwDECLARE) { // declaration - char *p; - decl = 1; - p = (char *)comp_next_word(comp_bc_parm, comp_bc_name); - idx = comp_is_keyword(comp_bc_name); - if (idx == -1) { - idx = comp_is_proc(comp_bc_name); + p = get_param_sect(p, ":", comp_bc_parm); + parms = comp_bc_parm; + bc_add_code(&comp_prog, kwCONST); + } else if (ladd) { + bc_add_code(&comp_prog, kwAPPEND); + parms += 2; + } else if (linc) { + bc_add_code(&comp_prog, kwLET); + strcpy(comp_bc_parm, "="); + strcat(comp_bc_parm, comp_bc_name); + if (array_index) { + strcat(comp_bc_parm, array_index); } - strcpy(comp_bc_parm, p); - if (idx != kwPROC && idx != kwFUNC) { - sc_raise(MSG_USE_DECL); - return; + strcat(comp_bc_parm, "+1"); + } else if (ldec) { + bc_add_code(&comp_prog, kwLET); + strcpy(comp_bc_parm, "="); + strcat(comp_bc_parm, comp_bc_name); + if (array_index) { + strcat(comp_bc_parm, array_index); } - } - if (idx == kwREM) { - return; + strcat(comp_bc_parm, "-1"); + } else if (leqop) { + // a += 10: b -= 10 etc + char *buf; + bc_add_code(&comp_prog, kwLET); + int len = strlen(comp_bc_parm) + strlen(comp_bc_name) + 1; + if (array_index) { + len += strlen(array_index); + } + buf = tmp_alloc(len); + memset(buf, 0, len); + strcpy(buf, "="); + strcat(buf, comp_bc_name); + if (array_index) { + strcat(buf, array_index); + } + buf[strlen(buf)] = leqop; + strcat(buf, comp_bc_parm + 2); + strcpy(comp_bc_parm, buf); + tmp_free(buf); + } else if (idx != kwLET + && array_index != NULL + && strchr(comp_bc_name, '.') != NULL) { + // no unary operator found with array index + v_func = 1; + bc_add_pcode(&comp_prog, kwTYPE_CALL_VFUNC); + } else { + bc_add_code(&comp_prog, kwLET); } - sharp = (comp_bc_parm[0] == '#'); // if # -> file commands - ladd = (strncmp(comp_bc_parm, "<<", 2) == 0); // if << -> array, - - // append - linc = (strncmp(comp_bc_parm, "++", 2) == 0); - ldec = (strncmp(comp_bc_parm, "--", 2) == 0); + comp_error_if_keyword(comp_bc_name); + comp_add_variable(&comp_prog, comp_bc_name); - if (comp_bc_parm[1] == '=' && strchr("-+/\\*^%&|", comp_bc_parm[0])) { - leqop = comp_bc_parm[0]; - } else { - leqop = 0; + if (!comp_error) { + if (v_func) { + // a.b.c() + if (strlen(array_index) > 2) { + // more than empty brackets + comp_array_params(array_index, 0); + } + } + else if (parms[0] == '(') { + if (*comp_next_char(parms + 1) == ')') { + // vn()=fillarray + p = strchr(parms, '='); + comp_expression(p, 0); + } else { + // array(n) = expr + p = comp_array_params(parms, '='); + if (!comp_error) { + bc_add_code(&comp_prog, kwTYPE_CMPOPR); + bc_add_code(&comp_prog, '='); + comp_expression(p, 0); + } + } + } else { + if (array_index != NULL) { + comp_array_params(array_index, 0); + } + bc_add_code(&comp_prog, kwTYPE_CMPOPR); + bc_add_code(&comp_prog, '='); + comp_expression(parms + 1, 0); + } } - if ((comp_bc_parm[0] == '=' || ladd || linc || ldec || leqop) && (idx != -1)) { - sc_raise(MSG_IT_IS_KEYWORD, comp_bc_name); - return; + if (array_index != NULL) { + tmp_free(array_index); } +} - if ((idx == kwCONST) || - ((comp_bc_parm[0] == '=' || - (comp_bc_parm[0] == '(' && !comp_is_function(comp_bc_name)) || - ladd || linc || ldec || leqop) && (idx == -1))) { - // - // LET/CONST commands - // - char *parms = comp_bc_parm; - if (idx == kwCONST) { - // const a=10: b=10 - p = (char *)comp_next_word(comp_bc_parm, comp_bc_name); - p = get_param_sect(p, ":", comp_bc_parm); - parms = comp_bc_parm; - bc_add_code(&comp_prog, kwCONST); - } else if (ladd) { - bc_add_code(&comp_prog, kwAPPEND); - parms += 2; - } else if (linc) { - bc_add_code(&comp_prog, kwLET); - strcpy(comp_bc_parm, "="); - strcat(comp_bc_parm, comp_bc_name); - strcat(comp_bc_parm, "+1"); - } else if (ldec) { - bc_add_code(&comp_prog, kwLET); - strcpy(comp_bc_parm, "="); - strcat(comp_bc_parm, comp_bc_name); - strcat(comp_bc_parm, "-1"); - } else if (leqop) { - char *buf; - int l; - - // a += 10: b -= 10 etc - bc_add_code(&comp_prog, kwLET); - l = strlen(comp_bc_parm) + strlen(comp_bc_name) + 1; - buf = tmp_alloc(l); - memset(buf, 0, l); - strcpy(buf, "="); - strcat(buf, comp_bc_name); - buf[strlen(buf)] = leqop; - strcat(buf, comp_bc_parm + 2); - strcpy(comp_bc_parm, buf); - tmp_free(buf); - } else { - bc_add_code(&comp_prog, kwLET); - } +// User-defined procedures/functions +void comp_text_line_func(long idx, int decl) { + char *lpar_ptr, *eq_ptr; + char_p_t pars[256]; + int count; + char pname[SB_KEYWORD_SIZE + 1]; + char vname[SB_KEYWORD_SIZE + 1]; - comp_error_if_keyword(comp_bc_name); - comp_add_variable(&comp_prog, comp_bc_name); + // single-line function (DEF FN) + if ((eq_ptr = strchr(comp_bc_parm, '='))) { + *eq_ptr = '\0'; + } + // parameters start + if ((lpar_ptr = strchr(comp_bc_parm, '('))) { + *lpar_ptr = '\0'; + } - if (!comp_error) { - if (parms[0] == '(') { - char *p = strchr(parms, '='); - if (!p) { - sc_raise(MSG_LET_MISSING_EQ); - } else { - if (*comp_next_char(parms + 1) == ')') { - // its the variable's name only - comp_expression(p, 0); - } else { - // ARRAY (LEFT) - *p = '\0'; - comp_array_params(parms); - *p = '='; - if (!comp_error) { - bc_add_code(&comp_prog, kwTYPE_CMPOPR); - bc_add_code(&comp_prog, '='); - comp_expression(p + 1, 0); - } - } - } - } else { - bc_add_code(&comp_prog, kwTYPE_CMPOPR); - bc_add_code(&comp_prog, '='); - comp_expression(parms + 1, 0); - } + comp_prepare_name(pname, baseof(comp_bc_parm, '/'), SB_KEYWORD_SIZE); + comp_error_if_keyword(baseof(comp_bc_parm, '/')); + + if (decl) { + // its only a declaration (DECLARE) + if (comp_udp_getip(pname) == INVALID_ADDR) { + comp_add_udp(pname); } } else { - // add generic command - char_p_t pars[256]; - char *lpar_ptr, *eq_ptr, *p, *p_do; - int keep_ip, udp, count, i; - - switch (idx) { - case kwLABEL: - str_alltrim(comp_bc_parm); - idx = comp_label_getID(comp_bc_parm); - comp_label_setip(idx); - break; + // func/sub + if (comp_udp_getip(pname) != INVALID_ADDR) { + sc_raise(MSG_UDP_ALREADY_EXISTS, pname); + } else { + // setup routine's address (and get an id) + int pidx; + if ((pidx = comp_udp_setip(pname, comp_prog.count)) == -1) { + pidx = comp_add_udp(pname); + comp_udp_setip(pname, comp_prog.count); + } + // put JMP to the next command after the END + // (now we just keep the rq space, pass2 will + // update that) + bc_add_code(&comp_prog, kwGOTO); + bc_add_addr(&comp_prog, 0); + bc_add_code(&comp_prog, 0); - case kwEXIT: + comp_block_level++; + comp_block_id++; + // keep it in stack for 'pass2' + comp_push(comp_prog.count); + // store (FUNC/PROC) code bc_add_code(&comp_prog, idx); - str_alltrim(comp_bc_parm); - if (strlen(comp_bc_parm) && comp_bc_parm[0] != '\'') { - idx = comp_is_special_operator(comp_bc_parm); - if (idx == kwFORSEP || idx == kwLOOPSEP || idx == kwPROCSEP || idx == kwFUNCSEP) { - bc_add_code(&comp_prog, idx); - } else { - sc_raise(MSG_EXIT_ERR); - } + + // func/proc name (also, update comp_bc_proc) + if (comp_proc_level) { + strcat(comp_bc_proc, "/"); + strcat(comp_bc_proc, baseof(pname, '/')); } else { - bc_add_code(&comp_prog, 0); + strcpy(comp_bc_proc, pname); } - break; - case kwDECLARE: - break; + if (!comp_error) { + comp_proc_level++; - case kwPROC: - case kwFUNC: - // - // USER-DEFINED PROCEDURES/FUNCTIONS - // - // single-line function (DEF FN) - if ((eq_ptr = strchr(comp_bc_parm, '='))) { - *eq_ptr = '\0'; - } - // parameters start - if ((lpar_ptr = strchr(comp_bc_parm, '('))) { - *lpar_ptr = '\0'; - } - comp_prepare_name(pname, baseof(comp_bc_parm, '/'), SB_KEYWORD_SIZE); - comp_error_if_keyword(baseof(comp_bc_parm, '/')); - - if (decl) { - // its only a declaration (DECLARE) - if (comp_udp_getip(pname) == INVALID_ADDR) { - comp_add_udp(pname); - } - } else { - // func/sub - if (comp_udp_getip(pname) != INVALID_ADDR) { - sc_raise(MSG_UDP_ALREADY_EXISTS, pname); + // if its a function, + // setup the code for the return-value + // (vid={F}/{F}) + if (idx == kwFUNC) { + strcpy(comp_bc_tmp2, baseof(pname, '/')); + comp_udptable[pidx].vid = comp_var_getID(comp_bc_tmp2); } else { - // setup routine's address (and get an id) - int pidx; - if ((pidx = comp_udp_setip(pname, comp_prog.count)) == -1) { - pidx = comp_add_udp(pname); - comp_udp_setip(pname, comp_prog.count); - } - // put JMP to the next command after the END - // (now we just keep the rq space, pass2 will - // update that) - bc_add_code(&comp_prog, kwGOTO); - bc_add_addr(&comp_prog, 0); - bc_add_code(&comp_prog, 0); - - comp_block_level++; - comp_block_id++; - // keep it in stack for 'pass2' - comp_push(comp_prog.count); - // store (FUNC/PROC) code - bc_add_code(&comp_prog, idx); - - // func/proc name (also, update comp_bc_proc) - if (comp_proc_level) { - strcat(comp_bc_proc, "/"); - strcat(comp_bc_proc, baseof(pname, '/')); - } else { - strcpy(comp_bc_proc, pname); - } + // procedure, no return value here + comp_udptable[pidx].vid = INVALID_ADDR; + } - if (!comp_error) { - comp_proc_level++; + // parameters + if (lpar_ptr) { + int i; + int vattr; - // if its a function, - // setup the code for the return-value - // (vid={F}/{F}) - if (idx == kwFUNC) { - strcpy(comp_bc_tmp2, baseof(pname, '/')); - comp_udptable[pidx].vid = comp_var_getID(comp_bc_tmp2); - } else { - // procedure, no return value here - comp_udptable[pidx].vid = INVALID_ADDR; - } + *lpar_ptr = '('; + comp_getlist_insep(comp_bc_parm, pars, "()", ",", 256, &count); + bc_add_code(&comp_prog, kwTYPE_PARAM); + bc_add_code(&comp_prog, count); - // parameters - if (lpar_ptr) { - int i; - *lpar_ptr = '('; - comp_getlist_insep(comp_bc_parm, pars, "()", ",", 256, &count); - bc_add_code(&comp_prog, kwTYPE_PARAM); - bc_add_code(&comp_prog, count); - - for (i = 0; i < count; i++) { - if ((strncmp(pars[i], LCN_BYREF_WRS, 6) == 0) || (pars[i][0] == '@')) { - if (pars[i][0] == '@') { - comp_prepare_name(vname, pars[i] + 1, SB_KEYWORD_SIZE); - } else { - comp_prepare_name(vname, pars[i] + 6, SB_KEYWORD_SIZE); - } - vattr = 0x80; - } else { - comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); - vattr = 0; - } - if (strchr(pars[i], '(')) { - vattr |= 1; - } - - bc_add_code(&comp_prog, vattr); - bc_add_addr(&comp_prog, comp_var_getID(vname)); + for (i = 0; i < count; i++) { + if ((strncmp(pars[i], LCN_BYREF_WRS, 6) == 0) || (pars[i][0] == '@')) { + if (pars[i][0] == '@') { + comp_prepare_name(vname, pars[i] + 1, SB_KEYWORD_SIZE); + } else { + comp_prepare_name(vname, pars[i] + 6, SB_KEYWORD_SIZE); } + vattr = 0x80; } else { - // no parameters - bc_add_code(&comp_prog, kwTYPE_PARAM); - // params - bc_add_code(&comp_prog, 0); - // pcount = 0 + comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); + vattr = 0; } - - bc_eoc(&comp_prog); // EOC - // ----------------------------------------------- - // scan for single-line function (DEF FN - // format) - if (eq_ptr && idx == kwFUNC) { - eq_ptr++; // *eq_ptr was '\0' - SKIP_SPACES(eq_ptr); - if (strlen(eq_ptr)) { - char *macro = tmp_alloc(SB_SOURCELINE_SIZE + 1); - sprintf(macro, "%s=%s:%s", pname, eq_ptr, LCN_END); - - // run comp_text_line again - comp_text_line(macro); - tmp_free(macro); - } else { - sc_raise(MSG_MISSING_UDP_BODY); - } + if (strchr(pars[i], '(')) { + vattr |= 1; } + + bc_add_code(&comp_prog, vattr); + bc_add_addr(&comp_prog, comp_var_getID(vname)); } + } else { + // no parameters + bc_add_code(&comp_prog, kwTYPE_PARAM); + // params + bc_add_code(&comp_prog, 0); + // pcount = 0 } - } - break; - case kwLOCAL: - // local variables - count = comp_getlist(comp_bc_parm, pars, ",", 256); - bc_add_code(&comp_prog, kwTYPE_CRVAR); - bc_add_code(&comp_prog, count); - for (i = 0; i < count; i++) { - comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); - bc_add_addr(&comp_prog, comp_var_getID(vname)); - } - // handle same line variable assignment, eg local blah = foo - for (i = 0; i < count; i++) { - comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); - if (strlen(vname) != strlen(pars[i])) { - // kwTYPE_LINE is required for executor - bc_add_code(&comp_prog, kwTYPE_LINE); - bc_add_addr(&comp_prog, comp_line); - comp_text_line(pars[i]); + bc_eoc(&comp_prog); // EOC + // scan for single-line function (DEF FN format) + if (eq_ptr && idx == kwFUNC) { + eq_ptr++; // *eq_ptr was '\0' + SKIP_SPACES(eq_ptr); + if (strlen(eq_ptr)) { + char *macro = tmp_alloc(SB_SOURCELINE_SIZE + 1); + sprintf(macro, "%s=%s:%s", pname, eq_ptr, LCN_END); + // run comp_text_line again + comp_text_line(macro); + tmp_free(macro); + } else { + sc_raise(MSG_MISSING_UDP_BODY); + } } } + } + } +} - break; +void comp_text_line_on() { + char *p; + int count, i, keep_ip; + char_p_t pars[256]; - case kwREM: - return; + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, kwONJMP, 0, 0); - case kwEXPORT: // export - if (comp_unit_flag) { - bc_store_exports(comp_bc_parm); - } else { - sc_raise(MSG_UNIT_NAME_MISSING); - } - break; + if ((p = strstr(comp_bc_parm, LCN_GOTO_WS)) != NULL) { + bc_add_code(&comp_prog, kwGOTO); + // the command + *p = '\0'; + p += 6; + keep_ip = comp_prog.count; + bc_add_code(&comp_prog, 0); + count = comp_getlist(p, pars, ",", 256); + for (i = 0; i < count; i++) { + bc_add_addr(&comp_prog, comp_label_getID(pars[i])); // IDs + } - case kwOPTION: - comp_cmd_option(comp_bc_parm); - break; + if (count == 0) { + sc_raise(MSG_ON_GOTO_ERR); + } else { + comp_prog.ptr[keep_ip] = count; + } - case kwGOTO: - str_alltrim(comp_bc_parm); - comp_push(comp_prog.count); - bc_add_code(&comp_prog, idx); - bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm)); - bc_add_code(&comp_prog, comp_block_level); - break; + comp_expression(comp_bc_parm, 0); // the expression + bc_eoc(&comp_prog); + } else if ((p = strstr(comp_bc_parm, LCN_GOSUB_WS)) != NULL) { + bc_add_code(&comp_prog, kwGOSUB); + // the command + *p = '\0'; + p += 7; + keep_ip = comp_prog.count; + bc_add_code(&comp_prog, 0); + // the counter + + // count = bc_scan_label_list(p); + count = comp_getlist(p, pars, ",", 256); + for (i = 0; i < count; i++) { + bc_add_addr(&comp_prog, comp_label_getID(pars[i])); + } + if (count == 0) { + sc_raise(MSG_ON_GOSUB_ERR); + } else { + comp_prog.ptr[keep_ip] = count; + } + comp_expression(comp_bc_parm, 0); // the expression + bc_eoc(&comp_prog); + } else { + sc_raise(MSG_ON_NOTHING); + } +} - case kwGOSUB: - str_alltrim(comp_bc_parm); - bc_add_code(&comp_prog, idx); - bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm)); - break; +void comp_text_line_for() { + char *p = strchr(comp_bc_parm, '='); + char *p_do = strstr(comp_bc_parm, LCN_DO_WS); - case kwIF: - strcpy(comp_do_close_cmd, LCN_ENDIF); + // fix DO bug + if (p_do) { + if (p > p_do) { + p = NULL; + } + } + strcpy(comp_do_close_cmd, LCN_NEXT); + comp_block_level++; + comp_block_id++; + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, kwFOR, 0, 0); - // from here, we can scan for inline IF - if (comp_single_line_if(last_cmd)) { - // inline-IFs - return; + if (!p) { + // FOR [EACH] X IN Y + if ((p = strstr(comp_bc_parm, LCN_IN_WS)) == NULL) { + sc_raise(MSG_FOR_NOTHING); + } else { + *p = '\0'; + char *n = p; + strcpy(comp_bc_name, comp_bc_parm); + str_alltrim(comp_bc_name); + if (!is_alpha(*comp_bc_name)) { + sc_raise(MSG_FOR_COUNT_ERR, comp_bc_name); } else { - comp_block_level++; - comp_block_id++; - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, idx, 0, 0); - comp_expression(comp_bc_parm, 0); - bc_add_code(&comp_prog, kwTYPE_EOC); - // bc_eoc(); + char *p_lev = comp_bc_name; + while (is_alnum(*p_lev) || *p_lev == ' ') { + p_lev++; + } + if (*p_lev == '(') { + sc_raise(MSG_FOR_ARR_COUNT, comp_bc_name); + } else { + if (!comp_error_if_keyword(comp_bc_name)) { + comp_add_variable(&comp_prog, comp_bc_name); + *n = ' '; + bc_add_code(&comp_prog, kwIN); + comp_expression(n + 4, 0); + } + } } - break; + } + } else { + // FOR X=Y TO Z [STEP L] + *p = '\0'; + char *n = p; - case kwON: - // - // ON x GOTO|GOSUB ... - // - idx = kwONJMP; // WARNING! - comp_push(comp_prog.count); + strcpy(comp_bc_name, comp_bc_parm); + str_alltrim(comp_bc_name); + if (!is_alpha(*comp_bc_name)) { + sc_raise(MSG_FOR_COUNT_ERR, comp_bc_name); + } else { + char *p_lev = comp_bc_name; + while (is_alnum(*p_lev) || *p_lev == ' ') { + p_lev++; + } + if (*p_lev == '(') { + sc_raise(MSG_FOR_ARR_COUNT, comp_bc_name); + } else { + if (!comp_error_if_keyword(comp_bc_name)) { + comp_add_variable(&comp_prog, comp_bc_name); + *n = '='; + comp_expression(n + 1, 0); + } + } + } + } +} + +void comp_text_line_end(long idx) { + if (strncmp(comp_bc_parm, LCN_IF, 2) == 0 || + strncmp(comp_bc_parm, LCN_TRY, 3) == 0 || + strncmp(comp_bc_parm, LCN_SELECT, 6) == 0) { + idx = strncmp(comp_bc_parm, LCN_IF, 2) == 0 ? kwENDIF : + strncmp(comp_bc_parm, LCN_TRY, 3) == 0 ? kwENDTRY : kwENDSELECT; + comp_push(comp_prog.count); + if (idx == kwENDTRY) { + bc_add_code(&comp_prog, idx); + } else { bc_add_ctrl(&comp_prog, idx, 0, 0); + } + comp_block_level--; + comp_block_id--; + } else if (comp_proc_level) { + char *dol; - if ((p = strstr(comp_bc_parm, LCN_GOTO_WS)) != NULL) { - bc_add_code(&comp_prog, kwGOTO); - // the command - *p = '\0'; - p += 6; - keep_ip = comp_prog.count; - bc_add_code(&comp_prog, 0); - // the counter - - // count = bc_scan_label_list(p); - count = comp_getlist(p, pars, ",", 256); - for (i = 0; i < count; i++) { - bc_add_addr(&comp_prog, comp_label_getID(pars[i])); // IDs - } + // UDP/F RETURN + dol = strrchr(comp_bc_proc, '/'); + if (dol) { + *dol = '\0'; + } else { + *comp_bc_proc = '\0'; + } + comp_push(comp_prog.count); + bc_add_code(&comp_prog, kwTYPE_RET); + comp_proc_level--; + comp_block_level--; + comp_block_id++; // advance to next block + } else { + // END OF PROG + bc_add_code(&comp_prog, idx); + } +} - if (count == 0) { - sc_raise(MSG_ON_GOTO_ERR); - } else { - comp_prog.ptr[keep_ip] = count; - } +// External or user-defined procedure +void comp_text_line_ext_func() { + int udp = comp_is_external_proc(comp_bc_name); + if (udp > -1) { + bc_add_extpcode(&comp_prog, comp_extproctable[udp].lib_id, + comp_extproctable[udp].symbol_index); + char *next = trim_empty_parentheses(comp_bc_parm); + if (comp_is_parenthesized(next)) { + comp_expression(next, 0); + } else { + bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); + comp_expression(next, 0); + bc_add_code(&comp_prog, kwTYPE_LEVEL_END); + } + } else { + udp = comp_udp_id(comp_bc_name, 1); + if (udp == -1) { + udp = comp_add_udp(comp_bc_name); + } + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, kwTYPE_CALL_UDP, udp, 0); + char *next = trim_empty_parentheses(comp_bc_parm); + if (comp_is_parenthesized(next)) { + comp_expression(next, 0); + } else { + bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); + comp_expression(next, 0); + bc_add_code(&comp_prog, kwTYPE_LEVEL_END); + } + } +} - comp_expression(comp_bc_parm, 0); // the expression - bc_eoc(&comp_prog); - } else if ((p = strstr(comp_bc_parm, LCN_GOSUB_WS)) != NULL) { - bc_add_code(&comp_prog, kwGOSUB); - // the command - *p = '\0'; - p += 7; - keep_ip = comp_prog.count; - bc_add_code(&comp_prog, 0); - // the counter - - // count = bc_scan_label_list(p); - count = comp_getlist(p, pars, ",", 256); - for (i = 0; i < count; i++) { - bc_add_addr(&comp_prog, comp_label_getID(pars[i])); - } - if (count == 0) { - sc_raise(MSG_ON_GOSUB_ERR); - } else { - comp_prog.ptr[keep_ip] = count; - } - comp_expression(comp_bc_parm, 0); // the expression - bc_eoc(&comp_prog); +int comp_text_line_command(long idx, int decl, int sharp, char *last_cmd) { + char_p_t pars[256]; + int count, i, index; + char vname[SB_KEYWORD_SIZE + 1]; + int result = 1; + + switch (idx) { + case kwLABEL: + str_alltrim(comp_bc_parm); + idx = comp_label_getID(comp_bc_parm); + comp_label_setip(idx); + break; + + case kwEXIT: + bc_add_code(&comp_prog, idx); + str_alltrim(comp_bc_parm); + if (strlen(comp_bc_parm) && comp_bc_parm[0] != '\'') { + idx = comp_is_special_operator(comp_bc_parm); + if (idx == kwFORSEP || idx == kwLOOPSEP || idx == kwPROCSEP || idx == kwFUNCSEP) { + bc_add_code(&comp_prog, idx); } else { - sc_raise(MSG_ON_NOTHING); + sc_raise(MSG_EXIT_ERR); } - break; + } else { + bc_add_code(&comp_prog, 0); + } + break; - case kwFOR: - // - // FOR - // - p = strchr(comp_bc_parm, '='); - p_do = strstr(comp_bc_parm, LCN_DO_WS); - - // fix DO bug - if (p_do) { - if (p > p_do) { - p = NULL; - } - } - strcpy(comp_do_close_cmd, LCN_NEXT); - comp_block_level++; - comp_block_id++; - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, kwFOR, 0, 0); + case kwDECLARE: + break; - if (!p) { - // FOR [EACH] X IN Y - if ((p = strstr(comp_bc_parm, LCN_IN_WS)) == NULL) { - sc_raise(MSG_FOR_NOTHING); - } else { - *p = '\0'; - char *n = p; - strcpy(comp_bc_name, comp_bc_parm); - str_alltrim(comp_bc_name); - if (!is_alpha(*comp_bc_name)) { - sc_raise(MSG_FOR_COUNT_ERR, comp_bc_name); - } else { - char *p_lev = comp_bc_name; - while (is_alnum(*p_lev) || *p_lev == ' ') { - p_lev++; - } - if (*p_lev == '(') { - sc_raise(MSG_FOR_ARR_COUNT, comp_bc_name); - } else { - if (!comp_error_if_keyword(comp_bc_name)) { - comp_add_variable(&comp_prog, comp_bc_name); - *n = ' '; - bc_add_code(&comp_prog, kwIN); - comp_expression(n + 4, 0); - } - } - } - } - } else { - // FOR X=Y TO Z [STEP L] - *p = '\0'; - char *n = p; + case kwPROC: + case kwFUNC: + comp_text_line_func(idx, decl); + break; - strcpy(comp_bc_name, comp_bc_parm); - str_alltrim(comp_bc_name); - if (!is_alpha(*comp_bc_name)) { - sc_raise(MSG_FOR_COUNT_ERR, comp_bc_name); - } else { - char *p_lev = comp_bc_name; - while (is_alnum(*p_lev) || *p_lev == ' ') { - p_lev++; - } - if (*p_lev == '(') { - sc_raise(MSG_FOR_ARR_COUNT, comp_bc_name); - } else { - if (!comp_error_if_keyword(comp_bc_name)) { - comp_add_variable(&comp_prog, comp_bc_name); - *n = '='; - comp_expression(n + 1, 0); - } - } - } + case kwLOCAL: + // local variables + count = comp_getlist(comp_bc_parm, pars, ",", 256); + bc_add_code(&comp_prog, kwTYPE_CRVAR); + bc_add_code(&comp_prog, count); + for (i = 0; i < count; i++) { + comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); + bc_add_addr(&comp_prog, comp_var_getID(vname)); + } + // handle same line variable assignment, eg local blah = foo + for (i = 0; i < count; i++) { + comp_prepare_name(vname, pars[i], SB_KEYWORD_SIZE); + if (strlen(vname) != strlen(pars[i])) { + // kwTYPE_LINE is required for executor + bc_add_code(&comp_prog, kwTYPE_LINE); + bc_add_addr(&comp_prog, comp_line); + comp_text_line(pars[i]); } - break; + } + break; + + case kwREM: + result = 0; + break; + + case kwEXPORT: // export + if (comp_unit_flag) { + bc_store_exports(comp_bc_parm); + } else { + sc_raise(MSG_UNIT_NAME_MISSING); + } + break; + + case kwOPTION: + comp_cmd_option(comp_bc_parm); + break; + + case kwGOTO: + str_alltrim(comp_bc_parm); + comp_push(comp_prog.count); + bc_add_code(&comp_prog, idx); + bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm)); + bc_add_code(&comp_prog, comp_block_level); + break; + + case kwGOSUB: + str_alltrim(comp_bc_parm); + bc_add_code(&comp_prog, idx); + bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm)); + break; + + case kwIF: + strcpy(comp_do_close_cmd, LCN_ENDIF); - case kwWHILE: - strcpy(comp_do_close_cmd, LCN_WEND); + // from here, we can scan for inline IF + if (comp_single_line_if(last_cmd)) { + // inline-IFs + result = 0; + } else { comp_block_level++; comp_block_id++; comp_push(comp_prog.count); bc_add_ctrl(&comp_prog, idx, 0, 0); comp_expression(comp_bc_parm, 0); - break; + bc_add_code(&comp_prog, kwTYPE_EOC); + } + break; - case kwREPEAT: - // WHILE & REPEAT DOES NOT USE STACK - comp_block_level++; - comp_block_id++; - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, idx, 0, 0); - comp_expression(comp_bc_parm, 0); - break; + case kwON: + comp_text_line_on(); + break; - case kwSELECT: - comp_block_level++; - comp_block_id++; - comp_push(comp_prog.count); - bc_add_code(&comp_prog, idx); - // if comp_bc_parm starts with "CASE ", then skip first 5 chars - int index = strncasecmp("CASE ", comp_bc_parm, 5) == 0 ? 5 : 0; - comp_expression(comp_bc_parm + index, 0); - break; + case kwFOR: + comp_text_line_for(); + break; - case kwCASE: - // link to matched block or next CASE/END-SELECT - if (!comp_bc_parm || !comp_bc_parm[0] || strncasecmp(LCN_ELSE, comp_bc_parm, 4) == 0) { - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, kwCASE_ELSE, 0, 0); - } else { - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, idx, 0, 0); - comp_expression(comp_bc_parm, 0); - } - break; + case kwWHILE: + strcpy(comp_do_close_cmd, LCN_WEND); + comp_block_level++; + comp_block_id++; + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, idx, 0, 0); + comp_expression(comp_bc_parm, 0); + break; - case kwTRY: - comp_block_level++; - comp_block_id++; - comp_push(comp_prog.count); - bc_add_code(&comp_prog, idx); - bc_add_addr(&comp_prog, 0); - comp_expression(comp_bc_parm, 0); - break; + case kwREPEAT: + // WHILE & REPEAT DOES NOT USE STACK + comp_block_level++; + comp_block_id++; + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, idx, 0, 0); + comp_expression(comp_bc_parm, 0); + break; - case kwCATCH: + case kwSELECT: + comp_block_level++; + comp_block_id++; + comp_push(comp_prog.count); + bc_add_code(&comp_prog, idx); + // if comp_bc_parm starts with "CASE ", then skip first 5 chars + index = strncasecmp("CASE ", comp_bc_parm, 5) == 0 ? 5 : 0; + comp_expression(comp_bc_parm + index, 0); + break; + + case kwCASE: + // link to matched block or next CASE/END-SELECT + if (!comp_bc_parm || !comp_bc_parm[0] || strncasecmp(LCN_ELSE, comp_bc_parm, 4) == 0) { + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, kwCASE_ELSE, 0, 0); + } else { comp_push(comp_prog.count); bc_add_ctrl(&comp_prog, idx, 0, 0); comp_expression(comp_bc_parm, 0); - break; + } + break; - case kwELSE: - case kwELIF: { - int index = 0; - // handle "ELSE IF" - if (idx == kwELSE && strncasecmp(LCN_IF, comp_bc_parm, 2) == 0) { - idx = kwELIF; - index = 2; - } - // handle error for ELSE xxxx - if (idx == kwELSE && comp_bc_parm[0]) { - sc_raise(ERR_SYNTAX); - break; - } - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, idx, 0, 0); - comp_expression(comp_bc_parm + index, 0); + case kwTRY: + comp_block_level++; + comp_block_id++; + comp_push(comp_prog.count); + bc_add_code(&comp_prog, idx); + bc_add_addr(&comp_prog, 0); + comp_expression(comp_bc_parm, 0); + break; + + case kwCATCH: + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, idx, 0, 0); + bc_add_code(&comp_prog, comp_block_level); + comp_expression(comp_bc_parm, 0); + break; + + case kwELSE: + case kwELIF: + index = 0; + // handle "ELSE IF" + if (idx == kwELSE && strncasecmp(LCN_IF, comp_bc_parm, 2) == 0) { + idx = kwELIF; + index = 2; } + // handle error for ELSE xxxx + if (idx == kwELSE && comp_bc_parm[0]) { + sc_raise(ERR_SYNTAX); break; + } + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, idx, 0, 0); + comp_expression(comp_bc_parm + index, 0); + break; - case kwENDIF: - case kwNEXT: - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, idx, 0, 0); - comp_block_level--; - comp_block_id--; - break; + case kwENDIF: + case kwNEXT: + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, idx, 0, 0); + comp_block_level--; + comp_block_id--; + break; - case kwWEND: - case kwUNTIL: - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, idx, 0, 0); - comp_block_level--; - comp_block_id--; - comp_expression(comp_bc_parm, 0); - break; + case kwWEND: + case kwUNTIL: + comp_push(comp_prog.count); + bc_add_ctrl(&comp_prog, idx, 0, 0); + comp_block_level--; + comp_block_id--; + comp_expression(comp_bc_parm, 0); + break; - case kwSTEP: - case kwTO: - case kwIN: - case kwTHEN: - case kwCOS: - case kwSIN: - case kwLEN: - case kwLOOP: // functions... - sc_raise(MSG_SPECIAL_KW_ERR, comp_bc_name); - break; + case kwSTEP: + case kwTO: + case kwIN: + case kwTHEN: + case kwCOS: + case kwSIN: + case kwLEN: + case kwLOOP: // functions... + sc_raise(MSG_SPECIAL_KW_ERR, comp_bc_name); + break; - case kwRESTORE: - comp_push(comp_prog.count); - bc_add_code(&comp_prog, idx); - bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm)); - break; + case kwRESTORE: + comp_push(comp_prog.count); + bc_add_code(&comp_prog, idx); + bc_add_addr(&comp_prog, comp_label_getID(comp_bc_parm)); + break; - case kwEND: - if (strncmp(comp_bc_parm, LCN_IF, 2) == 0 || - strncmp(comp_bc_parm, LCN_TRY, 3) == 0 || - strncmp(comp_bc_parm, LCN_SELECT, 6) == 0) { - idx = strncmp(comp_bc_parm, LCN_IF, 2) == 0 ? kwENDIF : - strncmp(comp_bc_parm, LCN_TRY, 3) == 0 ? kwENDTRY : kwENDSELECT; - comp_push(comp_prog.count); - if (idx == kwENDTRY) { - bc_add_code(&comp_prog, idx); - } else { - bc_add_ctrl(&comp_prog, idx, 0, 0); - } - comp_block_level--; - comp_block_id--; - } else if (comp_proc_level) { - char *dol; + case kwEND: + comp_text_line_end(idx); + break; - // UDP/F RETURN - dol = strrchr(comp_bc_proc, '/'); - if (dol) { - *dol = '\0'; - } else { - *comp_bc_proc = '\0'; - } - comp_push(comp_prog.count); - bc_add_code(&comp_prog, kwTYPE_RET); + case kwDATA: + comp_data_seg(comp_bc_parm); + break; - comp_proc_level--; - comp_block_level--; - comp_block_id++; // advance to next block - } else { - // END OF PROG - bc_add_code(&comp_prog, idx); - } - break; + case kwREAD: + bc_add_code(&comp_prog, sharp ? kwFILEREAD : idx); + comp_expression(comp_bc_parm, 0); + break; - case kwDATA: - comp_data_seg(comp_bc_parm); - break; + case kwINPUT: + bc_add_code(&comp_prog, sharp ? kwFILEINPUT : idx); + comp_expression(comp_bc_parm, 0); + break; - case kwREAD: - bc_add_code(&comp_prog, sharp ? kwFILEREAD : idx); - comp_expression(comp_bc_parm, 0); - break; + case kwPRINT: + bc_add_code(&comp_prog, sharp ? kwFILEPRINT : idx); + comp_expression(comp_bc_parm, 0); + break; - case kwINPUT: - bc_add_code(&comp_prog, sharp ? kwFILEINPUT : idx); + case kwLINE: + if (strncmp(comp_bc_parm, LCN_INPUT_WRS, 6) == 0) { + bc_add_code(&comp_prog, kwLINEINPUT); + comp_expression(comp_bc_parm + 6, 0); + } else { + bc_add_code(&comp_prog, idx); comp_expression(comp_bc_parm, 0); - break; + } + break; - case kwPRINT: - bc_add_code(&comp_prog, sharp ? kwFILEPRINT : idx); - comp_expression(comp_bc_parm, 0); - break; + case -1: + comp_text_line_ext_func(); + break; - case kwLINE: - if (strncmp(comp_bc_parm, LCN_INPUT_WRS, 6) == 0) { - bc_add_code(&comp_prog, kwLINEINPUT); - comp_expression(comp_bc_parm + 6, 0); - } else { - bc_add_code(&comp_prog, idx); - comp_expression(comp_bc_parm, 0); + default: + // something else + bc_add_code(&comp_prog, idx); + comp_expression(comp_bc_parm, 0); + } + + return result; +} + +/* + * Pass 1: scan source line + */ +void comp_text_line(char *text) { + long idx; + int decl = 0; + + if (comp_error) { + return; + } + str_alltrim(text); + char *p = text; + + // EOL + if (*p == ':') { + p++; + comp_text_line(p); + return; + } + // remark + if (*p == '\'' || *p == '#') { + return; + } + // empty line + if (*p == '\0') { + return; + } + + char *lb_end = (char *)comp_next_word(text, comp_bc_name); + char *last_cmd = lb_end; + p = get_param_sect(lb_end, ":", comp_bc_parm); + + // check old style labels + if (is_all_digits(comp_bc_name)) { + str_alltrim(comp_bc_name); + idx = comp_label_getID(comp_bc_name); + comp_label_setip(idx); + if (comp_error) { + return; + } + // continue + last_cmd = p = (char *)comp_next_word(lb_end, comp_bc_name); + if (strlen(comp_bc_name) == 0) { + if (!p) { + return; } - break; + if (*p == '\0') { + return; + } + } + p = get_param_sect(p, ":", comp_bc_parm); + } - case -1: - // EXTERNAL OR USER-DEFINED PROCEDURE - udp = comp_is_external_proc(comp_bc_name); - if (udp > -1) { - bc_add_extpcode(&comp_prog, comp_extproctable[udp].lib_id, - comp_extproctable[udp].symbol_index); + idx = comp_is_keyword(comp_bc_name); + if (idx == kwREM) { + return; + } + if (idx == -1) { + idx = comp_is_proc(comp_bc_name); + if (idx != -1) { + if (idx == kwCALLCP) { + bc_add_code(&comp_prog, kwTYPE_CALL_UDP); + bc_add_addr(&comp_prog, idx); // place holder + bc_add_addr(&comp_prog, 0); // return-variable ID + bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); + // allow cmd_udp to find the initial var-ptr arg + bc_add_code(&comp_prog, kwTYPE_CALL_PTR); char *next = trim_empty_parentheses(comp_bc_parm); - if (comp_is_parenthesized(next)) { - comp_expression(next, 0); - } else { - bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); - comp_expression(next, 0); - bc_add_code(&comp_prog, kwTYPE_LEVEL_END); - } + comp_expression(next, 0); + bc_add_code(&comp_prog, kwTYPE_LEVEL_END); } else { - udp = comp_udp_id(comp_bc_name, 1); - if (udp == -1) { - udp = comp_add_udp(comp_bc_name); - } - comp_push(comp_prog.count); - bc_add_ctrl(&comp_prog, kwTYPE_CALL_UDP, udp, 0); + // simple buildin procedure + // there is no need to check it more... + // save it and return (go to next) + bc_add_pcode(&comp_prog, idx); char *next = trim_empty_parentheses(comp_bc_parm); - if (comp_is_parenthesized(next)) { - comp_expression(next, 0); - } else { - bc_add_code(&comp_prog, kwTYPE_LEVEL_BEGIN); - comp_expression(next, 0); - bc_add_code(&comp_prog, kwTYPE_LEVEL_END); - } + comp_expression(next, 0); } - break; - - default: - // something else - bc_add_code(&comp_prog, idx); - comp_expression(comp_bc_parm, 0); + if (*p == ':') { // command separator + bc_eoc(&comp_prog); + p++; + comp_text_line(p); + } + return; + } + } + if (idx == kwLET) { // old-style keyword LET + char *p; + idx = -1; + p = (char *)comp_next_word(comp_bc_parm, comp_bc_name); + strcpy(comp_bc_parm, p); + } else if (idx == kwDECLARE) { // declaration + char *p; + decl = 1; + p = (char *)comp_next_word(comp_bc_parm, comp_bc_name); + idx = comp_is_keyword(comp_bc_name); + if (idx == -1) { + idx = comp_is_proc(comp_bc_name); + } + strcpy(comp_bc_parm, p); + if (idx != kwPROC && idx != kwFUNC) { + sc_raise(MSG_USE_DECL); + return; } } + if (idx == kwREM) { + return; + } - if (*p == ':') { + int sharp, ladd,linc, ldec, leqop; + sharp = (comp_bc_parm[0] == '#'); // if # -> file commands + comp_get_unary(comp_bc_parm, &ladd, &linc, &ldec, &leqop); + + if ((comp_bc_parm[0] == '=' || ladd || linc || ldec || leqop) && (idx != -1)) { + sc_raise(MSG_IT_IS_KEYWORD, comp_bc_name); + return; + } + if ((idx == kwCONST) || + ((comp_bc_parm[0] == '=' || + (comp_bc_parm[0] == '(' && !comp_is_function(comp_bc_name)) || + ladd || linc || ldec || leqop) && (idx == -1))) { + comp_text_line_let(idx, ladd, linc, ldec, leqop); + } else { + if (!comp_text_line_command(idx, decl, sharp, last_cmd)) { + p = NULL; + } + } + if (p != NULL && *p == ':') { // command separator bc_eoc(&comp_prog); p++; @@ -2586,7 +2674,6 @@ addr_t comp_next_bc_cmd(addr_t ip) { case kwTYPE_CALLP: // [fcode_t] ip += CODESZ; break; - case kwTYPE_CALLEXTF: case kwTYPE_CALLEXTP: // [lib][index] ip += (ADDRSZ * 2); @@ -2601,7 +2688,6 @@ addr_t comp_next_bc_cmd(addr_t ip) { case kwTYPE_UNROPR: // [1B data] ip++; break; - case kwTRY: case kwRESTORE: case kwGOSUB: @@ -2647,10 +2733,11 @@ addr_t comp_next_bc_cmd(addr_t ip) { case kwCASE: case kwCASE_ELSE: case kwENDSELECT: - case kwCATCH: ip += BC_CTRLSZ; break; - + case kwCATCH: + ip += BC_CTRLSZ + 1; + break; case kwTYPE_EVAL_SC: ip += 2; // kwTYPE_LOGOPR+op ip += ADDRSZ; // the shortcut address @@ -3504,20 +3591,22 @@ char *comp_format_text(const char *source) { // skip the rest line while (*p) { - if (*p == '\n') + if (*p == '\n') { break; + } p++; } break; } else { - if ((*p > ' ') || (*p < 0)) { // simple - // code-character + if ((*p > ' ') || (*p < 0)) { + // simple code-character last_nonsp_ptr = ps; *ps++ = last_ch = to_upper(*p); p++; - } else + } else { + // else ignore it p++; - // else ignore it + } } } } else { // in quotes diff --git a/src/common/str.c b/src/common/str.c index 85c31765..3f3b12fb 100644 --- a/src/common/str.c +++ b/src/common/str.c @@ -47,12 +47,24 @@ char *trimdup(const char *str) { return buf; } +/** + * whether the string contains any whitespace characters + */ +int has_wspace(const char *s) { + int result = 0; + int i; + for (i = 0; s != NULL && s[i] && !result; i++) { + result |= is_wspace(s[i]); + } + return result; +} + /** * removes spaces */ void str_alltrim(char *str) { char *buf; - if (str && str[0]) { + if (str && has_wspace(str)) { buf = trimdup(str); strcpy(str, buf); tmp_free(buf); @@ -605,10 +617,12 @@ char *get_numexpr(char *text, char *dest, int *type, var_int_t *lv, var_num_t *d } } // - if (is_alpha(*p)) + if (is_alpha(*p)) { *type = -9; // ITS NOT A NUMBER - while (is_space(*p)) + } + while (is_space(*p)) { p++; + } return p; } @@ -661,8 +675,10 @@ long bintol(const char *str) { return 0; } while (*p) { - if (*p == 48 || *p == 49) // 01 + if (*p == 48 || *p == 49) { + // 01 r = (r << 1) + ((*p) - 48); + } p++; } return r; @@ -679,8 +695,10 @@ long octtol(const char *str) { return 0; } while (*p) { - if (*p >= 48 && *p <= 55) // 01234567 + if (*p >= 48 && *p <= 55) { + // 01234567 r = (r << 3) + ((*p) - 48); + } p++; } return r; @@ -697,12 +715,16 @@ long hextol(const char *str) { return 0; } while (*p) { - if (is_digit(*p)) // 0123456789 + if (is_digit(*p)) { + // 0123456789 r = (r << 4) + ((*p) - 48); - else if (*p >= 65 && *p <= 70) // ABCDEF + } else if (*p >= 65 && *p <= 70) { + // ABCDEF r = (r << 4) + ((*p) - 55); - else if (*p >= 97 && *p <= 102) // abcdef + } else if (*p >= 97 && *p <= 102) { + // abcdef r = (r << 4) + ((*p) - 87); + } p++; } return r; @@ -724,26 +746,25 @@ var_num_t sb_strtof(const char *str) { if (*p == '-') { sign = -1; p++; - } else if (*p == '+') + } else if (*p == '+') { p++; - + } while (*p) { if (is_digit(*p)) { - if (!decp) + if (!decp) { r = (r * 10) + ((*p) - 48); - else { + } else { r += (((*p) - 48) * 1 / d); d *= 10; } - } else if (*p == '.') + } else if (*p == '.') { decp = 1; - else if (*p == ' ') + } else if (*p == ' ') { break; - else { + } else { r = 0; break; } - p++; } @@ -761,7 +782,7 @@ long xstrtol(const char *str) { } /** - * + * whether the string is a number */ int is_number(const char *str) { char *p = (char *) str; @@ -770,25 +791,26 @@ int is_number(const char *str) { if (str == NULL) { return 0; } - if (*p == '+' || *p == '-') + if (*p == '+' || *p == '-') { p++; - + } while (*p) { - if (strchr("0123456789.", *p) == NULL - ) + if (strchr("0123456789.", *p) == NULL) { return 0; - else + } else { cnt++; + } if (*p == '.') { dpc++; - if (dpc > 1) + if (dpc > 1) { return 0; + } } p++; } - - if (cnt) + if (cnt) { return 1; + } return 0; } @@ -856,20 +878,21 @@ char *xbasename(char *dest, const char *source) { char *p; p = strrchr(source, OS_DIRSEP); - if (!p) + if (!p) { p = (char *) source; - else + } else { p++; - + } strcpy(dest, p); return dest; } /** - * + * returns whether the character is whitespace */ int is_wspace(int c) { - return (c != 0 && strchr(" \t\n\r\v\f", c)); + return (c != 0 && (c == ' ' || c == '\t' || c == '\r' || + c == '\n' || c == '\v' || c == '\f')); } /** @@ -905,16 +928,17 @@ char *sqzdup(const char *source) { } } } - } else + } else { (lc = 0, *d++ = *p); - + } p++; } *d = '\0'; if (d > rp) { - if (is_wspace(*(d - 1))) + if (is_wspace(*(d - 1))) { *(d - 1) = '\0'; + } } return rp; diff --git a/src/common/sys.h b/src/common/sys.h index 651d428c..5ed15a50 100644 --- a/src/common/sys.h +++ b/src/common/sys.h @@ -17,8 +17,6 @@ * * CPU_LITTLEENDIAN The words are stored normal; first the high-byte followed by low-byte (Motorola 68K) * - * CPU_REG16 16bit registers (64KB code|data segments) - * * OS FLAGS * * OS_PATHNAME_SIZE Maximum full-path name size (DOS=64,Unix/Win32=1024) diff --git a/src/common/tasks.h b/src/common/tasks.h index 56844b9b..f38f7c1c 100644 --- a/src/common/tasks.h +++ b/src/common/tasks.h @@ -10,9 +10,7 @@ #if !defined(__sb_tasks_h) #define __sb_tasks_h -#include "common/smbas.h" #include "common/bc.h" -#include "common/keymap.h" #if defined(__cplusplus) extern "C" { diff --git a/src/common/var.c b/src/common/var.c index 00204257..82383896 100644 --- a/src/common/var.c +++ b/src/common/var.c @@ -12,7 +12,7 @@ #include "common/var.h" #include "common/smbas.h" #include "common/sberr.h" -#include "common/var_hash.h" +#include "common/var_map.h" #define ARR_ALLOC 256 @@ -35,8 +35,8 @@ int v_isempty(var_t *var) { return (strlen((char *)var->v.p.ptr) == 0); case V_INT: return (var->v.i == 0); - case V_HASH: - return hash_is_empty(var); + case V_MAP: + return map_is_empty(var); case V_PTR: return (var->v.ap.p == 0); case V_NUM: @@ -59,8 +59,8 @@ int v_length(var_t *var) { switch (var->type) { case V_STR: return strlen((char *)var->v.p.ptr); - case V_HASH: - return hash_length(var); + case V_MAP: + return map_length(var); case V_PTR: ltostr(var->v.ap.p, tmpsb); return strlen(tmpsb); @@ -84,9 +84,9 @@ int v_length(var_t *var) { */ var_t *v_getelemptr(var_t *v, dword index) { if (v->type == V_ARRAY) { - if (index < v->v.a.size) + if (index < v->v.a.size) { return (var_t *)(v->v.a.ptr + (index * sizeof(var_t))); - else { + } else { err_vararridx(index, v->v.a.size); return NULL; } @@ -148,8 +148,9 @@ void v_resize_array(var_t *v, dword size) { // resize & copy prev = v->v.a.ptr; v->v.a.ptr = tmp_alloc((size + ARR_ALLOC) * sizeof(var_t)); - if (v->v.a.size > 0) + if (v->v.a.size > 0) { memcpy(v->v.a.ptr, prev, v->v.a.size * sizeof(var_t)); + } } else { prev = NULL; } @@ -255,8 +256,8 @@ int v_is_nonzero(var_t *v) { return (ABS(v->v.n) > 1E-308); case V_STR: return (v->v.p.size != 0); - case V_HASH: - return !hash_is_empty(v); + case V_MAP: + return !map_is_empty(v); case V_PTR: return (v->v.ap.p != 0); case V_ARRAY: @@ -275,8 +276,6 @@ int v_is_nonzero(var_t *v) { int v_compare(var_t *a, var_t *b) { var_num_t dt; var_int_t di; - int i, ci; - var_t *ea, *eb; if (a == 0 || b == 0) { err_evsyntax(); @@ -285,15 +284,13 @@ int v_compare(var_t *a, var_t *b) { if (a->type == V_INT && b->type == V_INT) { di = (a->v.i - b->v.i); - i = di < 0 ? -1 : di > 0 ? 1 : 0; - return i; + return (di < 0 ? -1 : di > 0 ? 1 : 0); } else if ((a->type == V_INT || a->type == V_NUM) && (b->type == V_INT || b->type == V_NUM)) { var_num_t left = (a->type == V_NUM) ? a->v.n : a->v.i; var_num_t right = (b->type == V_NUM) ? b->v.n : b->v.i; dt = (left - right); - i = dt < 0.0 ? -1 : dt > 0.0 ? 1 : 0; - return i; + return (dt < 0.0 ? -1 : dt < 0.0000000000000000001f ? 0 : 1); } if ((a->type == V_STR) && (b->type == V_STR)) { return strcmp(a->v.p.ptr, b->v.p.ptr); @@ -339,9 +336,10 @@ int v_compare(var_t *a, var_t *b) { return 1; } // check every element + int i, ci; for (i = 0; i < a->v.a.size; i++) { - ea = (var_t *)(a->v.a.ptr + sizeof(var_t) * i); - eb = (var_t *)(b->v.a.ptr + sizeof(var_t) * i); + var_t *ea = (var_t *)(a->v.a.ptr + sizeof(var_t) * i); + var_t *eb = (var_t *)(b->v.a.ptr + sizeof(var_t) * i); if ((ci = v_compare(ea, eb)) != 0) { return ci; } @@ -350,8 +348,8 @@ int v_compare(var_t *a, var_t *b) { return 0; } - if (a->type == V_HASH && b->type == V_HASH) { - return hash_compare(a, b); + if (a->type == V_MAP && b->type == V_MAP) { + return map_compare(a, b); } err_evtype(); @@ -436,8 +434,8 @@ void v_add(var_t *result, var_t *a, var_t *b) { * assign (dest = src) */ void v_set(var_t *dest, const var_t *src) { - if (src->type == V_HASH) { - hash_set(dest, (const var_p_t) src); + if (src->type == V_MAP) { + map_set(dest, (const var_p_t) src); return; } @@ -530,38 +528,49 @@ void v_createstr(var_t *v, const char *src) { strcpy(v->v.p.ptr, src); } +/* + * returns the string representation of the variable + */ +char *v_str(var_t *arg) { + char *buffer; + switch (arg->type) { + case V_INT: + buffer = tmp_alloc(64); + ltostr(arg->v.i, (char *)buffer); + break; + case V_NUM: + buffer = tmp_alloc(64); + ftostr(arg->v.n, (char *)buffer); + break; + case V_STR: + buffer = (byte *)tmp_strdup((char *)arg->v.p.ptr); + break; + case V_ARRAY: + case V_MAP: + buffer = map_to_str(arg); + break; + case V_FUNC: + buffer = tmp_alloc(5); + strcpy(buffer, "func"); + break; + default: + buffer = tmp_alloc(1); + buffer[0] = '\0'; + break; + } + return buffer; +} + /* * converts the variable to string-variable */ void v_tostr(var_t *arg) { if (arg->type != V_STR) { - int l; - char *tmp = tmp_alloc(64); - - switch (arg->type) { - case V_HASH: - hash_to_str(arg, tmp, 64); - hash_free(arg); - break; - case V_PTR: - ltostr(arg->v.ap.p, tmp); - break; - case V_INT: - ltostr(arg->v.i, tmp); - break; - case V_NUM: - ftostr(arg->v.n, tmp); - break; - default: - err_varisarray(); - tmp_free(tmp); - return; - } - - l = strlen(tmp) + 1; + char *tmp = v_str(arg); + int len = strlen(tmp) + 1; arg->type = V_STR; - arg->v.p.ptr = tmp_alloc(l); - arg->v.p.size = l; + arg->v.p.ptr = tmp_alloc(len); + arg->v.p.size = len; strcpy(arg->v.p.ptr, tmp); tmp_free(tmp); } @@ -587,7 +596,7 @@ void v_setstrn(var_t *var, const char *string, int len) { var->v.p.size = len + 1; var->v.p.ptr = tmp_alloc(var->v.p.size); strncpy(var->v.p.ptr, string, len); - var->v.p.ptr[len] = 0; + var->v.p.ptr[len] = '\0'; } } @@ -736,48 +745,3 @@ void v_input2var(const char *str, var_t *var) { } } -/* - * evaluate the pcode string - */ -void v_eval_str(var_p_t v) { - int len = code_getstrlen(); - v->type = V_STR; - v->v.p.size = len; - v->v.p.ptr = tmp_alloc(len + 1); - memcpy(v->v.p.ptr, &prog_source[prog_ip], len); - *((char *)(v->v.p.ptr + len)) = '\0'; - prog_ip += len; -} - -/* - * evaluate variable reference assignment - */ -void v_eval_ref(var_t *v_left) { - var_t *v_right = NULL; - // can only reference regular non-dynamic variable - if (code_peek() == kwTYPE_VAR) { - code_skipnext(); - v_right = tvar[code_getaddr()]; - switch (v_right->type) { - case V_ARRAY: - case V_HASH: - switch (code_peek()) { - case kwTYPE_LEVEL_BEGIN: - case kwTYPE_UDS_EL: - // base variable only supported - v_right = NULL; - break; - } - break; - default: - break; - } - } - if (v_right == NULL) { - err_ref_var(); - } else { - v_free(v_left); - v_left->type = V_REF; - v_left->v.ref = v_right; - } -} diff --git a/src/common/var.h b/src/common/var.h index 2dede6d3..9fe54629 100644 --- a/src/common/var.h +++ b/src/common/var.h @@ -35,8 +35,9 @@ #define V_STR 2 /**< variable type, string @ingroup var */ #define V_ARRAY 3 /**< variable type, array of variables @ingroup var */ #define V_PTR 4 /**< variable type, pointer to UDF or label @ingroup var */ -#define V_HASH 5 /**< variable type, hash @ingroup var */ +#define V_MAP 5 /**< variable type, associative array @ingroup var */ #define V_REF 6 /**< variable type, reference another var @ingroup var */ +#define V_FUNC 7 /**< variable type, object method @ingroup var */ /* * predefined system variables - index @@ -70,6 +71,8 @@ extern "C" { #endif +typedef void (*method) (const void *self); + /** * @ingroup var * @typedef var_s @@ -91,12 +94,18 @@ struct var_s { addr_t v; /** return-var ID */ } ap; - // hash map - void *hash; /** pointer the hash structure */ + // associative array/map + void *map; /** pointer the map structure */ // reference variable void *ref; + // object method + struct { + method cb; + void *self; + } fn; + // generic ptr (string) struct { byte *ptr; /**< data ptr (possibly, string pointer) */ @@ -313,6 +322,15 @@ var_t *v_getelemptr(var_t *v, dword index); */ void v_createstr(var_t *v, const char *src); +/** + * @ingroup var + * + * print variable as string + * + * @param arg is the variable + */ +char *v_str(var_t *arg); + /** * @ingroup var * @@ -640,29 +658,14 @@ char *v_getstr(var_t *v); /** * @ingroup var * - * populates the var to string from bytecode + * creates an image object * * @param v is the variable */ -void v_eval_str(var_p_t v); - -/** - * @ingroup var - * - * evaluate variable reference assignment - */ -void v_eval_ref(var_t *l_value); +void v_create_image(var_p_t var); -/* - * low-level byte-code parsing - * - * Usually you must not use these functions (except the rt_raise) - * try the parameters API. - */ - -int code_checkop(byte op); -int code_checkop_s(byte *str); void code_jump_label(word label_id); // IP <- LABEL_IP_TABLE[label_id] + #define code_jump(newip) prog_ip=(newip) /**< IP <- NewIP @ingroup exec */ /** @@ -683,15 +686,6 @@ void code_push(stknode_t *node); */ void code_pop(stknode_t *node); -/** - * @ingroup exec - * - * returns true if the next code is a single variable - * - * @return non-zero if the following code is a variable - */ -int code_isvar(void); - /** * @ingroup exec * diff --git a/src/common/var_eval.c b/src/common/var_eval.c new file mode 100644 index 00000000..4a62f110 --- /dev/null +++ b/src/common/var_eval.c @@ -0,0 +1,340 @@ +// This file is part of SmallBASIC +// +// Evaluate variables from bytecode +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2010-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] + +#include "common/sys.h" +#include "common/kw.h" +#include "common/sberr.h" +#include "common/smbas.h" +#include "common/var.h" +#include "common/var_eval.h" + +/** + * Convertion multi-dim index to one-dim index + */ +addr_t getarrayidx(var_t *array, var_t **var_map_val) { + addr_t idx = 0; + addr_t lev = 0; + addr_t m = 0; + var_t var; + addr_t i; + + do { + v_init(&var); + eval(&var); + IF_ERR_RETURN_0; + + if (var.type == V_STR || array->type == V_MAP) { + // array element is a string or element is addressing a map + map_get_value(array, &var, var_map_val); + + if (code_peek() == kwTYPE_LEVEL_END) { + code_skipnext(); + } else { + err_missing_sep(); + } + v_free(&var); + return 0; + } else { + addr_t idim = v_getint(&var); + v_free(&var); + IF_ERR_RETURN_0; + + idim = idim - array->v.a.lbound[lev]; + + m = idim; + for (i = lev + 1; i < array->v.a.maxdim; i++) { + m = m * (ABS(array->v.a.ubound[i] - array->v.a.lbound[i]) + 1); + } + idx += m; + + // skip separator + byte code = code_peek(); + if (code == kwTYPE_SEP) { + code_skipnext(); + if (code_getnext() != ',') { + err_syntax_error(); + } + } + // next + lev++; + } + } while (code_peek() != kwTYPE_LEVEL_END); + + if (!prog_error) { + if ((int) array->v.a.maxdim != lev) { + err_missing_sep(); + } + } + return idx; +} + +/** + * Used by code_getvarptr() to retrieve an element ptr of an array + */ +var_t *code_getvarptr_arridx(var_t *basevar_p) { + addr_t array_index; + var_t *var_p = NULL; + + if (code_peek() != kwTYPE_LEVEL_BEGIN) { + err_arrmis_lp(); + } else { + code_skipnext(); // '(' + array_index = getarrayidx(basevar_p, &var_p); + + if (var_p != NULL) { + // map value + return var_p; + } + + if (!prog_error) { + if ((int) array_index < basevar_p->v.a.size && (int) array_index >= 0) { + var_p = (var_t *)(basevar_p->v.a.ptr + (array_index * sizeof(var_t))); + + if (code_peek() == kwTYPE_LEVEL_END) { + code_skipnext(); // ')', ')' level + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + // there is a second array inside + if (var_p->type != V_ARRAY) { + err_varisnotarray(); + } else { + return code_getvarptr_arridx(var_p); + } + } + } else { + err_arrmis_rp(); + } + } else { + err_arridx(array_index, basevar_p->v.a.size); + } + } + } + return var_p; +} + +/** + * resolve a composite variable reference, eg: ar.ch(0).foo + */ +var_t *code_resolve_varptr(var_t *var_p, int until_parens) { + int deref = 1; + var_p = eval_ref_var(var_p); + while (deref && var_p != NULL) { + switch (code_peek()) { + case kwTYPE_LEVEL_BEGIN: + if (until_parens) { + deref = 0; + } else { + var_p = code_getvarptr_arridx(var_p); + } + break; + case kwTYPE_UDS_EL: + var_p = map_resolve_fields(var_p); + break; + default: + deref = 0; + } + var_p = eval_ref_var(var_p); + } + return var_p; +} + +/** + * Used by code_isvar() to retrieve an element ptr of an array + */ +var_t *code_isvar_arridx(var_t *basevar_p) { + addr_t array_index; + var_t *var_p = NULL; + + if (code_peek() != kwTYPE_LEVEL_BEGIN) { + return NULL; + } else { + code_skipnext(); // '(' + array_index = getarrayidx(basevar_p, &var_p); + + if (var_p != NULL) { + // map value + return var_p; + } + + if (!prog_error) { + if ((int) array_index < basevar_p->v.a.size) { + var_p = (var_t *)(basevar_p->v.a.ptr + (array_index * sizeof(var_t))); + + if (code_peek() == kwTYPE_LEVEL_END) { + code_skipnext(); // ')', ')' level + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + // there is a second array inside + if (var_p->type != V_ARRAY) { + return NULL; + } else { + return code_isvar_arridx(var_p); + } + } + } else { + return NULL; + } + } else { + return NULL; + } + } + } + + return var_p; +} + +/** + * returns true if the next code is a variable. if the following code is an + * expression (no matter if the first item is a variable), returns false + */ +int code_isvar() { + var_t *basevar_p; + var_t *var_p = NULL; + + // store IP + addr_t cur_ip = prog_ip; + + if (code_peek() == kwTYPE_VAR) { + code_skipnext(); + var_p = basevar_p = tvar[code_getaddr()]; + switch (basevar_p->type) { + case V_MAP: + case V_ARRAY: + // variable is an array or map + var_p = code_resolve_varptr(var_p, 0); + break; + default: + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + var_p = NULL; + } + } + } + + if (var_p) { + if (kw_check_evexit(code_peek()) || code_peek() == kwTYPE_LEVEL_END) { + // restore IP + prog_ip = cur_ip; + return 1; + } + } + + // restore IP + prog_ip = cur_ip; + return 0; +} + +var_t *eval_ref_var(var_t *var_p) { + var_t *result = var_p; + while (result != NULL && result->type == V_REF) { + if (result->v.ref == var_p) { + // circular referance error + result = NULL; + err_ref_circ_var(); + break; + } else { + result = result->v.ref; + } + } + return result; +} + +var_t *resolve_var_ref(var_t *var_p) { + switch (code_peek()) { + case kwTYPE_LEVEL_BEGIN: + var_p = resolve_var_ref(code_getvarptr_arridx(var_p)); + break; + case kwTYPE_UDS_EL: + var_p = resolve_var_ref(map_resolve_fields(var_p)); + break; + } + return var_p; +} + +void v_eval_ref(var_t *v_left) { + var_t *v_right = NULL; + // can only reference regular non-dynamic variable + if (code_peek() == kwTYPE_VAR) { + code_skipnext(); + v_right = tvar[code_getaddr()]; + switch (v_right->type) { + case V_ARRAY: + case V_MAP: + switch (code_peek()) { + case kwTYPE_LEVEL_BEGIN: + case kwTYPE_UDS_EL: + // base variable or element reference only supported + v_right = resolve_var_ref(v_right); + if (v_right != NULL && v_right->type != V_REF) { + v_right = NULL; + } + break; + } + break; + default: + break; + } + if (v_right != NULL) { + int i; + int level = 0; + int left_level = -1; + int right_level = -1; + for (i = 0; i < prog_stack_count; i++) { + stknode_t *cur_node = &prog_stack[i]; + var_t *v_stack = NULL; + switch (cur_node->type) { + case kwTYPE_CRVAR: + case kwBYREF: + v_stack = tvar[cur_node->x.vdvar.vid]; + break; + case kwTYPE_VAR: + v_stack = cur_node->x.param.res; + break; + case kwPROC: + case kwFUNC: + case kwFOR: + case kwSELECT: + level++; + break; + } + if (v_stack != NULL) { + int next_level = (cur_node->type == kwBYREF) ? level -1 : level; + if (left_level == -1 && v_left == v_stack) { + left_level = next_level; + } + if (right_level == -1 && v_right == v_stack) { + right_level = next_level; + } + } + } + if (left_level < right_level && right_level > 0) { + // cannot assign left to higher scope right variable + v_right = NULL; + } + } + } + if (v_right == NULL) { + err_ref_var(); + } else { + v_free(v_left); + v_left->type = V_REF; + v_left->v.ref = v_right; + } +} + +/* + * evaluate the pcode string + */ +void v_eval_str(var_p_t v) { + int len = code_getstrlen(); + v->type = V_STR; + v->v.p.size = len; + v->v.p.ptr = tmp_alloc(len + 1); + memcpy(v->v.p.ptr, &prog_source[prog_ip], len); + *((char *)(v->v.p.ptr + len)) = '\0'; + prog_ip += len; +} + diff --git a/src/common/var_eval.h b/src/common/var_eval.h new file mode 100644 index 00000000..4c7ec104 --- /dev/null +++ b/src/common/var_eval.h @@ -0,0 +1,61 @@ +// This file is part of SmallBASIC +// +// Evaluate code variables +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2010-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] + +#ifndef VAR_EVAL_H +#define VAR_EVAL_H + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * @ingroup var + * + * resolve var pointer + */ +var_t *code_resolve_varptr(var_t* var_p, int until_parens); + +/** + * @ingroup var + * + * evaluate variable reference assignment + */ +var_t *eval_ref_var(var_t *var_p); + +/** + * @ingroup var + * + * evaluate variable reference assignment + */ +void v_eval_ref(var_t *l_value); + +/** + * @ingroup exec + * + * returns true if the next code is a single variable + * + * @return non-zero if the following code is a variable + */ +int code_isvar(void); + +/** + * @ingroup var + * + * populates the var to string from bytecode + * + * @param v is the variable + */ +void v_eval_str(var_p_t v); + +#if defined(__cplusplus) +} +#endif + +#endif + diff --git a/src/common/var_hash.c b/src/common/var_hash.c deleted file mode 100644 index b84eb36a..00000000 --- a/src/common/var_hash.c +++ /dev/null @@ -1,353 +0,0 @@ -// This file is part of SmallBASIC -// -// Support for hash variables -// eg: dim foo: key="blah": foo(key) = "something": ? foo(key) -// -// This program is distributed under the terms of the GPL v2.0 or later -// Download the GNU Public License (GPL) from www.gnu.org -// -// Copyright(C) 2010-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] - -#include "common/sys.h" -#include "common/var.h" -#include "common/smbas.h" -#include "common/var_hash.h" -#include "common/search.h" - -/** - * Globals for callback access - */ -typedef struct CallbackData { - int method; - int handle; - int index; - int count; - int firstElement; - void* hash; - var_p_t var; -} CallbackData; - -CallbackData cb; - -/** - * Our internal tree element node - */ -typedef struct Element { - var_p_t key; - var_p_t value; -} Element; - -/** - * Return value from search routines - */ -typedef struct Node { - Element *element; -} Node; - -/** - * Returns a new Element - */ -Element *create_element(var_p_t key) { - Element *element = (Element *) tmp_alloc(sizeof (Element)); - element->key = v_new(); - v_set(element->key, key); - element->value = NULL; - return element; -} - -/** - * Returns a new Element using the integer based key - */ -Element *create_int_element(int key) { - Element *element = (Element *) tmp_alloc(sizeof (Element)); - element->key = v_new(); - v_setint(element->key, key); - element->value = NULL; - return element; -} - -/** - * cleanup the given element - */ -void delete_element(Element *element) { - // cleanup v_new - v_free(element->key); - tmp_free(element->key); - - // cleanup v_new - if (element->value) { - v_free(element->value); - tmp_free(element->value); - } - - // cleanup create_element() - tmp_free(element); -} - -/** - * Callback to compare Element's - */ -int cmp_fn(const void *a, const void *b) { - Element *el_a = (Element *)a; - Element *el_b = (Element *)b; - - int result; - if (el_a->key->type == V_STR && el_b->key->type == V_STR) { - result = strcasecmp(el_a->key->v.p.ptr, el_b->key->v.p.ptr); - } else { - result = v_compare(el_a->key, el_b->key); - } - return result; -} - -/** - * Compare one HASH to another. see v_compare comments for return spec. - */ -int hash_compare(const var_p_t var_a, const var_p_t var_b) { - return 0; -} - -/** - * Return true if the structure is empty - */ -int hash_is_empty(const var_p_t var_p) { - return (var_p->v.hash == NULL); -} - -/** - * Return the contents of the structure as an integer - */ -int hash_to_int(const var_p_t var_p) { - return hash_length(var_p); -} - -/** - * Helper for hash_length - */ -void hash_length_cb(const void *nodep, VISIT value, int level) { - if (value == leaf || value == endorder) { - cb.count++; - } -} - -/** - * Return the number of elements - */ -int hash_length(const var_p_t var_p) { - cb.count = 0; - if (var_p->type == V_HASH) { - twalk(var_p->v.hash, hash_length_cb); - } - return cb.count; -} - -/** - * Helper for hash_length - */ -void hash_elem_cb(const void *nodep, VISIT value, int level) { - if (value == leaf || value == endorder) { - if (cb.count++ == cb.index) { - Element *element = ((Node *) nodep)->element; - cb.var = element->key; - } - } -} - -/** - * return the element at the nth position - */ -var_p_t hash_elem(const var_p_t var_p, int index) { - cb.count = 0; - cb.index = index; - cb.var = NULL; - if (var_p->type == V_HASH) { - twalk(var_p->v.hash, hash_elem_cb); - } - return cb.var; -} - -/** - * Helper for hash_free - */ -void hash_free_cb(void *nodep) { - Element *element = (Element *) nodep; - delete_element(element); -} - -/** - * Free the hash data - */ -void hash_free(var_p_t var_p) { - if (var_p->type == V_HASH) { - tdestroy(var_p->v.hash, hash_free_cb); - v_init(var_p); - } -} - -/** - * Inserts the variable into the b-tree, set result to the key value - */ -void hash_insert_key(var_p_t base, var_p_t var_key, var_p_t *result) { - Element *key = create_element(var_key); - Node *node = tfind(key, &(base->v.hash), cmp_fn); - if (node != NULL) { - // item already exists - *result = node->element->value; - delete_element(key); - } - else { - key->value = *result = v_new(); - tsearch(key, &(base->v.hash), cmp_fn); - } -} - -/** - * Returns the final element eg z in foo.x.y.z - * - * Scan byte code for node kwTYPE_UDS_EL and attach as field elements - * if they don't already exist. - */ -var_p_t hash_resolve_fields(const var_p_t base) { - var_p_t field = NULL; - if (code_peek() == kwTYPE_UDS_EL) { - code_skipnext(); - if (code_peek() != kwTYPE_STR) { - err_stackmess(); - return NULL; - } else { - code_skipnext(); - } - - if (base->type != V_HASH) { - if (v_is_nonzero(base)) { - err_typemismatch(); - return NULL; - } else { - v_free(base); - base->type = V_HASH; - base->v.hash = NULL; - } - } - - // evaluate the variable 'key' name - var_t key; - v_eval_str(&key); - hash_insert_key(base, &key, &field); - v_free(&key); - - // evaluate the next sub-element - field = hash_resolve_fields(field); - } else { - field = base; - } - return field; -} - -/** - * Return the variable in base keyed by key, if not found then creates - * an empty variable that will be returned in a further call - */ -void hash_get_value(var_p_t base, var_p_t var_key, var_p_t *result) { - if (base->type == V_ARRAY && base->v.a.size) { - // convert the non-empty array to a hash - int i; - var_t *clone = v_clone(base); - - v_free(base); - base->type = V_HASH; - base->v.hash = NULL; - - for (i = 0; i < clone->v.a.size; i++) { - const var_t *element = (var_t *)(clone->v.a.ptr + (sizeof(var_t) * i)); - Element *key = create_int_element(i); - key->value = v_new(); - v_set(key->value, element); - tsearch(key, &(base->v.hash), cmp_fn); - } - - // free the clone - v_free(clone); - tmp_free(clone); - } else if (base->type != V_HASH) { - if (v_is_nonzero(base)) { - err_typemismatch(); - return; - } else { - // initialise as hash - v_free(base); - base->type = V_HASH; - base->v.hash = NULL; - } - } - - hash_insert_key(base, var_key, result); -} - -/** - * Callback for hash_set() - */ -void hash_set_cb(const void *nodep, VISIT value, int level) { - if (value == leaf || value == endorder) { - // copy the element and insert it into the destination - Element *element = ((Node *) nodep)->element; - Element *key = create_element(element->key); - key->value = v_new(); - v_set(key->value, element->value); - tsearch(key, &(cb.hash), cmp_fn); - } -} - -/** - * copy values from one structure to another - */ -void hash_set(var_p_t dest, const var_p_t src) { - if (dest != src) { - v_free(dest); - cb.hash = NULL; - - if (src->type == V_HASH) { - twalk(src->v.hash, hash_set_cb); - } - - dest->type = V_HASH; - dest->v.hash = cb.hash; - } -} - -/** - * Return the contents of the structure as a string - */ -void hash_to_str(const var_p_t var_p, char *out, int max_len) { - sprintf(out, "UDS:%d", hash_to_int(var_p)); -} - -/** - * Callback for hash_write() - */ -void hash_write_cb(const void *nodep, VISIT value, int level) { - if (value == leaf || value == endorder) { - Element *element = ((Node *) nodep)->element; - if (!cb.firstElement) { - pv_write(",", cb.method, cb.handle); - } - cb.firstElement = 0; - pv_writevar(element->key, cb.method, cb.handle); - pv_write("=", cb.method, cb.handle); - pv_writevar(element->value, cb.method, cb.handle); - } -} - -/** - * Print the contents of the structure - */ -void hash_write(const var_p_t var_p, int method, int handle) { - if (var_p->type == V_HASH) { - cb.method = method; - cb.handle = handle; - cb.firstElement = 1; - pv_write("[", method, handle); - twalk(var_p->v.hash, hash_write_cb); - pv_write("]", method, handle); - } -} - diff --git a/src/common/var_hash.h b/src/common/var_hash.h deleted file mode 100644 index 37bb6746..00000000 --- a/src/common/var_hash.h +++ /dev/null @@ -1,36 +0,0 @@ -// This file is part of SmallBASIC -// -// Support for hash variables -// -// This program is distributed under the terms of the GPL v2.0 or later -// Download the GNU Public License (GPL) from www.gnu.org -// -// Copyright(C) 2007-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] - -#include "common/var.h" - -#ifndef VAR_HASH_H -#define VAR_HASH_H - -#if defined(__cplusplus) -extern "C" { -#endif - -int hash_compare(const var_p_t var_a, const var_p_t var_b); -int hash_is_empty(const var_p_t var_p); -int hash_to_int(const var_p_t var_p); -int hash_length(const var_p_t var_p); -var_p_t hash_elem(const var_p_t var_p, int index); -var_p_t hash_resolve_fields(const var_p_t base); -void hash_free(var_p_t var_p); -void hash_get_value(var_p_t base, var_p_t key, var_p_t *result); -void hash_set(var_p_t dest, const var_p_t src); -void hash_to_str(const var_p_t var_p, char *out, int max_len); -void hash_write(const var_p_t var_p, int method, int handle); - -#if defined(__cplusplus) -} -#endif - -#endif - diff --git a/src/common/var_map.c b/src/common/var_map.c new file mode 100644 index 00000000..6d5d36a9 --- /dev/null +++ b/src/common/var_map.c @@ -0,0 +1,697 @@ +// This file is part of SmallBASIC +// +// Support for dictionary/associative array variables +// eg: dim foo: key="blah": foo(key) = "something": ? foo(key) +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2010-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] + +#include "common/sys.h" +#include "common/var.h" +#include "common/smbas.h" +#include "common/var_map.h" +#include "lib/search.h" +#include "lib/jsmn.h" + +#define BUFFER_GROW_SIZE 64 +#define BUFFER_PADDING 10 +#define TOKEN_GROW_SIZE 16 +#define ARRAY_GROW_SIZE 16 + +/** + * Globals for callback access + */ +typedef struct CallbackData { + int method; + int handle; + int index; + int count; + int firstElement; + var_p_t var; + char *buffer; +} CallbackData; + +/** + * Our internal tree element node + */ +typedef struct Element { + var_p_t key; + var_p_t value; +} Element; + +/** + * Cast for node_t->key + */ +typedef struct Node { + Element *element; +} Node; + +/** + * Container for map_from_str + */ +typedef struct JsonTokens { + const char *js; + jsmntok_t *tokens; + int num_tokens; +} JsonTokens; + +/** + * Returns a new Element + */ +Element *create_element(var_p_t key) { + Element *element = (Element *)tmp_alloc(sizeof(Element)); + element->key = v_new(); + v_set(element->key, key); + element->value = NULL; + return element; +} + +/** + * Returns a new Element using the integer based key + */ +Element *create_int_element(int key) { + Element *element = (Element *)tmp_alloc(sizeof(Element)); + element->key = v_new(); + v_setint(element->key, key); + element->value = NULL; + return element; +} + +int map_read_next_token(var_p_t dest, JsonTokens *json, int index); + +/** + * cleanup the given element + */ +void delete_element(Element *element) { + // cleanup v_new + v_free(element->key); + tmp_free(element->key); + + // cleanup v_new + if (element->value) { + v_free(element->value); + tmp_free(element->value); + } + + // cleanup create_element() + tmp_free(element); +} + +/** + * Callback to compare Element's + */ +int cmp_fn(const void *a, const void *b) { + Element *el_a = (Element *)a; + Element *el_b = (Element *)b; + + int result; + if (el_a->key->type == V_STR && el_b->key->type == V_STR) { + result = strcasecmp(el_a->key->v.p.ptr, el_b->key->v.p.ptr); + } else { + result = v_compare(el_a->key, el_b->key); + } + return result; +} + +/** + * Compare handler for map_resolve_key + */ +int cmp_fn_var(const void *a, const void *b) { + var_p_t key_a = (var_p_t)a; + var_p_t key_b = ((Element *)b)->key; + + int result; + if (key_a->type == V_STR && key_b->type == V_STR) { + result = strcasecmp(key_a->v.p.ptr, key_b->v.p.ptr); + } else { + result = v_compare(key_a, key_b); + } + return result; +} + +/** + * Compare one MAP to another. see v_compare comments for return spec. + */ +int map_compare(const var_p_t var_a, const var_p_t var_b) { + return 0; +} + +/** + * Return true if the structure is empty + */ +int map_is_empty(const var_p_t var_p) { + return (var_p->v.map == NULL); +} + +/** + * Return the contents of the structure as an integer + */ +int map_to_int(const var_p_t var_p) { + return map_length(var_p); +} + +void map_length_recurse(CallbackData *cb, const node_t *nodep) { + if (nodep->left != NULL) { + map_length_recurse(cb, nodep->left); + } + cb->count++; + if (nodep->right != NULL) { + map_length_recurse(cb, nodep->right); + } +} + +/** + * Return the number of elements + */ +int map_length(const var_p_t var_p) { + CallbackData cb; + cb.count = 0; + if (var_p->type == V_MAP) { + map_length_recurse(&cb, var_p->v.map); + } + return cb.count; +} + +void map_get_recurse(CallbackData *cb, const node_t *nodep, const char *name) { + if (nodep->left != NULL) { + map_get_recurse(cb, nodep->left, name); + } + Element *el = ((Node *)nodep)->element; + if (el != NULL && el->key->type == V_STR && + strcasecmp(el->key->v.p.ptr, name) == 0) { + cb->var = el->value; + } + if (nodep->right != NULL) { + map_get_recurse(cb, nodep->right, name); + } +} + +var_p_t map_get(var_p_t base, const char *name) { + CallbackData cb; + cb.var = NULL; + if (base->type == V_MAP) { + map_get_recurse(&cb, base->v.map, name); + } + return cb.var; +} + +int map_get_int(var_p_t base, const char *name) { + int result = -1; + var_p_t var = map_get(base, name); + if (var != NULL && var->type == V_INT) { + result = var->v.i; + } + return result; +} + +void map_elem_cb(CallbackData *cb, const node_t *nodep) { + if (cb->var == NULL) { + Element *element = ((Node *)nodep)->element; + if (cb->count++ == cb->index) { + cb->var = element->key; + } + } +} + +void map_elem_recurse(CallbackData *cb, const node_t *nodep) { + if (nodep->left != NULL) { + map_elem_recurse(cb, nodep->left); + } + map_elem_cb(cb, nodep); + if (nodep->right != NULL) { + map_elem_recurse(cb, nodep->right); + } +} + +/** + * return the element at the nth position + */ +var_p_t map_elem(const var_p_t var_p, int index) { + CallbackData cb; + cb.count = 0; + cb.index = index; + cb.var = NULL; + if (var_p->type == V_MAP) { + map_elem_recurse(&cb, var_p->v.map); + } + return cb.var; +} + +/** + * Helper for map_free + */ +void map_free_cb(void *nodep) { + Element *element = (Element *)nodep; + delete_element(element); +} + +/** + * Free the map data + */ +void map_free(var_p_t var_p) { + if (var_p->type == V_MAP) { + tdestroy(var_p->v.map, map_free_cb); + v_init(var_p); + } +} + +/** + * Inserts the variable into the b-tree, set result to the key value + */ +void map_insert_key(var_p_t base, Element *key, var_p_t *result) { + Node *node = tfind(key, &(base->v.map), cmp_fn); + + if (node != NULL) { + // item already exists + *result = node->element->value; + delete_element(key); + } + else { + key->value = *result = v_new(); + tsearch(key, &(base->v.map), cmp_fn); + } +} + +/** + * Resolve the variable into the b-tree without avoiding memory allocation + */ +void map_resolve_key(var_p_t base, var_p_t v_key, var_p_t *result) { + Node *node = tfind(v_key, &(base->v.map), cmp_fn_var); + + if (node != NULL) { + // item already exists + *result = node->element->value; + } + else { + Element *key = create_element(v_key); + key->value = *result = v_new(); + tsearch(key, &(base->v.map), cmp_fn); + } +} + +/** + * Returns the final element eg z in foo.x.y.z + * + * Scan byte code for node kwTYPE_UDS_EL and attach as field elements + * if they don't already exist. + */ +var_p_t map_resolve_fields(const var_p_t base) { + var_p_t field = NULL; + if (code_peek() == kwTYPE_UDS_EL) { + code_skipnext(); + if (code_peek() != kwTYPE_STR) { + err_stackmess(); + return NULL; + } else { + code_skipnext(); + } + + if (base->type != V_MAP) { + if (v_is_nonzero(base)) { + err_typemismatch(); + return NULL; + } else { + v_free(base); + base->type = V_MAP; + base->v.map = NULL; + } + } + + // evaluate the variable 'key' name + var_t key; + v_eval_str(&key); + map_resolve_key(base, &key, &field); + v_free(&key); + + // evaluate the next sub-element + field = map_resolve_fields(field); + } else { + field = base; + } + return field; +} + +/** + * Adds a new variable onto the map + */ +var_p_t map_add_var(var_p_t base, const char *name, int value) { + var_p_t result; + Element *node = (Element *)tmp_alloc(sizeof(Element)); + node->key = v_new(); + node->value = NULL; + v_setstr(node->key, name); + map_insert_key(base, node, &result); + v_setint(result, value); + return result; +} + +/** + * Return the variable in base keyed by key, if not found then creates + * an empty variable that will be returned in a further call + */ +void map_get_value(var_p_t base, var_p_t var_key, var_p_t *result) { + if (base->type == V_ARRAY && base->v.a.size) { + // convert the non-empty array to a map + int i; + var_t *clone = v_clone(base); + + v_free(base); + base->type = V_MAP; + base->v.map = NULL; + + for (i = 0; i < clone->v.a.size; i++) { + const var_t *element = (var_t *)(clone->v.a.ptr + (sizeof(var_t) * i)); + Element *key = create_int_element(i); + key->value = v_new(); + v_set(key->value, element); + tsearch(key, &(base->v.map), cmp_fn); + } + + // free the clone + v_free(clone); + tmp_free(clone); + } else if (base->type != V_MAP) { + if (v_is_nonzero(base)) { + err_typemismatch(); + return; + } else { + // initialise as map + v_free(base); + base->type = V_MAP; + base->v.map = NULL; + } + } + map_resolve_key(base, var_key, result); +} + +/** + * Copy the element and insert it into the destination + */ +void map_set_cb(var_p_t dest, const node_t *nodep) { + Element *element = ((Node *)nodep)->element; + Element *key = create_element(element->key); + key->value = v_new(); + v_set(key->value, element->value); + tsearch(key, &(dest->v.map), cmp_fn); + if (key->value->type == V_FUNC) { + key->value->v.fn.self = dest; + } +} + +/** + * Traverse the root to copy into dest + */ +void map_set_recurse(var_p_t dest, const node_t *root) { + if (root->left != NULL) { + map_set_recurse(dest, root->left); + } + map_set_cb(dest, root); + if (root->right != NULL) { + map_set_recurse(dest, root->right); + } +} + +/** + * Copy values from one structure to another + */ +void map_set(var_p_t dest, const var_p_t src) { + if (dest != src && src->type == V_MAP) { + v_free(dest); + dest->type = V_MAP; + map_set_recurse(dest, src->v.map); + } +} + +/** + * Helper for map_to_str + */ +void map_to_str_cb(CallbackData *cb, const node_t *nodep) { + Element *element = ((Node *)nodep)->element; + char *key = v_str(element->key); + char *value = v_str(element->value); + int required = strlen(cb->buffer) + strlen(key) + strlen(value) + BUFFER_PADDING; + if (required >= cb->count) { + cb->count = required + BUFFER_GROW_SIZE; + cb->buffer = tmp_realloc(cb->buffer, cb->count); + } + if (!cb->firstElement) { + strcat(cb->buffer, ","); + } + cb->firstElement = 0; + strcat(cb->buffer, "\""); + strcat(cb->buffer, key); + strcat(cb->buffer, "\""); + strcat(cb->buffer, ":"); + if (element->value->type == V_STR) { + strcat(cb->buffer, "\""); + } + strcat(cb->buffer, value); + if (element->value->type == V_STR) { + strcat(cb->buffer, "\""); + } + tmp_free(key); + tmp_free(value); +} + +/** + * Helper for map_to_str + */ +void map_to_str_recurse(CallbackData *cb, const node_t *nodep) { + if (nodep->left != NULL) { + map_to_str_recurse(cb, nodep->left); + } + map_to_str_cb(cb, nodep); + if (nodep->right != NULL) { + map_to_str_recurse(cb, nodep->right); + } +} + +/** + * Print the array element, growing the buffer as needed + */ +void array_append_elem(CallbackData *cb, var_t *elem) { + char *value = v_str(elem); + int required = strlen(cb->buffer) + strlen(value) + BUFFER_PADDING; + if (required >= cb->count) { + cb->count = required + BUFFER_GROW_SIZE; + cb->buffer = tmp_realloc(cb->buffer, cb->count); + } + strcat(cb->buffer, value); + tmp_free(value); +} + +/** + * print the array variable + */ +void array_to_str(CallbackData *cb, var_t *var) { + strcpy(cb->buffer, "["); + if (var->v.a.maxdim == 2) { + // NxN + int rows = ABS(var->v.a.ubound[0] - var->v.a.lbound[0]) + 1; + int cols = ABS(var->v.a.ubound[1] - var->v.a.lbound[1]) + 1; + int i, j; + + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + int pos = i * cols + j; + var_t *elem = (var_t *)(var->v.a.ptr + (sizeof(var_t) * pos)); + array_append_elem(cb, elem); + if (j != cols - 1) { + strcat(cb->buffer, ","); + } + } + if (i != rows - 1) { + strcat(cb->buffer, ";"); + } + } + } else { + int i; + for (i = 0; i < var->v.a.size; i++) { + var_t *elem = (var_t *)(var->v.a.ptr + (sizeof(var_t) * i)); + array_append_elem(cb, elem); + if (i != var->v.a.size - 1) { + strcat(cb->buffer, ","); + } + } + } + strcat(cb->buffer, "]"); +} + +/** + * Return the contents of the structure as a string + */ +char *map_to_str(const var_p_t var_p) { + CallbackData cb; + cb.count = BUFFER_GROW_SIZE; + cb.buffer = tmp_alloc(cb.count); + + if (var_p->type == V_MAP) { + cb.firstElement = 1; + strcpy(cb.buffer, "{"); + map_to_str_recurse(&cb, var_p->v.map); + strcat(cb.buffer, "}"); + } else if (var_p->type == V_ARRAY) { + array_to_str(&cb, var_p); + } + return cb.buffer; +} + +/** + * Print the contents of the structure + */ +void map_write(const var_p_t var_p, int method, int handle) { + if (var_p->type == V_MAP || var_p->type == V_ARRAY) { + char *buffer = map_to_str(var_p); + pv_write(buffer, method, handle); + tmp_free(buffer); + } +} + +/** + * Creates an array variable + */ +int map_create_array(var_p_t dest, JsonTokens *json, int end_position, int index) { + int size = ARRAY_GROW_SIZE; + int item_index = 0; + v_toarray1(dest, size); + int i = index; + while (i < json->num_tokens) { + jsmntok_t token = json->tokens[i]; + if (token.start > end_position) { + break; + } + if (item_index >= size) { + size += ARRAY_GROW_SIZE; + v_resize_array(dest, size); + } + var_t *elem = (var_t *)(dest->v.a.ptr + (sizeof(var_t) * item_index)); + i = map_read_next_token(elem, json, i); + item_index++; + } + v_resize_array(dest, item_index); + return i; +} + +/** + * Process the next primative value + */ +void map_set_primative(var_p_t dest, const char *s, int len) { + int value = 0; + int fract = 0; + int text = 0; + int i; + for (i = 0; i < len && !text; i++) { + int n = s[i] - '0'; + if (n >= 0 && n <= 9) { + value = value * 10 + n; + } else if (!fract && s[i] == '.') { + fract = 1; + } else { + text = 1; + } + } + if (text) { + v_setstrn(dest, s, len); + } else if (fract) { + v_setreal(dest, atof(s)); + } else { + v_setint(dest, value); + } +} + +/** + * Creates a map variable + */ +int map_create(var_p_t dest, JsonTokens *json, int end_position, int index) { + dest->type = V_MAP; + int i = index; + while (i < json->num_tokens) { + jsmntok_t token = json->tokens[i]; + if (token.start > end_position) { + break; + } else if (token.type == JSMN_STRING || token.type == JSMN_PRIMITIVE) { + var_p_t value = NULL; + Element *element = (Element *)tmp_alloc(sizeof(Element)); + element->key = v_new(); + element->value = NULL; + map_set_primative(element->key, json->js + token.start, token.end - token.start); + map_insert_key(dest, element, &value); + i = map_read_next_token(value, json, i + 1); + } else { + err_array(); + break; + } + } + return i; +} + +/** + * Process the next token + */ +int map_read_next_token(var_p_t dest, JsonTokens *json, int index) { + int next; + jsmntok_t token = json->tokens[index]; + switch(token.type) { + case JSMN_OBJECT: + next = map_create(dest, json, token.end, index + 1); + break; + case JSMN_ARRAY: + next = map_create_array(dest, json, token.end, index + 1); + break; + case JSMN_PRIMITIVE: + map_set_primative(dest, json->js + token.start, token.end - token.start); + next = index + 1; + break; + case JSMN_STRING: + v_setstrn(dest, json->js + token.start, token.end - token.start); + next = index + 1; + break; + } + return next; +} + +/** + * Initialise a map from a string + */ +void map_from_str(var_p_t dest) { + var_t arg; + v_init(&arg); + eval(&arg); + if (!prog_error) { + if (arg.type != V_STR) { + v_set(dest, &arg); + } else { + int num_tokens = TOKEN_GROW_SIZE; + jsmntok_t *tokens = tmp_alloc(sizeof(jsmntok_t) * num_tokens); + const char *js = arg.v.p.ptr; + size_t len = arg.v.p.size; + jsmnerr_t result; + jsmn_parser parser; + + jsmn_init(&parser); + for (result = jsmn_parse(&parser, js, len, tokens, num_tokens); + result == JSMN_ERROR_NOMEM; + result = jsmn_parse(&parser, js, len, tokens, num_tokens)) { + num_tokens += TOKEN_GROW_SIZE; + tokens = tmp_realloc(tokens, sizeof(jsmntok_t) * num_tokens); + } + if (result < 0) { + err_array(); + } else if (result > 0) { + v_init(dest); + + JsonTokens json; + json.tokens = tokens; + json.js = js; + json.num_tokens = parser.toknext; + map_read_next_token(dest, &json, 0); + } + tmp_free(tokens); + } + } + v_free(&arg); +} + + diff --git a/src/common/var_map.h b/src/common/var_map.h new file mode 100644 index 00000000..3dcaaff8 --- /dev/null +++ b/src/common/var_map.h @@ -0,0 +1,40 @@ +// This file is part of SmallBASIC +// +// Support for dictionary/associative array variables +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2007-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] + +#include "common/var.h" + +#ifndef VAR_MAP_H +#define VAR_MAP_H + +#if defined(__cplusplus) +extern "C" { +#endif + +int map_compare(const var_p_t var_a, const var_p_t var_b); +int map_is_empty(const var_p_t var_p); +int map_to_int(const var_p_t var_p); +int map_length(const var_p_t var_p); +int map_get_int(var_p_t base, const char *name); +var_p_t map_get(var_p_t base, const char *name); +var_p_t map_elem(const var_p_t var_p, int index); +var_p_t map_resolve_fields(const var_p_t base); +var_p_t map_add_var(var_p_t base, const char *name, int value); +void map_free(var_p_t var_p); +void map_get_value(var_p_t base, var_p_t key, var_p_t *result); +void map_set(var_p_t dest, const var_p_t src); +char *map_to_str(const var_p_t var_p); +void map_write(const var_p_t var_p, int method, int handle); +void map_from_str(var_p_t var_p); + +#if defined(__cplusplus) +} +#endif + +#endif + diff --git a/src/common/var_obj.c b/src/common/var_obj.c new file mode 100644 index 00000000..85a68f19 --- /dev/null +++ b/src/common/var_obj.c @@ -0,0 +1,83 @@ +// This file is part of SmallBASIC +// +// System objects +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2007-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] + +#include "common/sys.h" +#include "common/messages.h" +#include "common/sberr.h" +#include "common/device.h" +#include "common/var.h" +#include "common/var_map.h" + +#define IMG_X "x" +#define IMG_Y "y" +#define IMG_OFFSET_TOP "offsetTop" +#define IMG_OFFSET_LEFT "offsetLeft" +#define IMG_WIDTH "width" +#define IMG_HEIGHT "height" +#define IMG_ZINDEX "zIndex" +#define IMG_OPACITY "opacity" +#define IMG_ID "id" +#define IMG_HANDLE "handle" + +int next_id = 0; + +var_image var_get_image(var_p_t self) { + var_image result; + result.x = map_get_int(self, IMG_X); + result.y = map_get_int(self, IMG_Y); + result.offsetTop = map_get_int(self, IMG_OFFSET_TOP); + result.offsetLeft = map_get_int(self, IMG_OFFSET_LEFT); + result.width = map_get_int(self, IMG_WIDTH); + result.height = map_get_int(self, IMG_HEIGHT); + result.zIndex = map_get_int(self, IMG_ZINDEX); + result.opacity = map_get_int(self, IMG_OPACITY); + result.id = map_get_int(self, IMG_ID); + result.handle = map_get_int(self, IMG_HANDLE); + return result; +} + +void cmd_image_show(const void *arg) { + var_image image = var_get_image((var_p_t)arg); + dev_image_show(&image); +} + +void cmd_image_hide(const void *arg) { + int handle = map_get_int((var_p_t)arg, IMG_ID); + dev_image_hide(handle); +} + +void var_create_image(var_p_t var, int handle) { + int loaded = dev_image_load(handle); + if (!loaded) { + err_throw(ERR_IMAGE_LOAD); + } else { + int w = dev_image_width(handle); + int h = dev_image_height(handle); + var->type = V_MAP; + map_add_var(var, IMG_X, 0); + map_add_var(var, IMG_Y, 0); + map_add_var(var, IMG_OFFSET_TOP, 0); + map_add_var(var, IMG_OFFSET_LEFT, 0); + map_add_var(var, IMG_WIDTH, w); + map_add_var(var, IMG_HEIGHT, h); + map_add_var(var, IMG_ZINDEX, 1); + map_add_var(var, IMG_OPACITY, 1); + map_add_var(var, IMG_HANDLE, handle); + map_add_var(var, IMG_ID, ++next_id); + var_p_t v_show = map_add_var(var, "show", 0); + v_show->type = V_FUNC; + v_show->v.fn.self = var; + v_show->v.fn.cb = cmd_image_show; + + var_p_t v_hide = map_add_var(var, "hide", 0); + v_hide->type = V_FUNC; + v_hide->v.fn.self = var; + v_hide->v.fn.cb = cmd_image_hide; + } +} diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index a037b700..4080a236 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -234,6 +234,8 @@ struct func_keyword_s func_table[] = { { "ISARRAY", kwISARRAY }, { "ISNUMBER", kwISNUMBER }, { "ISSTRING", kwISSTRING }, +{ "ISMAP", kwISMAP }, +{ "ISREF", kwISREF }, { "ATAN2", kwATAN2 }, { "POW", kwPOW }, { "ROUND", kwROUND }, @@ -306,7 +308,6 @@ struct func_keyword_s func_table[] = { { "XPOS", kwXPOS }, { "YPOS", kwYPOS }, { "INPUT", kwINPUTF }, -{ "ARRAY", kwCODEARRAY }, { "LINEQN", kwGAUSSJORDAN }, { "FILES", kwFILES }, { "INVERSE", kwINVERSE }, @@ -347,8 +348,8 @@ struct func_keyword_s func_table[] = { { "CBS", kwCBS }, { "BCS", kwBCS }, { "CALL", kwCALLCF }, -{ "IMAGEW", kwIMGW }, -{ "IMAGEH", kwIMGH }, +{ "IMAGE", kwIMAGE }, +{ "ARRAY", kwARRAY }, { "", 0 } }; @@ -420,7 +421,6 @@ struct proc_keyword_s proc_table[] = { { "EXPRSEQ", kwEXPRSEQ }, { "UNLOADLIB", kwUNLOADLIB }, { "CALL", kwCALLCP }, -{ "IMAGE", kwIMAGE }, { "DEFINEKEY", kwDEFINEKEY }, { "SHOWPAGE", kwSHOWPAGE }, diff --git a/src/languages/messages.en.h b/src/languages/messages.en.h index 2da9b676..5127e0d5 100644 --- a/src/languages/messages.en.h +++ b/src/languages/messages.en.h @@ -103,7 +103,7 @@ #define MSG_PASS1 "Pass1...\n" #define MSG_PASS1_COUNT "\rPASS1: Line %d" #define MSG_UNDEFINED_UDP "Undefined SUB/FUNC code: %s" -#define MSG_UNDEFINED_HASH "Undefined HASH: %s, (Use DIM)" +#define MSG_UNDEFINED_HASH "Undefined HASH: '%s' or missing parenthesis" #define MSG_PASS1_FIN "\rPASS1: Line %d; finished\n" #define MSG_EXP_SYM_NOT_FOUND "Export symbol '%s' not found" #define MSG_PASS2 "PASS2..." @@ -200,7 +200,7 @@ #define ERR_BOUND_VAR "U/LBOUND: Variable is not an array" #define ERR_FORMAT_INVALID_FORMAT "Wrong format" #define ERR_CENTROID "Centroid is undefined" -#define ERR_WRONG_POLY "Polygon is wrong" +#define ERR_WRONG_POLY "Polygon is incorrect" #define ERR_POINT "POINT() Invalid parameter" #define ERR_LINEEQN_ADIM "MATRIX A, WRONG DIM %dx%d" #define ERR_LINEEQN_BDIM "MATRIX B, WRONG DIM %dx%d" @@ -214,14 +214,18 @@ #define ERR_READ_DATA_INDEX_FMT "READ: Data out of range (IDX=%d). Use RESTORE [label]" #define ERR_PUTENV "ENV failed" #define ERR_EXPRSEQ_WITHOUT_EXPR "EXPRSEQ: Missing expression" -#define ERR_POLY_POINT "Parsing point: type mismatch!" +#define ERR_POLY_POINT "Parsing point: type mismatch" #define ERR_VP_POS "Viewport out of screen" #define ERR_VP_ZERO "Viewport of zero size" #define ERR_WIN_ZERO "Window of zero size" #define ERR_DRAW_SEP "DRAW: Missing separator (,)" #define ERR_DRAW_CMD "DRAW: Command '%c' unsupported" #define ERR_FILE_PERM "File IO permission error" -#define ERR_REF_VAR "Invalid variable reference" +#define ERR_REF_VAR "BYREF error. Invalid variable reference" +#define ERR_REF_CIRC_VAR "BYREF error. Circular reference" +#define ERR_INVALID_CATCH "Invalid catch argument" +#define ERR_NO_FUNC "Not a function" +#define ERR_IMAGE_LOAD "Failed to load image" // memory manager #define MEM_OUT_OF_MEM "OUT OF MEMORY" diff --git a/src/lib/jsmn.c b/src/lib/jsmn.c new file mode 100644 index 00000000..56381396 --- /dev/null +++ b/src/lib/jsmn.c @@ -0,0 +1,282 @@ +#include + +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Filsl next token with JSON string. + */ +static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\') { + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + int i = 0; + for(; i < 4 && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + jsmnerr_t r; + int i; + jsmntok_t *token; + int count = 0; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + diff --git a/src/lib/jsmn.h b/src/lib/jsmn.h new file mode 100644 index 00000000..c8f388cd --- /dev/null +++ b/src/lib/jsmn.h @@ -0,0 +1,73 @@ +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_PRIMITIVE = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3 +} jsmntype_t; + +typedef enum { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3, +} jsmnerr_t; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/src/common/match.c b/src/lib/match.c similarity index 99% rename from src/common/match.c rename to src/lib/match.c index c34b7860..e3c53cfd 100644 --- a/src/common/match.c +++ b/src/lib/match.c @@ -39,7 +39,7 @@ * and RegMatch the character exactly, precede it with a `\'. */ -#include "common/match.h" +#include "lib/match.h" #include "common/smbas.h" #include "common/sberr.h" diff --git a/src/common/match.h b/src/lib/match.h similarity index 100% rename from src/common/match.h rename to src/lib/match.h diff --git a/src/common/matrix.c b/src/lib/matrix.c similarity index 89% rename from src/common/matrix.c rename to src/lib/matrix.c index 2c79699d..2d99d88b 100644 --- a/src/common/matrix.c +++ b/src/lib/matrix.c @@ -27,18 +27,18 @@ #include "common/blib_math.h" // creates a matrix of the given size -var_num_t** mat_create(int row, int col) { - var_num_t** m = (var_num_t**) tmp_alloc(sizeof(var_num_t*) * row); +var_num_t **mat_create(int row, int col) { + var_num_t **m = (var_num_t **)tmp_alloc(sizeof(var_num_t *) * row); int i; for (i = 0; i < row; i++) { - m[i] = (var_num_t*) tmp_alloc(sizeof(var_num_t) * col); + m[i] = (var_num_t *)tmp_alloc(sizeof(var_num_t) * col); } return m; } // free an allocated matrix -void mat_free(var_num_t** m, int n) { +void mat_free(var_num_t **m, int n) { int i; for (i = 0; i < n; i++) { tmp_free(m[i]); @@ -47,7 +47,7 @@ void mat_free(var_num_t** m, int n) { } // fill the matrix with zero values -void mat_fill(var_num_t** A, int row, int col) { +void mat_fill(var_num_t **A, int row, int col) { int i, j; for (i = 0; i < row; i++) { @@ -72,7 +72,7 @@ void mat_fill(var_num_t** A, int row, int col) { * rows interchanged matrix. *----------------------------------------------------------------------------- */ -int mat_lu(var_num_t** A, var_num_t** P, int n) { +int mat_lu(var_num_t **A, var_num_t **P, int n) { int i, j, k, maxi, tmp, p; var_num_t c, c1; @@ -140,7 +140,7 @@ int mat_lu(var_num_t** A, var_num_t** P, int n) { * comen: B will be overwritten *----------------------------------------------------------------------------- */ -var_num_t** mat_backsubs1(var_num_t** A, var_num_t** B, var_num_t** X, var_num_t** P, int xcol, int n) { +var_num_t **mat_backsubs1(var_num_t **A, var_num_t **B, var_num_t **X, var_num_t **P, int xcol, int n) { int i, j, k; var_num_t sum; @@ -171,11 +171,11 @@ var_num_t** mat_backsubs1(var_num_t** A, var_num_t** B, var_num_t** X, var_num_t * NULL = fails, singular matrix, or malloc() fails *----------------------------------------------------------------------------- */ -void mat_inverse(var_num_t* a, const int n) { - var_num_t** A = mat_create(n, n); - var_num_t** B = mat_create(n, 1); - var_num_t** C = mat_create(n, n); - var_num_t** P = mat_create(n, 1); +void mat_inverse(var_num_t *a, const int n) { + var_num_t **A = mat_create(n, n); + var_num_t **B = mat_create(n, 1); + var_num_t **C = mat_create(n, n); + var_num_t **P = mat_create(n, 1); int i, j; diff --git a/src/common/search.c b/src/lib/search.c similarity index 96% rename from src/common/search.c rename to src/lib/search.c index b8144bfb..2560f0af 100644 --- a/src/common/search.c +++ b/src/lib/search.c @@ -17,11 +17,6 @@ #include "search.h" -typedef struct node { - void *key; - struct node *left, *right; -} node_t; - /* Walk the nodes of a tree */ tdestroy_recurse(node_t *root, tdestroy_cb freefct) { if (root->left != NULL) { diff --git a/src/common/search.h b/src/lib/search.h similarity index 90% rename from src/common/search.h rename to src/lib/search.h index 8ddc10c9..7e077f99 100644 --- a/src/common/search.h +++ b/src/lib/search.h @@ -17,6 +17,11 @@ typedef enum { leaf } VISIT; +typedef struct node { + void *key; + struct node *left, *right; +} node_t; + typedef void (*twalk_cb) (const void *nodep, VISIT value, int level); typedef void (*tdestroy_cb) (void *__nodep); typedef int (*tcompare_cb) (const void *, const void *); diff --git a/src/platform/android/jni/common/Android.mk b/src/platform/android/jni/common/Android.mk index 7b5bfefe..8cbfb109 100644 --- a/src/platform/android/jni/common/Android.mk +++ b/src/platform/android/jni/common/Android.mk @@ -14,13 +14,16 @@ LOCAL_C_INCLUDES := $(SB_HOME) $(SB_HOME)/src LOCAL_MODULE := sb_common LOCAL_CFLAGS := -DHAVE_CONFIG_H=1 LOCAL_SRC_FILES := \ + $(COMMON)/../lib/matrix.c \ + $(COMMON)/../lib/search.c \ + $(COMMON)/../lib/match.c \ + $(COMMON)/../lib/jsmn.c \ $(COMMON)/bc.c \ $(COMMON)/blib.c \ $(COMMON)/blib_db.c \ $(COMMON)/blib_func.c \ $(COMMON)/blib_graph.c \ $(COMMON)/blib_math.c \ - $(COMMON)/matrix.c \ $(COMMON)/blib_sound.c \ $(COMMON)/brun.c \ $(COMMON)/ceval.c \ @@ -41,7 +44,6 @@ LOCAL_SRC_FILES := \ $(COMMON)/geom.c \ $(COMMON)/inet.c \ $(COMMON)/kw.c \ - $(COMMON)/match.c \ $(COMMON)/mem.c \ $(COMMON)/panic.c \ $(COMMON)/pfill.c \ @@ -51,8 +53,9 @@ LOCAL_SRC_FILES := \ $(COMMON)/scan.c \ $(COMMON)/str.c \ $(COMMON)/tasks.c \ - $(COMMON)/search.c \ - $(COMMON)/var_hash.c \ + $(COMMON)/var_map.c \ + $(COMMON)/var_obj.c \ + $(COMMON)/var_eval.c \ $(COMMON)/keymap.c \ $(COMMON)/units.c \ $(COMMON)/var.c \ diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 0471f672..36aaff9d 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -22,6 +22,7 @@ #include "common/device.h" #include "common/blib_ui.h" #include "common/fs_socket_client.h" +#include "common/keymap.h" #define WAIT_INTERVAL 10 #define MAIN_BAS "__main_bas__" diff --git a/src/platform/fltk/MainWindow.cxx b/src/platform/fltk/MainWindow.cxx index ab02ffb8..487b5dbb 100644 --- a/src/platform/fltk/MainWindow.cxx +++ b/src/platform/fltk/MainWindow.cxx @@ -29,6 +29,7 @@ #include "common/sbapp.h" #include "common/sys.h" #include "common/fs_socket_client.h" +#include "common/keymap.h" using namespace fltk; @@ -1914,9 +1915,11 @@ bool BaseWindow::handleKeyEvent() { dev_pushkey(SB_KEY_RIGHT); break; case BackSpaceKey: - case DeleteKey: dev_pushkey(SB_KEY_BACKSPACE); break; + case DeleteKey: + dev_pushkey(SB_KEY_DELETE); + break; case ReturnKey: dev_pushkey(13); break; diff --git a/src/platform/fltk/dev_fltk.cxx b/src/platform/fltk/dev_fltk.cxx index 31bc95c2..22b29c49 100644 --- a/src/platform/fltk/dev_fltk.cxx +++ b/src/platform/fltk/dev_fltk.cxx @@ -28,6 +28,7 @@ #include "ui/utils.h" #include "ui/interface.h" #include "common/fs_socket_client.h" +#include "common/keymap.h" #ifdef WIN32 #include @@ -532,55 +533,27 @@ Image *getImage(dev_file_t *filep, int index) { return image; } -void dev_image(int handle, int index, int x, int y, int sx, int sy, int w, int h) { - int imgw = -1; - int imgh = -1; - dev_file_t *filep = dev_getfileptr(handle); - if (filep == 0) { - return; - } - - if (filep->open_flags == DEV_FILE_INPUT) { - Image *img = getImage(filep, index); - if (img != 0) { - // input/read image and display - img->measure(imgw, imgh); - wnd->_out->drawImage(img, x, y, sx, sy, (w == 0 ? imgw : w), (h == 0 ? imgh : h)); - } - } else { - // output screen area image to jpeg - wnd->_out->saveImage(filep->name, x, y, sx, sy); - } +int dev_load_image(int handle) { + // TODO + return -1; } -int dev_image_width(int handle, int index) { - int imgw = -1; - int imgh = -1; - dev_file_t *filep = dev_getfileptr(handle); - if (filep == 0 || filep->open_flags != DEV_FILE_INPUT) { - return 0; - } +int dev_image_width(int handle) { + // TODO + return -1; +} - Image *img = getImage(filep, index); - if (img) { - img->measure(imgw, imgh); - } - return imgw; +int dev_image_height(int handle) { + // TODO + return -1; } -int dev_image_height(int handle, int index) { - int imgw = -1; - int imgh = -1; - dev_file_t *filep = dev_getfileptr(handle); - if (filep == 0 || filep->open_flags != DEV_FILE_INPUT) { - return 0; - } +void dev_image_show(var_image *image) { + // TODO +} - Image *img = getImage(filep, index); - if (img) { - img->measure(imgw, imgh); - } - return imgh; +void dev_image_hide(int handle) { + // TODO } //--DELAY----------------------------------------------------------------------- diff --git a/src/platform/sdl/Makefile.am b/src/platform/sdl/Makefile.am index 184fc9fa..2ed87687 100644 --- a/src/platform/sdl/Makefile.am +++ b/src/platform/sdl/Makefile.am @@ -5,7 +5,7 @@ # Download the GNU Public License (GPL) from www.gnu.org # -AM_CPPFLAGS = -I$(top_srcdir)/src -I. @PACKAGE_CFLAGS@ +AM_CPPFLAGS = -I$(top_srcdir)/src -I. @PACKAGE_CFLAGS@ -Wunused -Wno-unused-result EXTRA_DIST = \ fixedfont.xpm \ diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index 946174ec..75c8b980 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -14,6 +14,7 @@ #include "common/device.h" #include "common/blib_ui.h" #include "common/fs_socket_client.h" +#include "common/keymap.h" #include "ui/maapi.h" #include "ui/utils.h" #include "ui/form_ui.h" diff --git a/src/platform/sdl/settings.cpp b/src/platform/sdl/settings.cpp new file mode 100644 index 00000000..bafca46f --- /dev/null +++ b/src/platform/sdl/settings.cpp @@ -0,0 +1,107 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2014 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#include "config.h" + +#include +#include +#include +#include + +#include "settings.h" +#include "ui/utils.h" + +static const char *ENV_VARS[] = { + "APPDATA", "HOME", "TMP", "TEMP", "TMPDIR" +}; + +#define PATH_MAX 256 +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define DEFAULT_SCALE 100 + +#if defined(__MINGW32__) +#define makedir(f) mkdir(f) +#else +#define makedir(f) mkdir(f, 0700) +#endif + +FILE *openConfig(const char *configName, const char *flags) { + FILE *result = NULL; + char path[PATH_MAX]; + int vars_len = sizeof(ENV_VARS) / sizeof(ENV_VARS[0]); + + path[0] = 0; + for (int i = 0; i < vars_len && result == NULL; i++) { + const char *home = getenv(ENV_VARS[i]); + if (home && access(home, R_OK) == 0) { + strcpy(path, home); + if (i == 1) { + // unix path + strcat(path, "/.config"); + makedir(path); + } + strcat(path, "/"); + strcat(path, configName); + makedir(path); + + strcat(path, "/settings.txt"); + result = fopen(path, flags); + } + } + return result; +} + +// +// returns the next integer from the file +// +int nextInteger(FILE *fp, int def) { + int result = 0; + for (int c = fgetc(fp); c != EOF && c != ',' && c != '\n'; c = fgetc(fp)) { + result = (result * 10) + (c - '0'); + } + if (!result) { + result = def; + } + return result; +} + +// +// restore window position +// +void restoreSettings(const char *configName, SDL_Rect &rect, int &fontScale) { + FILE *fp = openConfig(configName, "r"); + if (fp) { + rect.x = nextInteger(fp, SDL_WINDOWPOS_UNDEFINED); + rect.y = nextInteger(fp, SDL_WINDOWPOS_UNDEFINED); + rect.w = nextInteger(fp, DEFAULT_WIDTH); + rect.h = nextInteger(fp, DEFAULT_HEIGHT); + fontScale = nextInteger(fp, DEFAULT_SCALE); + fclose(fp); + } else { + rect.x = SDL_WINDOWPOS_UNDEFINED; + rect.y = SDL_WINDOWPOS_UNDEFINED; + rect.w = DEFAULT_WIDTH; + rect.h = DEFAULT_HEIGHT; + fontScale = DEFAULT_SCALE; + } +} + +// +// save the window position +// +void saveSettings(const char *configName, SDL_Window *window, int fontScale) { + FILE *fp = openConfig(configName, "w"); + if (fp) { + int x, y, w, h; + SDL_GetWindowPosition(window, &x, &y); + SDL_GetWindowSize(window, &w, &h); + fprintf(fp, "%d,%d,%d,%d,%d\n", x, y, w, h, fontScale); + } +} + diff --git a/src/platform/sdl/settings.h b/src/platform/sdl/settings.h new file mode 100644 index 00000000..8dc1d1d2 --- /dev/null +++ b/src/platform/sdl/settings.h @@ -0,0 +1,18 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2014 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +void restoreSettings(const char *configName, SDL_Rect &rect, int &fontScale); +void saveSettings(const char *configName, SDL_Window *window, int fontScale); + +#endif + diff --git a/src/ui/form_ui.cpp b/src/ui/form_ui.cpp index 9a357896..572697d2 100644 --- a/src/ui/form_ui.cpp +++ b/src/ui/form_ui.cpp @@ -15,6 +15,7 @@ #include "common/osd.h" #include "common/device.h" #include "common/blib_ui.h" +#include "common/keymap.h" #include "ui/utils.h" #include "ui/form_ui.h" diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index 5a115de6..8c4ebe6c 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -196,7 +196,6 @@ void Graphics::drawRectFilled(int left, int top, int width, int height) { } void Graphics::drawChar(FT_Bitmap *bitmap, FT_Int x, FT_Int y) { - FT_Int p, q; FT_Int xMax = x + bitmap->width; FT_Int yMax = y + bitmap->rows; @@ -245,14 +244,14 @@ void Graphics::drawText(int left, int top, const char *str, int len) { } } -int Graphics::getPixel(int posX, int posY) { +int Graphics::getPixel(Canvas *canvas, int posX, int posY) { int result = 0; - if (_drawTarget + if (canvas && posX > -1 && posY > -1 && posX < _drawTarget->_w && posY < _drawTarget->_h - 1) { - pixel_t *line = _drawTarget->getLine(posY); + pixel_t *line = canvas->getLine(posY); result = line[posX]; } return result; @@ -374,7 +373,7 @@ void maDestroyPlaceholder(MAHandle maHandle) { void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int scanlength) { Canvas *holder = (Canvas *)maHandle; // maGetImageData is only used for getPixel() - *((int *)dst) = graphics->getPixel(srcRect->left, srcRect->top); + *((int *)dst) = graphics->getPixel(holder, srcRect->left, srcRect->top); } MAHandle maSetDrawTarget(MAHandle maHandle) { diff --git a/src/ui/graphics.h b/src/ui/graphics.h index ea092dcb..4bd1d36b 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -48,7 +48,7 @@ struct Graphics { void drawPixel(int posX, int posY); void drawRectFilled(int left, int top, int width, int height); void drawText(int left, int top, const char *str, int len); - int getPixel(int x, int y); + int getPixel(Canvas *canvas, int x, int y); MAExtent getTextSize(const char *str, int len); int getHeight() { return _h; } int getWidth() { return _w; } diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 065d401d..1167719e 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -18,6 +18,7 @@ #include "common/device.h" #include "common/blib_ui.h" #include "common/fs_socket_client.h" +#include "common/keymap.h" #define SYSTEM_MENU "\033[ OConsole|Show keypad|View source|Restart" #define MENU_CONSOLE 0 @@ -621,3 +622,22 @@ char *dev_read(const char *fileName) { void dev_show_page() { g_system->_output->flushNow(); } + +int dev_image_load(int handle) { + return -1; +} + +int dev_image_width(int handle) { + return -1; +} + +int dev_image_height(int handle) { + return -1; +} + +void dev_image_show(var_image *image) { +} + +void dev_image_hide(int handle) { +} +