Example #1
0
// 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();
}
Example #2
0
// 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;
}
Example #3
0
static void op_I_set(const void *NOTUSED(data), scene_state_t *NOTUSED(ss),
                     exec_state_t *es, command_state_t *cs) {
    es_variables(es)->i = cs_pop(cs);
}
Example #4
0
static void op_I_get(const void *NOTUSED(data), scene_state_t *NOTUSED(ss),
                     exec_state_t *es, command_state_t *cs) {
    cs_push(cs, es_variables(es)->i);
}
Example #5
0
// run a single command inside a given exec_state
process_result_t process_command(scene_state_t *ss, exec_state_t *es,
                                 const tele_command_t *c) {
    command_state_t cs;
    cs_init(&cs);  // initialise this here as well as inside the loop, in case
                   // the command has 0 length

    // 1. Do we have a PRE seperator?
    // ------------------------------
    // if we do then only process the PRE part, the MOD will determine if the
    // POST should be run and take care of running it
    ssize_t start_idx = 0;
    ssize_t end_idx = c->separator == -1 ? c->length : c->separator;

    // 2. Determine the location of all the SUB commands
    // -------------------------------------------------
    // an array of structs to hold the start and end of each sub command
    struct sub_idx {
        ssize_t start;
        ssize_t end;
    } subs[COMMAND_MAX_LENGTH];

    ssize_t sub_len = 0;
    ssize_t sub_start = 0;

    // iterate through c->data to find all the SUB_SEPs and add to the array
    for (ssize_t idx = start_idx; idx < end_idx; idx++) {
        tele_word_t word_type = c->data[idx].tag;
        if (word_type == SUB_SEP && idx > sub_start) {
            subs[sub_len].start = sub_start;
            subs[sub_len].end = idx - 1;
            sub_len++;
            sub_start = idx + 1;
        }
    }

    // the last sub command won't have been added, manually add it here
    if (end_idx > sub_start) {
        subs[sub_len].start = sub_start;
        subs[sub_len].end = end_idx - 1;
        sub_len++;
    }

    // 3. Loop through each sub command and execute it
    // -----------------------------------------------
    // iterate through sub commands from left to right
    for (ssize_t sub_idx = 0; sub_idx < sub_len && !es_variables(es)->breaking;
         sub_idx++) {
        const ssize_t sub_start = subs[sub_idx].start;
        const ssize_t sub_end = subs[sub_idx].end;

        // initialise the command state for each sub, otherwise a value left on
        // the stack for the previous sub, can cause the set fn to trigger when
        // it shouldn't
        cs_init(&cs);

        // as we are using a stack based language, we must process commands from
        // right to left
        for (ssize_t idx = sub_end; idx >= sub_start; idx--) {
            const tele_word_t word_type = c->data[idx].tag;
            const int16_t word_value = c->data[idx].value;

            if (word_type == NUMBER) { cs_push(&cs, word_value); }
            else if (word_type == OP) {
                const tele_op_t *op = tele_ops[word_value];

                // if we're in the first command position, and there is a set fn
                // pointer and we have enough params, then run set, else run get
                if (idx == sub_start && op->set != NULL &&
                    cs_stack_size(&cs) >= op->params + 1)
                    op->set(op->data, ss, es, &cs);
                else
                    op->get(op->data, ss, es, &cs);
            }
            else if (word_type == MOD) {
                tele_command_t post_command;
                post_command.comment = false;
                copy_post_command(&post_command, c);
                tele_mods[word_value]->func(ss, es, &cs, &post_command);
            }
        }
    }

    // 4. Return
    // ---------
    // sometimes we have single value left of the stack, if so return it
    if (cs_stack_size(&cs)) {
        process_result_t o = {.has_value = true, .value = cs_pop(&cs) };
        return o;
    }