File Coverage

blib/lib/Tree/Term.pm
Criterion Covered Total %
statement 260 265 98.1
branch 84 106 79.2
condition 8 10 80.0
subroutine 52 53 98.1
pod 25 40 62.5
total 429 474 90.5


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   741 use v5.26;
  1         10  
9             our $VERSION = 20210727; # Version
10 1     1   6 use warnings FATAL => qw(all);
  1         2  
  1         32  
11 1     1   5 use strict;
  1         2  
  1         33  
12 1     1   6 use Carp qw(confess cluck);
  1         2  
  1         108  
13 1     1   548 use Data::Dump qw(dump ddx pp);
  1         7832  
  1         72  
14 1     1   3946 use Data::Table::Text qw(:all);
  1         144030  
  1         1967  
15 1     1   14 use feature qw(say state current_sub);
  1         3  
  1         2266  
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 270     270 1 450 {my ($count) = @_; # Number of terms
28              
29 270         629 my ($operator, @operands) = splice @$stack, -$count; # Remove lexical items from stack
30              
31 270 100       929 my $t = genHash(__PACKAGE__, # Description of a term in the expression.
32             operands => @operands ? [@operands] : undef, # Operands to which the operator will be applied.
33             operator => $operator, # Operator to be applied to one or more operands.
34             up => undef, # Parent term if this is a sub term.
35             );
36              
37 270         12794 $_->up = $t for grep {ref $_} @operands; # Link to parent if possible
  197         3094  
38              
39 270         2385 push @$stack, $t; # Save newly created term on the stack
40             }
41              
42             sub LexicalCode($$$$) #P Lexical code definition
43 9     9 1 867 {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
44 9         20 genHash(q(Tree::Term::LexicalCode), # Lexical item codes.
45             letter => $letter, # Letter code used to refer to the lexical item.
46             next => $next, # Letters codes of items that can follow this lexical item.
47             name => $name, # Descriptive name of lexical item.
48             short => $short, # Short name of lexical item.
49             );
50             }
51              
52             my $LexicalCodes = genHash(q(Tree::Term::Codes), # Lexical item codes.
53             a => LexicalCode('a', 'bpv', q(assignment operator), qq(assign)), # Infix operator with priority 2 binding right to left typically used in an assignment.
54             b => LexicalCode('b', 'bBpsv', q(opening parenthesis), qq(OpenBracket)), # Opening parenthesis.
55             B => LexicalCode('B', 'aBdqs', q(closing parenthesis), qq(CloseBracket)), # Closing parenthesis.
56             d => LexicalCode('d', 'bpv', q(dyadic operator), qq(dyad)), # Infix operator with priority 3 binding left to right typically used in arithmetic.
57             p => LexicalCode('p', 'bpv', q(prefix operator), qq(prefix)), # Monadic prefix operator.
58             q => LexicalCode('q', 'aBdqs', q(suffix operator), qq(suffix)), # Monadic suffix operator.
59             s => LexicalCode('s', 'bBpsv', q(semi-colon), qq(semiColon)), # Infix operator with priority 1 binding left to right typically used to separate statements.
60             t => LexicalCode('t', 'aBdqs', q(term), qq(term)), # A term in the expression.
61             v => LexicalCode('v', 'aBdqs', q(variable), qq(variable)), # A variable in the expression.
62             );
63              
64             my $first = 'bpsv'; # First element
65             my $last = 'Bqsv'; # Last element
66              
67             sub LexicalStructure() # Return the lexical codes and their relationships in a data structure so this information can be used in other contexts.
68 4     4 1 18 {genHash(q(Tree::Term::LexicalStructure), # Lexical item codes.
69             codes => $LexicalCodes, # Code describing each lexical item
70             first => $first, # Lexical items we can start with
71             last => $last, # Lexical items we can end with
72             );
73             }
74              
75             sub type($) #P Type of term
76 3699     3699 1 5749 {my ($s) = @_; # Term to test
77 3699 100       9764 return 't' if ref $s; # Term on top of stack
78 3483         32877 substr($s, 0, 1); # Something other than a term defines its type by its first letter
79             }
80              
81             sub expandElement($) #P Describe a lexical element
82 195     195 1 364 {my ($e) = @_; # Element to expand
83 195         346 my $x = $LexicalCodes->{type $e}->name; # Expansion
84 195         1114 "'$x': $e"
85             }
86              
87             sub expandCodes($) #P Expand a string of codes
88 64     64 1 271 {my ($e) = @_; # Codes to expand
89 64         200 my @c = map {qq('$_')} sort map {$LexicalCodes->{$_}->name} split //, $e; # Codes for next possible items
  252         794  
  252         4470  
90 64         146 my $c = pop @c;
91 64         163 my $t = join ', ', @c;
92 64         176 "$t or $c"
93             }
94              
95             sub expected($) #P String of next possible lexical items
96 60     60 1 108 {my ($s) = @_; # Lexical item
97 60         101 my $e = expandCodes $LexicalCodes->{type $s}->next; # Codes for next possible items
98 60         126 "Expected: $e"
99             }
100              
101             sub unexpected($$$) #P Complain about an unexpected element
102             {my ($element, $unexpected, $position) = @_; # Last good element, unexpected element, position
103             my $j = $position + 1;
104             my $E = expandElement $unexpected;
105             my $X = expected $element;
106              
107             my sub de($) # Extract an error message and die
108             {my ($message) = @_; # Message
109             $message =~ s(\n) ( )gs;
110             die "$message\n";
111             }
112              
113             de <
114             Unexpected $E following term ending at position $j.
115             $X.
116             END
117             my $S = expandElement $element;
118             de <
119             Unexpected $E following $S at position $j.
120             $X.
121             END
122             }
123              
124             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.
125             {my (@expression) = @_; # Expression to parse
126             my @e = @_;
127              
128             return '' unless @e; # An empty string is valid
129              
130             my sub test($$$) # Test a transition
131             {my ($current, $following, $position) = @_; # Current element, following element, position
132             my $n = $LexicalCodes->{type $current}->next; # Elements expected next
133             return if index($n, type $following) > -1; # Transition allowed
134             unexpected $current, $following, $position - 1; # Complain about the unexpected element
135             }
136              
137             my sub testFirst # Test first transition
138             {return if index($first, type $e[0]) > -1; # Transition allowed
139             my $E = expandElement $e[0];
140             my $C = expandCodes $first;
141             die <
142             Expression must start with $C, not $E.
143             END
144             }
145              
146             my sub testLast($$) # Test last transition
147             {my ($current, $position) = @_; # Current element, position
148             return if index($last, type $current) > -1; # Transition allowed
149             my $C = expandElement $current;
150             my $E = expected $current;
151             die <
152             $E after final $C.
153             END
154             }
155              
156             if (1) # Test parentheses
157             {my @b;
158             for my $i(keys @e) # Each element
159             {my $e = $e[$i];
160             if (type($e) eq 'b') # Open
161             {push @b, [$i, $e];
162             }
163             elsif (type($e) eq 'B') # Close
164             {if (@b > 0)
165             {my ($h, $a) = pop(@b)->@*;
166             my $j = $i + 1;
167             my $g = $h + 1;
168             die <
169             Parenthesis mismatch between $a at position $g and $e at position $j.
170             END
171             }
172             else # No corresponding open
173             {my $j = $i + 1;
174             my $E = $i ? expected($e[$i-1]) : testFirst; # What we might have had instead
175             die <
176             Unexpected closing parenthesis $e at position $j. $E.
177             END
178             }
179             }
180             }
181             if (@b > 0) # Closing parentheses at end
182             {my ($h, $a) = pop(@b)->@*;
183             my $g = $h + 1;
184             die <
185             No closing parenthesis matching $a at position $g.
186             END
187             }
188             }
189              
190             if (1) # Test transitions
191             {testFirst $e[0]; # First transition
192             test $e[$_-1], $e[$_], $_+1 for 1..$#e; # Each element beyond the first
193             testLast $e[-1], scalar @e; # Final transition
194             }
195             }
196              
197             BEGIN # Generate recognition routines.
198 1     1   6 {for my $t(qw(abdps bst t))
199 3         6 {my $c = <<'END';
200             sub check_XXXX() #P Check that the top of the stack has one of XXXX
201             {$tested {type $$expression[$position]}{type $$expression[$position-1]}++; # Check that one lexical item has been seen to follow after another
202             if (index("XXXX", type($$stack[-1])) > -1) # Check type allowed
203             {$follows{type $$expression[$position]}{type $$expression[$position-1]}++; # Shows that one lexical item can possibly follow after another in some circumstances
204             return 1; # Type allowed
205             }
206             unexpected $$stack[-1], $$expression[$position], $position; # Complain about an unexpected type
207             }
208             END
209 3         22 $c =~ s(XXXX) ($t)gs;
210 3 50   133 0 605 eval $c; $@ and confess "$@\n";
  3 100   132 0 27  
  133 100   116 0 389  
  133 100       400  
  124         407  
  124         318  
  9         100  
  132         427  
  132         408  
  125         328  
  125         349  
  7         50  
  116         371  
  116         352  
  101         285  
  101         261  
  15         93  
211             }
212              
213 1         4 for my $t(qw(ads b B bpsv bst d p s v)) # Test various sets of items
214 9         21 {my $c = <<'END';
215             sub test_XXXX($) #P Check that we have XXXX
216             {my ($item) = @_; # Item to test
217             !ref($item) and index('XXXX', substr($item, 0, 1)) > -1
218             }
219             END
220 9         51 $c =~ s(XXXX) ($t)gs;
221 9 50   144 0 956 eval $c; $@ and confess "$@\n";
  9 100   49 0 2919  
  144 50   218 0 329  
  144 100   86 0 616  
  49 50   0 0 120  
  49 0   11 0 258  
  218 50   184 0 513  
  218 100   151 0 983  
  86 100   82 0 214  
  86 50       614  
  0         0  
  0         0  
  11         26  
  11         63  
  184         448  
  184         989  
  151         358  
  151         716  
  82         205  
  82         395  
222             }
223             }
224              
225             sub test_t($) #P Check that we have a term
226 321     321 1 482 {my ($item) = @_; # Item to test
227 321         697 ref $item
228             }
229              
230             sub reduce($) #P Reduce the stack at the specified priority
231 322     322 1 523 {my ($priority) = @_; # Priority
232             #lll "Reduce at $priority: ", scalar(@s), "\n", dump([@s]);
233              
234 322 100       618 if (@$stack >= 3) # term infix-operator term
235 163         340 {my ($l, $d, $r) = ($$stack[-3], $$stack[-2], $$stack[-1]); # Left infix right
236              
237 163 100       281 if (test_t($l)) # Parse out infix operator expression
238 71 100       108 {if (test_t($r))
239 60 100       1151 {if ($priority == 1 ? test_ads($d) : test_d($d)) # Amount of reduction
    100          
240 53         172 {pop @$stack for 1..3;
241 53         102 push @$stack, $d, $l, $r;
242 53         122 new 3;
243 53         197 return 1;
244             }
245             }
246             }
247              
248 110 100       1934 if (test_b($l)) # Parse parenthesized term
249 64 100       1143 {if (test_B($r))
250 40 100       81 {if (test_t($d))
251 39         126 {pop @$stack for 1..3;
252 39         62 push @$stack, $d;
253 39         127 return 1;
254             }
255             }
256             }
257             }
258              
259 230 100       441 if (@$stack >= 2) # Convert an empty pair of parentheses to an empty term
260 108         219 {my ($l, $r) = ($$stack[-2], $$stack[-1]);
261 108 100       1857 if (test_b($l)) # Empty pair of parentheses
262 67 100       1152 {if (test_B($r))
263 5         19 {pop @$stack for 1..2;
264 5         13 push @$stack, 'empty1';
265 5         18 new 1;
266 5         22 return 1;
267             }
268             }
269 103 100       1839 if (test_s($l)) # Semi-colon, close implies remove unneeded semi
270 13 100       221 {if (test_B($r))
271 11         41 {pop @$stack for 1..2;
272 11         19 push @$stack, $r;
273 11         41 return 1;
274             }
275             }
276 92 100       1606 if (test_p($l)) # Prefix, term
277 11 50       26 {if (test_t($r))
278 11         28 {new 2;
279 11         46 return 1;
280             }
281             }
282             }
283              
284             0 # No move made
285 203         516 }
286              
287             sub reduce1() #P Reduce the stack at priority 1
288 291     291 1 516 {reduce 1;
289             }
290              
291             sub reduce2() #P Reduce the stack at priority 2
292 31     31 1 65 {reduce 2;
293             }
294              
295             sub pushElement() #P Push an element
296 306     306 1 697 {push @$stack, $$expression[$position];
297             }
298              
299             sub accept_a() #P Assign
300 32     32 1 619 {check_t;
301 27         71 1 while reduce2;
302 27         58 pushElement;
303             }
304              
305             sub accept_b() #P Open
306 27     27 1 486 {check_abdps;
307 24         47 pushElement;
308             }
309              
310             sub accept_B() #P Closing parenthesis
311 48     48 1 866 {check_bst;
312 45         100 1 while reduce1;
313 45         113 pushElement;
314 45         83 1 while reduce1;
315 45         793 check_bst;
316             }
317              
318             sub accept_d() #P Infix but not assign or semi-colon
319 28     28 1 511 {check_t;
320 23         49 pushElement;
321             }
322              
323             sub accept_p() #P Prefix
324 35     35 1 634 {check_abdps;
325 32         65 pushElement;
326             }
327              
328             sub accept_q() #P Post fix
329 56     56 1 1011 {check_t;
330 51         95 my $p = pop @$stack;
331 51         129 pushElement;
332 51         78 push @$stack, $p;
333 51         86 new 2;
334             }
335              
336             sub accept_s() #P Semi colon
337 39     39 1 711 {check_bst;
338 36 100       87 if (!test_t($$stack[-1])) # Insert an empty element between two consecutive semicolons
339 12         26 {push @$stack, 'empty2';
340 12         22 new 1;
341             }
342 36         133 1 while reduce1;
343 36         70 pushElement;
344             }
345              
346             sub accept_v() #P Variable
347 71     71 1 1276 {check_abdps;
348 68         158 pushElement;
349 68         153 new 1;
350 68   100     1477 new 2 while @$stack >= 2 and test_p($$stack[-2]); # Check for preceding prefix operators
351             }
352             # Action on each lexical item
353             my $Accept = # Dispatch the action associated with the lexical item
354             {a => \&accept_a, # Assign
355             b => \&accept_b, # Open
356             B => \&accept_B, # Closing parenthesis
357             d => \&accept_d, # Infix but not assign or semi-colon
358             p => \&accept_p, # Prefix
359             q => \&accept_q, # Post fix
360             s => \&accept_s, # Semi colon
361             v => \&accept_v, # Variable
362             };
363              
364             sub parseExpression() #P Parse an expression.
365 86 50   86 1 211 {if (@$expression)
366 86         142 {my $e = $$expression[$position = 0];
367              
368 86         186 my $E = expandElement $e;
369 86 100       1560 die <
370             Expression must start with 'opening parenthesis', 'prefix
371             operator', 'semi-colon' or 'variable', not $E.
372             END
373 82 100       1484 if (test_v($e)) # Single variable
374 34         78 {push @$stack, $e;
375 34         81 new 1;
376             }
377             else
378 48 100       837 {if (test_s($e)) # Semi
379 7         20 {push @$stack, 'empty3';
380 7         16 new 1;
381             }
382 48         128 push @$stack, $e;
383             }
384             }
385             else # Empty expression
386 0         0 {return undef;
387             }
388              
389 82         222 for(1..$#$expression) # Each input element
390 336         965 {$$Accept{substr($$expression[$position = $_], 0, 1)}(); # Dispatch the action associated with the lexical item
391             }
392              
393 51 100       118 if (index($last, type $$expression[-1]) == -1) # Check for incomplete expression
394 1         6 {my $C = expandElement $$expression[-1];
395 1         6 my $E = expected $$expression[-1];
396 1         6 die <
397             $E after final $C.
398             END
399             }
400              
401 50   100     251 pop @$stack while @$stack > 1 and $$stack[-1] =~ m(s); # Remove any trailing semi colons
402 50         112 1 while reduce1; # Final reductions
403              
404 50 100       105 if (@$stack != 1) # Incomplete expression
405 1         4 {my $E = expected $$expression[-1];
406 1         7 die "Incomplete expression. $E.\n";
407             }
408              
409 49         109 $first{type $$expression[ 0]}++; # Capture valid first and last lexical elements
410 49         105 $last {type $$expression[-1]}++;
411              
412 49         111 $$stack[0] # The resulting parse tree
413             } # parseExpression
414              
415             sub parse(@) # Parse an expression.
416 86     86 1 224 {my (@expression) = @_; # Expression to parse
417 86         125 my $s = $stack;
418 86         158 $stack = []; # Clear the current stack - the things we do to speed things up.
419 86         131 my $x = $expression;
420 86         159 $expression = \@expression; # Clear the current expression
421 86         130 my $p = $position;
422 86         128 $position = 0; # Clear the current parse position
423              
424 86         128 my $e = eval {parseExpression};
  86         159  
425 86         162 my $r = $@; # Save any error message
426 86         173 $stack = $s; $expression = $x; $position = $p; # Restore the stack and expression being parsed
  86         129  
  86         118  
427 86 100       828 die $r if $r; # Die again if we died the last time
428              
429 49         345 $e # Otherwise return the parse tree
430             } # parse
431              
432             #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.
433              
434             sub validPair($$) # Confirm that the specified pair of lexical elements can occur as a sequence.
435 4     4 1 12 {my ($A, $B) = @_; # First element, second element
436 4         11 my $a = type $A;
437 4         11 my $b = type $B;
438 4 50       15 if (my $l = $$LexicalCodes{$a})
439 4 100       74 {return 1 if (index $l->next, $b) > -1;
440             }
441             undef
442 1         10 }
443              
444             #D1 Print # Print a parse tree to make it easy to visualize its structure.
445              
446             sub depth($) #P Depth of a term in an expression.
447 482     482 1 765 {my ($term) = @_; # Term
448 482         674 my $d = 0;
449 482         863 for(my $t = $term; $t; $t = $t->up) {++$d}
  1734         31079  
450 482         3140 $d
451             }
452              
453             sub listTerms($) #P List the terms in an expression in post order
454 47     47 1 72 {my ($expression) = @_; # Root term
455 47         66 my @t; # Terms
456              
457             sub # Recurse through terms
458 241     241   419 {my ($e) = @_; # Term
459 241         4371 my $o = $e->operands;
460 241 50       1028 return unless $e; # Operator
461 241 100       502 if (my @o = $o ? grep {ref $_} @$o : ()) # Operands
  194 100       471  
462 141         256 {my ($p, @p) = @o;
463 141         661 __SUB__->($p); # First operand
464 141         197 push @t, $e; # Operator
465 141         277 __SUB__->($_) for @p; # Second and subsequent operands
466             }
467             else # No operands
468 100         220 {push @t, $e; # Operator
469             }
470 47         260 } ->($expression);
471              
472             @t
473 47         391 }
474              
475             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.
476             {my ($expression, @title) = @_; # Root term, optional title
477             my @t = $expression->listTerms; # Terms in expression in post order
478             my @s; # Print
479              
480             my sub align # Align the ends of the lines
481             {my $L = 0; # Longest line
482             for my $s(@s)
483             {my $l = length $s; $L = $l if $l > $L;
484             }
485              
486             for my $i(keys @s) # Pad to longest
487             {my $s = $s[$i] =~ s/\s+\Z//rs;
488             my $l = length($s);
489             if ($l < $L)
490             {my $p = ' ' x ($L - $l);
491             $s[$i] = $s . $p;
492             }
493             }
494             };
495              
496             for my $t(@t) # Initialize output rectangle
497             {$s[$_] //= '' for 0..$t->depth;
498             }
499              
500             for my $t(@t) # Traverse tree
501             {my $d = $t->depth;
502             my $p = $t->operator; # Operator
503             my $P = $p =~ s(\A\w+?_) ()gsr; # Remove leading type character if followed by underscore as this make for clearer results
504              
505             align if $p =~ m(\A(a|d|s)); # Shift over for some components
506              
507             $s[$d] .= " $P"; # Describe operator or operand with type component removed if requested
508             align if $p !~ m(\A(p|q|v)); # Vertical for some components
509             }
510              
511             shift @s while @s and $s[ 0] =~ m(\A\s*\Z)s; # Remove leading blank lines
512              
513             for(@s) # Clean up trailing blanks so that tests are not affected by spurious white space mismatches
514             {s/\s+\n/\n/gs; s/\s+\Z//gs;
515             }
516              
517             unshift @s, join(' ', @title) if @title; # Add title
518              
519             join "\n", @s, '';
520             }
521              
522             #D
523             #-------------------------------------------------------------------------------
524             # Export - eeee
525             #-------------------------------------------------------------------------------
526              
527 1     1   10 use Exporter qw(import);
  1         2  
  1         48  
528              
529 1     1   8 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         1  
  1         502  
530              
531             @ISA = qw(Exporter);
532             @EXPORT = qw();
533             @EXPORT_OK = qw(
534             );
535             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
536              
537             # podDocumentation
538             =pod
539              
540             =encoding utf-8
541              
542             =head1 Name
543              
544             Tree::Term - Create a parse tree from an array of terms representing an expression.
545              
546             =head1 Synopsis
547              
548             The expression to L is presented as an array of words, the first letter
549             of each word indicates its lexical role as in:
550              
551             my @e = qw(
552              
553             v_sub a_is v_array as
554             v1 d_== v2 a_then v3 d_plus v4 a_else
555             v5 d_== v6 a_then v7 d_minus v8 a_else v9 d_times b v10 a_+ v11 B);
556              
557             Where:
558              
559             a assign - infix operator with priority 2 binding right to left
560             b open - open parenthesis
561             B close - close parenthesis
562             d dyad - infix operator with priority 3 binding left to right
563             p prefix - monadic prefix operator
564             q suffix - monadic suffix operator
565             s semi-colon - infix operator with priority 1 binding left to right
566             v variable - a variable in the expression
567              
568             The results of parsing the expression can be printed with L which
569             provides a left to right representation of the parse tree.
570              
571             is_deeply parse(@e)->flat, <
572             is
573             sub as
574             array then
575             == else
576             v1 v2 plus then
577             v3 v4 == else
578             v5 v6 minus times
579             v7 v8 v9 +
580             v10 v11
581             END
582              
583             =head1 Description
584              
585             Create a parse tree from an array of terms representing an expression.
586              
587              
588             Version 20210724.
589              
590              
591             The following sections describe the methods in each functional area of this
592             module. For an alphabetic listing of all methods by name see L.
593              
594              
595              
596             =head1 Parse
597              
598             Create a parse tree from an array of terms representing an expression.
599              
600             =head2 LexicalStructure()
601              
602             Return the lexical codes and their relationships in a data structure so this information can be used in other contexts.
603              
604              
605             B
606              
607              
608              
609             is_deeply LexicalStructure, # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
610              
611              
612              
613             =head2 syntaxError(@expression)
614              
615             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.
616              
617             Parameter Description
618             1 @expression Expression to parse
619              
620             B
621              
622              
623             if (1)
624              
625             {eval {syntaxError(qw(v1 p1))}; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
626              
627             ok -1 < index $@, <
628             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2.
629             Expected: 'assignment operator', 'closing parenthesis',
630             'dyadic operator', 'semi-colon' or 'suffix operator'.
631             END
632             }
633              
634              
635             =head2 parse(@expression)
636              
637             Parse an expression.
638              
639             Parameter Description
640             1 @expression Expression to parse
641              
642             B
643              
644              
645             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)], <
646             is
647             sub as
648             array then
649             == else
650             v1 v2 plus then
651             v3 v4 == else
652             v5 v6 minus times
653             v7 v8 v9 +
654             v10 v11
655             END
656             }
657              
658             if (1) {
659             ok validPair('B', 'd');
660             ok validPair('b', 'B');
661             ok validPair('v', 'a');
662             ok !validPair('v', 'v');
663              
664              
665             =head1 Validate
666              
667             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.
668              
669             =head2 validPair($A, $B)
670              
671             Confirm that the specified pair of lexical elements can occur as a sequence.
672              
673             Parameter Description
674             1 $A First element
675             2 $B Second element
676              
677             B
678              
679              
680              
681             ok validPair('B', 'd'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
682              
683              
684             ok validPair('b', 'B'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
685              
686              
687             ok validPair('v', 'a'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
688              
689              
690             ok !validPair('v', 'v'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
691              
692              
693              
694             =head1 Print
695              
696             Print a parse tree to make it easy to visualize its structure.
697              
698             =head2 flat($expression, @title)
699              
700             Print the terms in the expression as a tree from left right to make it easier to visualize the structure of the tree.
701              
702             Parameter Description
703             1 $expression Root term
704             2 @title Optional title
705              
706             B
707              
708              
709              
710             my @e = qw(v1 a2 v3 d4 v5 s6 v8 a9 v10);
711              
712              
713             is_deeply parse(@e)->flat, <
714              
715             s6
716             a2 a9
717             v1 d4 v8 v10
718             v3 v5
719             END
720             }
721              
722             ok T [qw(v1 a2 v3 s s s v4 a5 v6 s s)], <
723             s
724             s empty2
725             s a5
726             s empty2 v4 v6
727             a2 empty2
728             v1 v3
729             END
730              
731             ok T [qw(b B)], <
732             empty1
733             END
734              
735             ok T [qw(b b B B)], <
736             empty1
737             END
738              
739             ok T [qw(b b v1 B B)], <
740             v1
741             END
742              
743             ok T [qw(b b v1 a2 v3 B B)], <
744             a2
745             v1 v3
746             END
747              
748             ok T [qw(b b v1 a2 v3 d4 v5 B B)], <
749             a2
750             v1 d4
751             v3 v5
752             END
753              
754             ok T [qw(p1 v1)], <
755             p1
756             v1
757             END
758              
759             ok T [qw(p2 p1 v1)], <
760             p2
761             p1
762             v1
763             END
764              
765             ok T [qw(v1 q1)], <
766             q1
767             v1
768             END
769              
770             ok T [qw(v1 q1 q2)], <
771             q2
772             q1
773             v1
774             END
775              
776             ok T [qw(p2 p1 v1 q1 q2)], <
777             q2
778             q1
779             p2
780             p1
781             v1
782             END
783              
784             ok T [qw(p2 p1 v1 q1 q2 d3 p4 p3 v2 q3 q4)], <
785             d3
786             q2 q4
787             q1 q3
788             p2 p4
789             p1 p3
790             v1 v2
791             END
792              
793             ok T [qw(p2 p1 v1 q1 q2 d3 p4 p3 v2 q3 q4 d4 p6 p5 v3 q5 q6 s)], <
794             d3
795             q2 d4
796             q1 q4 q6
797             p2 q3 q5
798             p1 p4 p6
799             v1 p3 p5
800             v2 v3
801             END
802              
803             ok T [qw(b s B)], <
804             empty2
805             END
806              
807             ok T [qw(b s s B)], <
808             s
809             empty2 empty2
810             END
811              
812              
813             if (1) {
814              
815             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);
816              
817              
818             is_deeply parse(@e)->flat, <
819              
820             d3
821             q2 d4
822             q1 q4 q6
823             p2 q3 q5
824             p1 p4 p6
825             v1 p3 p5
826             v2 v3
827             END
828              
829             }
830              
831             ok T [qw(b b v1 B s B s)], <
832             v1
833             END
834              
835             ok T [qw(v1 q1 s)], <
836             q1
837             v1
838             END
839              
840             ok T [qw(b b v1 q1 q2 B q3 q4 s B q5 q6 s)], <
841             q6
842             q5
843             q4
844             q3
845             q2
846             q1
847             v1
848             END
849              
850             ok T [qw(p1 p2 b v1 B)], <
851             p1
852             p2
853             v1
854             END
855              
856             ok T [qw(v1 d1 p1 p2 v2)], <
857             d1
858             v1 p1
859             p2
860             v2
861             END
862              
863             ok T [qw(p1 p2 b p3 p4 b p5 p6 v1 d1 v2 q1 q2 B q3 q4 s B q5 q6 s)], <
864             q6
865             q5
866             p1
867             p2
868             q4
869             q3
870             p3
871             p4
872             d1
873             p5 q2
874             p6 q1
875             v1 v2
876             END
877              
878             ok T [qw(p1 p2 b p3 p4 b p5 p6 v1 a1 v2 q1 q2 B q3 q4 s B q5 q6 s)], <
879             q6
880             q5
881             p1
882             p2
883             q4
884             q3
885             p3
886             p4
887             a1
888             p5 q2
889             p6 q1
890             v1 v2
891             END
892              
893             ok T [qw(b v1 B d1 b v2 B)], <
894             d1
895             v1 v2
896             END
897              
898             ok T [qw(b v1 B q1 q2 d1 b v2 B)], <
899             d1
900             q2 v2
901             q1
902             v1
903             END
904              
905             ok T [qw(v1 s)], <
906             v1
907             END
908              
909             ok T [qw(v1 s s)], <
910             s
911             v1 empty2
912             END
913              
914             ok T [qw(v1 s b s B)], <
915             s
916             v1 empty2
917             END
918              
919             ok T [qw(v1 s b b s s B B)], <
920             s
921             v1 s
922             empty2 empty2
923             END
924              
925             ok T [qw(b v1 s B s s)], <
926             s
927             v1 empty2
928             END
929              
930             ok T [qw(v1 a b1 b2 v2 B2 B1 s)], <
931             a
932             v1 v2
933             END
934              
935             ok T [qw(v1 a1 b1 v2 a2 b2 v3 B2 B1 s)], <
936             a1
937             v1 a2
938             v2 v3
939             END
940              
941             ok T [qw(v1 a1 p1 v2)], <
942             a1
943             v1 p1
944             v2
945             END
946              
947             ok T [qw(b1 v1 q1 q2 B1)], <
948             q2
949             q1
950             v1
951             END
952              
953             ok T [qw(b1 v1 q1 q2 s B1)], <
954             q2
955             q1
956             v1
957             END
958              
959             ok T [qw(p1 b1 v1 B1 q1)], <
960             q1
961             p1
962             v1
963             END
964              
965             ok T [qw(b1 v1 B1 a1 v2)], <
966             a1
967             v1 v2
968             END
969              
970             ok T [qw(v1 q1 a1 v2)], <
971             a1
972             q1 v2
973             v1
974             END
975              
976             ok T [qw(s1 p1 v1)], <
977             s1
978             empty3 p1
979             v1
980             END
981              
982             ok E <
983             a
984             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'assignment operator': a.
985             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'assignment operator': a.
986             END
987              
988             ok E <
989             B
990             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'closing parenthesis': B.
991             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'closing parenthesis': B.
992             END
993              
994             ok E <
995             d1
996             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'dyadic operator': d1.
997             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'dyadic operator': d1.
998             END
999              
1000             ok E <
1001             p1
1002             Expected: 'opening parenthesis', 'prefix operator' or 'variable' after final 'prefix operator': p1.
1003             Expected: 'opening parenthesis', 'prefix operator' or 'variable' after final 'prefix operator': p1.
1004             END
1005              
1006             ok E <
1007             q1
1008             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'suffix operator': q1.
1009             Expression must start with 'opening parenthesis', 'prefix operator', 'semi-colon' or 'variable', not 'suffix operator': q1.
1010             END
1011              
1012             ok E <
1013             s
1014              
1015              
1016             END
1017              
1018             ok E <
1019             v1
1020              
1021              
1022             END
1023              
1024             ok E <
1025             b v1
1026             Incomplete expression. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1027             No closing parenthesis matching b at position 1.
1028             END
1029              
1030             ok E <
1031             b v1 B B
1032             Unexpected 'closing parenthesis': B following 'closing parenthesis': B at position 4. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1033             Unexpected closing parenthesis B at position 4. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1034             END
1035              
1036             ok E <
1037             v1 d1 d2 v2
1038             Unexpected 'dyadic operator': d2 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1039             Unexpected 'dyadic operator': d2 following 'dyadic operator': d1 at position 3. Expected: 'opening parenthesis', 'prefix operator' or 'variable'.
1040             END
1041              
1042             ok E <
1043             v1 p1
1044             Unexpected 'prefix operator': p1 following term ending at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1045             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1046             END
1047              
1048             ok E <
1049             b1 B1 v1
1050             Unexpected 'variable': v1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1051             Unexpected 'variable': v1 following 'closing parenthesis': B1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1052             END
1053              
1054             ok E <
1055             b1 B1 p1 v1
1056             Unexpected 'prefix operator': p1 following term ending at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1057             Unexpected 'prefix operator': p1 following 'closing parenthesis': B1 at position 3. Expected: 'assignment operator', 'closing parenthesis', 'dyadic operator', 'semi-colon' or 'suffix operator'.
1058             END
1059              
1060             if (1)
1061             {eval {syntaxError(qw(v1 p1))};
1062             ok -1 < index $@, <
1063             Unexpected 'prefix operator': p1 following 'variable': v1 at position 2.
1064             Expected: 'assignment operator', 'closing parenthesis',
1065             'dyadic operator', 'semi-colon' or 'suffix operator'.
1066             END
1067              
1068              
1069              
1070             =head1 Hash Definitions
1071              
1072              
1073              
1074              
1075             =head2 Tree::Term Definition
1076              
1077              
1078             Description of a term in the expression.
1079              
1080              
1081              
1082              
1083             =head3 Output fields
1084              
1085              
1086             =head4 operands
1087              
1088             Operands to which the operator will be applied.
1089              
1090             =head4 operator
1091              
1092             Operator to be applied to one or more operands.
1093              
1094             =head4 up
1095              
1096             Parent term if this is a sub term.
1097              
1098              
1099              
1100             =head2 Tree::Term::Codes Definition
1101              
1102              
1103             Lexical item codes.
1104              
1105              
1106              
1107              
1108             =head3 Output fields
1109              
1110              
1111             =head4 B
1112              
1113             Closing parenthesis.
1114              
1115             =head4 a
1116              
1117             Infix operator with priority 2 binding right to left typically used in an assignment.
1118              
1119             =head4 b
1120              
1121             Opening parenthesis.
1122              
1123             =head4 d
1124              
1125             Infix operator with priority 3 binding left to right typically used in arithmetic.
1126              
1127             =head4 p
1128              
1129             Monadic prefix operator.
1130              
1131             =head4 q
1132              
1133             Monadic suffix operator.
1134              
1135             =head4 s
1136              
1137             Infix operator with priority 1 binding left to right typically used to separate statements.
1138              
1139             =head4 t
1140              
1141             A term in the expression.
1142              
1143             =head4 v
1144              
1145             A variable in the expression.
1146              
1147              
1148              
1149             =head2 Tree::Term::LexicalCode Definition
1150              
1151              
1152             Lexical item codes.
1153              
1154              
1155              
1156              
1157             =head3 Output fields
1158              
1159              
1160             =head4 letter
1161              
1162             Letter code used to refer to the lexical item.
1163              
1164             =head4 name
1165              
1166             Descriptive name of lexical item.
1167              
1168             =head4 next
1169              
1170             Letters codes of items that can follow this lexical item.
1171              
1172              
1173              
1174             =head2 Tree::Term::LexicalStructure Definition
1175              
1176              
1177             Lexical item codes.
1178              
1179              
1180              
1181              
1182             =head3 Output fields
1183              
1184              
1185             =head4 codes
1186              
1187             Code describing each lexical item
1188              
1189             =head4 first
1190              
1191             Lexical items we can start with
1192              
1193             =head4 last
1194              
1195             Lexical items we can end with
1196              
1197              
1198              
1199             =head1 Private Methods
1200              
1201             =head2 new($count)
1202              
1203             Create a new term from the indicated number of items on top of the stack
1204              
1205             Parameter Description
1206             1 $count Number of terms
1207              
1208             =head2 LexicalCode($letter, $next, $name)
1209              
1210             Lexical code definition
1211              
1212             Parameter Description
1213             1 $letter Letter used to refer to the lexical item
1214             2 $next Letters of items that can follow this lexical item
1215             3 $name Descriptive name of lexical item
1216              
1217             =head2 type($s)
1218              
1219             Type of term
1220              
1221             Parameter Description
1222             1 $s Term to test
1223              
1224             =head2 expandElement($e)
1225              
1226             Describe a lexical element
1227              
1228             Parameter Description
1229             1 $e Element to expand
1230              
1231             =head2 expandCodes($e)
1232              
1233             Expand a string of codes
1234              
1235             Parameter Description
1236             1 $e Codes to expand
1237              
1238             =head2 expected($s)
1239              
1240             String of next possible lexical items
1241              
1242             Parameter Description
1243             1 $s Lexical item
1244              
1245             =head2 unexpected($element, $unexpected, $position)
1246              
1247             Complain about an unexpected element
1248              
1249             Parameter Description
1250             1 $element Last good element
1251             2 $unexpected Unexpected element
1252             3 $position Position
1253              
1254             =head2 check_XXXX()
1255              
1256             Check that the top of the stack has one of XXXX
1257              
1258              
1259             =head2 test_XXXX($item)
1260              
1261             Check that we have XXXX
1262              
1263             Parameter Description
1264             1 $item Item to test
1265              
1266             =head2 test_t($item)
1267              
1268             Check that we have a term
1269              
1270             Parameter Description
1271             1 $item Item to test
1272              
1273             =head2 reduce($priority)
1274              
1275             Reduce the stack at the specified priority
1276              
1277             Parameter Description
1278             1 $priority Priority
1279              
1280             =head2 reduce1()
1281              
1282             Reduce the stack at priority 1
1283              
1284              
1285             =head2 reduce2()
1286              
1287             Reduce the stack at priority 2
1288              
1289              
1290             =head2 pushElement()
1291              
1292             Push an element
1293              
1294              
1295             =head2 accept_a()
1296              
1297             Assign
1298              
1299              
1300             =head2 accept_b()
1301              
1302             Open
1303              
1304              
1305             =head2 accept_B()
1306              
1307             Closing parenthesis
1308              
1309              
1310             =head2 accept_d()
1311              
1312             Infix but not assign or semi-colon
1313              
1314              
1315             =head2 accept_p()
1316              
1317             Prefix
1318              
1319              
1320             =head2 accept_q()
1321              
1322             Post fix
1323              
1324              
1325             =head2 accept_s()
1326              
1327             Semi colon
1328              
1329              
1330             =head2 accept_v()
1331              
1332             Variable
1333              
1334              
1335             =head2 parseExpression()
1336              
1337             Parse an expression.
1338              
1339              
1340             =head2 depth($term)
1341              
1342             Depth of a term in an expression.
1343              
1344             Parameter Description
1345             1 $term Term
1346              
1347             =head2 listTerms($expression)
1348              
1349             List the terms in an expression in post order
1350              
1351             Parameter Description
1352             1 $expression Root term
1353              
1354              
1355             =head1 Index
1356              
1357              
1358             1 L - Assign
1359              
1360             2 L - Closing parenthesis
1361              
1362             3 L - Open
1363              
1364             4 L - Infix but not assign or semi-colon
1365              
1366             5 L - Prefix
1367              
1368             6 L - Post fix
1369              
1370             7 L - Semi colon
1371              
1372             8 L - Variable
1373              
1374             9 L - Check that the top of the stack has one of XXXX
1375              
1376             10 L - Depth of a term in an expression.
1377              
1378             11 L - Expand a string of codes
1379              
1380             12 L - Describe a lexical element
1381              
1382             13 L - String of next possible lexical items
1383              
1384             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.
1385              
1386             15 L - Lexical code definition
1387              
1388             16 L - Return the lexical codes and their relationships in a data structure so this information can be used in other contexts.
1389              
1390             17 L - List the terms in an expression in post order
1391              
1392             18 L - Create a new term from the indicated number of items on top of the stack
1393              
1394             19 L - Parse an expression.
1395              
1396             20 L - Parse an expression.
1397              
1398             21 L - Push an element
1399              
1400             22 L - Reduce the stack at the specified priority
1401              
1402             23 L - Reduce the stack at priority 1
1403              
1404             24 L - Reduce the stack at priority 2
1405              
1406             25 L - Check the syntax of an expression without parsing it.
1407              
1408             26 L - Check that we have a term
1409              
1410             27 L - Check that we have XXXX
1411              
1412             28 L - Type of term
1413              
1414             29 L - Complain about an unexpected element
1415              
1416             30 L - Confirm that the specified pair of lexical elements can occur as a sequence.
1417              
1418             =head1 Installation
1419              
1420             This module is written in 100% Pure Perl and, thus, it is easy to read,
1421             comprehend, use, modify and install via B:
1422              
1423             sudo cpan install Tree::Term
1424              
1425             =head1 Author
1426              
1427             L
1428              
1429             L
1430              
1431             =head1 Copyright
1432              
1433             Copyright (c) 2016-2021 Philip R Brenan.
1434              
1435             This module is free software. It may be used, redistributed and/or modified
1436             under the same terms as Perl itself.
1437              
1438             =cut
1439              
1440              
1441              
1442             # Tests and documentation
1443              
1444             sub test
1445 1     1 0 8 {my $p = __PACKAGE__;
1446 1         10 binmode($_, ":utf8") for *STDOUT, *STDERR;
1447 1 50       82 return if eval "eof(${p}::DATA)";
1448 1         69 my $s = eval "join('', <${p}::DATA>)";
1449 1 50       11 $@ and die $@;
1450 1 50 50 1 0 7 eval $s;
  1 50 50 1 0 2  
  1 0   39   10  
  1 50   45   812  
  1 50       68189  
  1 50       9  
  1         95  
  39         105  
  39         191  
  39         184  
  39         69  
  39         58  
  39         120  
  39         218  
  39         91  
  39         63  
  39         313  
  39         200  
  39         97  
  39         85  
  0         0  
  0         0  
  39         216  
  45         135  
  45         486  
  45         178  
  45         160  
  45         663  
  45         147  
  45         321  
1451 1 50       541 $@ and die $@;
1452 1         142 1
1453             }
1454              
1455             test unless caller;
1456              
1457             1;
1458             # podDocumentation
1459             __DATA__