File Coverage

blib/lib/Tree/Term.pm
Criterion Covered Total %
statement 257 267 96.2
branch 83 108 76.8
condition 8 10 80.0
subroutine 52 53 98.1
pod 25 40 62.5
total 425 478 88.9


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib/
2             #-------------------------------------------------------------------------------
3             # Create a parse tree from an array of terms representing an expression.
4             # Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2021
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             package Tree::Term;
8 1     1   770 use v5.26;
  1         10  
9             our $VERSION = 20210810; # Version
10 1     1   5 use warnings FATAL => qw(all);
  1         2  
  1         33  
11 1     1   5 use strict;
  1         2  
  1         51  
12 1     1   7 use Carp qw(confess cluck);
  1         1  
  1         120  
13 1     1   572 use Data::Dump qw(dump ddx pp);
  1         8128  
  1         76  
14 1     1   4053 use Data::Table::Text qw(:all);
  1         147167  
  1         1931  
15 1     1   12 use feature qw(say state current_sub);
  1         3  
  1         2837  
16              
17             #D1 Parse # Create a parse tree from an array of terms representing an expression.
18             my $stack = undef; # Stack of lexical items
19             my $expression = undef; # Expression being parsed
20             my $position = undef; # Position in expression
21             our %tested; # Pairs of lexical items (b, a) such that 'b' is observed to follow 'a' in a test.
22             our %follows; # Pairs of lexical items (b, a) such that 'b' is observed to follow 'a' in a test without causing a syntax error.
23             our %first; # Lexical elements that can come first
24             our %last; # Lexical elements that can come last
25              
26             sub new($) #P Create a new term from the indicated number of items on top of the stack
27 309     309 1 483 {my ($count) = @_; # Number of terms
28              
29 309 50       647 @$stack >= $count or confess "Stack underflow";
30              
31 309         709 my ($operator, @operands) = splice @$stack, -$count; # Remove lexical items from stack
32              
33 309 100       1170 my $t = genHash(__PACKAGE__, # Description of a term in the expression.
34             operands => @operands ? [@operands] : undef, # Operands to which the operator will be applied.
35             operator => $operator, # Operator to be applied to one or more operands.
36             up => undef, # Parent term if this is a sub term.
37             );
38              
39 309         14187 $_->up = $t for grep {ref $_} @operands; # Link to parent if possible
  236         3825  
40              
41 309         2574 push @$stack, $t; # Save newly created term on the stack
42             }
43              
44             sub LexicalCode($$$$) #P Lexical code definition
45 9     9 1 821 {my ($letter, $next, $name, $short) = @_; # Letter used to refer to the lexical item, letters of items that can follow this lexical item, descriptive name of lexical item, short name
46 9         24 genHash(q(Tree::Term::LexicalCode), # Lexical item codes.
47             letter => $letter, # Letter code used to refer to the lexical item.
48             next => $next, # Letters codes of items that can follow this lexical item.
49             name => $name, # Descriptive name of lexical item.
50             short => $short, # Short name of lexical item.
51             );
52             }
53              
54             my $LexicalCodes = genHash(q(Tree::Term::Codes), # Lexical item codes.
55             a => LexicalCode('a', 'bpv', q(assignment operator), qq(assign)), # Infix operator with priority 2 binding right to left typically used in an assignment.
56             b => LexicalCode('b', 'bBpsv', q(opening parenthesis), qq(OpenBracket)), # Opening parenthesis.
57             B => LexicalCode('B', 'aBdqs', q(closing parenthesis), qq(CloseBracket)), # Closing parenthesis.
58             d => LexicalCode('d', 'bpv', q(dyadic operator), qq(dyad)), # Infix operator with priority 3 binding left to right typically used in arithmetic.
59             p => LexicalCode('p', 'bpv', q(prefix operator), qq(prefix)), # Monadic prefix operator.
60             q => LexicalCode('q', 'aBdqs', q(suffix operator), qq(suffix)), # Monadic suffix operator.
61             s => LexicalCode('s', 'bBpsv', q(semi-colon), qq(semiColon)), # Infix operator with priority 1 binding left to right typically used to separate statements.
62             t => LexicalCode('t', 'aBdqs', q(term), qq(term)), # A term in the expression.
63             v => LexicalCode('v', 'aBdqs', q(variable), qq(variable)), # A variable in the expression.
64             );
65              
66             my $first = 'bpsv'; # First element
67             my $last = 'Bqsv'; # Last element
68              
69             sub LexicalStructure() # Return the lexical codes and their relationships in a data structure so this information can be used in other contexts.
70 4     4 1 28 {genHash(q(Tree::Term::LexicalStructure), # Lexical item codes.
71             codes => $LexicalCodes, # Code describing each lexical item
72             first => $first, # Lexical items we can start with
73             last => $last, # Lexical items we can end with
74             );
75             }
76              
77             sub type($) #P Type of term
78 3796     3796 1 5776 {my ($s) = @_; # Term to test
79 3796 100       9674 return 't' if ref $s; # Term on top of stack
80 3580         32441 substr($s, 0, 1); # Something other than a term defines its type by its first letter
81             }
82              
83             sub expandElement($) #P Describe a lexical element
84 195     195 1 299 {my ($e) = @_; # Element to expand
85 195         335 my $x = $LexicalCodes->{type $e}->name; # Expansion
86 195         1031 "'$x': $e"
87             }
88              
89             sub expandCodes($) #P Expand a string of codes
90 64     64 1 267 {my ($e) = @_; # Codes to expand
91 64         204 my @c = map {qq('$_')} sort map {$LexicalCodes->{$_}->name} split //, $e; # Codes for next possible items
  252         852  
  252         4405  
92 64         134 my $c = pop @c;
93 64         149 my $t = join ', ', @c;
94 64         179 "$t or $c"
95             }
96              
97             sub expected($) #P String of next possible lexical items
98 60     60 1 107 {my ($s) = @_; # Lexical item
99 60         104 my $e = expandCodes $LexicalCodes->{type $s}->next; # Codes for next possible items
100 60         134 "Expected: $e"
101             }
102              
103             sub unexpected($$$) #P Complain about an unexpected element
104             {my ($element, $unexpected, $position) = @_; # Last good element, unexpected element, position
105             my $j = $position + 1;
106             my $E = expandElement $unexpected;
107             my $X = expected $element;
108              
109             my sub de($) # Extract an error message and die
110             {my ($message) = @_; # Message
111             $message =~ s(\n) ( )gs;
112             die "$message\n";
113             }
114              
115             de <
116             Unexpected $E following term ending at position $j.
117             $X.
118             END
119             my $S = expandElement $element;
120             de <
121             Unexpected $E following $S at position $j.
122             $X.
123             END
124             }
125              
126             sub syntaxError(@) # Check the syntax of an expression without parsing it. Die with a helpful message if an error occurs. The helpful message will be slightly different from that produced by L as it cannot contain information from the non existent parse tree.
127             {my (@expression) = @_; # Expression to parse
128             my @e = @_;
129              
130             return '' unless @e; # An empty string is valid
131              
132             my sub test($$$) # Test a transition
133             {my ($current, $following, $position) = @_; # Current element, following element, position
134             my $n = $LexicalCodes->{type $current}->next; # Elements expected next
135             return if index($n, type $following) > -1; # Transition allowed
136             unexpected $current, $following, $position - 1; # Complain about the unexpected element
137             }
138              
139             my sub testFirst # Test first transition
140             {return if index($first, type $e[0]) > -1; # Transition allowed
141             my $E = expandElement $e[0];
142             my $C = expandCodes $first;
143             die <
144             Expression must start with $C, not $E.
145             END
146             }
147              
148             my sub testLast($$) # Test last transition
149             {my ($current, $position) = @_; # Current element, position
150             return if index($last, type $current) > -1; # Transition allowed
151             my $C = expandElement $current;
152             my $E = expected $current;
153             die <
154             $E after final $C.
155             END
156             }
157              
158             if (1) # Test parentheses
159             {my @b;
160             for my $i(keys @e) # Each element
161             {my $e = $e[$i];
162             if (type($e) eq 'b') # Open
163             {push @b, [$i, $e];
164             }
165             elsif (type($e) eq 'B') # Close
166             {if (@b > 0)
167             {my ($h, $a) = pop(@b)->@*;
168             my $j = $i + 1;
169             my $g = $h + 1;
170             die <
171             Parenthesis mismatch between $a at position $g and $e at position $j.
172             END
173             }
174             else # No corresponding open
175             {my $j = $i + 1;
176             my $E = $i ? expected($e[$i-1]) : testFirst; # What we might have had instead
177             die <
178             Unexpected closing parenthesis $e at position $j. $E.
179             END
180             }
181             }
182             }
183             if (@b > 0) # Closing parentheses at end
184             {my ($h, $a) = pop(@b)->@*;
185             my $g = $h + 1;
186             die <
187             No closing parenthesis matching $a at position $g.
188             END
189             }
190             }
191              
192             if (1) # Test transitions
193             {testFirst $e[0]; # First transition
194             test $e[$_-1], $e[$_], $_+1 for 1..$#e; # Each element beyond the first
195             testLast $e[-1], scalar @e; # Final transition
196             }
197             }
198              
199             BEGIN # Generate recognition routines.
200 1     1   6 {for my $t(qw(abdps bst t))
201 3         9 {my $c = <<'END';
202             sub check_XXXX() #P Check that the top of the stack has one of XXXX
203             {$tested {type $$expression[$position]}{type $$expression[$position-1]}++; # Check that one lexical item has been seen to follow after another
204             if (index("XXXX", type($$stack[-1])) > -1) # Check type allowed
205             {$follows{type $$expression[$position]}{type $$expression[$position-1]}++; # Shows that one lexical item can possibly follow after another in some circumstances
206             return 1; # Type allowed
207             }
208             unexpected $$stack[-1], $$expression[$position], $position; # Complain about an unexpected type
209             }
210             END
211 3         22 $c =~ s(XXXX) ($t)gs;
212 3 50   133 0 750 eval $c; $@ and confess "$@\n";
  3 100   132 0 38  
  133 100   116 0 372  
  133 100       387  
  124         313  
  124         297  
  9         54  
  132         395  
  132         349  
  125         308  
  125         337  
  7         44  
  116         340  
  116         316  
  101         248  
  101         234  
  15         92  
213             }
214              
215 1         2 for my $t(qw(ads b B bpsv bst d p s v)) # Test various sets of items
216 9         21 {my $c = <<'END';
217             sub test_XXXX($) #P Check that we have XXXX
218             {my ($item) = @_; # Item to test
219             !ref($item) and index('XXXX', substr($item, 0, 1)) > -1
220             }
221             END
222 9         52 $c =~ s(XXXX) ($t)gs;
223 9 50   144 0 1003 eval $c; $@ and confess "$@\n";
  9 100   49 0 3338  
  144 50   218 0 346  
  144 100   86 0 607  
  49 50   0 0 119  
  49 0   11 0 275  
  218 50   184 0 480  
  218 100   151 0 981  
  86 100   82 0 199  
  86 50       553  
  0         0  
  0         0  
  11         27  
  11         60  
  184         440  
  184         1001  
  151         341  
  151         657  
  82         179  
  82         387  
224             }
225             }
226              
227             sub test_t($) #P Check that we have a term
228 321     321 1 515 {my ($item) = @_; # Item to test
229 321         725 ref $item
230             }
231              
232             sub reduce($) #P Reduce the stack at the specified priority
233 322     322 1 518 {my ($priority) = @_; # Priority
234             #lll "Reduce at $priority: ", scalar(@s), "\n", dump([@s]);
235              
236 322 100       652 if (@$stack >= 3) # term infix-operator term
237 163         384 {my ($l, $d, $r) = ($$stack[-3], $$stack[-2], $$stack[-1]); # Left infix right
238              
239 163 100       290 if (test_t($l)) # Parse out infix operator expression
240 71 100       103 {if (test_t($r))
241 60 100       1178 {if ($priority == 1 ? test_ads($d) : test_d($d)) # Amount of reduction
    100          
242 53         171 {pop @$stack for 1..3;
243 53         103 push @$stack, $d, $l, $r;
244 53         111 new 3;
245 53         210 return 1;
246             }
247             }
248             }
249              
250 110 100       1914 if (test_b($l)) # Parse parenthesized term keeping the opening parenthesis
251 64 100       1097 {if (test_B($r))
252 40 100       78 {if (test_t($d))
253 39         128 {pop @$stack for 1..3;
254 39         93 push @$stack, "$l$r", $d;
255 39         83 new 2;
256 39         154 return 1;
257             }
258             }
259             }
260             }
261              
262 230 100       448 if (@$stack >= 2) # Convert an empty pair of parentheses to an empty term
263 108         219 {my ($l, $r) = ($$stack[-2], $$stack[-1]);
264 108 100       1886 if (test_b($l)) # Empty pair of parentheses
265 67 100       1121 {if (test_B($r))
266 5         22 {pop @$stack for 1..2;
267 5         17 push @$stack, "$l$r";
268 5         14 new 1;
269 5         22 return 1;
270             }
271             }
272 103 100       1733 if (test_s($l)) # Semi-colon, close implies remove unneeded semi
273 13 100       215 {if (test_B($r))
274 11         41 {pop @$stack for 1..2;
275 11         19 push @$stack, $r;
276 11         40 return 1;
277             }
278             }
279 92 100       1547 if (test_p($l)) # Prefix, term
280 11 50       25 {if (test_t($r))
281 11         24 {new 2;
282 11         39 return 1;
283             }
284             }
285             }
286              
287             0 # No move made
288 203         479 }
289              
290             sub reduce1() #P Reduce the stack at priority 1
291 291     291 1 503 {reduce 1;
292             }
293              
294             sub reduce2() #P Reduce the stack at priority 2
295 31     31 1 62 {reduce 2;
296             }
297              
298             sub pushElement() #P Push an element
299 306     306 1 689 {push @$stack, $$expression[$position];
300             }
301              
302             sub accept_a() #P Assign
303 32     32 1 614 {check_t;
304 27         67 1 while reduce2;
305 27         53 pushElement;
306             }
307              
308             sub accept_b() #P Open
309 27     27 1 480 {check_abdps;
310 24         49 pushElement;
311             }
312              
313             sub accept_B() #P Closing parenthesis
314 48     48 1 845 {check_bst;
315 45         103 1 while reduce1;
316 45         99 pushElement;
317 45         75 1 while reduce1;
318 45         831 check_bst;
319             }
320              
321             sub accept_d() #P Infix but not assign or semi-colon
322 28     28 1 547 {check_t;
323 23         49 pushElement;
324             }
325              
326             sub accept_p() #P Prefix
327 35     35 1 602 {check_abdps;
328 32         62 pushElement;
329             }
330              
331             sub accept_q() #P Post fix
332 56     56 1 987 {check_t;
333 51         110 my $p = pop @$stack;
334 51         120 pushElement;
335 51         68 push @$stack, $p;
336 51         92 new 2;
337             }
338              
339             sub accept_s() #P Semi colon
340 39     39 1 704 {check_bst;
341 36 100       88 if (!test_t($$stack[-1])) # Insert an empty element between two consecutive semicolons
342 12         26 {push @$stack, 'empty1';
343 12         21 new 1;
344             }
345 36         76 1 while reduce1;
346 36         77 pushElement;
347             }
348              
349             sub accept_v() #P Variable
350 71     71 1 1252 {check_abdps;
351 68         219 pushElement;
352 68         160 new 1;
353 68   100     1433 new 2 while @$stack >= 2 and test_p($$stack[-2]); # Check for preceding prefix operators
354             }
355             # Action on each lexical item
356             my $Accept = # Dispatch the action associated with the lexical item
357             {a => \&accept_a, # Assign
358             b => \&accept_b, # Open
359             B => \&accept_B, # Closing parenthesis
360             d => \&accept_d, # Infix but not assign or semi-colon
361             p => \&accept_p, # Prefix
362             q => \&accept_q, # Post fix
363             s => \&accept_s, # Semi colon
364             v => \&accept_v, # Variable
365             };
366              
367             sub parseExpression() #P Parse an expression.
368 86 50   86 1 163 {if (@$expression)
369 86         143 {my $e = $$expression[$position = 0];
370              
371 86         171 my $E = expandElement $e;
372 86 100       1498 die <
373             Expression must start with 'opening parenthesis', 'prefix
374             operator', 'semi-colon' or 'variable', not $E.
375             END
376 82 100       1416 if (test_v($e)) # Single variable
377 34         78 {push @$stack, $e;
378 34         78 new 1;
379             }
380             else
381 48 100       808 {if (test_s($e)) # Semi
382 7         39 {push @$stack, 'empty2';
383 7         19 new 1;
384             }
385 48         129 push @$stack, $e;
386             }
387             }
388             else # Empty expression
389 0         0 {return undef;
390             }
391              
392 82         239 for(1..$#$expression) # Each input element
393 336         987 {$$Accept{substr($$expression[$position = $_], 0, 1)}(); # Dispatch the action associated with the lexical item
394             }
395              
396 51 100       140 if (index($last, type $$expression[-1]) == -1) # Check for incomplete expression
397 1         5 {my $C = expandElement $$expression[-1];
398 1         4 my $E = expected $$expression[-1];
399 1         19 die <
400             $E after final $C.
401             END
402             }
403              
404 50   100     226 pop @$stack while @$stack > 1 and $$stack[-1] =~ m(s); # Remove any trailing semi colons
405 50         94 1 while reduce1; # Final reductions
406              
407 50 100       98 if (@$stack != 1) # Incomplete expression
408 1         5 {my $E = expected $$expression[-1];
409 1         7 die "Incomplete expression. $E.\n";
410             }
411              
412 49         90 $first{type $$expression[ 0]}++; # Capture valid first and last lexical elements
413 49         102 $last {type $$expression[-1]}++;
414              
415 49         114 $$stack[0] # The resulting parse tree
416             } # parseExpression
417              
418             sub parse(@) # Parse an expression.
419 86     86 1 215 {my (@expression) = @_; # Expression to parse
420 86         131 my $s = $stack;
421 86         145 $stack = []; # Clear the current stack - the things we do to speed things up.
422 86         120 my $x = $expression;
423 86         135 $expression = \@expression; # Clear the current expression
424 86         161 my $p = $position;
425 86         128 $position = 0; # Clear the current parse position
426              
427 86         118 my $e = eval {parseExpression};
  86         163  
428 86         164 my $r = $@; # Save any error message
429 86         202 $stack = $s; $expression = $x; $position = $p; # Restore the stack and expression being parsed
  86         116  
  86         101  
430 86 100       215 die $r if $r; # Die again if we died the last time
431              
432 49         338 $e # Otherwise return the parse tree
433             } # parse
434              
435             #D1 Validate # Validating is the same as parsing except we do not start at the beginning, instead we start at any lexical element and proceed a few steps from there.
436              
437             sub validPair($$) # Confirm that the specified pair of lexical elements can occur as a sequence.
438 4     4 1 14 {my ($A, $B) = @_; # First element, second element
439 4         23 my $a = type $A;
440 4         9 my $b = type $B;
441 4 50       14 if (my $l = $$LexicalCodes{$a})
442 4 100       102 {return 1 if (index $l->next, $b) > -1;
443             }
444             undef
445 1         10 }
446              
447             #D1 Print # Print a parse tree to make it easy to visualize its structure.
448              
449             sub depth($) #P Depth of a term in an expression.
450 558     558 1 862 {my ($term) = @_; # Term
451 558         686 my $d = 0;
452 558         1080 for(my $t = $term; $t; $t = $t->up) {++$d}
  2216         39521  
453 558         3634 $d
454             }
455              
456             sub listTerms($) #P List the terms in an expression in post order
457 47     47 1 69 {my ($expression) = @_; # Root term
458 47         65 my @t; # Terms
459              
460             sub # Recurse through terms
461 279     279   482 {my ($e) = @_; # Term
462 279         5071 my $o = $e->operands;
463 279 50       1184 return unless $e; # Operator
464 279 100       601 if (my @o = $o ? grep {ref $_} @$o : ()) # Operands
  232 100       668  
465 179         330 {my ($p, @p) = @o;
466 179         752 __SUB__->($p); # First operand
467 179         258 push @t, $e; # Operator
468 179         352 __SUB__->($_) for @p; # Second and subsequent operands
469             }
470             else # No operands
471 100         245 {push @t, $e; # Operator
472             }
473 47         248 } ->($expression);
474              
475             @t
476 47         414 }
477              
478             sub flat($@) # Print the terms in the expression as a tree from left right to make it easier to visualize the structure of the tree.
479             {my ($expression, @title) = @_; # Root term, optional title
480             my @t = $expression->listTerms; # Terms in expression in post order
481             my @s; # Print
482              
483             my sub align # Align the ends of the lines
484             {my $L = 0; # Longest line
485             for my $s(@s)
486             {my $l = length $s; $L = $l if $l > $L;
487             }
488              
489             for my $i(keys @s) # Pad to longest
490             {my $s = $s[$i] =~ s/\s+\Z//rs;
491             my $l = length($s);
492             if ($l < $L)
493             {my $p = ' ' x ($L - $l);
494             $s[$i] = $s . $p;
495             }
496             }
497             };
498              
499             for my $t(@t) # Initialize output rectangle
500             {$s[$_] //= '' for 0..$t->depth;
501             }
502              
503             for my $t(@t) # Traverse tree
504             {my $d = $t->depth;
505             my $p = $t->operator; # Operator
506             my $P = $p =~ s(\A\w+?_) ()gsr; # Remove leading type character if followed by underscore as this make for clearer results
507              
508             align if $p =~ m(\A(a|d|s)); # Shift over for some components
509              
510             $s[$d] .= " $P"; # Describe operator or operand with type component removed if requested
511             align if $p !~ m(\A(p|q|v)); # Vertical for some components
512             }
513              
514             shift @s while @s and $s[ 0] =~ m(\A\s*\Z)s; # Remove leading blank lines
515              
516             for(@s) # Clean up trailing blanks so that tests are not affected by spurious white space mismatches
517             {s/\s+\n/\n/gs; s/\s+\Z//gs;
518             }
519              
520             unshift @s, join(' ', @title) if @title; # Add title
521              
522             join "\n", @s, '';
523             }
524              
525             #D
526             #-------------------------------------------------------------------------------
527             # Export - eeee
528             #-------------------------------------------------------------------------------
529              
530 1     1   9 use Exporter qw(import);
  1         2  
  1         49  
531              
532 1     1   7 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         4  
  1         406  
533              
534             @ISA = qw(Exporter);
535             @EXPORT = qw();
536             @EXPORT_OK = qw(
537             );
538             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
539              
540             # podDocumentation
541             =pod
542              
543             =encoding utf-8
544              
545             =head1 Name
546              
547             Tree::Term - Create a parse tree from an array of terms representing an expression.
548              
549             =head1 Synopsis
550              
551             The expression to L is presented as an array of words, the first letter
552             of each word indicates its lexical role as in:
553              
554             my @e = qw(
555              
556             v_sub a_is v_array as
557             v1 d_== v2 a_then v3 d_plus v4 a_else
558             v5 d_== v6 a_then v7 d_minus v8 a_else v9 d_times b v10 a_+ v11 B);
559              
560             Where:
561              
562             a assign - infix operator with priority 2 binding right to left
563             b open - open parenthesis
564             B close - close parenthesis
565             d dyad - infix operator with priority 3 binding left to right
566             p prefix - monadic prefix operator
567             q suffix - monadic suffix operator
568             s semi-colon - infix operator with priority 1 binding left to right
569             v variable - a variable in the expression
570              
571             The results of parsing the expression can be printed with L which
572             provides a left to right representation of the parse tree.
573              
574             is_deeply parse(@e)->flat, <
575             is
576             sub as
577             array then
578             == else
579             v1 v2 plus then
580             v3 v4 == else
581             v5 v6 minus times
582             v7 v8 v9 +
583             v10 v11
584             END
585              
586             =head1 Description
587              
588             Create a parse tree from an array of terms representing an expression.
589              
590              
591             Version 20210724.
592              
593              
594             The following sections describe the methods in each functional area of this
595             module. For an alphabetic listing of all methods by name see L.
596              
597              
598              
599             =head1 Parse
600              
601             Create a parse tree from an array of terms representing an expression.
602              
603             =head2 LexicalStructure()
604              
605             Return the lexical codes and their relationships in a data structure so this information can be used in other contexts.
606              
607              
608             B
609              
610              
611              
612             is_deeply LexicalStructure, # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
613              
614              
615              
616             =head2 syntaxError(@expression)
617              
618             Check the syntax of an expression without parsing it. Die with a helpful message if an error occurs. The helpful message will be slightly different from that produced by L as it cannot contain information from the non existent parse tree.
619              
620             Parameter Description
621             1 @expression Expression to parse
622              
623             B
624              
625              
626             if (1)
627              
628             {eval {syntaxError(qw(v1 p1))}; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
629              
630             ok -1 < index $@, <
631             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2.
632             Expected: 'assignment operator', 'closing parenthesis',
633             'dyadic operator', 'semi-colon' or 'suffix operator'.
634             END
635             }
636              
637              
638             =head2 parse(@expression)
639              
640             Parse an expression.
641              
642             Parameter Description
643             1 @expression Expression to parse
644              
645             B
646              
647              
648             ok T [qw(v_sub a_is v_array as v1 d_== v2 a_then v3 d_plus v4 a_else v5 d_== v6 a_then v7 d_minus v8 a_else v9 d_times b v10 a_+ v11 B)], <
649             is
650             sub as
651             array then
652             == else
653             v1 v2 plus then
654             v3 v4 == else
655             v5 v6 minus times
656             v7 v8 v9 +
657             v10 v11
658             END
659             }
660              
661             if (1) {
662             ok validPair('B', 'd');
663             ok validPair('b', 'B');
664             ok validPair('v', 'a');
665             ok !validPair('v', 'v');
666              
667              
668             =head1 Validate
669              
670             Validating is the same as parsing except we do not start at the beginning, instead we start at any lexical element and proceed a few steps from there.
671              
672             =head2 validPair($A, $B)
673              
674             Confirm that the specified pair of lexical elements can occur as a sequence.
675              
676             Parameter Description
677             1 $A First element
678             2 $B Second element
679              
680             B
681              
682              
683              
684             ok validPair('B', 'd'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
685              
686              
687             ok validPair('b', 'B'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
688              
689              
690             ok validPair('v', 'a'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
691              
692              
693             ok !validPair('v', 'v'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
694              
695              
696              
697             =head1 Print
698              
699             Print a parse tree to make it easy to visualize its structure.
700              
701             =head2 flat($expression, @title)
702              
703             Print the terms in the expression as a tree from left right to make it easier to visualize the structure of the tree.
704              
705             Parameter Description
706             1 $expression Root term
707             2 @title Optional title
708              
709             B
710              
711              
712              
713             my @e = qw(v1 a2 v3 d4 v5 s6 v8 a9 v10);
714              
715              
716             is_deeply parse(@e)->flat, <
717              
718             s6
719             a2 a9
720             v1 d4 v8 v10
721             v3 v5
722             END
723             }
724              
725             ok T [qw(v1 a2 v3 s s s v4 a5 v6 s s)], <
726             s
727             s empty1
728             s a5
729             s empty1 v4 v6
730             a2 empty1
731             v1 v3
732             END
733              
734             ok T [qw(b B)], <
735             empty1
736             END
737              
738             ok T [qw(b b B B)], <
739             empty1
740             END
741              
742             ok T [qw(b b v1 B B)], <
743             v1
744             END
745              
746             ok T [qw(b b v1 a2 v3 B B)], <
747             a2
748             v1 v3
749             END
750              
751             ok T [qw(b b v1 a2 v3 d4 v5 B B)], <
752             a2
753             v1 d4
754             v3 v5
755             END
756              
757             ok T [qw(p1 v1)], <
758             p1
759             v1
760             END
761              
762             ok T [qw(p2 p1 v1)], <
763             p2
764             p1
765             v1
766             END
767              
768             ok T [qw(v1 q1)], <
769             q1
770             v1
771             END
772              
773             ok T [qw(v1 q1 q2)], <
774             q2
775             q1
776             v1
777             END
778              
779             ok T [qw(p2 p1 v1 q1 q2)], <
780             q2
781             q1
782             p2
783             p1
784             v1
785             END
786              
787             ok T [qw(p2 p1 v1 q1 q2 d3 p4 p3 v2 q3 q4)], <
788             d3
789             q2 q4
790             q1 q3
791             p2 p4
792             p1 p3
793             v1 v2
794             END
795              
796             ok T [qw(p2 p1 v1 q1 q2 d3 p4 p3 v2 q3 q4 d4 p6 p5 v3 q5 q6 s)], <
797             d3
798             q2 d4
799             q1 q4 q6
800             p2 q3 q5
801             p1 p4 p6
802             v1 p3 p5
803             v2 v3
804             END
805              
806             ok T [qw(b s B)], <
807             empty1
808             END
809              
810             ok T [qw(b s s B)], <
811             s
812             empty1 empty1
813             END
814              
815              
816             if (1) {
817              
818             my @e = qw(b b p2 p1 v1 q1 q2 B d3 b p4 p3 v2 q3 q4 d4 p6 p5 v3 q5 q6 B s B s);
819              
820              
821             is_deeply parse(@e)->flat, <
822              
823             d3
824             q2 d4
825             q1 q4 q6
826             p2 q3 q5
827             p1 p4 p6
828             v1 p3 p5
829             v2 v3
830             END
831              
832             }
833              
834             ok T [qw(b b v1 B s B s)], <
835             v1
836             END
837              
838             ok T [qw(v1 q1 s)], <
839             q1
840             v1
841             END
842              
843             ok T [qw(b b v1 q1 q2 B q3 q4 s B q5 q6 s)], <
844             q6
845             q5
846             q4
847             q3
848             q2
849             q1
850             v1
851             END
852              
853             ok T [qw(p1 p2 b v1 B)], <
854             p1
855             p2
856             v1
857             END
858              
859             ok T [qw(v1 d1 p1 p2 v2)], <
860             d1
861             v1 p1
862             p2
863             v2
864             END
865              
866             ok T [qw(p1 p2 b p3 p4 b p5 p6 v1 d1 v2 q1 q2 B q3 q4 s B q5 q6 s)], <
867             q6
868             q5
869             p1
870             p2
871             q4
872             q3
873             p3
874             p4
875             d1
876             p5 q2
877             p6 q1
878             v1 v2
879             END
880              
881             ok T [qw(p1 p2 b p3 p4 b p5 p6 v1 a1 v2 q1 q2 B q3 q4 s B q5 q6 s)], <
882             q6
883             q5
884             p1
885             p2
886             q4
887             q3
888             p3
889             p4
890             a1
891             p5 q2
892             p6 q1
893             v1 v2
894             END
895              
896             ok T [qw(b v1 B d1 b v2 B)], <
897             d1
898             v1 v2
899             END
900              
901             ok T [qw(b v1 B q1 q2 d1 b v2 B)], <
902             d1
903             q2 v2
904             q1
905             v1
906             END
907              
908             ok T [qw(v1 s)], <
909             v1
910             END
911              
912             ok T [qw(v1 s s)], <
913             s
914             v1 empty1
915             END
916              
917             ok T [qw(v1 s b s B)], <
918             s
919             v1 empty1
920             END
921              
922             ok T [qw(v1 s b b s s B B)], <
923             s
924             v1 s
925             empty1 empty1
926             END
927              
928             ok T [qw(b v1 s B s s)], <
929             s
930             v1 empty1
931             END
932              
933             ok T [qw(v1 a b1 b2 v2 B2 B1 s)], <
934             a
935             v1 v2
936             END
937              
938             ok T [qw(v1 a1 b1 v2 a2 b2 v3 B2 B1 s)], <
939             a1
940             v1 a2
941             v2 v3
942             END
943              
944             ok T [qw(v1 a1 p1 v2)], <
945             a1
946             v1 p1
947             v2
948             END
949              
950             ok T [qw(b1 v1 q1 q2 B1)], <
951             q2
952             q1
953             v1
954             END
955              
956             ok T [qw(b1 v1 q1 q2 s B1)], <
957             q2
958             q1
959             v1
960             END
961              
962             ok T [qw(p1 b1 v1 B1 q1)], <
963             q1
964             p1
965             v1
966             END
967              
968             ok T [qw(b1 v1 B1 a1 v2)], <
969             a1
970             v1 v2
971             END
972              
973             ok T [qw(v1 q1 a1 v2)], <
974             a1
975             q1 v2
976             v1
977             END
978              
979             ok T [qw(s1 p1 v1)], <
980             s1
981             empty2 p1
982             v1
983             END
984              
985             ok E <
986             a
987             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'assignment operator': a.
988             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'assignment operator': a.
989             END
990              
991             ok E <
992             B
993             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'closing parenthesis': B.
994             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'closing parenthesis': B.
995             END
996              
997             ok E <
998             d1
999             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'dyadic operator': d1.
1000             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'dyadic operator': d1.
1001             END
1002              
1003             ok E <
1004             p1
1005             Expected: 'opening parenthesis', 'prefix operator' or 'variable' after final 'prefix operator': p1.
1006             Expected: 'opening parenthesis', 'prefix operator' or 'variable' after final 'prefix operator': p1.
1007             END
1008              
1009             ok E <
1010             q1
1011             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'suffix operator': q1.
1012             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'suffix operator': q1.
1013             END
1014              
1015             ok E <
1016             s
1017              
1018              
1019             END
1020              
1021             ok E <
1022             v1
1023              
1024              
1025             END
1026              
1027             ok E <
1028             b v1
1029             Incomplete expression. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1030             No closing parenthesis matching b at position 1.
1031             END
1032              
1033             ok E <
1034             b v1 B B
1035             Unexpected 'closing parenthesis': B following 'closing parenthesis': B at position 4. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1036             Unexpected closing parenthesis B at position 4. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1037             END
1038              
1039             ok E <
1040             v1 d1 d2 v2
1041             Unexpected 'dyadic operator': d2 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1042             Unexpected 'dyadic operator': d2 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1043             END
1044              
1045             ok E <
1046             v1 p1
1047             Unexpected 'prefix operator': p1 following term ending at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1048             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1049             END
1050              
1051             ok E <
1052             b1 B1 v1
1053             Unexpected 'variable': v1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1054             Unexpected 'variable': v1 following 'closing parenthesis': B1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1055             END
1056              
1057             ok E <
1058             b1 B1 p1 v1
1059             Unexpected 'prefix operator': p1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1060             Unexpected 'prefix operator': p1 following 'closing parenthesis': B1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1061             END
1062              
1063             if (1)
1064             {eval {syntaxError(qw(v1 p1))};
1065             ok -1 < index $@, <
1066             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2.
1067             Expected: 'assignment operator', 'closing parenthesis',
1068             'dyadic operator', 'semi-colon' or 'suffix operator'.
1069             END
1070              
1071              
1072              
1073             =head1 Hash Definitions
1074              
1075              
1076              
1077              
1078             =head2 Tree::Term Definition
1079              
1080              
1081             Description of a term in the expression.
1082              
1083              
1084              
1085              
1086             =head3 Output fields
1087              
1088              
1089             =head4 operands
1090              
1091             Operands to which the operator will be applied.
1092              
1093             =head4 operator
1094              
1095             Operator to be applied to one or more operands.
1096              
1097             =head4 up
1098              
1099             Parent term if this is a sub term.
1100              
1101              
1102              
1103             =head2 Tree::Term::Codes Definition
1104              
1105              
1106             Lexical item codes.
1107              
1108              
1109              
1110              
1111             =head3 Output fields
1112              
1113              
1114             =head4 B
1115              
1116             Closing parenthesis.
1117              
1118             =head4 a
1119              
1120             Infix operator with priority 2 binding right to left typically used in an assignment.
1121              
1122             =head4 b
1123              
1124             Opening parenthesis.
1125              
1126             =head4 d
1127              
1128             Infix operator with priority 3 binding left to right typically used in arithmetic.
1129              
1130             =head4 p
1131              
1132             Monadic prefix operator.
1133              
1134             =head4 q
1135              
1136             Monadic suffix operator.
1137              
1138             =head4 s
1139              
1140             Infix operator with priority 1 binding left to right typically used to separate statements.
1141              
1142             =head4 t
1143              
1144             A term in the expression.
1145              
1146             =head4 v
1147              
1148             A variable in the expression.
1149              
1150              
1151              
1152             =head2 Tree::Term::LexicalCode Definition
1153              
1154              
1155             Lexical item codes.
1156              
1157              
1158              
1159              
1160             =head3 Output fields
1161              
1162              
1163             =head4 letter
1164              
1165             Letter code used to refer to the lexical item.
1166              
1167             =head4 name
1168              
1169             Descriptive name of lexical item.
1170              
1171             =head4 next
1172              
1173             Letters codes of items that can follow this lexical item.
1174              
1175              
1176              
1177             =head2 Tree::Term::LexicalStructure Definition
1178              
1179              
1180             Lexical item codes.
1181              
1182              
1183              
1184              
1185             =head3 Output fields
1186              
1187              
1188             =head4 codes
1189              
1190             Code describing each lexical item
1191              
1192             =head4 first
1193              
1194             Lexical items we can start with
1195              
1196             =head4 last
1197              
1198             Lexical items we can end with
1199              
1200              
1201              
1202             =head1 Private Methods
1203              
1204             =head2 new($count)
1205              
1206             Create a new term from the indicated number of items on top of the stack
1207              
1208             Parameter Description
1209             1 $count Number of terms
1210              
1211             =head2 LexicalCode($letter, $next, $name)
1212              
1213             Lexical code definition
1214              
1215             Parameter Description
1216             1 $letter Letter used to refer to the lexical item
1217             2 $next Letters of items that can follow this lexical item
1218             3 $name Descriptive name of lexical item
1219              
1220             =head2 type($s)
1221              
1222             Type of term
1223              
1224             Parameter Description
1225             1 $s Term to test
1226              
1227             =head2 expandElement($e)
1228              
1229             Describe a lexical element
1230              
1231             Parameter Description
1232             1 $e Element to expand
1233              
1234             =head2 expandCodes($e)
1235              
1236             Expand a string of codes
1237              
1238             Parameter Description
1239             1 $e Codes to expand
1240              
1241             =head2 expected($s)
1242              
1243             String of next possible lexical items
1244              
1245             Parameter Description
1246             1 $s Lexical item
1247              
1248             =head2 unexpected($element, $unexpected, $position)
1249              
1250             Complain about an unexpected element
1251              
1252             Parameter Description
1253             1 $element Last good element
1254             2 $unexpected Unexpected element
1255             3 $position Position
1256              
1257             =head2 check_XXXX()
1258              
1259             Check that the top of the stack has one of XXXX
1260              
1261              
1262             =head2 test_XXXX($item)
1263              
1264             Check that we have XXXX
1265              
1266             Parameter Description
1267             1 $item Item to test
1268              
1269             =head2 test_t($item)
1270              
1271             Check that we have a term
1272              
1273             Parameter Description
1274             1 $item Item to test
1275              
1276             =head2 reduce($priority)
1277              
1278             Reduce the stack at the specified priority
1279              
1280             Parameter Description
1281             1 $priority Priority
1282              
1283             =head2 reduce1()
1284              
1285             Reduce the stack at priority 1
1286              
1287              
1288             =head2 reduce2()
1289              
1290             Reduce the stack at priority 2
1291              
1292              
1293             =head2 pushElement()
1294              
1295             Push an element
1296              
1297              
1298             =head2 accept_a()
1299              
1300             Assign
1301              
1302              
1303             =head2 accept_b()
1304              
1305             Open
1306              
1307              
1308             =head2 accept_B()
1309              
1310             Closing parenthesis
1311              
1312              
1313             =head2 accept_d()
1314              
1315             Infix but not assign or semi-colon
1316              
1317              
1318             =head2 accept_p()
1319              
1320             Prefix
1321              
1322              
1323             =head2 accept_q()
1324              
1325             Post fix
1326              
1327              
1328             =head2 accept_s()
1329              
1330             Semi colon
1331              
1332              
1333             =head2 accept_v()
1334              
1335             Variable
1336              
1337              
1338             =head2 parseExpression()
1339              
1340             Parse an expression.
1341              
1342              
1343             =head2 depth($term)
1344              
1345             Depth of a term in an expression.
1346              
1347             Parameter Description
1348             1 $term Term
1349              
1350             =head2 listTerms($expression)
1351              
1352             List the terms in an expression in post order
1353              
1354             Parameter Description
1355             1 $expression Root term
1356              
1357              
1358             =head1 Index
1359              
1360              
1361             1 L - Assign
1362              
1363             2 L - Closing parenthesis
1364              
1365             3 L - Open
1366              
1367             4 L - Infix but not assign or semi-colon
1368              
1369             5 L - Prefix
1370              
1371             6 L - Post fix
1372              
1373             7 L - Semi colon
1374              
1375             8 L - Variable
1376              
1377             9 L - Check that the top of the stack has one of XXXX
1378              
1379             10 L - Depth of a term in an expression.
1380              
1381             11 L - Expand a string of codes
1382              
1383             12 L - Describe a lexical element
1384              
1385             13 L - String of next possible lexical items
1386              
1387             14 L - Print the terms in the expression as a tree from left right to make it easier to visualize the structure of the tree.
1388              
1389             15 L - Lexical code definition
1390              
1391             16 L - Return the lexical codes and their relationships in a data structure so this information can be used in other contexts.
1392              
1393             17 L - List the terms in an expression in post order
1394              
1395             18 L - Create a new term from the indicated number of items on top of the stack
1396              
1397             19 L - Parse an expression.
1398              
1399             20 L - Parse an expression.
1400              
1401             21 L - Push an element
1402              
1403             22 L - Reduce the stack at the specified priority
1404              
1405             23 L - Reduce the stack at priority 1
1406              
1407             24 L - Reduce the stack at priority 2
1408              
1409             25 L - Check the syntax of an expression without parsing it.
1410              
1411             26 L - Check that we have a term
1412              
1413             27 L - Check that we have XXXX
1414              
1415             28 L - Type of term
1416              
1417             29 L - Complain about an unexpected element
1418              
1419             30 L - Confirm that the specified pair of lexical elements can occur as a sequence.
1420              
1421             =head1 Installation
1422              
1423             This module is written in 100% Pure Perl and, thus, it is easy to read,
1424             comprehend, use, modify and install via B:
1425              
1426             sudo cpan install Tree::Term
1427              
1428             =head1 Author
1429              
1430             L
1431              
1432             L
1433              
1434             =head1 Copyright
1435              
1436             Copyright (c) 2016-2021 Philip R Brenan.
1437              
1438             This module is free software. It may be used, redistributed and/or modified
1439             under the same terms as Perl itself.
1440              
1441             =cut
1442              
1443              
1444              
1445             # Tests and documentation
1446              
1447             sub test
1448 1     1 0 89 {my $p = __PACKAGE__;
1449 1         10 binmode($_, ":utf8") for *STDOUT, *STDERR;
1450 1 50       74 return if eval "eof(${p}::DATA)";
1451 0         0 my $s = eval "join('', <${p}::DATA>)";
1452 0 0       0 $@ and die $@;
1453 0         0 eval $s;
1454 0 0       0 $@ and die $@;
1455 0         0 1
1456             }
1457              
1458             test unless caller;
1459              
1460             1;
1461             # podDocumentation
1462             #__DATA__
1463 1     1   8 use Time::HiRes qw(time);
  1         3  
  1         9  
1464 1     1   976 use Test::More;
  1         70031  
  1         10  
1465              
1466             my $develop = -e q(/home/phil/); # Developing
1467             my $log = q(/home/phil/perl/cpan/TreeTerm/lib/Tree/zzz.txt); # Log file
1468             my $localTest = ((caller(1))[0]//'Tree::Term') eq "Tree::Term"; # Local testing mode
1469              
1470             Test::More->builder->output("/dev/null") if $localTest; # Reduce number of confirmation messages during testing
1471              
1472             if ($^O =~ m(bsd|linux|darwin)i) # Supported systems
1473             {plan tests => 222
1474             }
1475             else
1476             {plan skip_all =>qq(Not supported on: $^O);
1477             }
1478              
1479             sub T #P Test a parse
1480 46     46 0 108 {my ($expression, $expected) = @_; # Expression, expected result
1481 46         473 syntaxError @$expression; # Syntax check without creating parse tree
1482 46         144 my $g = parse(@$expression)->flat;
1483 46         127 my $r = $g eq $expected;
1484 46 50       679 owf($log, $g) if -e $log; # Save result if testing
1485 46 50       143 confess "Failed test" unless $r;
1486 46         323 $r
1487             }
1488              
1489             sub E($) #P Test a parse error
1490 39     39 0 99 {my ($text) = @_;
1491 39         187 my ($test, $parse, $syntax) = split /\n/, $text; # Parse test description
1492              
1493 39         219 my @e = split /\s+/, $test;
1494 39         68 my $e = 0;
1495 39 50 50     58 eval {parse @e}; ++$e unless index($@, $parse) > -1; my $a = $@ // '';
  39         94  
  39         198  
  39         88  
1496 39 50 50     65 eval {syntaxError @e}; ++$e unless index($@, $syntax) > -1; my $b = $@ // '';
  39         315  
  39         185  
  39         88  
1497 39 50       78 if ($e)
1498 0 0       0 {owf($log, "$a$b") if -e $log; # Save result if testing
1499 0         0 confess;
1500             }
1501 39         220 !$e
1502             }
1503              
1504             my $startTime = time;
1505              
1506             eval {goto latest};
1507              
1508             ok T [qw(v1)], <
1509             v1
1510             END
1511              
1512             ok T [qw(s)], <
1513             empty2
1514             END
1515              
1516             ok T [qw(s s)], <
1517             s
1518             empty2 empty1
1519             END
1520              
1521             ok T [qw(v1 d2 v3)], <
1522             d2
1523             v1 v3
1524             END
1525              
1526             ok T [qw(v1 a2 v3)], <
1527             a2
1528             v1 v3
1529             END
1530              
1531             ok T [qw(v1 a2 v3 d4 v5)], <
1532             a2
1533             v1 d4
1534             v3 v5
1535             END
1536              
1537             if (1) { #Tflat
1538              
1539             my @e = qw(v1 a2 v3 d4 v5 s6 v8 a9 v10);
1540              
1541             is_deeply parse(@e)->flat, <
1542             s6
1543             a2 a9
1544             v1 d4 v8 v10
1545             v3 v5
1546             END
1547             }
1548              
1549             ok T [qw(v1 a2 v3 s s s v4 a5 v6 s s)], <
1550             s
1551             s empty1
1552             s a5
1553             s empty1 v4 v6
1554             a2 empty1
1555             v1 v3
1556             END
1557              
1558             ok T [qw(b B)], <
1559             bB
1560             END
1561              
1562             ok T [qw(b b B B)], <
1563             bB
1564             bB
1565             END
1566              
1567             ok T [qw(b b v1 B B)], <
1568             bB
1569             bB
1570             v1
1571             END
1572              
1573             ok T [qw(b b v1 a2 v3 B B)], <
1574             bB
1575             bB
1576             a2
1577             v1 v3
1578             END
1579              
1580             ok T [qw(b b v1 a2 v3 d4 v5 B B)], <
1581             bB
1582             bB
1583             a2
1584             v1 d4
1585             v3 v5
1586             END
1587              
1588             ok T [qw(p1 v1)], <
1589             p1
1590             v1
1591             END
1592              
1593             ok T [qw(p2 p1 v1)], <
1594             p2
1595             p1
1596             v1
1597             END
1598              
1599             ok T [qw(v1 q1)], <
1600             q1
1601             v1
1602             END
1603              
1604             ok T [qw(v1 q1 q2)], <
1605             q2
1606             q1
1607             v1
1608             END
1609              
1610             ok T [qw(p2 p1 v1 q1 q2)], <
1611             q2
1612             q1
1613             p2
1614             p1
1615             v1
1616             END
1617              
1618             ok T [qw(p2 p1 v1 q1 q2 d3 p4 p3 v2 q3 q4)], <
1619             d3
1620             q2 q4
1621             q1 q3
1622             p2 p4
1623             p1 p3
1624             v1 v2
1625             END
1626              
1627             ok T [qw(p2 p1 v1 q1 q2 d3 p4 p3 v2 q3 q4 d4 p6 p5 v3 q5 q6 s)], <
1628             d3
1629             q2 d4
1630             q1 q4 q6
1631             p2 q3 q5
1632             p1 p4 p6
1633             v1 p3 p5
1634             v2 v3
1635             END
1636              
1637             ok T [qw(b s B)], <
1638             bB
1639             empty1
1640             END
1641              
1642             ok T [qw(b s s B)], <
1643             bB
1644             s
1645             empty1 empty1
1646             END
1647              
1648              
1649             if (1) {
1650              
1651             my @e = qw(b b p2 p1 v1 q1 q2 B d3 b p4 p3 v2 q3 q4 d4 p6 p5 v3 q5 q6 B s B s);
1652              
1653             ok T [@e], <
1654             bB
1655             d3
1656             bB bB
1657             q2 d4
1658             q1 q4 q6
1659             p2 q3 q5
1660             p1 p4 p6
1661             v1 p3 p5
1662             v2 v3
1663             END
1664              
1665             }
1666              
1667             ok T [qw(b b v1 B s B s)], <
1668             bB
1669             bB
1670             v1
1671             END
1672              
1673             ok T [qw(v1 q1 s)], <
1674             q1
1675             v1
1676             END
1677              
1678             ok T [qw(b b v1 q1 q2 B q3 q4 s B q5 q6 s)], <
1679             q6
1680             q5
1681             bB
1682             q4
1683             q3
1684             bB
1685             q2
1686             q1
1687             v1
1688             END
1689              
1690             ok T [qw(p1 p2 b v1 B)], <
1691             p1
1692             p2
1693             bB
1694             v1
1695             END
1696              
1697             ok T [qw(v1 d1 p1 p2 v2)], <
1698             d1
1699             v1 p1
1700             p2
1701             v2
1702             END
1703              
1704             ok T [qw(p1 p2 b p3 p4 b p5 p6 v1 d1 v2 q1 q2 B q3 q4 s B q5 q6 s)], <
1705             q6
1706             q5
1707             p1
1708             p2
1709             bB
1710             q4
1711             q3
1712             p3
1713             p4
1714             bB
1715             d1
1716             p5 q2
1717             p6 q1
1718             v1 v2
1719             END
1720              
1721             ok T [qw(p1 p2 b p3 p4 b p5 p6 v1 a1 v2 q1 q2 B q3 q4 s B q5 q6 s)], <
1722             q6
1723             q5
1724             p1
1725             p2
1726             bB
1727             q4
1728             q3
1729             p3
1730             p4
1731             bB
1732             a1
1733             p5 q2
1734             p6 q1
1735             v1 v2
1736             END
1737              
1738             ok T [qw(b v1 B d1 b v2 B)], <
1739             d1
1740             bB bB
1741             v1 v2
1742             END
1743              
1744             ok T [qw(b v1 B q1 q2 d1 b v2 B)], <
1745             d1
1746             q2 bB
1747             q1 v2
1748             bB
1749             v1
1750             END
1751              
1752             ok T [qw(v1 s)], <
1753             v1
1754             END
1755              
1756             ok T [qw(v1 s s)], <
1757             s
1758             v1 empty1
1759             END
1760              
1761             ok T [qw(v1 s b s B)], <
1762             s
1763             v1 bB
1764             empty1
1765             END
1766              
1767             ok T [qw(v1 s b b s s B B)], <
1768             s
1769             v1 bB
1770             bB
1771             s
1772             empty1 empty1
1773             END
1774              
1775             ok T [qw(b v1 s B s s)], <
1776             s
1777             bB empty1
1778             v1
1779             END
1780              
1781             ok T [qw(v1 a b1 b2 v2 B2 B1 s)], <
1782             a
1783             v1 b1B1
1784             b2B2
1785             v2
1786             END
1787              
1788             ok T [qw(v1 a1 b1 v2 a2 b2 v3 B2 B1 s)], <
1789             a1
1790             v1 b1B1
1791             a2
1792             v2 b2B2
1793             v3
1794             END
1795              
1796             ok T [qw(v1 a1 p1 v2)], <
1797             a1
1798             v1 p1
1799             v2
1800             END
1801              
1802             ok T [qw(b1 v1 q1 q2 B1)], <
1803             b1B1
1804             q2
1805             q1
1806             v1
1807             END
1808              
1809             ok T [qw(b1 v1 q1 q2 s B1)], <
1810             b1B1
1811             q2
1812             q1
1813             v1
1814             END
1815              
1816             ok T [qw(p1 b1 v1 B1 q1)], <
1817             q1
1818             p1
1819             b1B1
1820             v1
1821             END
1822              
1823             ok T [qw(b1 v1 B1 a1 v2)], <
1824             a1
1825             b1B1 v2
1826             v1
1827             END
1828              
1829             ok T [qw(v1 q1 a1 v2)], <
1830             a1
1831             q1 v2
1832             v1
1833             END
1834              
1835             ok T [qw(s1 p1 v1)], <
1836             s1
1837             empty2 p1
1838             v1
1839             END
1840              
1841             ok E <
1842             a
1843             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'assignment operator': a.
1844             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'assignment operator': a.
1845             END
1846              
1847             ok E <
1848             B
1849             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'closing parenthesis': B.
1850             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'closing parenthesis': B.
1851             END
1852              
1853             ok E <
1854             d1
1855             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'dyadic operator': d1.
1856             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'dyadic operator': d1.
1857             END
1858              
1859             ok E <
1860             p1
1861             Expected: 'opening parenthesis', 'prefix operator' or 'variable' after final 'prefix operator': p1.
1862             Expected: 'opening parenthesis', 'prefix operator' or 'variable' after final 'prefix operator': p1.
1863             END
1864              
1865             ok E <
1866             q1
1867             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'suffix operator': q1.
1868             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'suffix operator': q1.
1869             END
1870              
1871             ok E <
1872             s
1873              
1874              
1875             END
1876              
1877             ok E <
1878             v1
1879              
1880              
1881             END
1882              
1883             ok E <
1884             b v1
1885             Incomplete expression. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1886             No closing parenthesis matching b at position 1.
1887             END
1888              
1889             ok E <
1890             b v1 B B
1891             Unexpected 'closing parenthesis': B following 'closing parenthesis': B at position 4. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1892             Unexpected closing parenthesis B at position 4. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1893             END
1894              
1895             ok E <
1896             v1 d1 d2 v2
1897             Unexpected 'dyadic operator': d2 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1898             Unexpected 'dyadic operator': d2 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1899             END
1900              
1901             ok E <
1902             v1 p1
1903             Unexpected 'prefix operator': p1 following term ending at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1904             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1905             END
1906              
1907             ok E <
1908             b1 B1 v1
1909             Unexpected 'variable': v1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1910             Unexpected 'variable': v1 following 'closing parenthesis': B1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1911             END
1912              
1913             ok E <
1914             b1 B1 p1 v1
1915             Unexpected 'prefix operator': p1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1916             Unexpected 'prefix operator': p1 following 'closing parenthesis': B1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1917             END
1918              
1919             if (1) #TsyntaxError
1920             {eval {syntaxError(qw(v1 p1))};
1921             ok -1 < index $@, <
1922             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2.
1923             Expected: 'assignment operator', 'closing parenthesis',
1924             'dyadic operator', 'semi-colon' or 'suffix operator'.
1925             END
1926             }
1927              
1928             ok E <
1929             v1 q1 v2
1930             Unexpected 'variable': v2 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1931             Unexpected 'variable': v2 following 'suffix operator': q1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1932             END
1933              
1934             ok E <
1935             b1 v2 a2 B1
1936             Unexpected 'closing parenthesis': B1 following 'assignment operator': a2 at position 4. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1937             Unexpected 'closing parenthesis': B1 following 'assignment operator': a2 at position 4. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1938             END
1939              
1940             ok E <
1941             b1 v2 d2 B1
1942             Unexpected 'closing parenthesis': B1 following 'dyadic operator': d2 at position 4. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1943             Unexpected 'closing parenthesis': B1 following 'dyadic operator': d2 at position 4. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1944             END
1945              
1946             ok E <
1947             b1 p1 B1
1948             Unexpected 'closing parenthesis': B1 following 'prefix operator': p1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1949             Unexpected 'closing parenthesis': B1 following 'prefix operator': p1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1950             END
1951              
1952             ok E <
1953             v1 v2
1954             Unexpected 'variable': v2 following term ending at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1955             Unexpected 'variable': v2 following 'variable': v1 at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1956             END
1957              
1958             ok E <
1959             b1 B1 b2
1960             Unexpected 'opening parenthesis': b2 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1961             No closing parenthesis matching b2 at position 3.
1962             END
1963              
1964             ok E <
1965             v1 a1 a2
1966             Unexpected 'assignment operator': a2 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1967             Unexpected 'assignment operator': a2 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1968             END
1969              
1970             ok E <
1971             v1 a1 d2
1972             Unexpected 'dyadic operator': d2 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1973             Unexpected 'dyadic operator': d2 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1974             END
1975              
1976             ok E <
1977             v1 a1 q1
1978             Unexpected 'suffix operator': q1 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1979             Unexpected 'suffix operator': q1 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1980             END
1981              
1982             ok E <
1983             v1 a1 s1
1984             Unexpected 'semi-colon': s1 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1985             Unexpected 'semi-colon': s1 following 'assignment operator': a1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1986             END
1987              
1988             ok E <
1989             b1 a1
1990             Unexpected 'assignment operator': a1 following 'opening parenthesis': b1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
1991             No closing parenthesis matching b1 at position 1.
1992             END
1993              
1994             ok E <
1995             b1 d1
1996             Unexpected 'dyadic operator': d1 following 'opening parenthesis': b1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
1997             No closing parenthesis matching b1 at position 1.
1998             END
1999              
2000             ok E <
2001             b1 q1
2002             Unexpected 'suffix operator': q1 following 'opening parenthesis': b1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2003             No closing parenthesis matching b1 at position 1.
2004             END
2005              
2006             ok E <
2007             v1 d1 a1
2008             Unexpected 'assignment operator': a1 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2009             Unexpected 'assignment operator': a1 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2010             END
2011              
2012             ok E <
2013             v1 d1 q1
2014             Unexpected 'suffix operator': q1 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2015             Unexpected 'suffix operator': q1 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2016             END
2017              
2018             ok E <
2019             v1 d1 s1
2020             Unexpected 'semi-colon': s1 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2021             Unexpected 'semi-colon': s1 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2022             END
2023              
2024             ok E <
2025             p1 a1
2026             Unexpected 'assignment operator': a1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2027             Unexpected 'assignment operator': a1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2028             END
2029              
2030             ok E <
2031             p1 d1
2032             Unexpected 'dyadic operator': d1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2033             Unexpected 'dyadic operator': d1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2034             END
2035              
2036             ok E <
2037             p1 q1
2038             Unexpected 'suffix operator': q1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2039             Unexpected 'suffix operator': q1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2040             END
2041              
2042             ok E <
2043             p1 s1
2044             Unexpected 'semi-colon': s1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2045             Unexpected 'semi-colon': s1 following 'prefix operator': p1 at position 2. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
2046             END
2047              
2048             ok E <
2049             v1 q1 b1
2050             Unexpected 'opening parenthesis': b1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
2051             No closing parenthesis matching b1 at position 3.
2052             END
2053              
2054             ok E <
2055             v1 q1 p1
2056             Unexpected 'prefix operator': p1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
2057             Unexpected 'prefix operator': p1 following 'suffix operator': q1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
2058             END
2059              
2060             ok E <
2061             s1 a1
2062             Unexpected 'assignment operator': a1 following 'semi-colon': s1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2063             Unexpected 'assignment operator': a1 following 'semi-colon': s1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2064             END
2065              
2066             ok E <
2067             s1 d1
2068             Unexpected 'dyadic operator': d1 following 'semi-colon': s1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2069             Unexpected 'dyadic operator': d1 following 'semi-colon': s1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2070             END
2071              
2072             ok E <
2073             s1 q1
2074             Unexpected 'suffix operator': q1 following 'semi-colon': s1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2075             Unexpected 'suffix operator': q1 following 'semi-colon': s1 at position 2. Expected: 'closing parenthesis', 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable'.
2076             END
2077              
2078             ok E <
2079             v1 b1
2080             Unexpected 'opening parenthesis': b1 following term ending at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
2081             No closing parenthesis matching b1 at position 2.
2082             END
2083              
2084             if (1) { #Tparse
2085             ok T [qw(v_sub a_is v_array as v1 d_== v2 a_then v3 d_plus v4 a_else v5 d_== v6 a_then v7 d_minus v8 a_else v9 d_times b v10 a_+ v11 B)], <
2086             is
2087             sub as
2088             array then
2089             == else
2090             v1 v2 plus then
2091             v3 v4 == else
2092             v5 v6 minus times
2093             v7 v8 v9 bB
2094             +
2095             v10 v11
2096             END
2097             }
2098              
2099             if (1) { #TvalidPair
2100             ok validPair('B', 'd');
2101             ok validPair('b', 'B');
2102             ok validPair('v', 'a');
2103             ok !validPair('v', 'v');
2104             }
2105              
2106             is_deeply LexicalStructure, #TLexicalStructure
2107             bless({
2108             codes => bless({
2109             a => bless({ letter => "a", name => "assignment operator", short=> qq(assign), next => "bpv" }, "Tree::Term::LexicalCode"),
2110             b => bless({ letter => "b", name => "opening parenthesis", short=> qq(OpenBracket), next => "bBpsv" }, "Tree::Term::LexicalCode"),
2111             B => bless({ letter => "B", name => "closing parenthesis", short=> qq(CloseBracket), next => "aBdqs" }, "Tree::Term::LexicalCode"),
2112             d => bless({ letter => "d", name => "dyadic operator", short=> qq(dyad), next => "bpv" }, "Tree::Term::LexicalCode"),
2113             p => bless({ letter => "p", name => "prefix operator", short=> qq(prefix), next => "bpv" }, "Tree::Term::LexicalCode"),
2114             q => bless({ letter => "q", name => "suffix operator", short=> qq(suffix), next => "aBdqs" }, "Tree::Term::LexicalCode"),
2115             s => bless({ letter => "s", name => "semi-colon", short=> qq(semiColon), next => "bBpsv" }, "Tree::Term::LexicalCode"),
2116             t => bless({ letter => "t", name => "term", short=> qq(term), next => "aBdqs" }, "Tree::Term::LexicalCode"),
2117             v => bless({ letter => "v", name => "variable", short=> qq(variable), next => "aBdqs" }, "Tree::Term::LexicalCode"),
2118             }, "Tree::Term::Codes"),
2119             first => "bpsv",
2120             last => "Bqsv",
2121             }, "Tree::Term::LexicalStructure");
2122              
2123             is_deeply LexicalStructure->first, join '', sort keys %first; # Prove first and last
2124             is_deeply LexicalStructure->last, join '', sort keys %last;
2125              
2126             if (1) { # Prove $LexicalCodes
2127             my %C = LexicalStructure->codes->%*;
2128             my %N = map {$_ => $C{$_}->next} keys %C;
2129             for my $b(sort keys %N) {
2130             for my $a(sort keys %N) {
2131             next if $a eq 't' or $b eq 't' ;
2132             ok !$follows{$b}{$a} || index($N{$a}, $b) > -1;
2133             ok $follows{$b}{$a} || index($N{$a}, $b) == -1;
2134             next if $a =~ m([adp]) and $b eq 'B' ; # The first cannot be followed by the second
2135             next if $a =~ m([abdps]) and $b eq 'a' ;
2136             next if $a =~ m([Bqv]) and $b eq 'b' ;
2137             next if $a =~ m([abpsd]) and $b eq 'd' ;
2138             next if $a =~ m([aBqv]) and $b eq 'p' ;
2139             next if $a =~ m([abdps]) and $b eq 'q' ;
2140             next if $a =~ m([adp]) and $b eq 's' ;
2141             next if $a =~ m([aBdpqv]) and $b eq 'v' ;
2142             next if $follows{$b}{$a};
2143             confess sprintf("Failed to observe %20s before: %20s\n", $a, $b); # An unobserved combination
2144             }}
2145              
2146             if (0) { # Print table of allowed and disallowed combinations
2147             my @l = grep {!m/t/} sort keys %N;
2148             my @t = [' ', @l];
2149             for my $b(@l)
2150             {my @r;
2151             for my $a(@l)
2152             {push @r, $follows{$a}{$b} ? 'X' : $tested{$a}{$b} ? '-' : ' ';
2153             }
2154             push @t, [$b, @r];
2155             }
2156             say STDERR "Column can follow row";
2157             say STDERR formatTableBasic(\@t);
2158             }
2159             }
2160              
2161             lll "Finished in", sprintf("%7.4f", time - $startTime), "seconds";