+ + +

To provide debugging and tuning data on the Preform parser's performance.

+ +
+ +

§1. What data we collect. This ought to be a privacy policy under GDPR, somehow. If so, our justification +for logging usage data would be this: +

+ +
  • (a) the Preform parser does something very complicated and has to be tuned just +right to be efficient, so debugging logs are helpful; +
  • (b) but it runs millions of times in each Inform compilation, in a wide variety +of ways, and any kind of complete log would be both too large and too complex +to take in. We want to be selective, and to be able to summarise. +
+

So, in instrumentation mode, we gather the following data. For nonterminals, +we record the number of hits and misses. If a nonterminal is "watched", we +log its every parse. +

+ +
+typedef struct nonterminal_instrumentation_data {
+    int watched;  watch goings-on to the debugging log
+    int nonterminal_tries;  for statistics collected in instrumented mode
+    int nonterminal_matches;  ditto
+} nonterminal_instrumentation_data;
+
+void Instrumentation::initialise_nonterminal_data(nonterminal_instrumentation_data *ins) {
+    ins->watched = FALSE;
+    ins->nonterminal_tries = 0; ins->nonterminal_matches = 0;
+}
+
+void Instrumentation::watch(nonterminal *nt, int state) {
+    nt->ins.watched = state;
+}
+
+void Instrumentation::note_nonterminal_match(nonterminal *nt, wording W) {
+    nt->ins.nonterminal_tries++;
+    nt->ins.nonterminal_matches++;
+}
+
+void Instrumentation::note_nonterminal_fail(nonterminal *nt) {
+    nt->ins.nonterminal_tries++;
+}
+
+
  • The structure nonterminal_instrumentation_data is accessed in 4/prf and here.
+

§2. We count the number of hits and misses on each production, and also store +some sample text matching it. (In fact, we store the longest text which ever +matches it.) +

+ +
+typedef struct production_instrumentation_data {
+    int production_tries;  for statistics collected in instrumented mode
+    int production_matches;  ditto
+    struct wording sample_text;  ditto
+} production_instrumentation_data;
+
+void Instrumentation::initialise_production_data(production_instrumentation_data *ins) {
+    ins->production_tries = 0; ins->production_matches = 0;
+    ins->sample_text = EMPTY_WORDING;
+}
+
+void Instrumentation::note_production_match(production *pr, wording W) {
+    pr->ins.production_tries++;
+    pr->ins.production_matches++;
+    if (Wordings::length(pr->ins.sample_text) < Wordings::length(W))
+        pr->ins.sample_text = W;
+}
+
+void Instrumentation::note_production_fail(production *pr) {
+    pr->ins.production_tries++;
+}
+
+
  • The structure production_instrumentation_data is private to this section.
+

§3. At present, we collect no data on individual ptokens. +

+ +
+typedef struct ptoken_instrumentation_data {
+    int to_keep_this_from_being_empty_which_is_nonstandard_C;
+} ptoken_instrumentation_data;
+
+void Instrumentation::initialise_ptoken_data(ptoken_instrumentation_data *ins) {
+    ins->to_keep_this_from_being_empty_which_is_nonstandard_C = 0;
+}
+
+
  • The structure ptoken_instrumentation_data is private to this section.
+

§4. Logging. Descending the wheels within wheels of the Preform data structures, then: +

+ +
+void Instrumentation::log(void) {
+    int detailed = FALSE;
+    nonterminal *nt;
+    LOOP_OVER(nt, nonterminal) {
+        LOG("%d/%d: ", nt->ins.nonterminal_matches, nt->ins.nonterminal_tries);
+        LOG("%V: ", nt->nonterminal_id);
+        Optimiser::log_range_requirement(&(nt->opt.nonterminal_req));
+        LOG("\n");
+        if (nt->internal_definition)
+            LOG("  (internal)\n");
+        else
+            for (production_list *pl = nt->first_production_list; pl;
+                pl = pl->next_production_list)
+                Instrumentation::log_production_list(pl, detailed);
+        LOG("  min %d, max %d\n\n", nt->opt.min_nt_words, nt->opt.max_nt_words);
+    }
+}
+
+

§5.

+ +
+void Instrumentation::log_production_list(production_list *pl, int detailed) {
+    LOG("  $J:\n", pl->definition_language);
+    production *pr;
+    for (pr = pl->first_production; pr; pr = pr->next_production) {
+        LOG("   "); Instrumentation::log_production(pr, detailed);
+        LOG("      %d/%d: ", pr->ins.production_matches, pr->ins.production_tries);
+        if (Wordings::nonempty(pr->ins.sample_text)) LOG("<%W>", pr->ins.sample_text);
+        LOG(" ==> ");
+        Optimiser::log_range_requirement(&(pr->opt.production_req));
+        LOG("\n");
+    }
+}
+
+

§6.

+ +
+void Instrumentation::log_production(production *pr, int detailed) {
+    if (pr->first_ptoken == NULL) LOG("<empty-production>");
+    ptoken *pt;
+    for (pt = pr->first_ptoken; pt; pt = pt->next_ptoken) {
+        Instrumentation::log_ptoken(pt, detailed);
+        LOG(" ");
+    }
+}
+
+

§7.

+ +
+void Instrumentation::log_ptoken(ptoken *pt, int detailed) {
+    if ((detailed) && (pt->opt.ptoken_position != 0)) LOG("(@%d)", pt->opt.ptoken_position);
+    if ((detailed) && (pt->opt.strut_number >= 0)) LOG("(S%d)", pt->opt.strut_number);
+    if (pt->disallow_unexpected_upper) LOG("_");
+    if (pt->negated_ptoken) LOG("^");
+    if (pt->range_starts >= 0) { LOG("{"); if (detailed) LOG("%d:", pt->range_starts); }
+    ptoken *alt;
+    for (alt = pt; alt; alt = alt->alternative_ptoken) {
+        if (alt->nt_pt) {
+            LOG("%V", alt->nt_pt->nonterminal_id);
+            if (detailed) LOG("=%d", alt->result_index);
+        } else {
+            LOG("%V", alt->ve_pt);
+        }
+        if (alt->alternative_ptoken) LOG("/");
+    }
+    if (pt->range_ends >= 0) { if (detailed) LOG(":%d", pt->range_ends); LOG("}"); }
+}
+
+

§8. A less detailed form used in linguistic problem messages: +

+ +
+void Instrumentation::write_ptoken(OUTPUT_STREAM, ptoken *pt) {
+    if (pt->disallow_unexpected_upper) WRITE("_");
+    if (pt->negated_ptoken) WRITE("^");
+    if (pt->range_starts >= 0) WRITE("{");
+    ptoken *alt;
+    for (alt = pt; alt; alt = alt->alternative_ptoken) {
+        if (alt->nt_pt) {
+            WRITE("%V", alt->nt_pt->nonterminal_id);
+        } else {
+            WRITE("%V", alt->ve_pt);
+        }
+        if (alt->alternative_ptoken) WRITE("/");
+    }
+    if (pt->range_ends >= 0) WRITE("}");
+}
+
+ + +