To run through pipelines of code generation stages.
§1. Ephemeral data. This is temporary data meaningful only while a pipeline is running; it is "cleaned", that is, reinitialised, at the start of each pipeline run.
typedef struct pipeline_ephemera { struct inter_tree *memory_repository; struct inter_tree *trees[10]; struct inter_package *assimilation_modules[10]; struct linked_list *replacements_list[10]; } pipeline_ephemera; void RunningPipelines::clean_pipeline(inter_pipeline *pl) { pl->ephemera.memory_repository = NULL; for (int i=0; i<10; i++) { pl->ephemera.trees[i] = NULL; pl->ephemera.assimilation_modules[i] = NULL; pl->ephemera.replacements_list[i] = NEW_LINKED_LIST(text_stream); } }
- The structure pipeline_ephemera is accessed in 2/rmss, 3/ps, 3/css, 4/lbks and here.
typedef struct pipeline_step_ephemera { struct filename *parsed_filename; struct pathname *the_kit; if one is involved int to_debugging_log; int from_memory; struct text_stream *to_stream; struct linked_list *requirements_list; of attachment_instruction struct inter_tree *tree; struct inter_pipeline *pipeline; struct target_vm *for_VM; struct inter_symbol *cached_symbols[MAX_RPSYM]; struct inter_package *package_argument; int cached_symbols_fetched[MAX_RPSYM]; } pipeline_step_ephemera; void RunningPipelines::clean_step(pipeline_step *step) { step->ephemera.parsed_filename = NULL; step->ephemera.to_stream = NULL; step->ephemera.to_debugging_log = FALSE; step->ephemera.from_memory = FALSE; step->ephemera.the_kit = NULL; step->ephemera.tree = NULL; step->ephemera.pipeline = NULL; step->ephemera.requirements_list = NEW_LINKED_LIST(attachment_instruction); step->ephemera.for_VM = NULL; step->ephemera.package_argument = NULL; for (int i=0; i<MAX_RPSYM; i++) { step->ephemera.cached_symbols_fetched[i] = FALSE; step->ephemera.cached_symbols[i] = NULL; } }
- The structure pipeline_step_ephemera is accessed in 2/pp, 2/pe, 2/ns, 2/rmss, 3/ps, 3/rccs, 3/css, 4/lbks, 4/sws, 4/dics, 4/mius, 4/rvs, 5/msms, 6/erms, 6/erls, 6/eros and here.
§3. This outer layer is all just instrumentation, really: we run through the steps in turn, timing how long each one took us.
pipeline_step *currently_running_pipeline_step = NULL; void RunningPipelines::run(pathname *P, inter_pipeline *S, inter_tree *I, pathname *the_kit, linked_list *requirements_list, target_vm *VM, int tracing) { if (S == NULL) return; if (I) S->ephemera.memory_repository = I; stopwatch_timer *within = NULL; #ifdef CORE_MODULE within = inform7_timer; #endif stopwatch_timer *pipeline_timer = Time::start_stopwatch(within, I"running Inter pipeline"); int step_count = 0, step_total = LinkedLists::len(S->steps); int active = TRUE; stopwatch_timer *prep_timer = NULL; pipeline_step *step; LOOP_OVER_LINKED_LIST(step, pipeline_step, S->steps) if (active) Run this step, timing and logging it3.1; Time::stop_stopwatch(pipeline_timer); }
§3.1. Run this step, timing and logging it3.1 =
currently_running_pipeline_step = step; if (prep_timer == NULL) prep_timer = Time::start_stopwatch(pipeline_timer, I"step preparation"); else Time::resume_stopwatch(prep_timer); Prepare ephemeral data for this step3.1.1; Time::stop_stopwatch(prep_timer); TEMPORARY_TEXT(STAGE_NAME) WRITE_TO(STAGE_NAME, "step %d/%d: ", ++step_count, step_total); ParsingPipelines::write_step(STAGE_NAME, step); Log::new_stage(STAGE_NAME); if (tracing) WRITE_TO(STDOUT, "%S\n", STAGE_NAME); stopwatch_timer *step_timer = Time::start_stopwatch(pipeline_timer, STAGE_NAME); DISCARD_TEXT(STAGE_NAME) if (active) Run this step3.1.2; Time::stop_stopwatch(step_timer); currently_running_pipeline_step = NULL;
- This code is used in §3.
§3.1.1. Prepare ephemeral data for this step3.1.1 =
RunningPipelines::clean_step(step); step->ephemera.the_kit = the_kit; if (S->ephemera.trees[step->tree_argument] == NULL) { S->ephemera.trees[step->tree_argument] = InterTree::new(); S->ephemera.assimilation_modules[step->tree_argument] = NULL; } inter_tree *I = S->ephemera.trees[step->tree_argument]; if (I == NULL) { PipelineErrors::error(step, "no Inter tree to apply this step to"); active = FALSE; } step->ephemera.tree = I; step->ephemera.pipeline = S; step->ephemera.requirements_list = requirements_list; step->ephemera.for_VM = VM; if ((VM) && (step->take_generator_argument_from_VM)) { step->generator_argument = Generators::find_for(VM); if (step->generator_argument == NULL) { PipelineErrors::error(step, "unable to guess a suitable code-generator"); active = FALSE; } } step->ephemera.package_argument = NULL; if (Str::len(step->package_URL_argument) > 0) { step->ephemera.package_argument = InterPackage::from_URL(step->ephemera.tree, step->package_URL_argument); if (step->ephemera.package_argument == NULL) { PipelineErrors::error_with(step, "pipeline step applied to package which does not exist: '%S'", step->package_URL_argument); active = FALSE; } }
- This code is used in §3.1.
if (ParsingPipelines::will_write_a_file(step)) { if (Str::len(step->step_argument) == 0) { if (step->step_stage->stage_arg != OPTIONAL_TEXT_OUT_STAGE_ARG) { PipelineErrors::error(step, "no filename given in pipeline step"); active = FALSE; } } else { if (Str::eq(step->step_argument, I"*log")) { step->ephemera.to_stream = DL; Call the stage execution function3.1.2.2; } else if (Str::eq(step->step_argument, I"-")) { step->ephemera.to_stream = STDOUT; Call the stage execution function3.1.2.2; } else { Work out the filename3.1.2.1; text_stream text_output_struct; text_stream *T = &text_output_struct; if (STREAM_OPEN_TO_FILE(T, step->ephemera.parsed_filename, UTF8_ENC) == FALSE) { PipelineErrors::error(step, "unable to open file named in pipeline step"); active = FALSE; } else { step->ephemera.to_stream = T; Call the stage execution function3.1.2.2; STREAM_CLOSE(T); } } } } else if (ParsingPipelines::will_read_a_file(step)) { if (Str::len(step->step_argument) == 0) { PipelineErrors::error(step, "no filename given in pipeline step"); active = FALSE; } else if (Str::eq(step->step_argument, I"*memory")) { if (Str::eq(step->step_stage->stage_name, I"read") == FALSE) { PipelineErrors::error(step, "'*memory' can be used only on reads"); active = FALSE; } else { S->ephemera.trees[step->tree_argument] = S->ephemera.memory_repository; and do not call the executor function: that does the read } } else { Work out the filename3.1.2.1; Call the stage execution function3.1.2.2; } } else { Call the stage execution function3.1.2.2; }
- This code is used in §3.1.
§3.1.2.1. Work out the filename3.1.2.1 =
int contains_slash = FALSE; LOOP_THROUGH_TEXT(pos, step->step_argument) if (Str::get(pos) == '/') contains_slash = TRUE; if (contains_slash) step->ephemera.parsed_filename = Filenames::from_text(step->step_argument); else step->ephemera.parsed_filename = Filenames::in(P, step->step_argument);
- This code is used in §3.1.2 (twice).
§3.1.2.2. The pipeline stops running (becomes inactive) as soon as one of the stage functions returns FALSE, or as soon as a pipeline processing error occurs, whichever comes first.
Call the stage execution function3.1.2.2 =
active = (*(step->step_stage->execute))(step);
- This code is used in §3.1.2 (five times).
§4. In an ideal world, we would not track this in a global variable, but it is not simple to remove the need for this (though, at the same time, it is needed very little in practice, and never when this code runs in Inform 7).
pipeline_step *RunningPipelines::current_step(void) { return currently_running_pipeline_step; }
§5. Popular symbols cache. While working on a tree, the execution functions will frequently need its most popular symbols — searching for these is not too slow, but even so, once is enough. But we cache them on each step, wiping the cache at the end of the step, since running a step changes the Inter tree and could conceivably move, add or remove some of these symbols.
enum object_kind_RPSYM from 0 enum direction_kind_RPSYM enum verb_directive_meta_RPSYM enum verb_directive_noun_filter_RPSYM enum verb_directive_scope_filter_RPSYM enum verb_directive_reverse_RPSYM enum verb_directive_slash_RPSYM enum verb_directive_divider_RPSYM enum verb_directive_result_RPSYM enum verb_directive_special_RPSYM enum verb_directive_number_RPSYM enum verb_directive_noun_RPSYM enum verb_directive_multi_RPSYM enum verb_directive_multiinside_RPSYM enum verb_directive_multiheld_RPSYM enum verb_directive_held_RPSYM enum verb_directive_creature_RPSYM enum verb_directive_topic_RPSYM enum verb_directive_multiexcept_RPSYM enum code_ptype_RPSYM enum plain_ptype_RPSYM enum submodule_ptype_RPSYM enum function_ptype_RPSYM enum action_ptype_RPSYM enum command_ptype_RPSYM enum property_ptype_RPSYM enum to_phrase_ptype_RPSYM define MAX_RPSYM 100
inter_symbol *RunningPipelines::get_symbol(pipeline_step *step, int id) { if ((id < 0) || (id >= MAX_RPSYM)) internal_error("bad ID"); if (step == NULL) internal_error("no step"); inter_tree *I = step->ephemera.tree; if (step->ephemera.cached_symbols_fetched[id] == FALSE) { step->ephemera.cached_symbols_fetched[id] = TRUE; switch (id) { case code_ptype_RPSYM: step->ephemera.cached_symbols[code_ptype_RPSYM] = LargeScale::package_type(I, I"_code"); break; case plain_ptype_RPSYM: step->ephemera.cached_symbols[plain_ptype_RPSYM] = LargeScale::package_type(I, I"_plain"); break; case submodule_ptype_RPSYM: step->ephemera.cached_symbols[submodule_ptype_RPSYM] = LargeScale::package_type(I, I"_submodule"); break; case function_ptype_RPSYM: step->ephemera.cached_symbols[function_ptype_RPSYM] = LargeScale::package_type(I, I"_function"); break; case action_ptype_RPSYM: step->ephemera.cached_symbols[action_ptype_RPSYM] = LargeScale::package_type(I, I"_action"); break; case command_ptype_RPSYM: step->ephemera.cached_symbols[command_ptype_RPSYM] = LargeScale::package_type(I, I"_command"); break; case property_ptype_RPSYM: step->ephemera.cached_symbols[property_ptype_RPSYM] = LargeScale::package_type(I, I"_property"); break; case to_phrase_ptype_RPSYM: step->ephemera.cached_symbols[to_phrase_ptype_RPSYM] = LargeScale::package_type(I, I"_to_phrase"); break; case object_kind_RPSYM: step->ephemera.cached_symbols[object_kind_RPSYM] = LargeScale::find_symbol_in_tree(I, I"K_object"); break; case direction_kind_RPSYM: step->ephemera.cached_symbols[direction_kind_RPSYM] = LargeScale::find_symbol_in_tree(I, I"K3_direction"); break; case verb_directive_meta_RPSYM: step->ephemera.cached_symbols[verb_directive_meta_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_META"); break; case verb_directive_noun_filter_RPSYM: step->ephemera.cached_symbols[verb_directive_noun_filter_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_NOUN_FILTER"); break; case verb_directive_scope_filter_RPSYM: step->ephemera.cached_symbols[verb_directive_scope_filter_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_SCOPE_FILTER"); break; case verb_directive_reverse_RPSYM: step->ephemera.cached_symbols[verb_directive_reverse_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_REVERSE"); break; case verb_directive_slash_RPSYM: step->ephemera.cached_symbols[verb_directive_slash_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_SLASH"); break; case verb_directive_divider_RPSYM: step->ephemera.cached_symbols[verb_directive_divider_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_DIVIDER"); break; case verb_directive_result_RPSYM: step->ephemera.cached_symbols[verb_directive_result_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_RESULT"); break; case verb_directive_special_RPSYM: step->ephemera.cached_symbols[verb_directive_special_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_SPECIAL"); break; case verb_directive_number_RPSYM: step->ephemera.cached_symbols[verb_directive_number_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_NUMBER"); break; case verb_directive_noun_RPSYM: step->ephemera.cached_symbols[verb_directive_noun_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_NOUN"); break; case verb_directive_multi_RPSYM: step->ephemera.cached_symbols[verb_directive_multi_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_MULTI"); break; case verb_directive_multiinside_RPSYM: step->ephemera.cached_symbols[verb_directive_multiinside_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_MULTIINSIDE"); break; case verb_directive_multiheld_RPSYM: step->ephemera.cached_symbols[verb_directive_multiheld_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_MULTIHELD"); break; case verb_directive_held_RPSYM: step->ephemera.cached_symbols[verb_directive_held_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_HELD"); break; case verb_directive_creature_RPSYM: step->ephemera.cached_symbols[verb_directive_creature_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_CREATURE"); break; case verb_directive_topic_RPSYM: step->ephemera.cached_symbols[verb_directive_topic_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_TOPIC"); break; case verb_directive_multiexcept_RPSYM: step->ephemera.cached_symbols[verb_directive_multiexcept_RPSYM] = LargeScale::find_symbol_in_tree(I, I"VERB_DIRECTIVE_MULTIEXCEPT"); break; } } return step->ephemera.cached_symbols[id]; }
§6. This variant is more interesting: it tries to find the symbol in the tree, but if it can't, it makes one and creates a plug for it, expecting the true definition (and thus a socket) to come later from some other material not yet linked in.
inter_symbol *RunningPipelines::ensure_symbol(pipeline_step *step, int id, text_stream *identifier) { inter_tree *I = step->ephemera.tree; inter_symbol *S = RunningPipelines::get_symbol(step, id); if (S) return S; step->ephemera.cached_symbols[id] = Wiring::plug(I, identifier); return step->ephemera.cached_symbols[id]; }