To compile run-time support for rules.
- §4. Printing rule names at run time
- §6. Compilation of I6-format rulebook
- §13. Compiling the firing test
- §13.2. Scene test
- §13.4. Action test
- §13.7. Actor-is-player test
- §13.9. Activity-or-condition test
- §14. Synoptic resources
rule *rule_being_compiled = NULL; rule whose phrase's definition is being compiled rule *rule_to_which_this_is_a_response = NULL; when a new response is being compiled int response_marker_within_that_rule = -1; when a new response is being compiled
§2. In Inter code, a rule is compiled to the name of the routine implementing it.
typedef struct rule_compilation_data { struct package_request *rule_package; struct inter_name *shell_routine_iname; struct inter_name *rule_extern_iname; if externally defined, this is the I6 routine struct inter_name *xiname; struct inter_name *rule_extern_response_handler_iname; and this produces any response texts it has int defn_compiled; has the definition of this rule, if needed, been compiled yet? } rule_compilation_data; rule_compilation_data RTRules::new_compilation_data(rule *R) { rule_compilation_data rcd; rcd.rule_extern_iname = NULL; rcd.xiname = NULL; rcd.rule_extern_response_handler_iname = NULL; rcd.shell_routine_iname = NULL; rcd.rule_package = Hierarchy::local_package(RULES_HAP); rcd.defn_compiled = FALSE; if (Wordings::nonempty(R->name)) Hierarchy::apply_metadata_from_wording(rcd.rule_package, RULE_NAME_METADATA_HL, R->name); return rcd; } stack_frame *RTRules::stack_frame(rule *R) { if ((R == NULL) || (R->defn_as_I7_source == NULL)) return NULL; return &(R->defn_as_I7_source->body_of_defn->compilation_data.id_stack_frame); } void RTRules::prepare_rule(imperative_defn *id, rule *R) { rule_family_data *rfd = RETRIEVE_POINTER_rule_family_data(id->family_specific_data); package_request *P = RTRules::package(R); if (Wordings::empty(rfd->constant_name)) Hierarchy::apply_metadata_from_wording(P, RULE_NAME_METADATA_HL, Node::get_text(id->at)); CompileImperativeDefn::set_iname(id->body_of_defn, Hierarchy::make_iname_in(RULE_FN_HL, P)); } package_request *RTRules::package(rule *R) { return R->compilation_data.rule_package; } inter_name *RTRules::shell_iname(rule *R) { if (R->compilation_data.shell_routine_iname == NULL) R->compilation_data.shell_routine_iname = Hierarchy::make_iname_in(SHELL_FN_HL, R->compilation_data.rule_package); return R->compilation_data.shell_routine_iname; } inter_name *RTRules::iname(rule *R) { if (R->defn_as_I7_source) return CompileImperativeDefn::iname(R->defn_as_I7_source->body_of_defn); else if (R->compilation_data.rule_extern_iname) { if (LinkedLists::len(R->applicability_constraints) > 0) { return RTRules::shell_iname(R); } else { return R->compilation_data.rule_extern_iname; } } else internal_error("tried to symbolise nameless rule"); return NULL; } void RTRules::define_by_Inter_function(rule *R) { R->compilation_data.rule_extern_iname = Hierarchy::make_iname_in(EXTERIOR_RULE_HL, R->compilation_data.rule_package); R->compilation_data.xiname = Produce::find_by_name(Emit::tree(), R->defn_as_Inter_function); text_stream *desc = Str::new(); WRITE_TO(desc, "external iname for '%W'", R->name); Sequence::queue(&RTRules::from_Inter_function_agent, STORE_POINTER_rule(R), desc); } inter_name *RTRules::get_handler_definition(rule *R) { if (R->compilation_data.rule_extern_response_handler_iname == NULL) { R->compilation_data.rule_extern_response_handler_iname = Hierarchy::derive_iname_in(RESPONDER_FN_HL, R->compilation_data.xiname, R->compilation_data.rule_package); Hierarchy::make_available(R->compilation_data.rule_extern_response_handler_iname); } return R->compilation_data.rule_extern_response_handler_iname; } void RTRules::from_Inter_function_agent(compilation_subtask *t) { rule *R = RETRIEVE_POINTER_rule(t->data); Emit::iname_constant(R->compilation_data.rule_extern_iname, K_value, R->compilation_data.xiname); } void RTRules::from_source_text_agent(compilation_subtask *t) { rule *R = RETRIEVE_POINTER_rule(t->data); if (R->compilation_data.defn_compiled == FALSE) { R->compilation_data.defn_compiled = TRUE; rule_being_compiled = R; if (R->defn_as_I7_source) { R->defn_as_I7_source->body_of_defn->compilation_data.at_least_one_compiled_form_needed = TRUE; current_sentence = R->defn_as_I7_source->at; CompileImperativeDefn::not_from_phrase( R->defn_as_I7_source->body_of_defn, &total_phrases_compiled, total_phrases_to_compile, R->variables_visible_in_definition, R); R->defn_as_I7_source->body_of_defn->compilation_data.at_least_one_compiled_form_needed = FALSE; } if ((R->compilation_data.rule_extern_iname) && (LinkedLists::len(R->applicability_constraints) > 0)) Compile a shell routine to apply conditions to an I6 rule2.1; rule_being_compiled = NULL; } }
- The structure rule_compilation_data is private to this section.
§2.1. This is the trickiest case: where the user has asked for something like
The carrying requirements rule does nothing when eating the lollipop.
and the carrying requirements rule is defined by an I6 routine, which we are unable to modify. What we do is to create a shell routine to call it, and put the conditions into this outer shell; we then use the outer shell as the definition of the rule in future.
Compile a shell routine to apply conditions to an I6 rule2.1 =
inter_name *shell_iname = RTRules::shell_iname(R); packaging_state save = Functions::begin(shell_iname); if (RTRules::compile_constraint(R) == FALSE) { EmitCode::inv(RETURN_BIP); EmitCode::down(); EmitCode::call(R->compilation_data.rule_extern_iname); EmitCode::up(); } Functions::end(save);
- This code is used in §2.
§3. The following generates code to terminate a rule early if its applicability conditions have not been met.
int RTRules::compile_constraint(rule *R) { if (R) { applicability_constraint *acl; LOOP_OVER_LINKED_LIST(acl, applicability_constraint, R->applicability_constraints) { current_sentence = acl->where_imposed; if (Wordings::nonempty(acl->text_of_condition)) { EmitCode::inv(IF_BIP); EmitCode::down(); if (acl->sense_of_applicability) { EmitCode::inv(NOT_BIP); EmitCode::down(); } Compile the constraint condition3.1; if (acl->sense_of_applicability) { EmitCode::up(); } EmitCode::code(); EmitCode::down(); } Compile the rule termination code used if the constraint was violated3.2; if (Wordings::nonempty(acl->text_of_condition)) { EmitCode::up(); EmitCode::up(); } else { return TRUE; } } } return FALSE; }
§3.1. Compile the constraint condition3.1 =
if (Wordings::nonempty(acl->text_of_condition) == FALSE) { EmitCode::val_true(); } else { if (<s-condition>(acl->text_of_condition)) { parse_node *spec = <<rp>>; Dash::check_condition(spec); CompileValues::to_code_val_of_kind(spec, K_truth_state); } else { Problems::quote_source(1, current_sentence); Problems::quote_wording(2, acl->text_of_condition); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_BadRuleConstraint)); Problems::issue_problem_segment( "In %1, you placed a constraint '%2' on a rule, but this isn't " "a condition I can understand."); Problems::issue_problem_end(); EmitCode::val_number(1); } }
- This code is used in §3.
§3.2. Note that in the does nothing case, the rule ends without result, rather than failing; so it doesn't terminate the following of its rulebook.
Compile the rule termination code used if the constraint was violated3.2 =
EmitCode::inv(RETURN_BIP); EmitCode::down(); if (acl->substituted_rule) { inter_name *subbed = RTRules::iname(acl->substituted_rule); if (Inter::Constant::is_routine(InterNames::to_symbol(subbed)) == FALSE) { EmitCode::val_number(0); } else { EmitCode::call(subbed); } } else { EmitCode::val_number(0); } EmitCode::up();
- This code is used in §3.
§4. Printing rule names at run time.
void RTRules::RulePrintingRule_routine(void) { inter_name *iname = Hierarchy::find(RULEPRINTINGRULE_HL); packaging_state save = Functions::begin(iname); inter_symbol *R_s = LocalVariables::new_other_as_symbol(I"R"); EmitCode::inv(IFELSE_BIP); EmitCode::down(); EmitCode::inv(AND_BIP); EmitCode::down(); EmitCode::inv(GE_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, R_s); EmitCode::val_number(0); EmitCode::up(); EmitCode::inv(LT_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, R_s); EmitCode::val_iname(K_value, Hierarchy::find(NUMBER_RULEBOOKS_CREATED_HL)); EmitCode::up(); EmitCode::up(); EmitCode::code(); EmitCode::down(); Print a rulebook name4.1; EmitCode::up(); EmitCode::code(); EmitCode::down(); Print a rule name4.2; EmitCode::up(); EmitCode::up(); Functions::end(save); Hierarchy::make_available(iname); }
§4.1. Print a rulebook name4.1 =
if (global_compilation_settings.memory_economy_in_force) { EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I"(rulebook "); EmitCode::up(); EmitCode::inv(PRINTNUMBER_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, R_s); EmitCode::up(); EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I")"); EmitCode::up(); } else { EmitCode::inv(PRINTSTRING_BIP); EmitCode::down(); EmitCode::inv(LOOKUP_BIP); EmitCode::down(); EmitCode::val_iname(K_value, Hierarchy::find(RULEBOOKNAMES_HL)); EmitCode::val_symbol(K_value, R_s); EmitCode::up(); EmitCode::up(); }
- This code is used in §4.
if (global_compilation_settings.memory_economy_in_force) { EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I"(rule at address "); EmitCode::up(); EmitCode::inv(PRINTNUMBER_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, R_s); EmitCode::up(); EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I")"); EmitCode::up(); } else { rule *R; LOOP_OVER(R, rule) { if ((Wordings::nonempty(R->name) == FALSE) && ((R->defn_as_I7_source == NULL) || (R->defn_as_I7_source->at == NULL) || (R->defn_as_I7_source->at->down == NULL))) continue; EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::inv(EQ_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, R_s); EmitCode::val_iname(K_value, RTRules::iname(R)); EmitCode::up(); EmitCode::code(); EmitCode::down(); TEMPORARY_TEXT(OUT) Print a textual name for this rule4.2.1; EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(OUT); EmitCode::up(); EmitCode::rtrue(); DISCARD_TEXT(OUT) EmitCode::up(); EmitCode::up(); } EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I"(nameless rule at address "); EmitCode::up(); EmitCode::inv(PRINTNUMBER_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, R_s); EmitCode::up(); EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I")"); EmitCode::up(); }
- This code is used in §4.
§4.2.1. Print a textual name for this rule4.2.1 =
if (Wordings::nonempty(R->name)) { TranscodeText::from_text(OUT, R->name); } else if (R->defn_as_I7_source->at) { TranscodeText::from_text(OUT, Articles::remove_the( Node::get_text(R->defn_as_I7_source->at))); } else WRITE("%n", RTRules::iname(R));
- This code is used in §4.2.
void RTRules::compile_comment(rule *R, int index, int from) { TEMPORARY_TEXT(C) WRITE_TO(C, "Rule %d/%d", index, from); if (R->defn_as_I7_source == NULL) { WRITE_TO(C, ": %n", R->compilation_data.rule_extern_iname); } EmitCode::comment(C); DISCARD_TEXT(C) if (R->defn_as_I7_source) { TEMPORARY_TEXT(C) WRITE_TO(C, "%~W:", R->defn_as_I7_source->log_text); EmitCode::comment(C); DISCARD_TEXT(C) } }
§6. Compilation of I6-format rulebook. The following can generate both old-style array rulebooks and routine rulebooks, which were introduced in December 2010.
void RTRules::start_list_compilation(void) { inter_name *iname = Hierarchy::find(EMPTY_RULEBOOK_INAME_HL); packaging_state save = Functions::begin(iname); LocalVariables::new_other_parameter(I"forbid_breaks"); EmitCode::rfalse(); Functions::end(save); Hierarchy::make_available(iname); }
define ARRAY_RBF 1 format as an array simply listing the rules define GROUPED_ARRAY_RBF 2 format as a grouped array, for quicker action testing define ROUTINE_RBF 3 format as a routine which runs the rulebook define RULE_OPTIMISATION_THRESHOLD 20 group arrays when larger than this number of rules
inter_name *RTRules::list_compile(booking_list *L, inter_name *identifier, int action_based, int parameter_based) { if (L == NULL) return NULL; inter_name *rb_symb = NULL; int countup = BookingLists::length(L); if (countup == 0) { rb_symb = Emit::iname_constant(identifier, K_value, Hierarchy::find(EMPTY_RULEBOOK_INAME_HL)); } else { int format = ROUTINE_RBF; Compile the rulebook in the given format7.1; } return rb_symb; }
§7.1. Grouping is the practice of gathering together rules which all rely on the same action going on; it's then efficient to test the action once rather than once for each rule.
Compile the rulebook in the given format7.1 =
int grouping = FALSE, group_cap = 0; switch (format) { case GROUPED_ARRAY_RBF: grouping = TRUE; group_cap = 31; break; case ROUTINE_RBF: grouping = TRUE; group_cap = 2000000000; break; } if (action_based == FALSE) grouping = FALSE; inter_symbol *forbid_breaks_s = NULL, *rv_s = NULL, *original_deadflag_s = NULL, *p_s = NULL; packaging_state save_array = Emit::new_packaging_state(); Open the rulebook compilation7.1.1; int group_size = 0, group_started = FALSE, entry_count = 0, action_group_open = FALSE; LOOP_OVER_BOOKINGS(br, L) { parse_node *spec = Rvalues::from_rule(RuleBookings::get_rule(br)); if (grouping) { if (group_size == 0) { if (group_started) End an action group in the rulebook7.1.4; #ifdef IF_MODULE action_name *an = RTRules::br_required_action(br); booking *brg = br; while ((brg) && (an == RTRules::br_required_action(brg))) { group_size++; brg = brg->next_booking; } #endif #ifndef IF_MODULE booking *brg = br; while (brg) { group_size++; brg = brg->next_booking; } #endif if (group_size > group_cap) group_size = group_cap; group_started = TRUE; Begin an action group in the rulebook7.1.2; } group_size--; } Compile an entry in the rulebook7.1.3; entry_count++; } if (group_started) End an action group in the rulebook7.1.4; Close the rulebook compilation7.1.5;
- This code is used in §7.
§7.1.1. Open the rulebook compilation7.1.1 =
rb_symb = identifier; switch (format) { case ARRAY_RBF: save_array = EmitArrays::begin(identifier, K_value); break; case GROUPED_ARRAY_RBF: save_array = EmitArrays::begin(identifier, K_value); EmitArrays::numeric_entry((inter_ti) -2); break; case ROUTINE_RBF: { save_array = Functions::begin(identifier); forbid_breaks_s = LocalVariables::new_other_as_symbol(I"forbid_breaks"); rv_s = LocalVariables::new_internal_commented_as_symbol(I"rv", I"return value"); if (countup > 1) original_deadflag_s = LocalVariables::new_internal_commented_as_symbol(I"original_deadflag", I"saved state"); if (parameter_based) p_s = LocalVariables::new_internal_commented_as_symbol(I"p", I"rulebook parameter"); RTRules::commentary(L); if (countup > 1) { EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::ref_symbol(K_value, original_deadflag_s); EmitCode::val_iname(K_value, Hierarchy::find(DEADFLAG_HL)); EmitCode::up(); } if (parameter_based) { EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::ref_symbol(K_value, p_s); EmitCode::val_iname(K_value, Hierarchy::find(PARAMETER_VALUE_HL)); EmitCode::up(); } break; } }
- This code is used in §7.1.
§7.1.2. Begin an action group in the rulebook7.1.2 =
switch (format) { case GROUPED_ARRAY_RBF: #ifdef IF_MODULE if (an) RTActions::action_array_entry(an); else #endif EmitArrays::numeric_entry((inter_ti) -2); if (group_size > 1) EmitArrays::numeric_entry((inter_ti) group_size); action_group_open = TRUE; break; case ROUTINE_RBF: #ifdef IF_MODULE if (an) { EmitCode::inv(IFELSE_BIP); EmitCode::down(); EmitCode::inv(EQ_BIP); EmitCode::down(); EmitCode::val_iname(K_value, Hierarchy::find(ACTION_HL)); EmitCode::val_iname(K_value, RTActions::double_sharp(an)); EmitCode::up(); EmitCode::code(); EmitCode::down(); action_group_open = TRUE; } #endif break; }
- This code is used in §7.1.
§7.1.3. Compile an entry in the rulebook7.1.3 =
switch (format) { case ARRAY_RBF: case GROUPED_ARRAY_RBF: CompileValues::to_array_entry(spec); break; case ROUTINE_RBF: if (entry_count > 0) { EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::inv(NE_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, original_deadflag_s); EmitCode::val_iname(K_value, Hierarchy::find(DEADFLAG_HL)); EmitCode::up(); EmitCode::code(); EmitCode::down(); EmitCode::inv(RETURN_BIP); EmitCode::down(); EmitCode::val_number(0); EmitCode::up(); EmitCode::up(); EmitCode::up(); } Compile an optional mid-rulebook paragraph break7.1.3.1; if (parameter_based) { EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::ref_iname(K_value, Hierarchy::find(PARAMETER_VALUE_HL)); EmitCode::val_symbol(K_value, p_s); EmitCode::up(); } EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::ref_symbol(K_value, rv_s); EmitCode::inv(INDIRECT0_BIP); EmitCode::down(); CompileValues::to_code_val(spec); EmitCode::up(); EmitCode::up(); EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, rv_s); EmitCode::code(); EmitCode::down(); EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::inv(EQ_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, rv_s); EmitCode::val_number(2); EmitCode::up(); EmitCode::code(); EmitCode::down(); EmitCode::inv(RETURN_BIP); EmitCode::down(); EmitCode::val_iname(K_value, Hierarchy::find(REASON_THE_ACTION_FAILED_HL)); EmitCode::up(); EmitCode::up(); EmitCode::up(); EmitCode::inv(RETURN_BIP); EmitCode::down(); CompileValues::to_code_val(spec); EmitCode::up(); EmitCode::up(); EmitCode::up(); EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::inv(LOOKUPREF_BIP); EmitCode::down(); EmitCode::val_iname(K_value, Hierarchy::find(LATEST_RULE_RESULT_HL)); EmitCode::val_number(0); EmitCode::up(); EmitCode::val_number(0); EmitCode::up(); break; }
- This code is used in §7.1.
§7.1.4. End an action group in the rulebook7.1.4 =
if (action_group_open) { switch (format) { case ROUTINE_RBF: EmitCode::up(); EmitCode::code(); EmitCode::down(); Compile an optional mid-rulebook paragraph break7.1.3.1; EmitCode::up(); EmitCode::up(); break; } action_group_open = FALSE; }
- This code is used in §7.1 (twice).
§7.1.5. Close the rulebook compilation7.1.5 =
switch (format) { case ARRAY_RBF: case GROUPED_ARRAY_RBF: EmitArrays::null_entry(); EmitArrays::end(save_array); break; case ROUTINE_RBF: EmitCode::inv(RETURN_BIP); EmitCode::down(); EmitCode::val_number(0); EmitCode::up(); Functions::end(save_array); break; }
- This code is used in §7.1.
§7.1.3.1. Compile an optional mid-rulebook paragraph break7.1.3.1 =
if (entry_count > 0) { EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::val_iname(K_number, Hierarchy::find(SAY__P_HL)); EmitCode::code(); EmitCode::down(); EmitCode::call(Hierarchy::find(RULEBOOKPARBREAK_HL)); EmitCode::down(); EmitCode::val_symbol(K_value, forbid_breaks_s); EmitCode::up(); EmitCode::up(); EmitCode::up(); }
#ifdef IF_MODULE action_name *RTRules::br_required_action(booking *br) { imperative_defn *id = Rules::get_imperative_definition(br->rule_being_booked); if (id) return ActionRules::required_action(&(id->body_of_defn->runtime_context_data)); return NULL; } #endif
typedef struct rulebook_compilation_data { struct inter_name *stv_creator_iname; struct package_request *rb_package; struct inter_name *rb_iname; run-time storage/routine holding contents struct inter_name *rb_id_iname; struct inter_name *rb_run_md_iname; } rulebook_compilation_data; rulebook_compilation_data RTRules::new_rulebook_compilation_data(rulebook *rb, package_request *R) { rulebook_compilation_data rcd; rcd.stv_creator_iname = NULL; rcd.rb_package = R; rcd.rb_iname = Hierarchy::make_iname_in(RUN_FN_HL, R); rcd.rb_id_iname = Hierarchy::make_iname_in(RULEBOOK_ID_HL, R); rcd.rb_run_md_iname = Hierarchy::make_iname_in(RULEBOOK_RUN_FN_METADATA_HL, R); return rcd; }
- The structure rulebook_compilation_data is accessed in 5/act, 6/scn, 7/act and here.
§10. We do not actually compile the I6 routines for a rulebook here, but simply act as a proxy. The I6 arrays making the rulebooks available to run-time code are the real outcome of the code in this section.
void RTRules::commentary(booking_list *L) { RuleBookings::list_judge_ordering(L); if (BookingLists::is_empty_of_i7_rules(L)) return; BookingLists::commentary(L); } void RTRules::compile_rulebooks(void) { RTRules::start_list_compilation(); rulebook *B; LOOP_OVER(B, rulebook) { Emit::numeric_constant(B->compilation_data.rb_id_iname, 0); Emit::iname_constant(B->compilation_data.rb_run_md_iname, K_value, B->compilation_data.rb_iname); int act = FALSE; if (Rulebooks::action_focus(B)) act = TRUE; if (B->automatically_generated) act = FALSE; int par = FALSE; if (Rulebooks::action_focus(B) == FALSE) par = TRUE; LOGIF(RULEBOOK_COMPILATION, "Compiling rulebook: %W = %n\n", B->primary_name, B->compilation_data.rb_iname); RTRules::list_compile(B->contents, B->compilation_data.rb_iname, act, par); } rule *R; LOOP_OVER(R, rule) Rules::check_constraints_are_typesafe(R); } inter_name *RTRules::get_stv_creator_iname(rulebook *B) { if (B->compilation_data.stv_creator_iname == NULL) B->compilation_data.stv_creator_iname = Hierarchy::make_iname_in(RULEBOOK_STV_CREATOR_FN_HL, B->compilation_data.rb_package); return B->compilation_data.stv_creator_iname; } void RTRules::rulebook_var_creators(void) { rulebook *B; LOOP_OVER(B, rulebook) if (SharedVariables::set_empty(B->my_variables) == FALSE) { RTVariables::set_shared_variables_creator(B->my_variables, RTRules::get_stv_creator_iname(B)); RTVariables::compile_frame_creator(B->my_variables); inter_name *vc = Hierarchy::make_iname_in(RULEBOOK_VARC_METADATA_HL, B->compilation_data.rb_package); Emit::iname_constant(vc, K_value, RTVariables::get_shared_variables_creator(B->my_variables)); } }
<notable-rulebook-outcomes> ::= it is very likely | it is likely | it is possible | it is unlikely | it is very unlikely
- This is Preform grammar, not regular C code.
void RTRules::new_outcome(named_rulebook_outcome *rbno, wording W) { package_request *R = Hierarchy::local_package(OUTCOMES_HAP); Hierarchy::apply_metadata_from_wording(R, OUTCOME_NAME_METADATA_HL, W); rbno->nro_iname = Hierarchy::make_iname_with_memo(OUTCOME_HL, R, W); if (<notable-rulebook-outcomes>(W)) { int i = -1; switch (<<r>>) { case 0: i = RBNO4_INAME_HL; break; case 1: i = RBNO3_INAME_HL; break; case 2: i = RBNO2_INAME_HL; break; case 3: i = RBNO1_INAME_HL; break; case 4: i = RBNO0_INAME_HL; break; } if (i >= 0) { inter_name *iname = Hierarchy::find(i); Hierarchy::make_available(iname); Emit::iname_constant(iname, K_value, rbno->nro_iname); } } } inter_name *RTRules::outcome_identifier(named_rulebook_outcome *rbno) { return rbno->nro_iname; } inter_name *RTRules::default_outcome_identifier(void) { named_rulebook_outcome *rbno; LOOP_OVER(rbno, named_rulebook_outcome) return rbno->nro_iname; return NULL; } void RTRules::compile_default_outcome(outcomes *outs) { int rtrue = FALSE; rulebook_outcome *rbo = outs->default_named_outcome; if (rbo) { switch(rbo->kind_of_outcome) { case SUCCESS_OUTCOME: { inter_name *iname = Hierarchy::find(RULEBOOKSUCCEEDS_HL); EmitCode::call(iname); EmitCode::down(); RTKinds::emit_weak_id_as_val(K_rulebook_outcome); EmitCode::val_iname(K_value, rbo->outcome_name->nro_iname); EmitCode::up(); rtrue = TRUE; break; } case FAILURE_OUTCOME: { inter_name *iname = Hierarchy::find(RULEBOOKFAILS_HL); EmitCode::call(iname); EmitCode::down(); RTKinds::emit_weak_id_as_val(K_rulebook_outcome); EmitCode::val_iname(K_value, rbo->outcome_name->nro_iname); EmitCode::up(); rtrue = TRUE; break; } } } else { switch(outs->default_rule_outcome) { case SUCCESS_OUTCOME: { inter_name *iname = Hierarchy::find(RULEBOOKSUCCEEDS_HL); EmitCode::call(iname); EmitCode::down(); EmitCode::val_number(0); EmitCode::val_number(0); EmitCode::up(); rtrue = TRUE; break; } case FAILURE_OUTCOME: { inter_name *iname = Hierarchy::find(RULEBOOKFAILS_HL); EmitCode::call(iname); EmitCode::down(); EmitCode::val_number(0); EmitCode::val_number(0); EmitCode::up(); rtrue = TRUE; break; } } } if (rtrue) EmitCode::rtrue(); } void RTRules::compile_outcome(named_rulebook_outcome *rbno) { id_body *idb = Functions::defn_being_compiled(); rulebook_outcome *rbo = FocusAndOutcome::rbo_from_context(rbno, idb); if (rbo == NULL) { rulebook *rb; LOOP_OVER(rb, rulebook) { outcomes *outs = Rulebooks::get_outcomes(rb); rulebook_outcome *ro; LOOP_OVER_LINKED_LIST(ro, rulebook_outcome, outs->named_outcomes) if (ro->outcome_name == rbno) { rbo = ro; break; } } if (rbo == NULL) internal_error("rbno with no rb context"); } switch(rbo->kind_of_outcome) { case SUCCESS_OUTCOME: { inter_name *iname = Hierarchy::find(RULEBOOKSUCCEEDS_HL); EmitCode::call(iname); EmitCode::down(); RTKinds::emit_weak_id_as_val(K_rulebook_outcome); EmitCode::val_iname(K_value, rbno->nro_iname); EmitCode::up(); EmitCode::rtrue(); break; } case FAILURE_OUTCOME: { inter_name *iname = Hierarchy::find(RULEBOOKFAILS_HL); EmitCode::call(iname); EmitCode::down(); RTKinds::emit_weak_id_as_val(K_rulebook_outcome); EmitCode::val_iname(K_value, rbno->nro_iname); EmitCode::up(); EmitCode::rtrue(); break; } case NO_OUTCOME: EmitCode::rfalse(); break; default: internal_error("bad RBO outcome kind"); } } void RTRules::RulebookOutcomePrintingRule(void) { named_rulebook_outcome *rbno; LOOP_OVER(rbno, named_rulebook_outcome) { TEMPORARY_TEXT(RV) WRITE_TO(RV, "%+W", Nouns::nominative_singular(rbno->name)); Emit::text_constant(rbno->nro_iname, RV); DISCARD_TEXT(RV) } inter_name *printing_rule_name = Kinds::Behaviour::get_iname(K_rulebook_outcome); packaging_state save = Functions::begin(printing_rule_name); inter_symbol *rbnov_s = LocalVariables::new_other_as_symbol(I"rbno"); EmitCode::inv(IFELSE_BIP); EmitCode::down(); EmitCode::inv(EQ_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, rbnov_s); EmitCode::val_number(0); EmitCode::up(); EmitCode::code(); EmitCode::down(); EmitCode::inv(PRINT_BIP); EmitCode::down(); EmitCode::val_text(I"(no outcome)"); EmitCode::up(); EmitCode::up(); EmitCode::code(); EmitCode::down(); EmitCode::inv(PRINTSTRING_BIP); EmitCode::down(); EmitCode::val_symbol(K_value, rbnov_s); EmitCode::up(); EmitCode::rfalse(); EmitCode::up(); EmitCode::up(); Functions::end(save); }
§13. Compiling the firing test. Each rule compiles to a routine, and this routine is called whenever the opportunity might exist for the rule to fire. The structure of this is similar to:
[ Rule; if (some-firing-condition) { ... return some-default-outcome; } ];
The "test head" is the "if" line here, and the "test tail" is the "}". The return statement isn't necessarily reached, because even if the firing condition holds, the "..." code may decide to return in some other way. It provides only a default to cover rules which don't specify an outcome.
In general the test is more elaborate than a single "if", though not very much.
int RTRules::compile_test_head(id_body *idb, rule *R) { inter_name *identifier = CompileImperativeDefn::iname(idb); id_runtime_context_data *phrcd = &(idb->runtime_context_data); if (RTRules::compile_constraint(R) == TRUE) return TRUE; int tests = 0; if (PluginCalls::compile_test_head(idb, R, &tests) == FALSE) { if (ActionRules::get_ap(phrcd)) Compile an action test head13.4; } if (Wordings::nonempty(phrcd->activity_context)) Compile an activity or explicit condition test head13.9; if ((tests > 0) || (idb->compilation_data.compile_with_run_time_debugging)) { EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::val_iname(K_number, Hierarchy::find(DEBUG_RULES_HL)); EmitCode::code(); EmitCode::down(); EmitCode::call(Hierarchy::find(DB_RULE_HL)); EmitCode::down(); EmitCode::val_iname(K_value, identifier); EmitCode::val_number((inter_ti) idb->allocation_id); EmitCode::val_number(0); EmitCode::up(); EmitCode::up(); EmitCode::up(); } return FALSE; } int RTRules::actions_compile_test_head(id_body *idb, rule *R, int *tests) { id_runtime_context_data *phrcd = &(idb->runtime_context_data); if (Scenes::get_rcd_spec(phrcd)) Compile a scene test head13.2; if (ActionRules::get_ap(phrcd)) Compile possibly testing actor action test head13.5 else if (ActionRules::get_always_test_actor(phrcd)) Compile an actor-is-player test head13.7; return TRUE; } int RTRules::actions_compile_test_tail(id_body *idb, rule *R) { inter_name *identifier = CompileImperativeDefn::iname(idb); id_runtime_context_data *phrcd = &(idb->runtime_context_data); if (ActionRules::get_ap(phrcd)) Compile an action test tail13.6 else if (ActionRules::get_always_test_actor(phrcd)) Compile an actor-is-player test tail13.8; if (Scenes::get_rcd_spec(phrcd)) Compile a scene test tail13.3; return TRUE; }
§13.1. This is almost the up-down reflection of the head, but note that it begins with the default outcome return (see above).
void RTRules::compile_test_tail(id_body *idb, rule *R) { inter_name *identifier = CompileImperativeDefn::iname(idb); id_runtime_context_data *phrcd = &(idb->runtime_context_data); rulebook *rb = RuleFamily::get_rulebook(idb->head_of_defn); if (rb) RTRules::compile_default_outcome(Rulebooks::get_outcomes(rb)); if (Wordings::nonempty(phrcd->activity_context)) Compile an activity or explicit condition test tail13.1.1; if (PluginCalls::compile_test_tail(idb, R) == FALSE) { if (ActionRules::get_ap(phrcd)) Compile an action test tail13.6; } }
§13.2. Scene test. Compile a scene test head13.2 =
EmitCode::inv(IFELSE_BIP); EmitCode::down(); RTScenes::emit_during_clause(Scenes::get_rcd_spec(phrcd)); EmitCode::code(); EmitCode::down(); (*tests)++;
- This code is used in §13.
§13.3. Compile a scene test tail13.3 =
inter_ti failure_code = 1; Compile a generic test fail13.3.1;
- This code is used in §13.
§13.4. Action test. Compile an action test head13.4 =
EmitCode::inv(IFELSE_BIP); EmitCode::down(); RTActionPatterns::emit_pattern_match(ActionRules::get_ap(phrcd), TRUE); EmitCode::code(); EmitCode::down(); tests++; if (ActionPatterns::involves_actions(ActionRules::get_ap(phrcd))) { EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::ref_iname(K_object, Hierarchy::find(SELF_HL)); EmitCode::val_iname(K_object, Hierarchy::find(NOUN_HL)); EmitCode::up(); }
- This code is used in §13.
§13.5. Compile possibly testing actor action test head13.5 =
EmitCode::inv(IFELSE_BIP); EmitCode::down(); if (ActionRules::get_never_test_actor(phrcd)) RTActionPatterns::emit_pattern_match(ActionRules::get_ap(phrcd), TRUE); else RTActionPatterns::emit_pattern_match(ActionRules::get_ap(phrcd), FALSE); EmitCode::code(); EmitCode::down(); (*tests)++; if (ActionPatterns::involves_actions(ActionRules::get_ap(phrcd))) { EmitCode::inv(STORE_BIP); EmitCode::down(); EmitCode::ref_iname(K_object, Hierarchy::find(SELF_HL)); EmitCode::val_iname(K_object, Hierarchy::find(NOUN_HL)); EmitCode::up(); }
- This code is used in §13.
§13.6. Compile an action test tail13.6 =
inter_ti failure_code = 2; Compile a generic test fail13.3.1;
§13.7. Actor-is-player test. Compile an actor-is-player test head13.7 =
EmitCode::inv(IFELSE_BIP); EmitCode::down(); EmitCode::inv(EQ_BIP); EmitCode::down(); EmitCode::val_iname(K_object, Hierarchy::find(ACTOR_HL)); EmitCode::val_iname(K_object, Hierarchy::find(PLAYER_HL)); EmitCode::up(); EmitCode::code(); EmitCode::down(); (*tests)++;
- This code is used in §13.
§13.8. Compile an actor-is-player test tail13.8 =
inter_ti failure_code = 3; Compile a generic test fail13.3.1;
- This code is used in §13.
§13.9. Activity-or-condition test. Compile an activity or explicit condition test head13.9 =
EmitCode::inv(IFELSE_BIP); EmitCode::down(); activity_list *avl = phrcd->avl; if (avl) { RTActivities::emit_activity_list(avl); } else { StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BadWhenWhile), "I don't understand the 'when/while' clause", "which should name activities or conditions."); EmitCode::val_false(); } EmitCode::code(); EmitCode::down(); IXActivities::annotate_list_for_cross_references(avl, idb); tests++;
- This code is used in §13.
§13.1.1. Compile an activity or explicit condition test tail13.1.1 =
inter_ti failure_code = 4; Compile a generic test fail13.3.1;
- This code is used in §13.1.
§13.3.1. Compile a generic test fail13.3.1 =
EmitCode::up(); EmitCode::code(); EmitCode::down(); EmitCode::inv(IF_BIP); EmitCode::down(); EmitCode::inv(GT_BIP); EmitCode::down(); EmitCode::val_iname(K_number, Hierarchy::find(DEBUG_RULES_HL)); EmitCode::val_number(1); EmitCode::up(); EmitCode::code(); EmitCode::down(); EmitCode::call(Hierarchy::find(DB_RULE_HL)); EmitCode::down(); EmitCode::val_iname(K_value, identifier); EmitCode::val_number((inter_ti) idb->allocation_id); EmitCode::val_number(failure_code); EmitCode::up(); EmitCode::up(); EmitCode::up(); EmitCode::up(); EmitCode::up();
void RTRules::compile_synoptic_resources(void) { Provide placeholder for the NUMBER_RULEBOOKS_CREATED constant14.1; Provide placeholder for the RULEBOOKS_ARRAY array14.2; Provide placeholder for one of the ways to look up rulebook names14.3; Provide placeholder for one of the ways to look up shared variables14.4; }
§14.1. Provide placeholder for the NUMBER_RULEBOOKS_CREATED constant14.1 =
inter_name *iname = Hierarchy::find(NUMBER_RULEBOOKS_CREATED_HL); Produce::annotate_i(iname, SYNOPTIC_IANN, NUMBER_RULEBOOKS_CREATED_SYNID); Emit::numeric_constant(iname, 0); Hierarchy::make_available(iname);
- This code is used in §14.
§14.2. Provide placeholder for the RULEBOOKS_ARRAY array14.2 =
inter_name *iname = Hierarchy::find(RULEBOOKS_ARRAY_HL); Produce::annotate_i(iname, SYNOPTIC_IANN, RULEBOOKS_ARRAY_SYNID); packaging_state save = EmitArrays::begin(iname, K_value); EmitArrays::end(save); Hierarchy::make_available(iname);
- This code is used in §14.
§14.3. Provide placeholder for one of the ways to look up rulebook names14.3 =
inter_name *iname = Hierarchy::find(RULEBOOKNAMES_HL); if (global_compilation_settings.memory_economy_in_force) Produce::annotate_i(iname, SYNOPTIC_IANN, ECONOMY_RULEBOOKNAMES_SYNID); else Produce::annotate_i(iname, SYNOPTIC_IANN, RULEBOOKNAMES_SYNID); packaging_state save = EmitArrays::begin(iname, K_value); EmitArrays::end(save); Hierarchy::make_available(iname);
- This code is used in §14.
§14.4. Provide placeholder for one of the ways to look up shared variables14.4 =
if (global_compilation_settings.memory_economy_in_force == FALSE) { inter_name *iname = Hierarchy::find(RULEBOOK_VAR_CREATORS_HL); Produce::annotate_i(iname, SYNOPTIC_IANN, RULEBOOK_VAR_CREATORS_SYNID); packaging_state save = EmitArrays::begin(iname, K_value); EmitArrays::end(save); Hierarchy::make_available(iname); } else { inter_name *iname = Hierarchy::find(SLOW_LOOKUP_HL); Produce::annotate_i(iname, SYNOPTIC_IANN, SLOW_LOOKUP_SYNID); packaging_state save = Functions::begin(iname); LocalVariables::new_other_as_symbol(I"rb"); EmitCode::comment(I"This function is consolidated"); Functions::end(save); }
- This code is used in §14.