1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-09 02:24:21 +03:00
inform7/inter/codegen-module/Chapter 1/Pipelines.w
2021-04-15 22:42:28 +01:00

430 lines
16 KiB
OpenEdge ABL

[CodeGen::Pipeline::] Pipelines.
To build and run through pipelines of code generation stages.
@h Steps.
A "step" is a single step in a pipeline of commands: it consists of a
choice of stage, together with a choice of argument. The other elements here
are all temporary storage for information needed when the step is run; they
have no meaningful contents when the step is not running.
=
typedef struct pipeline_step {
struct pipeline_stage *step_stage;
struct text_stream *step_argument;
struct code_generation_target *target_argument;
struct text_stream *package_argument;
struct filename *parsed_filename;
struct linked_list *the_PP; /* of |pathname| */
int to_debugging_log;
int from_memory;
int repository_argument;
struct text_stream *text_out_file;
struct linked_list *requirements_list; /* of |inter_library| */
struct inter_tree *repository;
struct codegen_pipeline *pipeline;
CLASS_DEFINITION
} pipeline_step;
pipeline_step *CodeGen::Pipeline::new_step(void) {
pipeline_step *step = CREATE(pipeline_step);
step->step_stage = NULL;
step->step_argument = NULL;
step->package_argument = NULL;
step->repository_argument = 0;
CodeGen::Pipeline::clean_step(step);
return step;
}
@ This wipes clean the temporary storage for a step.
=
void CodeGen::Pipeline::clean_step(pipeline_step *step) {
step->parsed_filename = NULL;
step->text_out_file = NULL;
step->to_debugging_log = FALSE;
step->from_memory = FALSE;
step->the_PP = NULL;
step->repository = NULL;
step->pipeline = NULL;
step->requirements_list = NEW_LINKED_LIST(inter_library);
}
@ Here we write a textual description to a string, which is useful for
logging:
=
void CodeGen::Pipeline::write_step(OUTPUT_STREAM, pipeline_step *step) {
WRITE("%S", step->step_stage->stage_name);
if (step->step_stage->stage_arg != NO_STAGE_ARG) {
if (step->repository_argument > 0) {
WRITE(" %d", step->repository_argument);
if (Str::len(step->package_argument) > 0) WRITE(":%S", step->package_argument);
} else {
if (Str::len(step->package_argument) > 0) WRITE(" %S", step->package_argument);
}
if (step->step_stage->takes_repository) WRITE(" <- %S", step->step_argument);
if (step->target_argument) WRITE(" %S -> %S", step->target_argument->target_name, step->step_argument);
}
}
pipeline_step *CodeGen::Pipeline::read_step(text_stream *step, dictionary *D,
text_file_position *tfp) {
CodeGen::Stage::make_stages();
CodeGen::Targets::make_targets();
pipeline_step *ST = CodeGen::Pipeline::new_step();
match_results mr = Regexp::create_mr();
int left_arrow_used = FALSE;
if (Regexp::match(&mr, step, L"(%c+?) *<- *(%c*)")) {
if (Str::len(mr.exp[1]) > 0) {
ST->step_argument = CodeGen::Pipeline::read_parameter(mr.exp[1], D, tfp);
if (ST->step_argument == NULL) return NULL;
} else {
Errors::in_text_file_S(I"no source to right of arrow", tfp);
return NULL;
}
Str::copy(step, mr.exp[0]);
left_arrow_used = TRUE;
} else if (Regexp::match(&mr, step, L"(%c+?) *(%C*) *-> *(%c*)")) {
code_generation_target *cgt;
LOOP_OVER(cgt, code_generation_target)
if (Str::eq(mr.exp[1], cgt->target_name))
ST->target_argument = cgt;
if (ST->target_argument == NULL) {
TEMPORARY_TEXT(ERR)
WRITE_TO(ERR, "no such code generation format as '%S'\n", mr.exp[1]);
Errors::in_text_file_S(ERR, tfp);
DISCARD_TEXT(ERR)
return NULL;
}
ST->step_argument = CodeGen::Pipeline::read_parameter(mr.exp[2], D, tfp);
if (ST->step_argument == NULL) return NULL;
Str::copy(step, mr.exp[0]);
}
if (Regexp::match(&mr, step, L"(%C+?) (%d)")) {
ST->repository_argument = Str::atoi(mr.exp[1], 0);
Str::copy(step, mr.exp[0]);
} else if (Regexp::match(&mr, step, L"(%C+?) (%d):(%c*)")) {
ST->repository_argument = Str::atoi(mr.exp[1], 0);
if (Str::len(mr.exp[2]) > 0) {
ST->package_argument = CodeGen::Pipeline::read_parameter(mr.exp[2], D, tfp);
if (ST->package_argument == NULL) return NULL;
}
Str::copy(step, mr.exp[0]);
} else if (Regexp::match(&mr, step, L"(%C+?) (%c+)")) {
ST->package_argument = CodeGen::Pipeline::read_parameter(mr.exp[1], D, tfp);
if (ST->package_argument == NULL) return NULL;
Str::copy(step, mr.exp[0]);
}
pipeline_stage *stage;
LOOP_OVER(stage, pipeline_stage)
if (Str::eq(step, stage->stage_name))
ST->step_stage = stage;
if (ST->step_stage == NULL) {
TEMPORARY_TEXT(ERR)
WRITE_TO(ERR, "no such stage as '%S'\n", step);
Errors::in_text_file_S(ERR, tfp);
DISCARD_TEXT(ERR)
return NULL;
}
if (ST->step_stage->takes_repository) {
if (left_arrow_used == FALSE) {
Errors::in_text_file_S(I"this stage should take a left arrow and a source", tfp);
return NULL;
}
} else {
if (left_arrow_used) {
Errors::in_text_file_S(I"this stage should not take a left arrow and a source", tfp);
return NULL;
}
}
Regexp::dispose_of(&mr);
return ST;
}
text_stream *CodeGen::Pipeline::read_parameter(text_stream *from, dictionary *D,
text_file_position *tfp) {
if (Str::get_first_char(from) == '*') {
text_stream *find = Dictionaries::get_text(D, from);
if (find) return Str::duplicate(find);
TEMPORARY_TEXT(ERR)
WRITE_TO(ERR, "no such pipeline variable as '%S'\n", from);
Errors::in_text_file_S(ERR, tfp);
DISCARD_TEXT(ERR)
}
return Str::duplicate(from);
}
@h Pipelines.
And then a pipeline is just a linked list of steps.
=
typedef struct codegen_pipeline {
struct dictionary *variables;
struct inter_tree *memory_repository;
struct inter_tree *repositories[10];
struct linked_list *steps; /* of |pipeline_step| */
int erroneous;
CLASS_DEFINITION
} codegen_pipeline;
dictionary *CodeGen::Pipeline::basic_dictionary(text_stream *leafname) {
dictionary *D = Dictionaries::new(16, TRUE);
if (Str::len(leafname) > 0) Str::copy(Dictionaries::create_text(D, I"*out"), leafname);
Str::copy(Dictionaries::create_text(D, I"*log"), I"*log");
return D;
}
codegen_pipeline *CodeGen::Pipeline::new(dictionary *D) {
codegen_pipeline *S = CREATE(codegen_pipeline);
S->variables = D;
S->steps = NEW_LINKED_LIST(pipeline_step);
S->memory_repository = NULL;
S->erroneous = FALSE;
for (int i=0; i<10; i++) S->repositories[i] = NULL;
return S;
}
codegen_pipeline *CodeGen::Pipeline::parse_from_file(filename *F, dictionary *D) {
codegen_pipeline *S = CodeGen::Pipeline::new(D);
TextFiles::read(F, FALSE, "can't open inter pipeline file",
TRUE, CodeGen::Pipeline::scan_line, NULL, (void *) S);
if (S->erroneous) return NULL;
return S;
}
void CodeGen::Pipeline::scan_line(text_stream *line, text_file_position *tfp, void *X) {
codegen_pipeline *S = (codegen_pipeline *) X;
CodeGen::Pipeline::parse_into(S, line, tfp);
}
codegen_pipeline *CodeGen::Pipeline::parse(text_stream *instructions, dictionary *D) {
codegen_pipeline *S = CodeGen::Pipeline::new(D);
CodeGen::Pipeline::parse_into(S, instructions, NULL);
if (S->erroneous) return NULL;
return S;
}
void CodeGen::Pipeline::parse_into(codegen_pipeline *S, text_stream *instructions,
text_file_position *tfp) {
TEMPORARY_TEXT(T)
LOOP_THROUGH_TEXT(P, instructions)
if (Characters::is_babel_whitespace(Str::get(P)))
PUT_TO(T, ' ');
else
PUT_TO(T, Str::get(P));
match_results mr = Regexp::create_mr();
while (Regexp::match(&mr, T, L" *(%c+?) *,+ *(%c*?) *")) {
pipeline_step *ST = CodeGen::Pipeline::read_step(mr.exp[0], S->variables, tfp);
if (ST) ADD_TO_LINKED_LIST(ST, pipeline_step, S->steps);
else S->erroneous = TRUE;
Str::copy(T, mr.exp[1]);
}
if (Regexp::match(&mr, T, L" *(%c+?) *")) {
pipeline_step *ST = CodeGen::Pipeline::read_step(mr.exp[0], S->variables, tfp);
if (ST) ADD_TO_LINKED_LIST(ST, pipeline_step, S->steps);
else S->erroneous = TRUE;
}
Regexp::dispose_of(&mr);
DISCARD_TEXT(T)
}
void CodeGen::Pipeline::set_repository(codegen_pipeline *S, inter_tree *I) {
S->memory_repository = I;
}
void CodeGen::Pipeline::run(pathname *P, codegen_pipeline *S, linked_list *PP,
linked_list *requirements_list) {
if (S == NULL) return;
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 = 0;
pipeline_step *step;
LOOP_OVER_LINKED_LIST(step, pipeline_step, S->steps) step_total++;
int active = TRUE;
stopwatch_timer *prep_timer = NULL;
LOOP_OVER_LINKED_LIST(step, pipeline_step, S->steps)
if (active) {
if (prep_timer == NULL)
prep_timer = Time::start_stopwatch(pipeline_timer, I"step preparation");
else
Time::resume_stopwatch(prep_timer);
if (S->repositories[step->repository_argument] == NULL)
S->repositories[step->repository_argument] = InterTree::new();
inter_tree *I = S->repositories[step->repository_argument];
if (I == NULL) internal_error("no repository");
CodeGen::Pipeline::prepare_to_run(I);
CodeGen::Pipeline::lint(I);
CodeGen::Pipeline::clean_step(step);
step->the_PP = PP;
step->repository = I;
step->pipeline = S;
step->requirements_list = requirements_list;
Time::stop_stopwatch(prep_timer);
int skip_step = FALSE;
TEMPORARY_TEXT(STAGE_NAME)
WRITE_TO(STAGE_NAME, "inter step %d/%d: ", ++step_count, step_total);
CodeGen::Pipeline::write_step(STAGE_NAME, step);
Log::new_stage(STAGE_NAME);
stopwatch_timer *step_timer =
Time::start_stopwatch(pipeline_timer, STAGE_NAME);
DISCARD_TEXT(STAGE_NAME)
if ((step->step_stage->stage_arg == FILE_STAGE_ARG) ||
(step->step_stage->stage_arg == TEXT_OUT_STAGE_ARG) ||
(step->step_stage->stage_arg == EXT_FILE_STAGE_ARG) ||
(step->step_stage->stage_arg == EXT_TEXT_OUT_STAGE_ARG)) {
if (Str::eq(step->step_argument, I"*log")) {
step->to_debugging_log = TRUE;
} else if (Str::eq(step->step_argument, I"*memory")) {
S->repositories[step->repository_argument] = S->memory_repository;
skip_step = TRUE;
} else {
int slashes = FALSE;
LOOP_THROUGH_TEXT(pos, step->step_argument)
if (Str::get(pos) == '/')
slashes = TRUE;
if (slashes) step->parsed_filename = Filenames::from_text(step->step_argument);
else step->parsed_filename = Filenames::in(P, step->step_argument);
}
}
text_stream text_output_struct; /* For any text file we might write */
text_stream *T = &text_output_struct;
if (step->to_debugging_log) {
step->text_out_file = DL;
} else if ((step->step_stage->stage_arg == TEXT_OUT_STAGE_ARG) ||
(step->step_stage->stage_arg == EXT_TEXT_OUT_STAGE_ARG)) {
if (STREAM_OPEN_TO_FILE(T, step->parsed_filename, ISO_ENC) == FALSE) {
#ifdef PROBLEMS_MODULE
Problems::fatal_on_file("Can't open output file", step->parsed_filename);
#endif
#ifndef PROBLEMS_MODULE
Errors::fatal_with_file("Can't open output file", step->parsed_filename);
exit(1);
#endif
}
step->text_out_file = T;
}
if (skip_step == FALSE)
active = (*(step->step_stage->execute))(step);
if (((step->step_stage->stage_arg == TEXT_OUT_STAGE_ARG) ||
(step->step_stage->stage_arg == EXT_TEXT_OUT_STAGE_ARG)) &&
(step->to_debugging_log == FALSE)) {
STREAM_CLOSE(T);
}
Time::stop_stopwatch(step_timer);
}
Time::stop_stopwatch(pipeline_timer);
}
@h Following.
=
inter_symbol *unchecked_kind_symbol = NULL;
inter_symbol *unchecked_function_symbol = NULL;
inter_symbol *typeless_int_symbol = NULL;
inter_symbol *list_of_unchecked_kind_symbol = NULL;
inter_symbol *object_kind_symbol = NULL;
inter_symbol *action_kind_symbol = NULL;
inter_symbol *truth_state_kind_symbol = NULL;
inter_symbol *direction_kind_symbol = NULL;
inter_symbol *verb_directive_reverse_symbol = NULL;
inter_symbol *verb_directive_slash_symbol = NULL;
inter_symbol *verb_directive_divider_symbol = NULL;
inter_symbol *verb_directive_result_symbol = NULL;
inter_symbol *verb_directive_special_symbol = NULL;
inter_symbol *verb_directive_number_symbol = NULL;
inter_symbol *verb_directive_noun_symbol = NULL;
inter_symbol *verb_directive_multi_symbol = NULL;
inter_symbol *verb_directive_multiinside_symbol = NULL;
inter_symbol *verb_directive_multiheld_symbol = NULL;
inter_symbol *verb_directive_held_symbol = NULL;
inter_symbol *verb_directive_creature_symbol = NULL;
inter_symbol *verb_directive_topic_symbol = NULL;
inter_symbol *verb_directive_multiexcept_symbol = NULL;
inter_symbol *code_ptype_symbol = NULL;
inter_symbol *plain_ptype_symbol = NULL;
inter_symbol *submodule_ptype_symbol = NULL;
inter_symbol *function_ptype_symbol = NULL;
inter_symbol *action_ptype_symbol = NULL;
inter_symbol *command_ptype_symbol = NULL;
inter_symbol *property_ptype_symbol = NULL;
inter_symbol *to_phrase_ptype_symbol = NULL;
void CodeGen::Pipeline::prepare_to_run(inter_tree *I) {
code_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_code");
plain_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_plain");
submodule_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_submodule");
function_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_function");
action_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_action");
command_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_command");
property_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_property");
to_phrase_ptype_symbol = InterSymbolsTables::url_name_to_symbol(I, NULL, I"/_to_phrase");
unchecked_kind_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_unchecked");
unchecked_function_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_unchecked_function");
typeless_int_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_typeless_int");
list_of_unchecked_kind_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_list_of_values");
object_kind_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_object");
action_kind_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_action_name");
truth_state_kind_symbol = Inter::Packages::search_resources_exhaustively(I, I"K_truth_state");
direction_kind_symbol = Inter::Packages::search_resources_exhaustively(I, I"K3_direction");
verb_directive_reverse_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_REVERSE");
verb_directive_slash_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_SLASH");
verb_directive_divider_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_DIVIDER");
verb_directive_result_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_RESULT");
verb_directive_special_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_SPECIAL");
verb_directive_number_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_NUMBER");
verb_directive_noun_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_NOUN");
verb_directive_multi_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_MULTI");
verb_directive_multiinside_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_MULTIINSIDE");
verb_directive_multiheld_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_MULTIHELD");
verb_directive_held_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_HELD");
verb_directive_creature_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_CREATURE");
verb_directive_topic_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_TOPIC");
verb_directive_multiexcept_symbol = Inter::Packages::search_resources_exhaustively(I, I"VERB_DIRECTIVE_MULTIEXCEPT");
}
void CodeGen::Pipeline::lint(inter_tree *I) {
InterTree::traverse(I, CodeGen::Pipeline::visitor, NULL, NULL, -PACKAGE_IST);
}
void CodeGen::Pipeline::visitor(inter_tree *I, inter_tree_node *P, void *state) {
inter_ti c = Inode::get_package(P)->index_n;
inter_ti a = Inode::get_package_alt(P);
if (c != a) {
LOG("Frame gives package as $6, but its location is in package $6\n",
Inode::ID_to_package(P, c),
Inode::ID_to_package(P, a));
WRITE_TO(STDERR, "Frame gives package as %d, but its location is in package %d\n",
Inode::ID_to_package(P, c)->index_n,
Inode::ID_to_package(P, a)->index_n);
internal_error("misplaced package");
}
Produce::guard(Inter::Defn::verify_children_inner(P));
}
inter_symbol *CodeGen::Pipeline::uks(void) {
if (unchecked_kind_symbol == NULL) internal_error("no unchecked kind symbol");
return unchecked_kind_symbol;
}