///////// // // Parse and interpret a stream, and return its value // // This is used in doCommand to execute a passed-in or collected text command, // in domacrocommand() when a macro/function is called from within a parse stream, // and in runBackgroundTasks to kick off the background run. // // numvar execscript(byte scripttype, numvar scriptaddress, char *scriptname) { // save parse context parsepoint fetchmark; markparsepoint(&fetchmark); byte thesym = sym; vpush(symval); // if this is the first stream context in this invocation, // set up our error recovery point and init the value stack // otherwise we skip this to allow nested execution calls // to properly return to top // if (fetchtype == SCRIPT_NONE) { // Exceptions come here via longjmp; see bitlash-error.c switch(setjmp(env)) { case 0: break; case X_EXIT: { // POLICY: Stop all background tasks on any error // // It is not possible to be certain that continuing here will work. // Not all errors leave the interpreter in a working state. Though most do. // The conservative/deterministic choice is to stop all background tasks // and drop the user back to the command prompt. // // On the other hand, you may find this inconvenient in your application, // and may be happy taking the risk of continuing. // // In which case, comment out this line and proceed with caution. // // TODO: if the macro "onerror" exists, call it here instead. Let it "stop *". // // -br // initTaskList(); // stop all pending tasks #ifdef SOFTWARE_SERIAL_TX resetOutput(); // clean up print module #endif // Other cleanups here vinit(); // initialize the expression stack fetchtype = SCRIPT_NONE; // reset parse context fetchptr = 0L; // reset parse location // sd_up = 0; // TODO: reset file system return (numvar) -1; } // X_EXIT case } // switch } initparsepoint(scripttype, scriptaddress, scriptname); getsym(); // interpret the function text and collect its result numvar ret = getstatementlist(); returntoparsepoint(&fetchmark, 1); // now where were we? sym = thesym; symval = vpop(); return ret; }
///////// // // "cat": copy file to serial out // numvar sdcat(void) { if (!scriptfileexists((char *) getarg(1))) return 0; numvar fetchmark = markparsepoint(); initparsepoint(SCRIPT_FILE, 0L, (char *) getarg(1)); while (inchar) { if (inchar == '\n') spb('\r'); spb(inchar); fetchc(); } returntoparsepoint(fetchmark, 1); return 1; }
////////// // // func_fprintf(): implementation of fprintf() function // // numvar func_fprintf(void) { numvar fetchmark = markparsepoint(); scriptwrite((char *) getarg(1), "", 1); // open the file for append (but append nothing) //serialOutputFunc saved_handler = serial_override_handler; // save previous output handler setOutputHandler(scriptwritebyte); // set file output handler func_printf_handler(2,3); // format=arg(2), optional args start at 3 //setOutputHandler(saved_handler);// restore output handler resetOutputHandler(); returntoparsepoint(fetchmark, 1); }
///////// // // sdwrite: write or append a line to a file // numvar sdwrite(char *filename, char *contents, byte append) { #if !defined(UNIX_BUILD) parsepoint fetchmark; markparsepoint(&fetchmark); #endif if (!scriptwrite(filename, contents, append)) unexpected(M_oops); #if !defined(UNIX_BUILD) returntoparsepoint(&fetchmark, 1); #endif return 1; }
// The switch statement: execute one of N statements based on a selector value // switch <numval> { stmt0; stmt1;...;stmtN } // numval < 0: treated as numval == 0 // numval > N: treated as numval == N // numvar getswitchstatement(void) { numvar thesymval = symval; //char *fetchmark; numvar fetchmark; numvar retval = 0; byte thesym = sym; getsym(); // eat "switch" getnum(); // evaluate the switch selector if (expval < 0) expval = 0; // map negative values to zero byte which = (byte) expval; // and stash it for reference if (sym != s_lcurly) expectedchar('{'); getsym(); // eat "{" // we sit before the first statement // scan and discard the <selector>'s worth of statements // that sit before the one we want while ((which > 0) && (sym != s_eof) && (sym != s_rcurly)) { //fetchmark = fetchptr; fetchmark = markparsepoint(); thesym = sym; thesymval = symval; skipstatement(); if ((sym != s_eof) && (sym != s_rcurly)) --which; } // If the selector is greater than the number of statements, // back up and execute the last one if (which > 0) { // oops ran out of piddys //fetchptr = fetchmark; // restore to last statement //primec(); // set up for getsym() returntoparsepoint(fetchmark, 0); sym = thesym; symval = thesymval; } //unexpected(M_number); // execute the statement we're pointing at retval = getstatement(); // eat the rest of the statement block to "}" while ((sym != s_eof) && (sym != s_rcurly)) skipstatement(); if (sym == s_rcurly) getsym(); // eat "}" return retval; }
////////// // // func_fprintf(): implementation of fprintf() function // // numvar func_fprintf(void) { parsepoint fetchmark; markparsepoint(&fetchmark); scriptwrite((char *) getarg(1), "", 1); // open the file for append (but append nothing) //serialOutputFunc saved_handler = serial_override_handler; // save previous output handler void scriptwritebyte(byte); setOutputHandler(scriptwritebyte); // set file output handler func_printf_handler(2,3); // format=arg(2), optional args start at 3 //setOutputHandler(saved_handler);// restore output handler resetOutputHandler(); #ifdef scriptclose scriptclose(); // close and flush the output #endif returntoparsepoint(&fetchmark, 1); }
// Get a statement numvar getstatement(void) { numvar retval = 0; //char *fetchmark; numvar fetchmark; chkbreak(); if (sym == s_while) { // at this point sym is pointing at s_while, before the conditional expression // save fetchptr so we can restart parsing from here as the while iterates //fetchmark = fetchptr; fetchmark = markparsepoint(); for (;;) { //fetchptr = fetchmark; // restore to mark //primec(); // set up for mr. getsym() returntoparsepoint(fetchmark, 0); getsym(); // fetch the start of the conditional if (getnum()) { retval = getstatement(); if (sym == s_returning) break; // exit if we caught a return } else { skipstatement(); break; } } } else if (sym == s_if) { getsym(); // eat "if" if (getnum()) { retval = getstatement(); if (sym == s_else) { getsym(); // eat "else" skipstatement(); } } else { skipstatement(); if (sym == s_else) { getsym(); // eat "else" retval = getstatement(); } } } else if (sym == s_lcurly) { getsym(); // eat "{" while ((sym != s_eof) && (sym != s_returning) && (sym != s_rcurly)) retval = getstatement(); if (sym == s_rcurly) getsym(); // eat "}" } else if (sym == s_return) { getsym(); // eat "return" if ((sym != s_eof) && (sym != s_semi)) retval = getnum(); sym = s_returning; // signal we're returning up the line } else if (sym == s_switch) retval = getswitchstatement(); else if (sym == s_function) cmd_function(); else if (sym == s_run) { // run macroname getsym(); if ((sym != s_script_eeprom) && (sym != s_script_progmem) && (sym != s_script_file)) unexpected(M_id); // address of macroid is in symval via parseid // check for [,snoozeintervalms] getsym(); // eat macroid to check for comma; symval untouched if (sym == s_comma) { vpush(symval); getsym(); // eat the comma getnum(); // get a number or else startTask(vpop(), expval); } else startTask(symval, 0); } else if (sym == s_stop) { getsym(); if (sym == s_mul) { // stop * stops all tasks initTaskList(); getsym(); } else if ((sym == s_semi) || (sym == s_eof)) { if (background) stopTask(curtask); // stop with no args stops the current task IF we're in back else initTaskList(); // in foreground, stop all } else stopTask(getnum()); } else if (sym == s_boot) reboot(); else if (sym == s_rm) { // rm "sym" or rm * getsym(); if (sym == s_script_eeprom) { eraseentry(idbuf); } else if (sym == s_mul) nukeeeprom(); else if (sym != s_undef) expected(M_id); getsym(); } else if (sym == s_ps) { getsym(); showTaskList(); } else if (sym == s_peep) { getsym(); cmd_peep(); } else if (sym == s_ls) { getsym(); cmd_ls(); } else if (sym == s_help) { getsym(); cmd_help(); } else if (sym == s_print) { getsym(); cmd_print(); } else if (sym == s_semi) { ; } // ;) #ifdef HEX_UPLOAD // a line beginning with a colon is treated as a hex record // containing data to upload to eeprom // // TODO: verify checksum // else if (sym == s_colon) { // fetchptr points at the byte count byte byteCount = gethex(2); // 2 bytes byte count int addr = gethex(4); // 4 bytes address byte recordType = gethex(2); // 2 bytes record type; now fetchptr -> data if (recordType == 1) reboot(); // reboot on EOF record (01) if (recordType != 0) return; // we only handle the data record (00) if (addr == 0) nukeeeprom(); // auto-clear eeprom on write to 0000 while (byteCount--) eewrite(addr++, gethex(2)); // update the eeprom gethex(2); // discard the checksum getsym(); // and re-prime the parser } #endif else getexpression(); if (sym == s_semi) getsym(); // eat trailing ';' return retval; }
///////// // // sdwrite: write or append a line to a file // numvar sdwrite(char *filename, char *contents, byte append) { numvar fetchmark = markparsepoint(); if (!scriptwrite(filename, contents, append)) unexpected(M_oops); returntoparsepoint(fetchmark, 1); return 1; }