To generate final code from intermediate code.


§1. Pipeline stage.

void CodeGen::create_pipeline_stage(void) {
    CodeGen::Stage::new(I"generate", CodeGen::run_pipeline_stage, TEXT_OUT_STAGE_ARG, FALSE);
}

int CodeGen::run_pipeline_stage(pipeline_step *step) {
    if (step->target_argument == NULL) internal_error("no target specified");
    inter_package *which = NULL;
    if (Str::len(step->package_argument) > 0) {
        which = Inter::Packages::by_url(step->repository,
            step->package_argument);
        if (which == NULL) {
            LOG("Arg %S\n", step->package_argument);
            internal_error("no such package name");
        }
    }

    code_generation *gen =
        CodeGen::new_generation(step, step->repository, which, step->target_argument);
    if (CodeGen::Targets::begin_generation(gen) == FALSE) {
        CodeGen::generate(gen);
        CodeGen::Targets::end_generation(gen);
        CodeGen::write(step->text_out_file, gen);
    }
    return TRUE;
}

§2. Generations. A "generation" is a single act of translating inter code into final code. That final code will be a text file written in some other programming language, though probably a low-level one.

The "target" of a generation is the final language: for example, Inform 6.

During a generation, textual output is assembled as a set of "segments". Different targets may need different segments. This is all to facilitate rearranging content as necessary to get it to compile in the target language: for example, one might need to have all constants defined first, then all arrays, and one could do this by creating two segments, one to accumulate the constants in, one to accumulate the arrays.

At any given time, a generation has a "current" segment, to which output is being written. Ome segment is special: the temporary one, which is used only when assembling other material, and not for the final output.

enum temporary_I7CGS from 0
define MAX_CG_SEGMENTS 100
typedef struct code_generation {
    struct pipeline_step *from_step;
    struct inter_tree *from;
    struct code_generation_target *target;
    struct inter_package *just_this_package;
    struct generated_segment *segments[MAX_CG_SEGMENTS];
    struct linked_list *segment_sequence;  of generated_segment
    struct generated_segment *current_segment;  an entry in that array, or null
    int temporarily_diverted;  to the temporary segment
    void *target_specific_data;  depending on the target generated to
    CLASS_DEFINITION
} code_generation;

code_generation *CodeGen::new_generation(pipeline_step *step, inter_tree *I,
    inter_package *just, code_generation_target *target) {
    code_generation *gen = CREATE(code_generation);
    gen->from_step = step;
    gen->from = I;
    gen->target = target;
    if (just) gen->just_this_package = just;
    else gen->just_this_package = Site::main_package(I);
    gen->current_segment = NULL;
    gen->segment_sequence = NEW_LINKED_LIST(generated_segment);
    gen->temporarily_diverted = FALSE;
    for (int i=0; i<MAX_CG_SEGMENTS; i++) gen->segments[i] = NULL;
    return gen;
}

§3. At present, at least, a "segment" is nothing more than a wrapper for a text. But we abstract it in case it's ever useful for it to be more.

typedef struct generated_segment {
    struct text_stream *generated_code;
    CLASS_DEFINITION
} generated_segment;

generated_segment *CodeGen::new_segment(void) {
    generated_segment *seg = CREATE(generated_segment);
    seg->generated_code = Str::new();
    return seg;
}

void CodeGen::create_segments(code_generation *gen, void *data, int codes[]) {
    gen->segment_sequence = NEW_LINKED_LIST(generated_segment);
    for (int i=0; codes[i] >= 0; i++) {
        if ((codes[i] >= MAX_CG_SEGMENTS) ||
            (codes[i] == temporary_I7CGS)) internal_error("bad segment sequence");
        gen->segments[codes[i]] = CodeGen::new_segment();
        ADD_TO_LINKED_LIST(gen->segments[codes[i]], generated_segment, gen->segment_sequence);
    }
    gen->target_specific_data = data;
}

§4. And then all we do is concatenate them in order:

void CodeGen::write(OUTPUT_STREAM, code_generation *gen) {
    generated_segment *seg;
    LOOP_OVER_LINKED_LIST(seg, generated_segment, gen->segment_sequence)
        WRITE("%S", seg->generated_code);
}

§5. Here we switch the output, by changing the segment selection. This must always be done in a way which is then undone, restoring the previous state:

generated_segment *CodeGen::select(code_generation *gen, int i) {
    generated_segment *saved = gen->current_segment;
    if ((i < 0) || (i >= MAX_CG_SEGMENTS)) internal_error("out of range");
    if (gen->temporarily_diverted) internal_error("poorly timed selection");
    gen->current_segment = gen->segments[i];
    return saved;
}

void CodeGen::deselect(code_generation *gen, generated_segment *saved) {
    if (gen->temporarily_diverted) internal_error("poorly timed deselection");
    gen->current_segment = saved;
}

§6. The procedure for selecting the temporary segment is different, because we also have to direct it to a given text.

void CodeGen::select_temporary(code_generation *gen, text_stream *T) {
    if (gen->segments[temporary_I7CGS] == NULL) {
        gen->segments[temporary_I7CGS] = CodeGen::new_segment();
        gen->segments[temporary_I7CGS]->generated_code = NULL;
    }
    if (gen->temporarily_diverted)
        internal_error("nested temporary cgs");
    gen->temporarily_diverted = TRUE;
    gen->segments[temporary_I7CGS]->generated_code = T;
}

void CodeGen::deselect_temporary(code_generation *gen) {
    gen->temporarily_diverted = FALSE;
}

§7. Note that temporary selections take precedence over the regular selection.

text_stream *CodeGen::current(code_generation *gen) {
    if (gen->temporarily_diverted)
        return gen->segments[temporary_I7CGS]->generated_code;
    if (gen->current_segment == NULL) return NULL;
    return gen->current_segment->generated_code;
}

§8. Actual generation happens in three phases:

void CodeGen::generate(code_generation *gen) {
    Phase one - preparation8.1;
    Phase two - traverse8.2;
    Phase three - consolidation8.3;
}

§8.1. Phase one - preparation8.1 =

    InterTree::traverse(gen->from, CodeGen::clear_transients, NULL, NULL, PACKAGE_IST);
    CodeGen::FC::prepare(gen);
    CodeGen::CL::prepare(gen);
    CodeGen::Var::prepare(gen);
    CodeGen::IP::prepare(gen);

§8.2. Phase two - traverse8.2 =

    InterTree::traverse_root_only(gen->from, CodeGen::pragma, gen, PRAGMA_IST);
    InterTree::traverse(gen->from, CodeGen::FC::pre_iterate, gen, NULL, -PACKAGE_IST);
    InterTree::traverse(gen->from, CodeGen::FC::iterate, gen, NULL, -PACKAGE_IST);

§8.3. Phase three - consolidation8.3 =

    CodeGen::IP::write_properties(gen);
    CodeGen::CL::sort_literals(gen);

§9.

void CodeGen::pragma(inter_tree *I, inter_tree_node *P, void *state) {
    code_generation *gen = (code_generation *) state;
    inter_symbol *target_symbol = InterSymbolsTables::symbol_from_frame_data(P, TARGET_PRAGMA_IFLD);
    if (target_symbol == NULL) internal_error("bad pragma");
    inter_ti ID = P->W.data[TEXT_PRAGMA_IFLD];
    text_stream *S = Inode::ID_to_text(P, ID);
    CodeGen::Targets::offer_pragma(gen, P, target_symbol->symbol_name, S);
}

§10. Marking. We use a transient flag on symbols, but abstract that here:

int CodeGen::marked(inter_symbol *symb_name) {
    return Inter::Symbols::get_flag(symb_name, TRAVERSE_MARK_BIT);
}

void CodeGen::mark(inter_symbol *symb_name) {
    Inter::Symbols::set_flag(symb_name, TRAVERSE_MARK_BIT);
}

void CodeGen::unmark(inter_symbol *symb_name) {
    Inter::Symbols::clear_flag(symb_name, TRAVERSE_MARK_BIT);
}

void CodeGen::clear_transients(inter_tree *I, inter_tree_node *P, void *state) {
    inter_package *pack = Inter::Package::defined_by_frame(P);
    inter_symbols_table *T = Inter::Packages::scope(pack);
    for (int i=0; i<T->size; i++)
        if (T->symbol_array[i])
            Inter::Symbols::clear_transient_flags(T->symbol_array[i]);
}