Bookings are assignments of rules to rulebooks.
§1. Introduction.Rules are only really of use in rulebooks, but in fact they can belong to any number of rulebooks. Each placement of a rule in a rulebook is called a "booking", and results in a booking object. These are gathered into linked lists as booking_list objects: see Booking Lists.
We can think of rule bookings as being looseleaf pages, of which we have an unlimited supply: any rule can be written on them. They can be bound into any rulebook at any specified position, but each rulebook divides into three non-overlapping sections: the first rules, the middle rules, the last rules. And each booking records which of these sections it belongs to.
enum MIDDLE_PLACEMENT from 0 most bookings are in this middle section enum VERY_FIRST_PLACEMENT enum FIRST_PLACEMENT enum LAST_PLACEMENT enum VERY_LAST_PLACEMENT
typedef struct booking { struct rule *rule_being_booked; what appears on this page int placement; one of the *_PLACEMENT values above int place_automatically; should this be inserted automatically? struct booking_commentary commentary; used only for indexing and code comments struct booking *next_booking; in its booking list CLASS_DEFINITION } booking;
- The structure booking is accessed in 6/bl and here.
§2. Here's a rather arcane notation used in the debugging log:
void RuleBookings::log(booking *br) { if (br == NULL) { LOG("BR:<null-booked-rule>"); return; } LOG("BR%d", br->allocation_id); switch (br->placement) { case MIDDLE_PLACEMENT: LOG("m"); break; case VERY_FIRST_PLACEMENT: LOG("vf"); break; case FIRST_PLACEMENT: LOG("f"); break; case LAST_PLACEMENT: LOG("l"); break; case VERY_LAST_PLACEMENT: LOG("vl"); break; default: LOG("?"); break; } Rules::log(br->rule_being_booked); }
booking *RuleBookings::new(rule *R) { booking *br = CREATE(booking); br->next_booking = NULL; br->rule_being_booked = R; br->placement = MIDDLE_PLACEMENT; br->place_automatically = FALSE; br->commentary = RuleBookings::new_commentary(); return br; }
rule *RuleBookings::get_rule(booking *br) { if (br == NULL) return NULL; return br->rule_being_booked; }
§5. Placement.When created, a booking is like a piece of looseleaf paper which has not yet been put into a binder (i.e., into the booking_list of a rulebook); the process of putting it into such a list is called "placement".
Most rules declare a single rulebook to belong to in their definitions:
Before eating something: ...
This nameless rule is to go into the "before eating" rulebook. When Inform reads this, it creates a booking, but does not immediately place this into the rulebook; instead, the booking is marked for automatic placement later on.
void RuleBookings::request_automatic_placement(booking *br) { br->place_automatically = TRUE; }
void RuleBookings::make_automatic_placements(void) { booking *br; LOOP_OVER(br, booking) if (br->place_automatically) { imperative_defn *id = Rules::get_imperative_definition(br->rule_being_booked); if (id) { current_sentence = id->at; id_body *idb = id->body_of_defn; rulebook *original_owner = RuleFamily::get_rulebook(idb->head_of_defn); int placement = RuleFamily::get_rulebook_placement(idb->head_of_defn); rulebook *owner = original_owner; PluginCalls::place_rule(br->rule_being_booked, original_owner, &owner); if (owner != original_owner) { RuleFamily::set_rulebook(idb->head_of_defn, owner); LOGIF(RULE_ATTACHMENTS, "Rerouting $b: $K --> $K\n", br, original_owner, owner); } Rulebooks::attach_rule(owner, br, placement, 0, NULL); Rules::set_kind_from(br->rule_being_booked, owner); } else { internal_error("Inter-defined rules cannot be automatically placed"); } } }
§7. Specificity of bookings.This strcmp-like function is intended to be used in sorting algorithms, and returns 1 if br1 is more specific than br2, -1 if br2 is more specific than br1, or 0 if they are equally good.
int RuleBookings::cmp(booking *br1, booking *br2, int log_this) { if ((br1 == NULL) || (br2 == NULL)) internal_error("compared null specificity"); if (log_this) LOG("Comparing specificity of rules:\n(1) $b\n(2) $b\n", br1, br2); return Rules::cmp(br1->rule_being_booked, br2->rule_being_booked, log_this); }
§8. Commentary.The sorting algorithm for rulebooks is very important, and Inform authors sometimes need to know why rulebooks come out in a particular order. So each booking includes a booking_commentary which explains how it came to end up where it did. This is used only for code comments and the index.
typedef struct booking_commentary { int next_rule_specificity; 1 for more specific than following, 0 equal, -1 less struct text_stream *tooltip_text; description of reason struct text_stream *law_applied; name of Law used to sort } booking_commentary; booking_commentary RuleBookings::new_commentary(void) { booking_commentary bc; bc.next_rule_specificity = 0; bc.tooltip_text = NULL; bc.law_applied = NULL; return bc; } void RuleBookings::comment(OUTPUT_STREAM, booking *br) { text_stream *law = br->commentary.law_applied; switch(br->commentary.next_rule_specificity) { case -1: WRITE(" <<< %S <<<", law); break; case 0: WRITE(" === equally specific with ==="); break; case 1: WRITE(" >>> %S >>>", law); break; } } void RuleBookings::list_judge_ordering(booking_list *L) { LOOP_OVER_BOOKINGS(br, L) if (br->next_booking) { if (br->placement != br->next_booking->placement) Calculate specificities when placements differ8.1 else Calculate specificities when placements are the same8.2; } else { br->commentary.next_rule_specificity = 0; br->commentary.tooltip_text = NULL; } }
- The structure booking_commentary is private to this section.
§8.1. Calculate specificities when placements differ8.1 =
br->commentary.next_rule_specificity = 1; switch(br->placement) { case VERY_FIRST_PLACEMENT: br->commentary.tooltip_text = I"the rule above was listed as 'very first' so it precedes everything"; break; case FIRST_PLACEMENT: switch(br->next_booking->placement) { case MIDDLE_PLACEMENT: br->commentary.tooltip_text = I"the rule above was listed as 'first' so precedes this one, which wasn't"; break; case LAST_PLACEMENT: br->commentary.tooltip_text = I"the rule above was listed as 'first' so precedes this one, listed as 'last'"; break; case VERY_LAST_PLACEMENT: br->commentary.tooltip_text = I"the rule below was listed as 'very last' so it comes after everything"; break; default: BookingLists::log(L); internal_error("booking list invariant broken"); break; } break; case MIDDLE_PLACEMENT: switch(br->next_booking->placement) { case LAST_PLACEMENT: br->commentary.tooltip_text = I"the rule below was listed as 'last' so comes after this one, which wasn't"; break; case VERY_LAST_PLACEMENT: br->commentary.tooltip_text = I"the rule below was listed as 'very last' so it comes after everything"; break; default: BookingLists::log(L); internal_error("booking list invariant broken"); break; } break; case LAST_PLACEMENT: switch(br->next_booking->placement) { case VERY_LAST_PLACEMENT: br->commentary.tooltip_text = I"the rule below was listed as 'very last' so it comes after everything"; break; default: BookingLists::log(L); internal_error("booking list invariant broken"); break; } break; default: BookingLists::log(L); internal_error("booking list invariant broken"); break; }
- This code is used in §8.
§8.2. Calculate specificities when placements are the same8.2 =
br->commentary.next_rule_specificity = 0; switch(br->placement) { case VERY_FIRST_PLACEMENT: BookingLists::log(L); internal_error("multiple very first rules for the same rulebook"); break; case FIRST_PLACEMENT: br->commentary.tooltip_text = I"these rules were both listed as 'first', so they appear in reverse order of listing"; break; case MIDDLE_PLACEMENT: br->commentary.next_rule_specificity = RuleBookings::cmp(br, br->next_booking, FALSE); if (br->commentary.next_rule_specificity == 0) br->commentary.tooltip_text = I"these rules are equally ranked"; else { br->commentary.tooltip_text = I"the arrow points from a more specific rule to a more general, as decided by Law"; br->commentary.law_applied = Specifications::law_applied(); } break; case LAST_PLACEMENT: br->commentary.tooltip_text = I"these rules were both listed as 'last', so they appear in order of listing"; break; case VERY_LAST_PLACEMENT: BookingLists::log(L); internal_error("multiple very last rules for the same rulebook"); break; }
- This code is used in §8.