// Check every op manipulates the stack correctly TEST op_stack_size() { for (size_t i = 0; i < E_OP__LENGTH; i++) { const tele_op_t *op = tele_ops[i]; if (op->get != NULL) { scene_state_t ss = {}; // initalise to empty // (needs dedicated initaliser) exec_state_t es; es_init(&es); es_push(&es); es_variables(&es)->script_number = 1; command_state_t cs; cs_init(&cs); // add params to stack (plus an extra 2, to check that too many // values aren't removed, warning: note the maximum stack size in // state.h) const int16_t stack_extra = 2; for (int j = 0; j < op->params + stack_extra; j++) cs_push(&cs, 0); // execute get op->get(op->data, &ss, &es, &cs); // check that the stack has the correct number of items in it if (op->returns) { ASSERT_EQm(op->name, cs_stack_size(&cs), stack_extra + 1); } else { ASSERT_EQm(op->name, cs_stack_size(&cs), stack_extra); } } if (op->set != NULL) { scene_state_t ss = {}; // initalise to empty // (needs dedicated initaliser) exec_state_t es; es_init(&es); command_state_t cs; cs_init(&cs); // add params to stack (plus an extra 2, to check that too many // values aren't removed, warning: note the maximum stack size in // state.h) // set functions require an extra value on the stack const int16_t stack_extra = 2; for (int j = 0; j < op->params + stack_extra + 1; j++) cs_push(&cs, 0); // execute get op->set(op->data, &ss, &es, &cs); // check that the stack has the correct number of items in it ASSERT_EQm(op->name, cs_stack_size(&cs), stack_extra); } } PASS(); }
// Everything needs to call this to execute code. An execution // context is required for proper operation of DEL, THIS, L, W, IF process_result_t run_script_with_exec_state(scene_state_t *ss, exec_state_t *es, size_t script_no) { #ifdef TELETYPE_PROFILE tele_profile_script(script_no); #endif process_result_t result = {.has_value = false, .value = 0 }; es_set_script_number(es, script_no); for (size_t i = 0; i < ss_get_script_len(ss, script_no); i++) { es_set_line_number(es, i); // Commented code doesn't run. if (ss_get_script_comment(ss, script_no, i)) continue; // BREAK implemented with break... if (es_variables(es)->breaking) break; do { // TODO: Check for 0-length commands before we bother? result = process_command(ss, es, ss_get_script_command(ss, script_no, i)); // and WHILE implemented with while! } while (es_variables(es)->while_continue && !es_variables(es)->breaking); } es_variables(es)->breaking = false; ss_update_script_last(ss, script_no); #ifdef TELETYPE_PROFILE tele_profile_script(script_no); #endif return result; } // Only the test framework should call this, and it needs to follow up its // es_init() with an es_push(). // es_variables()->script_number should be set to test SCRIPT process_result_t run_command(scene_state_t *ss, const tele_command_t *cmd) { exec_state_t es; process_result_t o; es_init(&es); es_push(&es); // the lack of a script number here is a bug, so if you use this code, // something needs to set the script number // es_variables(es)->script_number = do { o = process_command(ss, &es, cmd); } while (es_variables(&es)->while_continue && !es_variables(&es)->breaking); return o; }
process_result_t run_script(scene_state_t *ss, size_t script_no) { exec_state_t es; es_init(&es); es_push(&es); return run_script_with_exec_state(ss, &es, script_no); }