File Coverage

blib/lib/Tree/Term.pm
Criterion Covered Total %
statement 262 267 98.1
branch 85 108 78.7
condition 8 10 80.0
subroutine 52 53 98.1
pod 25 40 62.5
total 432 478 90.3


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   753 use v5.26;
  1         10  
9             our $VERSION = 20210827; # Version
10 1     1   5 use warnings FATAL => qw(all);
  1         2  
  1         32  
11 1     1   5 use strict;
  1         2  
  1         49  
12 1     1   7 use Carp qw(confess cluck);
  1         2  
  1         109  
13 1     1   542 use Data::Dump qw(dump ddx pp);
  1         7867  
  1         72  
14 1     1   3992 use Data::Table::Text qw(:all);
  1         142429  
  1         1846  
15 1     1   11 use feature qw(say state current_sub);
  1         2  
  1         2302  
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 496 {my ($count) = @_; # Number of terms
28              
29 309 50       601 @$stack >= $count or confess "Stack underflow";
30              
31 309         682 my ($operator, @operands) = splice @$stack, -$count; # Remove lexical items from stack
32              
33 309 100       1104 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         14012 $_->up = $t for grep {ref $_} @operands; # Link to parent if possible
  236         4212  
40              
41 309         2597 push @$stack, $t; # Save newly created term on the stack
42             }
43              
44             sub LexicalCode($$$$) #P Lexical code definition
45 9     9 1 838 {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         21 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 17 {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 5809 {my ($s) = @_; # Term to test
79 3796 100       9885 return 't' if ref $s; # Term on top of stack
80 3580         32407 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 352 {my ($e) = @_; # Element to expand
85 195         375 my $x = $LexicalCodes->{type $e}->name; # Expansion
86 195         1115 "'$x': $e"
87             }
88              
89             sub expandCodes($) #P Expand a string of codes
90 64     64 1 277 {my ($e) = @_; # Codes to expand
91 64         198 my @c = map {qq('$_')} sort map {$LexicalCodes->{$_}->name} split //, $e; # Codes for next possible items
  252         842  
  252         4365  
92 64         160 my $c = pop @c;
93 64         150 my $t = join ', ', @c;
94 64         168 "$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         109 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   7 {for my $t(qw(abdps bst t))
201 3         7 {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         19 $c =~ s(XXXX) ($t)gs;
212 3 50   133 0 605 eval $c; $@ and confess "$@\n";
  3 100   132 0 27  
  133 100   116 0 386  
  133 100       376  
  124         330  
  124         336  
  9         101  
  132         408  
  132         375  
  125         360  
  125         340  
  7         53  
  116         400  
  116         350  
  101         263  
  101         253  
  15         93  
213             }
214              
215 1         3 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         47 $c =~ s(XXXX) ($t)gs;
223 9 50   144 0 874 eval $c; $@ and confess "$@\n";
  9 100   49 0 3030  
  144 50   218 0 328  
  144 100   86 0 619  
  49 50   0 0 120  
  49 0   11 0 256  
  218 50   184 0 495  
  218 100   151 0 1027  
  86 100   82 0 215  
  86 50       596  
  0         0  
  0         0  
  11         28  
  11         76  
  184         459  
  184         1060  
  151         362  
  151         680  
  82         198  
  82         407  
224             }
225             }
226              
227             sub test_t($) #P Check that we have a term
228 321     321 1 498 {my ($item) = @_; # Item to test
229 321         739 ref $item
230             }
231              
232             sub reduce($) #P Reduce the stack at the specified priority
233 322     322 1 535 {my ($priority) = @_; # Priority
234             #lll "Reduce at $priority: ", scalar(@s), "\n", dump([@s]);
235              
236 322 100       635 if (@$stack >= 3) # term infix-operator term
237 163         353 {my ($l, $d, $r) = ($$stack[-3], $$stack[-2], $$stack[-1]); # Left infix right
238              
239 163 100       282 if (test_t($l)) # Parse out infix operator expression
240 71 100       118 {if (test_t($r))
241 60 100       1110 {if ($priority == 1 ? test_ads($d) : test_d($d)) # Amount of reduction
    100          
242 53         179 {pop @$stack for 1..3;
243 53         102 push @$stack, $d, $l, $r;
244 53         114 new 3;
245 53         232 return 1;
246             }
247             }
248             }
249              
250 110 100       1933 if (test_b($l)) # Parse parenthesized term keeping the opening parenthesis
251 64 100       1063 {if (test_B($r))
252 40 100       83 {if (test_t($d))
253 39         128 {pop @$stack for 1..3;
254 39         95 push @$stack, "$l$r", $d;
255 39         84 new 2;
256 39         139 return 1;
257             }
258             }
259             }
260             }
261              
262 230 100       465 if (@$stack >= 2) # Convert an empty pair of parentheses to an empty term
263 108         228 {my ($l, $r) = ($$stack[-2], $$stack[-1]);
264 108 100       1860 if (test_b($l)) # Empty pair of parentheses
265 67 100       1124 {if (test_B($r))
266 5         22 {pop @$stack for 1..2;
267 5         13 push @$stack, "$l$r";
268 5         13 new 1;
269 5         20 return 1;
270             }
271             }
272 103 100       1691 if (test_s($l)) # Semi-colon, close implies remove unneeded semi
273 13 100       221 {if (test_B($r))
274 11         44 {pop @$stack for 1..2;
275 11         25 push @$stack, $r;
276 11         39 return 1;
277             }
278             }
279 92 100       1586 if (test_p($l)) # Prefix, term
280 11 50       24 {if (test_t($r))
281 11         26 {new 2;
282 11         36 return 1;
283             }
284             }
285             }
286              
287             0 # No move made
288 203         492 }
289              
290             sub reduce1() #P Reduce the stack at priority 1
291 291     291 1 516 {reduce 1;
292             }
293              
294             sub reduce2() #P Reduce the stack at priority 2
295 31     31 1 82 {reduce 2;
296             }
297              
298             sub pushElement() #P Push an element
299 306     306 1 662 {push @$stack, $$expression[$position];
300             }
301              
302             sub accept_a() #P Assign
303 32     32 1 645 {check_t;
304 27         65 1 while reduce2;
305 27         55 pushElement;
306             }
307              
308             sub accept_b() #P Open
309 27     27 1 510 {check_abdps;
310 24         48 pushElement;
311             }
312              
313             sub accept_B() #P Closing parenthesis
314 48     48 1 836 {check_bst;
315 45         92 1 while reduce1;
316 45         104 pushElement;
317 45         75 1 while reduce1;
318 45         785 check_bst;
319             }
320              
321             sub accept_d() #P Infix but not assign or semi-colon
322 28     28 1 509 {check_t;
323 23         46 pushElement;
324             }
325              
326             sub accept_p() #P Prefix
327 35     35 1 712 {check_abdps;
328 32         67 pushElement;
329             }
330              
331             sub accept_q() #P Post fix
332 56     56 1 997 {check_t;
333 51         91 my $p = pop @$stack;
334 51         115 pushElement;
335 51         69 push @$stack, $p;
336 51         92 new 2;
337             }
338              
339             sub accept_s() #P Semi colon
340 39     39 1 690 {check_bst;
341 36 100       86 if (!test_t($$stack[-1])) # Insert an empty element between two consecutive semicolons
342 12         25 {push @$stack, 'empty1';
343 12         22 new 1;
344             }
345 36         79 1 while reduce1;
346 36         66 pushElement;
347             }
348              
349             sub accept_v() #P Variable
350 71     71 1 1235 {check_abdps;
351 68         157 pushElement;
352 68         169 new 1;
353 68   100     1489 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 186 {if (@$expression)
369 86         153 {my $e = $$expression[$position = 0];
370              
371 86         169 my $E = expandElement $e;
372 86 100       1542 die <
373             Expression must start with 'opening parenthesis', 'prefix
374             operator', 'semi-colon' or 'variable', not $E.
375             END
376 82 100       1441 if (test_v($e)) # Single variable
377 34         80 {push @$stack, $e;
378 34         82 new 1;
379             }
380             else
381 48 100       850 {if (test_s($e)) # Semi
382 7         31 {push @$stack, 'empty2';
383 7         18 new 1;
384             }
385 48         130 push @$stack, $e;
386             }
387             }
388             else # Empty expression
389 0         0 {return undef;
390             }
391              
392 82         259 for(1..$#$expression) # Each input element
393 336         976 {$$Accept{substr($$expression[$position = $_], 0, 1)}(); # Dispatch the action associated with the lexical item
394             }
395              
396 51 100       126 if (index($last, type $$expression[-1]) == -1) # Check for incomplete expression
397 1         5 {my $C = expandElement $$expression[-1];
398 1         6 my $E = expected $$expression[-1];
399 1         19 die <
400             $E after final $C.
401             END
402             }
403              
404 50   100     233 pop @$stack while @$stack > 1 and $$stack[-1] =~ m(s); # Remove any trailing semi colons
405 50         108 1 while reduce1; # Final reductions
406              
407 50 100       102 if (@$stack != 1) # Incomplete expression
408 1         4 {my $E = expected $$expression[-1];
409 1         6 die "Incomplete expression. $E.\n";
410             }
411              
412 49         104 $first{type $$expression[ 0]}++; # Capture valid first and last lexical elements
413 49         104 $last {type $$expression[-1]}++;
414              
415 49         107 $$stack[0] # The resulting parse tree
416             } # parseExpression
417              
418             sub parse(@) # Parse an expression.
419 86     86 1 211 {my (@expression) = @_; # Expression to parse
420 86         136 my $s = $stack;
421 86         160 $stack = []; # Clear the current stack - the things we do to speed things up.
422 86         120 my $x = $expression;
423 86         143 $expression = \@expression; # Clear the current expression
424 86         117 my $p = $position;
425 86         117 $position = 0; # Clear the current parse position
426              
427 86         135 my $e = eval {parseExpression};
  86         169  
428 86         151 my $r = $@; # Save any error message
429 86         169 $stack = $s; $expression = $x; $position = $p; # Restore the stack and expression being parsed
  86         139  
  86         104  
430 86 100       922 die $r if $r; # Die again if we died the last time
431              
432 49         380 $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         10 my $a = type $A;
440 4         10 my $b = type $B;
441 4 50       15 if (my $l = $$LexicalCodes{$a})
442 4 100       74 {return 1 if (index $l->next, $b) > -1;
443             }
444             undef
445 1         9 }
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 952 {my ($term) = @_; # Term
451 558         667 my $d = 0;
452 558         1007 for(my $t = $term; $t; $t = $t->up) {++$d}
  2216         38715  
453 558         3546 $d
454             }
455              
456             sub listTerms($) #P List the terms in an expression in post order
457 47     47 1 71 {my ($expression) = @_; # Root term
458 47         64 my @t; # Terms
459              
460             sub # Recurse through terms
461 279     279   441 {my ($e) = @_; # Term
462 279         4945 my $o = $e->operands;
463 279 50       1204 return unless $e; # Operator
464 279 100       573 if (my @o = $o ? grep {ref $_} @$o : ()) # Operands
  232 100       595  
465 179         333 {my ($p, @p) = @o;
466 179         841 __SUB__->($p); # First operand
467 179         268 push @t, $e; # Operator
468 179         346 __SUB__->($_) for @p; # Second and subsequent operands
469             }
470             else # No operands
471 100         219 {push @t, $e; # Operator
472             }
473 47         285 } ->($expression);
474              
475             @t
476 47         393 }
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   10 use Exporter qw(import);
  1         2  
  1         55  
531              
532 1     1   7 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         1  
  1         442  
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 8 {my $p = __PACKAGE__;
1449 1         10 binmode($_, ":utf8") for *STDOUT, *STDERR;
1450 1 50       84 return if eval "eof(${p}::DATA)";
1451 1         86 my $s = eval "join('', <${p}::DATA>)";
1452 1 50       11 $@ and die $@;
1453 1 50 50 1 0 7 eval $s;
  1 50 50 1 0 3  
  1 0   39   8  
  1 50   46   799  
  1 50       66431  
  1 50       12  
  1         96  
  39         109  
  39         193  
  39         178  
  39         68  
  39         62  
  39         117  
  39         216  
  39         92  
  39         64  
  39         325  
  39         198  
  39         101  
  39         78  
  0         0  
  0         0  
  39         198  
  46         126  
  46         501  
  46         176  
  46         148  
  46         691  
  46         159  
  46         349  
1454 1 50       545 $@ and die $@;
1455 1         140 1
1456             }
1457              
1458             test unless caller;
1459              
1460             1;
1461             # podDocumentation
1462             __DATA__