-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathContext-Free Parsing Engine.i7x
1864 lines (1363 loc) · 92.1 KB
/
Context-Free Parsing Engine.i7x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Version 2 of Context-Free Parsing Engine (for Glulx only) by Brady Garvin begins here.
"A library for parsing a generic sequence according to a grammar in Backus-Naur form."
Include Runtime Checks by Brady Garvin.
Include Low-Level Operations by Brady Garvin.
Include Object Pools by Brady Garvin.
Include Low-Level Text by Brady Garvin.
Include Low-Level Linked Lists by Brady Garvin.
Include Low-Level Hash Tables by Brady Garvin.
Use authorial modesty.
Book "Copyright and License"
[Copyright 2013 Brady J. Garvin]
[This extension is released under the Creative Commons Attribution 3.0 Unported License (CC BY 3.0) so that it can qualify as a public Inform extension. See the LICENSE file included in the release for further details.]
Book "Extension Information"
[For each of the kinds defined by Context-Free Parsing Engine you will see a sentence like
A parseme is an invalid parseme.
This bewildering statement actually sets up parsemes as a qualitative value with default value the parseme at address one, which, as we say, is invalid. (We could have gone with a quantitative kind for default zero, but then we would open up the possibility for arithmetic on the pointers.) I wish it weren't necessary, but at least in this build Inform doesn't let us provide a default value any other way, and, moreover, we need a default value or else only I6 substitutions are allowed to decide on parsemes.]
[The convenience phrases can build some lengthy expressions in the 6G60 parser.]
Use MAX_EXPRESSION_NODES of 4096.
Chapter "Use Options"
Use a context-free parseme hash table size of at least 311 translates as (- Constant CFPE_PARSEME_HASH_SIZE = {N}; -).
Use a context-free production hash table size of at least 311 translates as (- Constant CFPE_PRODUCTION_HASH_SIZE = {N}; -).
Use a parse step preallocation of at least 2048 translates as (- Constant CFPE_STEP_PREALLOC = {N}; -).
Use a parse goal preallocation of at least 1024 translates as (- Constant CFPE_GOAL_PREALLOC = {N}; -).
Use a parse tree vertex preallocation of at least 2048 translates as (- Constant CFPE_VERTEX_PREALLOC = {N}; -).
To decide what number is the context-free parseme hash table size: (- CFPE_PARSEME_HASH_SIZE -).
To decide what number is the context-free production hash table size: (- CFPE_PRODUCTION_HASH_SIZE -).
To decide what number is the parse step preallocation: (- CFPE_STEP_PREALLOC -).
To decide what number is the parse goal preallocation: (- CFPE_GOAL_PREALLOC -).
To decide what number is the parse tree vertex preallocation: (- CFPE_VERTEX_PREALLOC -).
Book "Runtime Checks"
Chapter "Messages" - unindexed
To fail at adding parsemes to a parser already in normal form (this is failing to add parsemes to a parser already in normal form):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I tried to add parsemes to a parser, but it had been already put in normal form, which ought not happen until all parsemes and productions have been added.[terminating the story]".
To fail at adding productions to a parser already in normal form (this is failing to add productions to a parser already in normal form):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I tried to add productions to a parser, but it had been already put in normal form, which ought not happen until all parsemes and productions have been added.[terminating the story]".
To fail at putting a parser into normal form twice (this is failing to put a parser into normal form twice):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I tried to put a parser into normal form, but that had already been done, suggesting a bug.[terminating the story]".
To fail at normalizing an absurd grammar (this is failing to normalize an absurd grammar):
say "[low-level runtime failure in]Context-Free Parsing Engine[with explanation]I was given a grammar in which the empty text could be understood as a particular parseme, as could several copies of the parseme itself. This is a problematic edge case that I don't handle, mostly to keep things simple, but also because it is almost always a bug.[terminating the story]".
To fail at initializing a parser that is not in normal form (this is failing to initialize a parser that is not in normal form):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I tried parse with a parser that hadn't yet been put in normal form.[terminating the story]".
To fail at initializing a parser with a foreign parseme (this is failing to initialize a parser with a foreign parseme):
say "[low-level runtime failure in]Context-Free Parsing Engine[with explanation]I was asked to parse for a parseme in a parser where it doesn't belong.[terminating the story]".
To fail at reinitializing a parser mid-parse (this is failing to reinitialize a parser mid-parse):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I tried to reinitialize a parser mid-parse, which should never happen.[terminating the story]".
To fail at locating a doppelganger parse tree vertex (this is failing to locate a doppelganger parse tree vertex):
say "[low-level runtime failure in]Context-Free Parsing Engine[with explanation]I cloned a parse tree and then went to find the doppelganger of a vertex from the original tree. But there was no such vertex, which means that the clone must have failed in a way that is only possible if the original tree's internal state has been corrupted.[terminating the story]".
To fail at making a mismatched unsubstitution (this is failing to make a mismatched unsubstitution):
say "[low-level runtime failure in]Context-Free Parsing Engine[with explanation]I was rewriting a parse tree after finding a match, but my list of rewrites contained a nonsense instruction to replace one vertex with two unrelated ones. Either the tree or the list must have been corrupted.[terminating the story]".
To fail at unrotating from a nonrotation parseme (this is failing to unrotate from a nonrotation parseme):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I was rewriting a parse tree after finding a match, but my list of rewrites contained a instruction that led me to try rotating a wrongly shaped or out-of-bounds region. Either the tree or the list must have been corrupted.[terminating the story]".
To fail at expanding an already expanded parse tree vertex (this is failing to expand an already expanded parse tree vertex):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I tried to expand a vertex in one of the candidate parse trees, but the vertex was already expanded, a sure sign that I'm confused.[terminating the story]".
To fail at rewriting trivial recursion (this is failing to rewrite trivial recursion):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I found a production that understand a parseme as itself, even after the code to prune such productions. Either my data structures have been tampered with or I am confused.[terminating the story]".
To fail at finding a parse step in a consistent state (this is failing to find a parse step in a consistent state):
say "[low-level runtime failure in]Context Free Parsing Engine[with explanation]I found a parse step that gave me a sequence of expansions to use for a production, but that sequence didn't match the production's length. I must have created that parse step wrongly, or else it has been corrupted.[terminating the story]".
Book "Data Structures"
Part "Grammar Structures"
Chapter "Parsemes"
[``Parseme'' (pronounced like ``lexeme'') is another, rarer term for a symbol (i.e., a terminal or a nonterminal) in a BNF grammar. It seemed like something unlikely to cause name clashes.]
Section "The Parseme Kind"
A parseme is a kind of value.
A parseme is an invalid parseme. [See the note in the book "Extension Information."]
The specification of a parseme is "Parsemes are Context-Free Parsing Engine's analog to understand tokens. Some match literal input (these are called terminals), while others match sequences of other parsemes (nonterminals). Unlike Inform understand tokens, parsemes can be recursive: it is possible for '[parseme A][parseme B]' to be understood as '[parseme A]', for instance."
Section "Parseme Constants" - unindexed
To decide what parseme is a null parseme: (- 0 -).
Section "Parseme Adjectives" - unindexed
Definition: a parseme is null if it is a null parseme.
Section "The Parseme Structure" - unindexed
[Layout:
4 bytes for the human-friendly name
4 bytes for the owning context-free parser
4 bytes for the parsing phrase
4 bytes for the production linked list
4 bytes for the original parseme (null if none)
4 bytes for the parse attempt linked list
4 bytes for the parse step linked list]
[Some parsemes are split into two during grammar normalization. One has roughly the same meaning as the original, and the other converts left recursion into right recursion (a ``rotation parseme''). The original parseme field is set in the latter so that we can replace it with the former when we recover the parse tree.]
[The parse attempt linked list contains every beginning index at which we have tried to match this parseme.]
[The parse step linked list maps beginning indices to zero or more parse steps, each of which matches this parseme at that beginning index.]
To decide what number is the size in memory of a parseme: (- 28 -).
Section "Private Parseme Construction and Destruction" - unindexed
To decide what text is the name for a rotation for (A - a parseme) (this is naming a rotation parseme):
let the human-friendly name be the human-friendly name of A;
let the length be 13 plus the length of the human-friendly name;
let the result be a new uninitialized permanent synthetic text with length the length characters;
overwrite the synthetic text the result with the text printed when we say "rotation for [the human-friendly name]";
decide on the result.
To decide what parseme is a new rotation parseme for (A - a parseme) (this is creating a rotation parseme):
let the result be a permanent memory allocation of the size in memory of a parseme bytes converted to a parseme;
zero the size in memory of a parseme bytes at address result converted to a number;
let the owner be the owner of A;
write the human-friendly name the name for a rotation for A to the result;
write the owner the owner to the result;
write the parsing phrase the standard nonterminal parsing phrase to the result;
write the original parseme A to the result;
push the key the result onto the parseme linked list of the owner;
decide on the result.
Section "Public Parseme Construction"
To decide what parseme is a new terminal in (A - a context-free parser) named (T - some text) and parsed by (P - a phrase (parseme, number) -> nothing) (this is creating a terminal):
check that A is not in normal form or else fail at adding parsemes to a parser already in normal form;
let the result be a permanent memory allocation of the size in memory of a parseme bytes converted to a parseme;
zero the size in memory of a parseme bytes at address result converted to a number;
write the human-friendly name T to the result;
write the owner A to the result;
write the parsing phrase P to the result;
push the key the result onto the parseme linked list of A;
decide on the result.
To decide what parseme is a new nonterminal in (A - a context-free parser) named (T - some text) (this is creating a nonterminal):
decide on a new terminal in A named T and parsed by the standard nonterminal parsing phrase.
Section "Private Parseme Accessors and Mutators" - unindexed
To write the human-friendly name (X - some text) to (A - a parseme): (- llo_setInt({A}, {X}); -).
To write the owner (X - a context-free parser) to (A - a parseme): (- llo_setField({A}, 1, {X}); -).
To decide what phrase (parseme, number) -> nothing is the parsing phrase of (A - a parseme): (- llo_getField({A}, 2) -).
To write the parsing phrase (X - a phrase (parseme, number) -> nothing) to (A - a parseme): (- llo_setField({A}, 2, {X}); -).
To decide what permanent linked list is the production linked list of (A - a parseme): (- ({A}-->3) -).
To write the production linked list (X - a permanent linked list) to (A - a parseme): (- llo_setField({A}, 3, {X}); -).
To decide what parseme is the original parseme of (A - a parseme): (- llo_getField({A}, 4) -).
To write the original parseme (X - a parseme) to (A - a parseme): (- llo_setField({A}, 4, {X}); -).
To decide what linked list is the parse attempt linked list of (A - a parseme): (- ({A}-->5) -).
To write the parse attempt linked list (X - a linked list) to (A - a parseme): (- llo_setField({A}, 5, {X}); -).
To decide what linked list is the parse step linked list of (A - a parseme): (- ({A}-->6) -).
To write the parse step linked list (X - a linked list) to (A - a parseme): (- llo_setField({A}, 6, {X}); -).
Section "Public Parseme Accessors and Mutators"
To decide what text is the human-friendly name of (A - a parseme): (- llo_getInt({A}) -).
To decide what context-free parser is the owner of (A - a parseme): (- llo_getField({A}, 1) -).
To decide whether (A - a parseme) is a terminal (this is testing for a terminal):
decide on whether or not the parsing phrase of A is not the standard nonterminal parsing phrase.
To decide whether (A - a parseme) is a nonterminal (this is testing for a nonterminal):
decide on whether or not the parsing phrase of A is the standard nonterminal parsing phrase.
Section "Saying and Debugging Parsemes"
To say (A - a parseme) (this is saying a parseme):
say "(parseme [A converted to a number in hexadecimal]: [the human-friendly name of A])".
To say the productions of (A - a parseme) (this is saying the productions of a parseme):
if the production linked list of A is empty:
say " (no productions)[line break]";
otherwise:
repeat with the production running through the production keys of the production linked list of A:
say " [the production][line break]".
Chapter "Productions"
Section "The Production Kind"
A production is a kind of value.
A production is an invalid production. [See the note in the book "Extension Information."]
The specification of a production is "Productions are the analog to understand lines in Context-Free Parsing Engine; each one represents a way that a sequence of parsemes can be understood as another parseme."
Section "Production Constants" - unindexed
To decide what production is a null production: (- 0 -).
Section "Production Adjectives" - unindexed
Definition: a production is null if it is a null production.
Section "The Production Structure" - unindexed
[Layout:
4 bytes for the left-hand parseme
4 bytes for the right-hand parseme linked list
4 bytes for the right-hand parseme linked list tail
4 bytes for the inner production (null if none)
4 bytes for the outer production (null if none)
4 bytes for the substitution size (zero if none, but possibility zero anyway)]
[During grammar normalization, some extra productions are created by substituting one production (the inner production) in for the first right-hand parseme of another (the outer production). We track both of the original productions and the number of parsemes substituted so that we can undo things in tree recovery.]
To decide what number is the size in memory of a production: (- 24 -).
Section "Helper Functions for Production Construction and Destruction" - unindexed
To decide what permanent linked list is (A - a permanent linked list) without its first vertex (this is skipping a linked list vertex):
if A is empty:
decide on an empty permanent linked list;
let the result be the link of A converted to a linked list vertex;
decide on the result converted to a permanent linked list.
Section "Private Production Construction and Destruction" - unindexed
[With this constructor we are lazy and do *not* ascribe the new production to its left-hand parseme. This construction is really only useful for normalization, which is going to redo the production linked list anyway.]
To decide what production is a new production with (I - a production) substituted in for the first right-hand parseme of (O - a production) (this is creating a production by substitution):
let the result be a permanent memory allocation of the size in memory of a production bytes converted to a production;
zero the size in memory of a production bytes at address result converted to a number;
write the left-hand parseme the left-hand parseme of O to the result;
write the inner production I to the result;
write the outer production O to the result;
let the parseme linked list be an empty permanent linked list;
let the parseme linked list tail be an empty permanent linked list's tail;
let the substitution size be zero;
repeat with the parseme running through the parseme keys of the right-hand parseme linked list of I:
enqueue the key the parseme in the parseme linked list through the parseme linked list tail;
increment the substitution size;
repeat with the parseme running through the parseme keys of the right-hand parseme linked list of O without its first vertex:
enqueue the key the parseme in the parseme linked list through the parseme linked list tail;
write the right-hand parseme linked list the parseme linked list to the result;
write the right-hand parseme linked list tail the parseme linked list tail to the result;
write the substitution size the substitution size to the result;
decide on the result.
Section "Public Production Construction and Destruction"
To decide what production is a new production for (S - a parseme) (this is creating a production):
always check that the owner of S is not in normal form or else fail at adding productions to a parser already in normal form;
let the result be a permanent memory allocation of the size in memory of a production bytes converted to a production;
zero the size in memory of a production bytes at address result converted to a number;
write the left-hand parseme S to the result;
push the key the result onto the production linked list of S;
decide on the result.
Section "Private Production Accessors and Mutators" - unindexed
To decide what parseme is the left-hand parseme of (A - a production): (- llo_getInt({A}) -).
To write the left-hand parseme (X - a parseme) to (A - a production): (- llo_setInt({A}, {X}); -).
To decide what permanent linked list is the right-hand parseme linked list of (A - a production): (- ({A}-->1) -).
To write the right-hand parseme linked list (X - a permanent linked list) to (A - a production): (- llo_setField({A}, 1, {X}); -).
To decide what permanent linked list tail is the right-hand parseme linked list tail of (A - a production): (- ({A}-->2) -).
To write the right-hand parseme linked list tail (X - a permanent linked list tail) to (A - a production): (- llo_setField({A}, 2, {X}); -).
To decide what production is the inner production of (A - a production): (- llo_getField({A}, 3) -).
To write the inner production (X - a production) to (A - a production): (- llo_setField({A}, 3, {X}); -).
To decide what production is the outer production of (A - a production): (- llo_getField({A}, 4) -).
To write the outer production (X - a production) to (A - a production): (- llo_setField({A}, 4, {X}); -).
To decide what number is the substitution size of (A - a production): (- llo_getField({A}, 5) -).
To write the substitution size (X - a number) to (A - a production): (- llo_setField({A}, 5, {X}); -).
To decide what parseme is the first right-hand parseme of (P - a production) (this is determining a production's first right-hand parseme):
let the linked list vertex be the right-hand parseme linked list of P converted to a permanent linked list vertex;
if the linked list vertex is null:
decide on a null parseme;
decide on the parseme key of the linked list vertex.
Section "Public Production Accessors and Mutators"
To decide what context-free parser is the owner of (A - a production) (this is determining a production's owner):
decide on the owner of the left-hand parseme of A.
To prepend (S - a parseme) to (A - a production) (this is prepending a parseme to a production):
[This is somewhat less than ideal: we depend on the internals of linked lists. If we have to do this anywhere else, it would be better to create a phrase in Low-Level Linked Lists.]
push the key S onto the right-hand parseme linked list of A;
if the right-hand parseme linked list of A is unit:
let the tail be the right-hand parseme linked list of A converted to a permanent linked list tail;
write the right-hand parseme linked list tail the tail to A.
To append (S - a parseme) to (A - a production) (this is appending a parseme to a production):
enqueue the key S in the right-hand parseme linked list of A through the right-hand parseme linked list tail of A.
Section "Saying and Debugging Productions"
To say (A - a production) (this is saying a production):
if A is an invalid production:
say "<invalid production>";
otherwise:
say "[the left-hand parseme of A] ->";
if the right-hand parseme linked list of A is empty:
say " ε";
otherwise:
repeat with the parseme running through the parseme keys of the right-hand parseme linked list of A:
say " [the parseme]".
Part "Parsing Structures"
Chapter "Parse Tree Rewrites" - unindexed
Section "The Parse Tree Rewrite Kind" - unindexed
A parse tree rewrite is a kind of value.
A parse tree rewrite is an invalid parse tree rewrite. [See the note in the book "Extension Information."]
The specification of a parse tree rewrite is "Context-Free Parsing Engine internally rewrites parsemes and productions in a form that simplifies left-to-right parsing. When the parse is complete, it has to undo the rewriting so that the parse tree corresponds to the original grammar. A parse tree rewrite is the internal data structure used to remember these rewrites in the interim."
Section "The Parse Tree Rewrite Structure" - unindexed
[Layout:
4 bytes for the rewriting phrase
4 bytes for the production]
[The rewriting phrase should take a production, and returning nothing. The standard phrases are defined later in this file.]
To decide what number is the size in memory of a parse tree rewrite: (- 8 -).
Section "Parse Tree Rewrite Construction and Destruction" - unindexed
To decide what parse tree rewrite is a new tree unsubstitution rewrite for (P - a production) (this is creating a tree unsubstitution):
let the result be a permanent memory allocation of the size in memory of a parse tree rewrite bytes converted to a parse tree rewrite;
write the rewriting phrase unsubstituting a production to the result;
write the production P to the result;
decide on the result.
To decide what parse tree rewrite is a new tree rotation rewrite for (P - a production) (this is creating a tree rotation):
let the result be a permanent memory allocation of the size in memory of a parse tree rewrite bytes converted to a parse tree rewrite;
write the rewriting phrase rotating a production to the result;
write the production P to the result;
decide on the result.
Section "Parse Tree Rewrite Accessors and Mutators" - unindexed
To decide what phrase production -> nothing is the rewriting phrase of (A - a parse tree rewrite): (- llo_getInt({A}) -).
To write the rewriting phrase (X - a phrase production -> nothing) to (A - a parse tree rewrite): (- llo_setInt({A}, {X}); -).
To decide what production is the production of (A - a parse tree rewrite): (- llo_getField({A}, 1) -).
To write the production (X - a production) to (A - a parse tree rewrite): (- llo_setField({A}, 1, {X}); -).
Section "Saying and Debugging Parse Tree Rewrites" - unindexed
To say (A - a parse tree rewrite) (this is saying a parse tree rewrite):
let the rewriting phrase be the rewriting phrase of A;
say "[if the rewriting phrase is unsubstituting a production]unsubstitution of[otherwise if the rewriting phrase is rotating a production]rotation of[otherwise]custom operation ([the rewriting phrase]) on[end if] [the production of A]".
Chapter "Parse Steps" - unindexed
A parse step is a kind of value.
A parse step is an invalid parse step. [See the note in the book "Extension Information."]
The specification of a parse step is "A parse step represents mostly the same information as a parse tree vertex, except that parse steps are shared between trees, and possibly even separate branches of the same tree. They therefore do not have unique parents, and may even end up with zero parents if none is ever synthesized."
Section "The Parse Step Structure" - unindexed
[Layout:
4 bytes for the production
4 bytes for the children linked list
4 bytes for the end lexeme index]
[Any code that looks up parse steps already knows the beginning index, so there's no point in storing it.]
To decide what number is the size in memory of a parse step: (- 12 -).
Section "Helper Variables and Functions for Parse Step Construction and Destruction" - unindexed
The parse step object pool is an object pool that varies.
To ensure that the parse step object pool is initialized (this is initializing the parse step object pool as necessary):
if the parse step object pool is an invalid object pool:
now the parse step object pool is a new permanent object pool with the parse step preallocation objects of size the size in memory of a parse step bytes.
Section "Parse Step Construction and Destruction" - unindexed
To decide what parse step is a new parse step ending at lexeme index (I - a number) (this is creating a parse step by end lexeme index):
let the result be a memory allocation from the parse step object pool converted to a parse step;
zero the size in memory of a parse step bytes at address result converted to a number;
write the end lexeme index I to the result;
decide on the result.
To decide what parse step is a new parse step for (P - a production) ending at lexeme index (I - a number) (this is creating a parse step by production and end lexeme index):
let the result be a new parse step ending at lexeme index I;
write the production P to the result;
decide on the result.
Section "Parse Step Accessors and Mutators" - unindexed
To decide what production is the production of (A - a parse step): (- llo_getInt({A}) -).
To write the production (X - a production) to (A - a parse step): (- llo_setInt({A}, {X}); -).
To decide what linked list is the children linked list of (A - a parse step): (- ({A}-->1) -).
To write the children linked list (X - a linked list) to (A - a parse step): (- llo_setField({A}, 1, {X}); -).
To decide what number is the end lexeme index of (A - a parse step): (- llo_getField({A}, 2) -).
To write the end lexeme index (X - a number) to (A - a parse step): (- llo_setField({A}, 2, {X}); -).
Part "Mixed-Purpose Data Structures"
Chapter "Parse Tree Vertices"
Section "The Parse Tree Vertex Kind"
A parse tree vertex is a kind of value. The plural of parse tree vertex is parse tree vertices.
A parse tree vertex is an invalid parse tree vertex. [See the note in the book "Extension Information."]
The specification of a parse tree vertex is "A parse tree vertex represents all of the input matched by a single occurrence of a parseme. For instance, if a parseme X matches two occurrences of the parseme Y, and the parseme Y matches the literal text 'z', a parse of the text 'zz' would have three parse tree vertices: one for each time Y matched a 'z' and another for X matching the whole input. We think of these vertices as organized in a family tree where children vertices constitute their parents, and, when we describe adjacent siblings, we say that the vertex that matched earlier input is on the left. Thus, Y has two vertices, one on the left and one on the right, and their parent is X's vertex."
Section "Parse Tree Vertex Constants"
To decide what parse tree vertex is a null parse tree vertex: (- 0 -).
Section "Parse Tree Vertex Adjectives"
Definition: a parse tree vertex is null if it is a null parse tree vertex.
Section "The Parse Tree Vertex Structure" - unindexed
[Layout:
4 bytes for the parseme
4 bytes for the production (null if none)
4 bytes for the parent (null if none)
4 bytes for the first child (null if none)
4 bytes for the last child (null if none)
4 bytes for the left sibling (null if none)
4 bytes for the right sibling (null if none)
4 bytes for the beginning lexeme index
4 bytes for the end lexeme index]
To decide what number is the size in memory of a parse tree vertex: (- 36 -).
Section "Helper Variables and Functions for Parse Tree Vertex Construction and Destruction" - unindexed
The parse tree vertex object pool is an object pool that varies.
To ensure that the parse tree vertex object pool is initialized (this is initializing the parse tree vertex object pool as necessary):
if the parse tree vertex object pool is an invalid object pool:
now the parse tree vertex object pool is a new permanent object pool with the parse tree vertex preallocation objects of size the size in memory of a parse tree vertex bytes.
The doppelganger parse tree vertex is a parse tree vertex that varies.
To decide what parse tree vertex is a clone of (A - a parse tree vertex) with the doppelganger noted for (B - a parse tree vertex) (this is creating a parse tree vertex by cloning and noting a doppelganger):
let the result be a memory allocation from the parse tree vertex object pool converted to a parse tree vertex;
if A is B:
now the doppelganger parse tree vertex is the result;
copy the size in memory of a parse tree vertex bytes from address (A converted to a number) to address (the result converted to a number);
let the first child be the first child of A;
unless the first child is null:
let the cloned child be a clone of the first child with the doppelganger noted for B;
write the first child the cloned child to the result;
write the parent the result to the cloned child;
repeat with the later child running through the right siblings of the first child:
let the cloned child's sibling be the cloned child;
now the cloned child is a clone of the later child with the doppelganger noted for B;
write the left sibling the cloned child's sibling to the cloned child;
write the right sibling the cloned child to the cloned child's sibling;
write the parent the result to the cloned child;
write the last child the cloned child to the result;
decide on the result.
Section "Private Parse Tree Vertex Construction and Destruction" - unindexed
To decide what parse tree vertex is a new parse tree root for (S - a parseme) (this is creating a parse tree root):
let the result be a memory allocation from the parse tree vertex object pool converted to a parse tree vertex;
zero the size in memory of a parse tree vertex bytes at address result converted to a number;
write the parseme S to the result;
decide on the result.
To decide what parse tree vertex is a new parse tree vertex for (S - a parseme) with the parent (A - a parse tree vertex), placed on the left (this is creating a parse tree vertex by parseme and parent):
let the result be a memory allocation from the parse tree vertex object pool converted to a parse tree vertex;
zero the size in memory of a parse tree vertex bytes at address result converted to a number;
write the parseme S to the result;
write the parent A to the result;
if placed on the left:
let the sibling be the first child of A;
if the sibling is null:
write the last child the result to A;
otherwise:
write the left sibling the result to the sibling;
write the right sibling the sibling to the result;
write the first child the result to A;
otherwise:
let the sibling be the last child of A;
if the sibling is null:
write the first child the result to A;
otherwise:
write the left sibling the sibling to the result;
write the right sibling the result to the sibling;
write the last child the result to A;
decide on the result.
To delete (A - a parse tree vertex) but not its descendants (this is deleting an individual parse tree vertex):
free the memory allocation at address (A converted to a number) to the parse tree vertex object pool.
Section "Public Parse Tree Vertex Construction and Destruction"
To decide what parse tree vertex is the parse tree vertex corresponding to (A - a parse tree vertex) in a new clone of its tree (this is creating a parse tree vertex by cloning):
let the root be the root of A;
now the doppelganger parse tree vertex is a null parse tree vertex;
let the cloned root be a clone of the root with the doppelganger noted for A;
always check that the doppelganger parse tree vertex is not null or else fail at locating a doppelganger parse tree vertex;
decide on the doppelganger parse tree vertex.
To delete (A - a parse tree vertex) and its descendants (this is deleting a parse tree):
let the child be the first child of A;
while the child is not null:
let the sibling be the right sibling of the child;
delete the child and its descendants;
now the child is the sibling;
free the memory allocation at address (A converted to a number) to the parse tree vertex object pool.
Section "Private Parse Tree Vertex Mutators" - unindexed
To write the parseme (X - a parseme) to (A - a parse tree vertex): (- llo_setInt({A}, {X}); -).
To write the production (X - a production) to (A - a parse tree vertex): (- llo_setField({A}, 1, {X}); -).
To write the parent (X - a parse tree vertex) to (A - a parse tree vertex): (- llo_setField({A}, 2, {X}); -).
To write the first child (X - a parse tree vertex) to (A - a parse tree vertex): (- llo_setField({A}, 3, {X}); -).
To write the last child (X - a parse tree vertex) to (A - a parse tree vertex): (- llo_setField({A}, 4, {X}); -).
To write the left sibling (X - a parse tree vertex) to (A - a parse tree vertex): (- llo_setField({A}, 5, {X}); -).
To write the right sibling (X - a parse tree vertex) to (A - a parse tree vertex): (- llo_setField({A}, 6, {X}); -).
To write the beginning lexeme index (X - a number) to (A - a parse tree vertex): (- llo_setField({A}, 7, {X}); -).
Section "Public Parse Tree Vertex Accessors"
To decide what parseme is the parseme of (A - a parse tree vertex): (- llo_getInt({A}) -).
To decide what production is the production of (A - a parse tree vertex): (- llo_getField({A}, 1) -).
To decide whether (A - a parse tree vertex) is a root: (- (~~llo_getField({A}, 2)) -).
To decide whether (A - a parse tree vertex) is not a root: (- llo_getField({A}, 2) -).
To decide what parse tree vertex is the parent of (A - a parse tree vertex): (- llo_getField({A}, 2) -).
To decide what parse tree vertex is the first child of (A - a parse tree vertex): (- llo_getField({A}, 3) -).
To decide what parse tree vertex is the last child of (A - a parse tree vertex): (- llo_getField({A}, 4) -).
To decide what parse tree vertex is the left sibling of (A - a parse tree vertex): (- llo_getField({A}, 5) -).
To decide what parse tree vertex is the right sibling of (A - a parse tree vertex): (- llo_getField({A}, 6) -).
To decide what number is the beginning lexeme index of (A - a parse tree vertex): (- llo_getField({A}, 7) -).
To decide what number is the end lexeme index of (A - a parse tree vertex): (- llo_getField({A}, 8) -).
To write the end lexeme index (X - a number) to (A - a parse tree vertex): (- llo_setField({A}, 8, {X}); -).
To decide what parse tree vertex is the root of (A - a parse tree vertex) (this is determining a parse tree's root):
let the result be A;
while the result is not a root:
now the result is the parent of the result;
decide on the result.
To decide what context-free parser is the owner of (A - a parse tree vertex) (this is determining a parse tree vertex's owner):
decide on the owner of the parseme of A.
Section "Loops over Parse Tree Vertices"
To repeat with (I - a nonexisting parse tree vertex variable) running through the children of (A - a parse tree vertex) begin -- end: (-
for({I} = llo_getField({A}, 3): {I}: {I} = llo_getField({I}, 6))
-).
To repeat with (I - a nonexisting parse tree vertex variable) running through the right siblings of (A - a parse tree vertex) begin -- end: (-
for({I} = llo_getField({A}, 6): {I}: {I} = llo_getField({I}, 6))
-).
To decide what parse tree vertex is the first match for (S - a parseme) among the children of (V - a parse tree vertex) (this is finding the first match for a parseme among a parse tree vertex's children):
repeat with the child running through the children of V:
if the parseme of the child is S:
decide on the child;
decide on an invalid parse tree vertex.
To decide what parse tree vertex is the first match for (S - a parseme) after the child (V - a parse tree vertex) (this is finding a subsequent match for a parseme among a parse tree vertex's children):
repeat with the child running through the right siblings of V:
if the parseme of the child is S:
decide on the child;
decide on an invalid parse tree vertex.
To decide whether (S - a parseme) appears among the children of (V - a parse tree vertex) (this is testing for a match for a parseme among a parse tree vertex's children):
repeat with the child running through the children of V:
if the parseme of the child is S:
decide yes;
decide no.
Section "Saying and Debugging Parse Tree Vertices"
To say (A - a parse tree vertex) (this is saying a parse tree vertex):
let the beginning lexeme index be the beginning lexeme index of A;
let the end lexeme index be the end lexeme index of A;
let the length be the end lexeme index minus the beginning lexeme index;
if the length is less than zero:
now the length is zero;
say "[the parseme of A][if the end lexeme index is not zero] [bracket][the beginning lexeme index]..[no line break][the end lexeme index])[end if]";
unless the first child of A is null:
say " {";
repeat with the child running through the children of A:
say " ";
apply saying a parse tree vertex to the child;
say " }".
Chapter "Context-Free Parsers"
Section "The Context-Free Parser Kind"
A context-free parser is a kind of value.
A context-free parser is an invalid context-free parser. [See the note in the book "Extension Information."]
The specification of a context-free parser is "A context-free parser matches an input (usually, but not necessarily text) against parsemes, which are like Inform's understand tokens."
Section "The Context-Free Parser Structure" - unindexed
[Layout:
4 bytes for the normalized flag
4 bytes for the parseme linked list
4 bytes for the parse tree rewrite linked list
4 bytes for the content
4 bytes for the lexeme count]
To decide what number is the size in memory of a context-free parser: (- 20 -).
Section "Context-Free Parser Construction and Destruction"
To decide what context-free parser is a new context-free parser (this is creating a context-free parser):
ensure that the parse step object pool is initialized;
ensure that the parse tree vertex object pool is initialized;
let the result be a permanent memory allocation of the size in memory of a context-free parser bytes converted to a context-free parser;
zero the size in memory of a context-free parser bytes at address result converted to a number;
decide on the result.
Section "Private Context-Free Parser Accessors and Mutators" - unindexed
To mark (A - a context-free parser) as being in normal form: (- llo_setInt({A}, 1); -).
To decide what permanent linked list is the parseme linked list of (A - a context-free parser): (- ({A}-->1) -).
To write the parseme linked list (X - a permanent linked list) to (A - a context-free parser): (- llo_setField({A}, 1, {X}); -).
To decide what permanent linked list is the parse tree rewrite linked list of (A - a context-free parser): (- ({A}-->2) -).
To write the parse tree rewrite linked list (X - a permanent linked list) to (A - a context-free parser): (- llo_setField({A}, 2, {X}); -).
Section "Public Context-Free Parser Accessors and Mutators"
To decide whether (A - a context-free parser) is in normal form: (- llo_getInt({A}) -).
To decide whether (A - a context-free parser) is not in normal form: (- (~~llo_getInt({A})) -).
To decide what K is the (D - a description of values of kind K) content of (A - a context-free parser): (- ({A}-->3) -).
To decide what number is the lexeme count of (A - a context-free parser): (- llo_getField({A}, 4) -).
To write the content (X - a value of kind K) and the lexeme count (N - a number) to (A - a context-free parser): (- llo_setField({A}, 3, {X}); llo_setField({A}, 4, {N}); -).
Section "Saying and Debugging Context-Free Parsers"
To say (A - a context-free parser) (this is saying a context-free parser):
say "Parser[if A is in normal form] in normal form[end if]:[line break]";
repeat with the parseme running through the parseme keys of the parseme linked list of A:
say " [the parseme][line break][the productions of the parseme]";
repeat with the parse tree rewrite running through the parse tree rewrite keys of the parse tree rewrite linked list of A:
say " Rewrite: [the parse tree rewrite][line break]".
To say the matches made by (A - a context-free parser) (this is saying the matches made by a context-free parser):
say "Matches made by a parser[if A is in normal form] in normal form[end if]:[line break]";
repeat with the parseme running through the parseme keys of the parseme linked list of A:
if the parse step linked list of the parseme is not empty:
say " [the parseme]";
repeat with the parse step linked list vertex running through the parse step linked list of the parseme:
let the current parse step be the parse step value of the parse step linked list vertex;
say " [bracket][the number key of the parse step linked list vertex]..[no line break][the end lexeme index of the current parse step])";
say "[line break]".
Book "Relations and Verbs"
Chapter "Having a Parseme"
Having a parseme relates a parse tree vertex (called V) to a parseme (called S) when S is the parseme of V.
The verb to have the parseme (it has the parseme, they have the parseme, it had the parseme, it is a parseme had by, it is having the parseme) implies the having a parseme relation.
Book "Parser Lifecycle"
Chapter "Normalization"
Section "Purging Indirect Cycles" - unindexed
To decide what permanent linked list is the list of left-recursive productions that remain after purging productions that immediately produce keys of (H - a hash table) from (S - a parseme) of (A - a context-free parser) (this is purging indirect cycles from a parseme):
let the production worklist be the production linked list of S;
let the composite production worklist be an empty permanent linked list;
let the non-left-recursive production linked list be an empty permanent linked list;
let the left-recursive production linked list be an empty permanent linked list;
while the production worklist is not empty:
repeat with the outer production running through the production keys of the production worklist:
let the first right-hand parseme be the first right-hand parseme of the outer production;
if the first right-hand parseme is S:
if the right-hand parseme linked list of the outer production is not unit:
[Categorize the production as left-recursive.]
push the key the outer production onto the left-recursive production linked list;
[Otherwise, discard the production as redundant.]
otherwise if H contains the key the first right-hand parseme:
[Expand the production by substituting in previously purged productions.]
repeat with the inner production running through the production keys of the production linked list of the first right-hand parseme:
let the composite production be a new production with the inner production substituted in for the first right-hand parseme of the outer production;
push the key the composite production onto the composite production worklist;
push the key a new tree unsubstitution rewrite for the composite production onto the parse tree rewrite linked list of A;
otherwise:
[Categorize the production as non-left-recursive.]
push the key the outer production onto the non-left-recursive production linked list;
[Throw away the production worklist.]
now the production worklist is the composite production worklist;
now the composite production worklist is an empty permanent linked list;
write the production linked list the non-left-recursive production linked list to S;
decide on the left-recursive production linked list.
Section "Purging All Cycles" - unindexed
To put (S - a parseme) of (A - a context-free parser) into normal form with the seen parsemes being the keys of (H - a hash table) (this is purging all cycles from a parseme):
let the left-recursive production linked list be the list of left-recursive productions that remain after purging productions that immediately produce keys of H from S of A;
insert the key S into H;
if the left-recursive production linked list is empty:
stop;
let the rotation parseme be a new rotation parseme for S;
repeat with the non-left-recursive production running through the production keys of the production linked list of S:
enqueue the key the rotation parseme in the right-hand parseme linked list of the non-left-recursive production through the right-hand parseme linked list tail of the non-left-recursive production;
repeat with the left-recursive production running through the production keys of the left-recursive production linked list:
write the left-hand parseme the rotation parseme to the left-recursive production;
let the repurposed vertex be the right-hand parseme linked list of the left-recursive production converted to a permanent linked list vertex;
let the tail-to-replace be the right-hand parseme linked list tail of the left-recursive production converted to a permanent linked list vertex;
let the replacement list be the link of the repurposed vertex converted to a permanent linked list;
always check that the replacement list is not empty or else fail at rewriting trivial recursion;
write the key the rotation parseme to the repurposed vertex;
write the link a null permanent linked list vertex to the repurposed vertex;
write the link the repurposed vertex to the tail-to-replace;
write the right-hand parseme linked list the replacement list to the left-recursive production;
write the right-hand parseme linked list tail (the repurposed vertex converted to a permanent linked list tail) to the left-recursive production;
write the production linked list the left-recursive production linked list to the rotation parseme;
let the rotation parseme's empty production be a new production for the rotation parseme;
push the key a new tree rotation rewrite for the rotation parseme's empty production onto the parse tree rewrite linked list of A;
now the left-recursive production linked list is the list of left-recursive productions that remain after purging productions that immediately produce keys of H from the rotation parseme of A;
[I haven't decided yet whether it's worth it to handle the case where this list comes back non-empty. It's a pain, and I can't imagine how anyone would find it useful. But having exceptional cases also bothers me.]
always check that the left-recursive production linked list is empty or else fail at normalizing an absurd grammar;
insert the key the rotation parseme into H.
Section "Normalization Proper"
To put (A - a context-free parser) into normal form (this is putting a context-free parser into normal form):
if A is in normal form:
fail at putting a parser into normal form twice;
let the seen hash table be a new hash table with the context-free parseme hash table size buckets;
repeat with the parseme running through the parseme keys of the parseme linked list of A:
if the parseme is a nonterminal:
put the parseme of A into normal form with the seen parsemes being the keys of the seen hash table;
delete the seen hash table;
mark A as being in normal form.
Chapter "Parsing" - unindexed
To parse for (S - a parseme) (this is parsing for a parseme):
let the owner be the owner of S;
repeat with the parseme running through the parseme keys of the parseme linked list of the owner:
delete the parse attempt linked list of the parseme;
write the parse attempt linked list an empty linked list to the parseme;
delete the parse step linked list of the parseme;
write the parse step linked list an empty linked list to the parseme;
apply the parsing phrase of S to S and zero.
Chapter "Iteration over Steps" - unindexed
To decide what linked list vertex is the first parse step linked list vertex for (S - a parseme) (this is finding a first parse step linked list vertex):
let the end lexeme index be the lexeme count of the owner of S;
repeat with the candidate running through occurrences of the key zero in the parse step linked list of S:
if the end lexeme index of the parse step value of the candidate is the end lexeme index:
decide on the candidate;
decide on a null linked list vertex.
To decide what linked list vertex is the next parse step for (S - a parseme) after (L - a linked list vertex) (this is finding a subsequent parse step linked list vertex):
let the end lexeme index be the lexeme count of the owner of S;
let the candidate be the first match for the key zero after L;
while the candidate is not null:
if the end lexeme index of the parse step value of the candidate is the end lexeme index:
break;
now the candidate is the first match for the key zero after the candidate;
decide on the candidate.
Chapter "Expansion" - unindexed
[V should already know its parseme, parent, siblings, and beginning lexeme index. We will set its production, children, and end lexeme index.]
To expand (V - a parse tree vertex) using (T - a parse step) (this is expanding a parse tree vertex according to a parse step):
write the end lexeme index the end lexeme index of T to V;
let the production be the production of T;
if the production is null:
stop;
write the production the production to V;
let the parseme linked list vertex be the right-hand parseme linked list of the production converted to a permanent linked list vertex;
let the child linked list vertex be the children linked list of T converted to a linked list vertex;
let the beginning lexeme index be the beginning lexeme index of V;
while the parseme linked list vertex is not null:
always check that the child linked list vertex is not null or else fail at finding a parse step in a consistent state;
let the child step be the parse step key of the child linked list vertex;
let the child vertex be a new parse tree vertex for the parseme key of the parseme linked list vertex with the parent V;
write the beginning lexeme index the beginning lexeme index to the child vertex;
expand the child vertex using the child step;
now the parseme linked list vertex is the link of the parseme linked list vertex;
now the child linked list vertex is the link of the child linked list vertex;
now the beginning lexeme index is the end lexeme index of the child vertex;
always check that the child linked list vertex is null or else fail at finding a parse step in a consistent state.
Chapter "Rewriting" - unindexed
[The rewriting phrases look a lot scarier than they actually are: one inserts a vertex between a parent and some of its children, while the other rotates a line of right descent to a line of left descent. It's just that all of the tidying up of pointers and lexeme indices is happening explicitly.]
Section "Iteration Order"
To decide what parse tree vertex is the parse tree vertex to visit after (V - a parse tree vertex) (this is finding a subsequent parse tree vertex):
let the child be the first child of V;
if the child is not null:
decide on the child;
let the current vertex be V;
let the sibling be the right sibling of the current vertex;
while the sibling is null:
let the parent be the parent of the current vertex;
if the parent is null:
decide on a null parse tree vertex;
now the current vertex is the parent;
now the sibling is the right sibling of the current vertex;
decide on the sibling.
Section "Hashing Production Instantiations" - unindexed
[The production instantiation hash table maps productions to the vertices that use them.]
The production instantiation hash table is a hash table that varies.
To hash the productions of vertices rooted at (V - a parse tree vertex) (this is hashing a parse tree's productions):
let the current vertex be V;
while the current vertex is not null:
let the production be the production of the current vertex;
insert the key the production and the value the current vertex into the production instantiation hash table;
now the current vertex is the parse tree vertex to visit after the current vertex.
Section "Unsubstitution" - unindexed
To unsubstitute (P - a production) (this is unsubstituting a production):
let the outer production be the outer production of P;
let the inner production be the inner production of P;
let the outer parseme be the left-hand parseme of the outer production;
let the inner parseme be the left-hand parseme of the inner production;
let the substitution size be the substitution size of P;
repeat with the parse tree vertex running through the parse tree vertex values matching the key P in the production instantiation hash table:
always check that the outer parseme is the parseme of the parse tree vertex or else fail at making a mismatched unsubstitution;
write the production the outer production to the parse tree vertex;
insert the key the outer production and the value the parse tree vertex into the production instantiation hash table;
let the beginning lexeme index be the beginning lexeme index of the parse tree vertex;
let the child be a new parse tree vertex for the inner parseme with the parent the parse tree vertex, placed on the left;
write the production the inner production to the child;
write the beginning lexeme index the beginning lexeme index to the child;
insert the key the inner production and the value the child into the production instantiation hash table;
if the substitution size is zero:
write the end lexeme index the beginning lexeme index to the child;
otherwise:
let the first grandchild be the right sibling of the child;
let the last grandchild be the child;
repeat with a counter running from one to the substitution size:
now the last grandchild is the right sibling of the last grandchild;
write the parent the child to the last grandchild;
let the sibling be the right sibling of the last grandchild;
if the sibling is null:
write the last child the child to the parse tree vertex;
otherwise:
write the left sibling the child to the sibling;
write the right sibling the sibling to the child;
write the first child the first grandchild to the child;
write the last child the last grandchild to the child;
write the left sibling a null parse tree vertex to the first grandchild;
write the right sibling a null parse tree vertex to the last grandchild;
write the end lexeme index the end lexeme index of the last grandchild to the child.
Section "Rotation" - unindexed
To detach vertices after (V - a parse tree vertex) from its parent (P - a parse tree vertex) (this is detaching subsequent vertices from a parent parse tree vertex):
if V is null:
if P is null:
stop;
write the first child a null parse tree vertex to P;
write the end lexeme index the beginning lexeme index of P to P;
otherwise:
write the end lexeme index the end lexeme index of V to P;
write the right sibling a null parse tree vertex to V;
write the last child V to P.
To remove the rotation terminator (V - a parse tree vertex) (this is removing a rotation terminator):
detach vertices after the left sibling of V from its parent the parent of V;
delete V and its descendants.
To have (V - a parse tree vertex) claim the detached (W - a parse tree vertex) as its first child (this is claiming a detached parse tree vertex as a first child):
write the left sibling a null parse tree vertex to W;
let the sibling be the first child of V;
if the sibling is null:
write the last child W to V;
otherwise:
write the left sibling W to the sibling;
write the right sibling the sibling to W;
write the parent V to W;
write the first child W to V.
To join (V - a parse tree vertex) to the parent (P - a parse tree vertex) and the left sibling (L - a parse tree vertex) and the right sibling (R - a parse tree vertex) (this is joining a parse tree vertex to new relatives):
if P is null:
write the parent a null parse tree vertex to V;
write the left sibling a null parse tree vertex to V;
write the right sibling a null parse tree vertex to V;
otherwise:
write the parent P to V;
if L is null:
write the first child V to P;
otherwise:
write the right sibling V to L;
write the left sibling L to V;
if R is null:
write the last child V to P;
otherwise:
write the left sibling V to R;
write the right sibling R to V.
To rotate away (P - a production) (this is rotating a production):
let the rotation parseme be the left-hand parseme of P;
let the original parseme be the original parseme of the rotation parseme;
repeat with the parse tree vertex running through the parse tree vertex values matching the key P in the production instantiation hash table:
let the eventual ancestor be the parent of the parse tree vertex;
always check that the eventual ancestor is not null or else fail at unrotating from a nonrotation parseme;
remove the rotation terminator the parse tree vertex;
let the locus be the eventual ancestor;
let the parent be the parent of the locus;
let the left sibling be the left sibling of the locus;
let the right sibling be the right sibling of the locus;
while the left-hand parseme of the production of the locus is the rotation parseme:
always check that the parent is not null or else fail at unrotating from a nonrotation parseme;
always check that the right sibling is null or else fail at unrotating from a nonrotation parseme;
let the grandparent be the parent of the parent;
detach vertices after the left sibling from its parent the parent;
[Update the siblings now, before we clobber them.]
now the left sibling is the left sibling of the parent;
now the right sibling is the right sibling of the parent;
[Clobbering here.]
have the locus claim the detached parent as its first child;
write the parseme the original parseme to the locus;
[Update everything else.]
now the locus is the parent;
now the parent is the grandparent;
join the eventual ancestor to the parent the parent and the left sibling the left sibling and the right sibling the right sibling;
let the beginning lexeme index be the beginning lexeme index of the locus;
now the locus is the parent of the locus;
while the locus is not the parent:
write the beginning lexeme index the beginning lexeme index to the locus;
now the locus is the parent of the locus.
Section "Rewriting Proper" - unindexed
To rewrite the match rooted at (V - a parse tree vertex) (this is rewriting a parse tree):
let the owner be the owner of V;
now the production instantiation hash table is a new hash table with the context-free production hash table size buckets;
hash the productions of vertices rooted at V;
repeat with the rewrite running through the parse tree rewrite keys of the parse tree rewrite linked list of the owner:
apply the rewriting phrase of the rewrite to the production of the rewrite;
delete the production instantiation hash table.
Chapter "Parsing Callbacks"
Section "Private Parsing Callbacks" - unindexed
To match (A - a parseme) from lexeme index (B - a number) to lexeme index (E - a number) via (P - a production) (this is matching a parseme via a production):
let the parse step be a new parse step for P ending at lexeme index E;
push the key B and the value the parse step onto the parse step linked list of A.
[L should be an expansion stack as described later. The relevant part here is that it should have the children of the new parse step as values of values in reverse order.]
To match (A - a parseme) from lexeme index (B - a number) to lexeme index (E - a number) via (P - a production) as described by (L - a linked list) (this is matching a parseme via a production and expansion stack):