File Coverage

blib/lib/YAMLScript/Reader.pm
Criterion Covered Total %
statement 668 828 80.6
branch 161 256 62.8
condition 66 110 60.0
subroutine 114 145 78.6
pod 0 117 0.0
total 1009 1456 69.3


line stmt bran cond sub pod time code
1 10     10   72 use strict; use warnings;
  10     10   21  
  10         281  
  10         47  
  10         19  
  10         390  
2              
3             # Needed for Perl versions < 5.20
4             BEGIN {
5 10 50   10   633 warnings->unimport('experimental::signatures')
  10     10   62  
  10         38  
  10         430  
6             if eval "use warnings 'experimental::signatures'; 1";
7             }
8 10     10   78 use feature 'signatures';
  10         20  
  10         14252  
9              
10             package YAMLScript::Reader;
11              
12 10     10   76 use YAMLScript::Common;
  10         20  
  10         2071  
13 10     10   4598 use Lingy::Reader;
  10         37470  
  10         347  
14              
15 10     10   70 use base 'Lingy::Reader';
  10         20  
  10         1299  
16              
17 10     10   5529 use Regexp::Common;
  10         49504  
  10         38  
18 10     10   1620258 use Scalar::Util 'refaddr';
  10         26  
  10         105062  
19              
20             our %events;
21             our %functions;
22             our %refs;
23              
24             my $main_called = 0;
25              
26             our $read_ys = 0;
27              
28             #------------------------------------------------------------------------------
29             # Convert YAMLScript into a Lingy AST
30             #------------------------------------------------------------------------------
31 29     29 0 444108 sub new { bless {}, shift }
32              
33             sub read_str {
34 73     73 0 12764 my $self = shift;
35 73         234 my ($str) = @_;
36 73 100       237 if ($read_ys) {
37 7         24 return $self->read_ys($str);
38             } else {
39 66         316 return $self->SUPER::read_str(@_);
40             }
41             }
42              
43             sub read_ys {
44 58     58 0 164 my ($self, $yaml, $file) = (@_, '');
45              
46 58         174 $self->{yaml} = $yaml;
47 58         116 $self->{file} = $file;
48              
49 58         390 %events = ();
50 58         104 %functions = ();
51 58         100 %refs = ();
52              
53 58         162 $self->{events} = $self->parse_yaml_pp($yaml);
54 58         163 my $dom = $self->compose_dom;
55 57 50       206 my $ast = $file
56             ? $self->construct_ast($dom)
57             : $self->construct_expr($dom);
58              
59 57         381 return $ast;
60             }
61              
62             our @event_keys = (qw<
63             type
64             bpos blin bcol
65             epos elin ecol
66             anch ytag
67             styl valu
68             >);
69              
70             sub parse_yaml_fy {
71 0     0 0 0 my ($self, $yaml) = @_;
72              
73 0         0 require IPC::Run;
74              
75 0         0 my ($out, $err);
76 0         0 IPC::Run::run(
77             [qw< fy-tool --testsuite --tsv-format >],
78             $yaml,
79             \$out,
80             \$err,
81             IPC::Run::timeout(5),
82             );
83              
84 0         0 [ map 'event'->new($_), split /\n/, $out ];
85             }
86              
87             my $event_dict = {
88             stream_start_event => '+str',
89             stream_end_event => '-str',
90             document_start_event => '+doc',
91             document_end_event => '-doc',
92             mapping_start_event => '+map',
93             mapping_end_event => '-map',
94             sequence_start_event => '+seq',
95             sequence_end_event => '-seq',
96             scalar_event => '=val',
97             alias_event => '=ali',
98             };
99              
100             sub parse_yaml_pp {
101 58     58 0 129 my ($self, $yaml) = @_;
102 58         340 require YAML::PP::Parser;
103 58         129 my $events = [];
104             YAML::PP::Parser->new(
105             receiver => sub {
106 539     539   103033 my ($self, undef, $event) = @_;
107             my @event = (
108             ($event_dict->{$event->{name}} || XXX($event)),
109             0, 0, 0, 0, 0, 0,
110             ($event->{anchor} || '-'),
111 539   33     3452 ($event->{tag} || '-'),
      50        
      50        
112             );
113 539 100       1228 if ($event->{name} eq 'scalar_event') {
114 149         262 my $value = $event->{value};
115 149         259 my $style = $event->{style};
116 149         312 $value =~ s/\\/\\\\/g;
117 149         242 $value =~ s/\n/\\n/g;
118 149 50       464 push @event,
    100          
119             (
120             $style == 1 ? ':' :
121             $style == 4 ? '|' :
122             '"'
123             ),
124             $value;
125             }
126 539         2836 push @$events, join "\t", @event;
127             },
128 58         482 )->parse_string($yaml);
129 58         1209 [ map 'event'->new($_), @$events ];
130             }
131              
132             #------------------------------------------------------------------------------
133             # AST Implicit Typing Methods
134             #------------------------------------------------------------------------------
135              
136             my $bp = $RE{balanced}{-parens=>'()'};
137             my $bs = $RE{balanced}{-parens=>'[]'};
138              
139             my $E_GROUP = 'event'->new("=xxx\t-1\t-1\t-1\t-1\t-1\t-1\t-\t-\t-\t-");
140             my $E_PLAIN = 'event'->new("=xxx\t-1\t-1\t-1\t-1\t-1\t-1\t-\t-\t:\t-");
141             my $E_QUOTE = 'event'->new("=xxx\t-1\t-1\t-1\t-1\t-1\t-1\t-\t-\t'\t-");
142 0     0 0 0 sub PAIR { 'pair'->new(@_) }
143 0     0 0 0 sub MAP { 'map'->new($E_GROUP, @_) }
144 35     35 0 100 sub SEQ { 'seq'->new($E_GROUP, @_) }
145 0     0 0 0 sub VAL { 'val'->new($E_PLAIN, @_) }
146 0     0 0 0 sub STR { 'val'->new($E_QUOTE, @_) }
147              
148 0     0 0 0 sub B { BOOLEAN->new($_[0]) }
149 0     0 0 0 sub K { KEYWORD->new(@_) }
150 105     105 0 729 sub L { LIST->new([@_]) }
151 20     20 0 100 sub N { NUMBER->new($_[0]) }
152 138     138 0 452 sub S { SYMBOL->new($_[0]) }
153 2     2 0 22 sub T { STRING->new($_[0]) }
154 14     14 0 91 sub V { VECTOR->new([@_]) }
155              
156 17     17 0 39 sub DEF { S 'def' }
157 6     6 0 20 sub DO { S 'do' }
158 14     14 0 90 sub FN { S 'fn*' }
159 0     0 0 0 sub IF { S 'if' }
160 0     0 0 0 sub LET { S 'let*' }
161              
162             my $sym = qr<(?:
163             [-:.]?
164             \w+
165             (?:
166             (?:[-./]|::)
167             \w+
168             )*
169             [\?\!\*]?
170             )>x;
171              
172 0     0 0 0 sub error($m) { die "YS Error: $m\n" }
  0         0  
  0         0  
  0         0  
173 368     368 0 462 sub event($n) { $events{refaddr($n)} }
  368         446  
  368         436  
  368         1936  
174 281     281 0 387 sub e_style($n) { event($n)->{styl} }
  281         348  
  281         333  
  281         427  
175 87     87 0 118 sub e_tag($n) { event($n)->{ytag} }
  87         120  
  87         115  
  87         196  
176 531     531 0 636 sub is_map($n) { ref($n) eq 'map' }
  531         690  
  531         688  
  531         1553  
177 234     234 0 326 sub is_seq($n) { ref($n) eq 'seq' }
  234         359  
  234         292  
  234         640  
178 328     328 0 393 sub is_val($n) { ref($n) eq 'val' }
  328         425  
  328         393  
  328         1082  
179 292     292 0 365 sub is_pair($n) { ref($n) eq 'pair' }
  292         417  
  292         354  
  292         832  
180 85     85 0 119 sub is_key($n) { $n->{xkey} }
  85         111  
  85         241  
  85         286  
181 121 100   121 0 222 sub is_plain($n) { is_val($n) and e_style($n) eq ':' }
  121         180  
  121         149  
  121         210  
182 87 50   87 0 138 sub is_double($n) { is_val($n) and e_style($n) eq '"' }
  87         112  
  87         119  
  87         158  
183 85 50   85 0 478 sub is_literal($n) { is_val($n) and e_style($n) eq '|' }
  85         120  
  85         105  
  85         142  
184 0     0 0 0 sub is_single($n) {
  0         0  
  0         0  
185 0 0 0     0 return unless is_map($n) and pairs($n) == 1;
186 0         0 @{$n->{pair}[0]};
  0         0  
187             }
188 0     0 0 0 sub is_assign($n) {
  0         0  
  0         0  
189 0 0       0 is_single($n) and
190             text(key(first_pair($n))) =~ /^$sym\s+=$/;
191             }
192 14 100   14 0 24 sub is_def($n) { is_map($n) and tag(key(first_pair($n))) eq 'def' }
  14         19  
  14         55  
  14         32  
193              
194 171 50   171 0 237 sub assert_map($n) { is_map($n) or ZZZ($n) }
  171         380  
  171         215  
  171         263  
195 72 50   72 0 97 sub assert_seq($n) { is_seq($n) or ZZZ($n) }
  72         94  
  72         86  
  72         120  
196 35 50   35 0 78 sub assert_val($n) { is_val($n) or ZZZ($n) }
  35         53  
  35         44  
  35         72  
197 57 50   57 0 76 sub assert_pair($n) { is_pair($n) or ZZZ($n) }
  57         85  
  57         67  
  57         100  
198 12 50   12 0 16 sub assert_elems($n) { assert_seq($n); @{$n->elem} > 0 or ZZZ($n) }
  12         20  
  12         15  
  12         29  
  12         18  
  12         38  
199 2 50   2 0 5 sub assert_pairs($n) { assert_map($n); @{$n->pair} > 0 or ZZZ($n) }
  2         5  
  2         3  
  2         7  
  2         3  
  2         10  
200 169     169 0 271 sub pairs($n) { assert_map($n); @{$n->pair} }
  169         247  
  169         203  
  169         364  
  169         269  
  169         346  
201 60     60 0 253 sub elems($n) { assert_seq($n); @{$n->elem} }
  60         100  
  60         79  
  60         140  
  60         105  
  60         135  
202 237     237 0 299 sub tag($n) { $n->{ytag} }
  237         326  
  237         272  
  237         506  
203 57     57 0 83 sub key($p) { assert_pair($p); $p->key }
  57         81  
  57         69  
  57         138  
  57         133  
204 0     0 0 0 sub val($p) { assert_pair($p); $p->val }
  0         0  
  0         0  
  0         0  
  0         0  
205 0     0 0 0 sub key_val($p) { assert_pair($p); @$p }
  0         0  
  0         0  
  0         0  
  0         0  
206 35     35 0 58 sub text($v) { assert_val($v); $v->{text} }
  35         48  
  35         44  
  35         83  
  35         655  
207 12     12 0 18 sub first_elem($n) { assert_elems($n); (elems($n))[0] }
  12         17  
  12         20  
  12         44  
  12         34  
208 2     2 0 37 sub first_pair($n) { assert_pairs($n); (pairs($n))[0] }
  2         4  
  2         4  
  2         6  
  2         11  
209              
210 57     57 0 100 sub construct_expr($s, $n) {
  57         95  
  57         80  
  57         79  
211 57         145 my @ast = $s->construct($n);
212              
213 57 100       201 @ast == 1
214             ? $ast[0]
215             : L(DO, @ast);
216             }
217              
218 0     0 0 0 sub construct_ast($s, $n) {
  0         0  
  0         0  
  0         0  
219 0         0 my $ast = $s->construct_expr($n);
220              
221 0 0       0 if (need_main_call($ast)) {
222 0         0 $ast = L(
223             DO,
224             $ast,
225             L(
226             S('apply'),
227             S('main'),
228             S('*command-line-args*'),
229             ),
230              
231             );
232             }
233              
234 0         0 return $ast;
235             }
236              
237 235     235 0 524 sub construct($s, $n) {
  235         303  
  235         310  
  235         299  
238 235 100       401 my $tag = is_pair($n) ? tag(key($n)) : tag($n);
239 235 50       511 XXX $n, "No tag for node" unless $tag;
240 235         470 my $constructor = "construct_$tag";
241 235         705 $s->$constructor($n);
242             }
243              
244 0     0 0 0 sub construct_boolean($s, $n) {
  0         0  
  0         0  
  0         0  
245 0 0       0 "$n" eq 'true' ? true :
    0          
246             "$n" eq 'false' ? false :
247             die;
248             }
249              
250 34     34 0 56 sub construct_call($s, $p) {
  34         51  
  34         52  
  34         42  
251 34         76 my ($k, $v) = @$p;
252 34 50       71 "$k" =~ /^($sym)($bp?)$/ or die;
253 34         3842 my $fn = $1;
254 34         72 my $args = $2; # TODO add these args to value args
255 34 100       81 if ($args) {
256 11 50       58 $args =~ s/^\((.*)\)$/$1/ or die;
257             }
258 34         91 $fn =~ s/^(let|try|catch)$/$1*/;
259 34 50       80 $main_called = 1 if $fn eq 'main';
260 34         101 $args = 'val'->new(undef, $args);
261 34         98 $args->{ytag} = 'ysexpr';
262 34 100       82 $v = SEQ($v) unless is_seq($v);
263 34         93 L(S($fn), map $s->construct($_), $args, elems($v));
264             }
265              
266 5     5 0 13 sub construct_def($s, $p) {
  5         11  
  5         7  
  5         9  
267 5         13 my ($k, $v) = @$p;
268 5 50       13 "$k" =~ /^($sym)\s*=$/ or die;
269 5         27 my $sym = S($1);
270 5         62 my $rhs = $s->construct($v);
271 5         38 return L(DEF, $sym, $rhs);
272             }
273              
274             sub get_sig {
275 12     12 0 29 my ($sig) = @_;
276 12         24 my $args = [];
277 12         19 my $dargs = [];
278 12         405 while ($sig =~ s/^($sym)(?=,?\s|$),?\s*//) {
279 16         152 push @$args, symbol($1);
280             }
281 12 100       316 if ($sig =~ s/^\*($sym)//) {
282 3         19 push @$args, symbol('&'), symbol($1);
283             }
284             else {
285 9 100       252 if ($sig =~ /^($sym)=/) {
286 1         4 push @$args, symbol('&'), symbol('_args_');
287             }
288 9         243 while ($sig =~ s/^($sym)=(\S+),?\s*//) {
289 1         6 my ($s, $x) = ($1, $2);
290 1         4 push @$dargs, $1;
291 1         6 push @$dargs, read_ysexpr($x);
292             }
293             }
294 12 50       85 err "Can't parse function signature '$_[0]'"
295             if length($sig);
296 12         39 return ($args, $dargs);
297             }
298              
299 6     6 0 11 sub construct_defn($s, $p) {
  6         9  
  6         8  
  6         10  
300 6         16 my ($k, $v) = @$p;
301 6         17 my ($def, $name, $args, $body) = $s->_defn_parse($k, $v, 0);
302 6         26 return L($def, $name, V(@$args), @$body);
303             }
304              
305 1     1 0 8 sub construct_defn_multi($s, $p) {
  1         2  
  1         3  
  1         1  
306 1         11 my ($k, $v) = @$p;
307 1 50       4 text($k) =~ /^(defn|defmacro)\s+($sym)$/ or die;
308 1         6 my $def = $1;
309 1         14 my $name = S($2);
310             my @defs = map {
311 1         19 my ($k, $v) = @$_;
  4         25  
312 4         10 my (undef, undef, $args, $body) = $s->_defn_parse($k, $v, 1);
313 4         14 L(V(@$args), @$body);
314             } pairs($v);
315 1         21 return L($def, $name, @defs);
316             }
317              
318 2     2 0 5 sub construct_fn($s, $p) {
  2         3  
  2         3  
  2         3  
319 2         4 my ($k, $v) = @$p;
320 2         6 my ($def, $name, $args, $body) = $s->_defn_parse($k, $v, 0);
321 2         7 return L(FN, V(@$args), @$body);
322             }
323              
324 12     12   21 sub _defn_parse($s, $k, $v, $m) {
  12         16  
  12         17  
  12         18  
  12         17  
  12         18  
325 12         20 my ($def, $name, $sig);
326 12 100       28 if ($m) {
327 4 50       13 text($k) =~ /^($sym?)?\((.*)\)$/ or XXX $k;
328 4         12 $def = '';
329 4         12 $name = S($1);
330 4         32 $sig = $2;
331             } else {
332 8 50       20 text($k) =~ /^(fn|defn|defmacro)\s+($sym?)?\((.*)\)$/ or XXX $k;
333 8         32 $def = S($1);
334 8         75 $name = S($2);
335 8         57 $sig = $3;
336             }
337 12         32 my ($args, $dargs) = get_sig($sig);
338 12         32 my $defn = L( DEF, $name, L( FN, L, nil ) );
339 12 100       71 my $seq = is_seq($v) ? $v : SEQ($v);
340 12         32 my $first = first_elem($seq);
341             my $body = [
342             (@$dargs or is_def($first) or is_map($first))
343             ? ($s->construct_let($seq, $args, $dargs))
344 12 100 66     55 : map $s->construct($_), @{$seq->elem},
  10         24  
345             ];
346 12         108 return $def, $name, $args, $body;
347             }
348              
349 6     6 0 17 sub construct_do($s, $n) {
  6         12  
  6         13  
  6         11  
350 6         18 my @elems = elems($n);
351 6 100       31 if (@elems == 1) {
352 3         15 $s->construct($elems[0]);
353             } else {
354 3         25 L(
355             DO,
356             map $s->construct($_), @elems,
357             );
358             }
359             }
360              
361 2     2 0 8 sub construct_if($s, $p) {
  2         5  
  2         5  
  2         4  
362 2         7 my ($k, $v) = @$p;
363 2 50       5 "$k" =~ /^if +($bp)/ or die;
364 2         284 my $cond = read_ysexpr($1);
365 2 50       9 my @elems = is_seq($v) ? elems($v) : $v;
366 2         9 L(
367             S('if'),
368             $cond,
369             map $s->construct($_), @elems,
370             );
371             }
372              
373 19     19 0 30 sub construct_int($s, $n) { N("$n") }
  19         30  
  19         32  
  19         24  
  19         38  
374              
375 0     0 0 0 sub construct_istr($s, $n) {
  0         0  
  0         0  
  0         0  
376 0         0 my @list;
377 0         0 local $_ = "$n";
378 0         0 while (length) {
379 0 0       0 if (s/\A\$($sym)//) {
    0          
    0          
380 0         0 push @list, S($1);
381             } elsif (s/\A\$($bp)//s) {
382 0         0 push @list, read_ysexpr($1);
383             } elsif (s/\A(.+?)(?=\$)//s) {
384 0         0 push @list, T($1);
385             } else {
386 0         0 push @list, T($_);
387 0         0 $_ = '';
388             }
389             }
390 0         0 L(S('str'), @list);
391             }
392              
393 0     0 0 0 sub construct_keyword($s, $n) {
  0         0  
  0         0  
  0         0  
394 0         0 K("$n");
395             }
396              
397 2     2 0 4 sub construct_let($s, $n, $a, $d) {
  2         4  
  2         3  
  2         3  
  2         4  
  2         3  
398 2         4 my @elems = elems($n);
399 2 50 66     9 if (is_map($elems[0]) and @{$elems[0]->{pair}} > 1) {
  1         5  
400 0         0 my $elem = shift @elems;
401 0         0 for my $pair (reverse @{$elem->{pair}}) {
  0         0  
402 0         0 unshift @elems, bless {
403             pair => [$pair],
404             ytag => 'module',
405             }, 'map';
406             }
407             }
408 2         3 my @defs;
409 2         6 my $i = 0;
410 2         6 while (@$d) {
411 1         4 my ($sym, $form) = splice(@$d, 0, 2);
412 1         5 push @defs, S($sym), L(S('nth'), S('_args_'), N($i), $form);
413 1         8 $i++;
414             }
415 2   66     14 while (@elems and is_def($elems[0])) {
416 1         20 my $d = shift @elems;
417 1         9 my ($p) = pairs($d);
418 1         27 my ($k, $v) = @$p;
419 1 50       3 (my $sym = "$k") =~ s/\s+=$// or die;
420 1         6 push @defs, S($sym), $s->construct($v);
421             }
422             L(
423 2         6 S('let*'),
424             V(@defs),
425             map $s->construct($_), @elems,
426             );
427             }
428              
429 0     0 0 0 sub construct_let1($s, $n) {
  0         0  
  0         0  
  0         0  
430 0         0 my @elems = elems($n->[1]);
431 0 0       0 my $assigns = shift @elems or die;
432 0         0 my $defs = [];
433 0 0       0 if (is_map($assigns)) {
    0          
434 0         0 for my $pair (pairs($assigns)) {
435 0         0 my ($k, $v) = @$pair;
436 0         0 $k = "$k";
437 0 0       0 $k =~ s/\ +=$// or die;
438 0         0 push @$defs, S($k);
439 0         0 push @$defs, $s->construct($v);
440             }
441             } elsif (is_seq($assigns)) {
442 0         0 XXX $n;
443             } else {
444 0         0 XXX $n;
445             }
446              
447 0         0 L(
448             S('let*'),
449             $defs,
450             map $s->construct($_), @elems,
451             );
452             }
453              
454 1     1 0 2 sub construct_loop($s, $p) {
  1         5  
  1         2  
  1         2  
455 1         3 my ($k, $v) = @$p;
456 1 50       3 "$k" =~ /^loop +($bs)/ or die;
457 1         239 my $bindings = read_ysexpr($1);
458 1 50       4 my @elems = is_seq($v) ? elems($v) : $v;
459 1         5 L(
460             S('loop'),
461             $bindings,
462             map $s->construct($_), @elems,
463             );
464             }
465              
466 53     53 0 89 sub construct_module($s, $n) {
  53         75  
  53         69  
  53         71  
467 53         110 my @forms = map $s->construct($_), pairs($n);
468 53 100       592 return $forms[0] if @forms == 1;
469 2         13 L(DO, @forms);
470             }
471              
472 2     2 0 5 sub construct_str($s, $n) {
  2         5  
  2         6  
  2         3  
473 2         5 T("$n");
474             }
475              
476 27     27 0 41 sub construct_sym($s, $n) {
  27         39  
  27         37  
  27         33  
477 27         57 S("$n");
478             }
479              
480 0     0 0 0 sub construct_try($s, $p) {
  0         0  
  0         0  
  0         0  
481             L(
482             S('try*'),
483             map $s->construct($_),
484             map {
485 0 0       0 is_map($_) ? first_pair($_) : $_
  0         0  
486             } elems(val($p)),
487             );
488             }
489              
490 0     0 0 0 sub construct_catch($s, $p) {
  0         0  
  0         0  
  0         0  
491 0 0       0 key($p) =~ /^catch\(($sym)\)$/ or die;
492 0         0 L(
493             S('catch*'),
494             S($1),
495             $s->construct(val($p)),
496             );
497             }
498              
499 1     1 0 3 sub construct_use($s, $p) {
  1         3  
  1         2  
  1         7  
500 1         3 my ($k, $v) = @$p;
501 1         3 $v = $s->construct($v);
502 1 50       12 if (ref($v) eq SYMBOL) {
503 1         3 $v = L(S('quote'), $v);
504             }
505 1         8 L(S("$k"), $v);
506             }
507              
508 0     0 0 0 sub construct_val($s, $n) {
  0         0  
  0         0  
  0         0  
509 0         0 T("$n");
510             }
511              
512 3     3 0 6 sub construct_when($s, $p) {
  3         4  
  3         6  
  3         4  
513 3         7 my ($k, $v) = @$p;
514 3 50       7 (my $expr = "$k") =~ s/ ?([?|])$// or die;
515 3 100       15 my $fn = $1 eq '?' ? 'when' : 'when-not';
516 3         8 my $cond = read_ysexpr($expr);
517 3 50       7 my @elems = is_seq($v) ? elems($v) : $v;
518 3         8 L(
519             S($fn),
520             $cond,
521             map $s->construct($_), @elems,
522             );
523             }
524              
525 73     73 0 106 sub construct_ysexpr($s, $n) {
  73         101  
  73         99  
  73         95  
526 73         143 read_ysexpr($n);
527             }
528              
529 0     0 0 0 sub is_main($n) {
  0         0  
  0         0  
530 0 0 0     0 ref($n) eq LIST and
      0        
      0        
      0        
      0        
531             @$n >= 2 and
532             ref($n->[0]) eq SYMBOL and
533             "$n->[0]" eq 'defn' and
534             ref($n->[1]) eq SYMBOL and
535             "$n->[1]" eq 'main' and
536             1;
537             }
538              
539 0     0 0 0 sub need_main_call($ast) {
  0         0  
  0         0  
540 0 0       0 return 0 if $main_called;
541 0 0       0 return 1 if is_main($ast);
542 0 0       0 return 0 unless ref($ast) eq LIST;
543 0         0 for my $node (@$ast) {
544 0 0       0 return 1 if is_main($node);
545             }
546 0         0 return 0;
547             }
548              
549             #------------------------------------------------------------------------------
550             # YS expression reader.
551             #
552             # Converts these special forms:
553             # x(...) -> (x ...)
554             # (x + y) -> (+ x y)
555             # (x + y * z) -> (+ x (* y z))
556             # x(y + z) -> (x (+ y z))
557             #------------------------------------------------------------------------------
558              
559             my $dyn = qr<(?:\*$sym\*)>;
560             my $op = qr{(?:[-+*/]|[<>=]=?|and|or|\.\.)};
561              
562             my $pn = qr=(?:->|~@|[\'\`\[\]\{\}\(\)\~\^\@])=;
563             # my $pn = qr<(?:~@|[\'\`\[\]\{\}\(\)\~\^\@])>;
564              
565             my $re = qr<(?:/(?:\\.|[^\\\/])*/)>;
566             my $str = qr<(?:#?"(?:\\.|[^\\"])*"?)>;
567             my $tok = qr<[^\s\[\]{}('",;)]>;
568             my $ws = qr<(?:[\s,])>;
569              
570             sub tokenize {
571             [
572             map {
573 80 100   80 0 2538 s/::/./g if /^\w+(?:::\w+)+$/;
  220         476  
574 220         498 $_;
575             }
576             $_[0] =~ /
577             $ws*
578             (
579             $re |
580             $pn |
581             $str |
582             $dyn |
583             $op(?=\s) |
584             $sym\( |
585             '?$sym |
586             '?$tok
587             )
588             /xog
589             ];
590             }
591              
592 80     80 0 110 sub read_ysexpr($expr) {
  80         123  
  80         101  
593 80         150 $expr = lingy_expr($expr);
594 80         305 my @ast = Lingy::Reader->new->read_str($expr);
595 80 100       11070 return @ast if wantarray;
596 6 50       22 ZZZ [@ast, "Should have got exactly one result"]
597             unless @ast == 1;
598 6         18 return $ast[0];
599 0         0 Lingy::Reader->new->read_str($expr)
600             }
601              
602 80     80 0 102 sub lingy_expr($expr) {
  80         102  
  80         108  
603 80         200 my $tokens = tokenize($expr);
604 80         287 my $self = bless { tokens => $tokens }, __PACKAGE__;
605 80         129 my @groups;
606 80         197 while (@$tokens) {
607 70         126 push @groups, eval { $self->group };
  70         155  
608 70 50       229 die "Failed to parse expr '$expr': '$@'" if $@;
609             }
610             join ' ', map {
611 80 100       220 ref($_) ? $self->group_print($_) : $_;
  70         281  
612             } @groups;
613             }
614              
615 72     72 0 99 sub group($s) {
  72         96  
  72         95  
616 72         123 my $tokens = $s->{tokens};
617 72         125 my $token = shift @$tokens;
618 72 100 100     366 if (@$tokens >= 2 and
      66        
619             $tokens->[0] eq '->' and
620             $tokens->[1] =~ /^$sym\($/
621             ) {
622 1         9 shift(@$tokens);
623 1         3 my $method = shift(@$tokens);
624 1 50       7 $method =~ s/\($// or die;
625 1         4 return [ '.', $token, $s->group_call($method) ];
626             }
627 71 100       1233 $token =~ s/^($sym)\($/$1/ ? $s->group_call($token) :
    100          
    100          
    50          
    100          
628             $token =~ /^\('\s$/ ? $s->group_list(1) :
629             $token eq '(' ? $s->group_list(0) :
630             $token eq '`' ? $token :
631             $token =~ /^$re$/ ? '#"' . substr($token, 1, length($token) - 2) . '"' :
632             $token;
633             # die "Unknown token '$token'";
634             }
635              
636 28     28 0 58 sub group_list($s, $l) {
  28         40  
  28         42  
  28         45  
637 28         46 my $tokens = $s->{tokens};
638 28         81 my $group = $s->group_rest;
639 28 100 66     731 return $group if $l or @$group != 3 or $group->[1] !~ qr<^$op$>;
      100        
640              
641 18         62 my $oper = $group->[1];
642 18 100       53 $oper = '-range' if $oper eq '..';
643              
644             # TODO Support infix group > 3
645 18         81 [ $oper, $group->[0], $group->[2] ];
646             }
647              
648 12     12 0 29 sub group_call($s, @t) {
  12         20  
  12         28  
  12         19  
649 12         25 my $tokens = $s->{tokens};
650 12         33 my $group = [@t];
651 12         37 my $rest = $s->group_rest;
652 12 50 33     55 if (@$rest == 3 and $rest->[1] =~ qr<^$op$>) {
653 0         0 $rest = [ $rest->[1], $rest->[0], $rest->[2] ];
654 0         0 $rest = ([$rest]);
655             }
656 12         49 push @$group, @$rest;
657 12         46 return $group;
658             }
659              
660 40     40 0 63 sub group_rest($s) {
  40         58  
  40         56  
661 40         65 my $tokens = $s->{tokens};
662 40         80 my $rest = [];
663 40         111 while (@$tokens) {
664 148 100       1452 if ($tokens->[0] eq ')') {
    100          
665 40         62 shift @$tokens;
666 40         111 return $rest;
667             } elsif ($tokens->[0] =~ qr<^$sym?\('?$>) {
668 2         15 push @$rest, $s->group;
669             } else {
670 106         387 push @$rest, shift @$tokens;
671             }
672             }
673 0         0 die "Failed to parse expression";
674             }
675              
676 41     41 0 69 sub group_print($s, $g) {
  41         63  
  41         60  
  41         58  
677             '(' .
678             join(' ',
679             map {
680 41 100       76 ref($_) ? $s->group_print($_) : $_;
  123         461  
681             } @$g
682             )
683             . ')';
684             }
685              
686              
687             #------------------------------------------------------------------------------
688             # AST Composer Methods
689             #------------------------------------------------------------------------------
690             sub compose_dom {
691 58     58 0 131 my ($self) = @_;
692 58         144 my $node = $self->compose_node;
693 58         188 $node->{xtop} = 1;
694 58         163 tag_node($node);
695 57         110 return $node;
696             }
697              
698             sub compose_node {
699 228     228 0 367 my ($self) = (@_, '');
700 228         346 my $events = $self->{events};
701 228         559 while (@$events) {
702 344         543 my $event = shift(@$events);
703 344 100       1445 if ($event->{type} =~ /^[+=](map|seq|val|ali)$/) {
704 228         562 my $composer = "compose_$1";
705 228         580 my $node = $self->$composer($event);
706 228 50       535 if ((my $ytag = $event->{ytag}) ne '-') {
707 0 0       0 $ytag =~ s/^!(\w*)$/$1/ or XXX $event;
708 0   0     0 $node->{ytag} = $ytag || ref($node);
709             }
710 228         456 return $node;
711             }
712             }
713             }
714              
715             sub compose_map {
716 57     57 0 115 my ($self, $event) = @_;
717 57         158 my $map = 'map'->new($event);;
718 57         107 my $events = $self->{events};
719 57         128 while (@$events) {
720 120 100       399 shift(@$events), return $map if $events->[0]{type} eq '-map';
721 63         155 my $k = $self->compose_node;
722 63         236 $k->{xkey} = 1;
723 63         130 my $v = $self->compose_node;
724 63         185 my $pair = 'pair'->new($k, $v);
725 63         140 $map->add($pair);
726             }
727 0         0 XXX $map, "problem composing map";
728             }
729              
730             sub compose_seq {
731 22     22 0 58 my ($self, $event) = @_;
732 22         73 my $seq = 'seq'->new($event);
733 22         46 my $events = $self->{events};
734 22         62 while (@$events) {
735 66 100       214 shift(@$events), return $seq if $events->[0]{type} eq '-seq';
736 44         106 my $elem = $self->compose_node;
737 44         114 $seq->add($elem);
738             }
739 0         0 XXX $seq, "problem composing seq";
740             }
741              
742             sub compose_val {
743 149     149 0 309 my ($self, $event) = @_;
744 149         365 'val'->new($event);
745             }
746              
747             sub compose_ali {
748 0     0 0 0 my ($self, $event) = @_;
749 0         0 'ali'->new($event);
750             }
751              
752             #------------------------------------------------------------------------------
753             # AST Tag Resolution Methods
754             #------------------------------------------------------------------------------
755             {
756 10     10   100 no warnings 'redefine';
  10         99  
  10         38145  
757             sub YAMLScript::Common::_dump {
758 0     0   0 (my $type = (caller(1))[3]) =~ s/.*://;
759 0         0 my $sub = (caller(2))[3];
760 0         0 my $line = (caller(1))[2];
761 0         0 require YAML::PP;
762 0         0 my $dump = YAML::PP->new(
763             schema => ['Core', 'Perl', '-dumpcode'],
764             )->dump_string(@_) . "\e[0;33m... $type $sub $line\e[0m\n\n";
765 0         0 $dump =~ s/\A(.*)/\n\e[0;33m$1\e[0m/;
766 0         0 $dump;
767             }
768             }
769              
770 0     0 0 0 sub tag_error($msg) { ZZZ "$msg: '$_'" }
  0         0  
  0         0  
  0         0  
771              
772             sub o {
773 375     375 0 9075 my $f = (caller(1))[3];
774 375   100     1691 my $t = $_[0] // '';
775 375         640 return 0;
776             }
777              
778 165     165 0 238 sub tag_node($n) { o;
  165         233  
  165         219  
  165         378  
779 165 50       416 return 1 if $n->{ytag};
780 165         336 $n = transform($n);
781 165 100       323 if (is_map($n)) {
    100          
782 55         112 for my $p (pairs($n)) {
783 57 50 66     133 tag_catch($p) or
      100        
      100        
      100        
      100        
      66        
      66        
      66        
      100        
      66        
784             tag_defn_multi($p) or
785             tag_defn($p) or
786             tag_def($p) or
787             tag_if($p) or
788             tag_fn($p) or
789             tag_let($p) or
790             tag_loop($p) or
791             tag_try($p) or
792             tag_when($p) or
793             tag_call($p) or
794             XXX $p, "Unable to implicitly tag this map pair.";
795             }
796 54         120 $n->{ytag} = 'module';
797             }
798             elsif (is_seq($n)) {
799 23         42 for my $e (@{$n->{elem}}) {
  23         67  
800 48         116 tag_node($e);
801             }
802 23         58 $n->{ytag} = 'do';
803             }
804             else {
805 87         181 tag_val($n);
806             }
807              
808 164         603 1;
809             }
810              
811 165     165 0 229 sub transform($n) {
  165         272  
  165         211  
812 165 100       326 if (is_map($n)) {
813 55         143 for my $p (pairs($n)) {
814 57         224 my ($k, $v) = @$p;
815              
816             $k->{text} =
817             "$k" eq '???' ? 'cond' :
818             "$k" eq '^^^' ? 'recur' :
819 57 50       149 $k->{text};
    100          
820              
821 57 100 100     132 if ("$k" eq 'cond' and is_map($v)) {
822             $p->[1] = bless {
823             elem => [
824 4         7 map { delete($_->{xkey}); $_ }
  4         10  
825 1         3 map { @$_ } @{$v->{pair}}
  2         7  
  1         3  
826             ],
827             }, 'seq';
828             }
829             }
830             }
831              
832 165         322 return $n;
833             }
834              
835 0     0 0 0 sub tag_map($n) { o;
  0         0  
  0         0  
  0         0  
836             }
837              
838 0     0 0 0 sub tag_seq($n) { o;
  0         0  
  0         0  
  0         0  
839             }
840              
841 87     87 0 121 sub tag_val($n) { o($n);
  87         119  
  87         115  
  87         191  
842 87 50 66     182 if (e_tag($n) ne '-') {
    100          
    50          
843 0         0 $n->{ytag} = substr(e_tag($n), 1);
844             } elsif (is_double($n) or is_literal($n)) {
845 2 50 33     23 ($n->{xtop} and tag_ysexpr($n)) or
      33        
846             tag_istr($n) or
847             tag_str($n);
848             } elsif (is_plain($n)) {
849 85 100 66     191 is_key($n) or
850             tag_scalar($n) or
851             tag_ysexpr($n);
852             } else {
853 0         0 tag_str($n);
854             }
855             }
856              
857 36     36 0 62 sub tag_call($p) {
  36         77  
  36         51  
858 36         62 my ($k, $v) = @$p;
859 36 50       633 if ($k =~ /^$sym($bp?)$/) {
860 36         97 my $args = $1;
861             $k->{ytag} =
862 36 100       69 "$k" eq 'use'
863             ? "$k" :'call';
864              
865             # Empty (null) value
866 36 100 100     113 if (is_plain($v) and text($v) eq '') {
867 3 100       15 err "Use 'foo():' for a call with no args"
868             if $args eq '';
869             }
870              
871 35         94 tag_node($v);
872             }
873             }
874              
875 57     57 0 121 sub tag_catch($n) {
  57         78  
  57         75  
876 57 50       1023 $n->{ytag} = 'catch' if $n =~ /^catch\($sym\)$/;
877             }
878              
879 50     50 0 97 sub tag_def($p) {
  50         86  
  50         62  
880 50         106 my ($k, $v) = @$p;
881 50 100       952 return unless $k =~ /^$sym\s*=$/;
882 6         42 $k->{ytag} = 'def';
883 6         27 tag_node($v);
884             }
885              
886 56     56 0 95 sub tag_defn($p) {
  56         86  
  56         130  
887 56         114 my ($k, $v) = @$p;
888 56 100       711 return unless $k =~ /^(?:defn|defmacro)\s+$sym$bp$/;
889 6         30 $k->{ytag} = 'defn';
890 6         28 tag_node($v);
891             }
892              
893 57     57 0 114 sub tag_defn_multi($p) {
  57         89  
  57         77  
894 57         109 my ($k, $v) = @$p;
895 57 100 66     665 return unless $k =~ /^(?:defn|defmacro)\s+$sym$/ and is_map($v);
896 1         4 for my $p (pairs($v)) {
897 4 50       40 return unless $p->[0] =~ /^$bp$/;
898             }
899 1         6 $k->{ytag} = 'defn_multi';
900 1         4 for my $p (pairs($v)) {
901 4         10 my ($k, $v) = @$p;
902 4         9 tag_node($v);
903             }
904 1         5 return 1;
905             }
906              
907 44     44 0 71 sub tag_if($p) { o;
  44         63  
  44         61  
  44         100  
908 44         149 my ($k, $v) = @$p;
909 44 100       111 return unless $k =~ /^if +\S/;
910 2         11 $k->{ytag} = 'if';
911 2         8 tag_node($v);
912             }
913              
914 2     2 0 5 sub tag_istr($n) {
  2         5  
  2         5  
915 2 50       132 $n->{ytag} = 'istr' if $n =~ /(\$$sym|\$\()/;
916             }
917              
918 42     42 0 70 sub tag_fn($p) {
  42         68  
  42         58  
919 42         78 my ($k, $v) = @$p;
920 42 100       258 return unless $k =~ /^fn\s+$bp$/;
921 2         11 $k->{ytag} = 'fn';
922 2         10 tag_node($v);
923             }
924              
925 40     40 0 77 sub tag_let($n) {
  40         57  
  40         61  
926 40 50       246 $n->{ytag} = 'let1' if $n =~ /^let$/;
927             }
928              
929 40     40 0 63 sub tag_loop($p) { o;
  40         61  
  40         54  
  40         95  
930 40         87 my ($k, $v) = @$p;
931 40 100       87 return unless $k =~ /^loop +\S/;
932 1         4 $k->{ytag} = 'loop';
933 1         13 tag_node($v);
934             }
935              
936 85     85 0 140 sub tag_scalar($n) {
  85         117  
  85         104  
937 85         141 local $_ = $n;
938             $n->{ytag} =
939             /^(true|false)$/ ? 'boolean' :
940             /^-?\d+$/ ? 'int' :
941             /^-?\d+\.\d*$/ ? 'float' :
942             /^:$sym$/ ? 'keyword' :
943             /^null$/ ? 'null' :
944 85 100       172 /^$sym$/ ? do {
    50          
    50          
    50          
    100          
    50          
945 27         62 $n->{text} =~ s/::/./g;
946 27         154 'sym';
947             } :
948             return;
949             }
950              
951 2     2 0 6 sub tag_str($n) {
  2         4  
  2         2  
952 2         6 $n->{ytag} = 'str';
953             }
954              
955 39     39 0 64 sub tag_try($n) {
  39         61  
  39         59  
956 39 50       201 $n->{ytag} = 'try' if $n =~ /^try$/;
957             }
958              
959 39     39 0 72 sub tag_when($p) { o;
  39         56  
  39         57  
  39         87  
960 39         79 my ($k, $v) = @$p;
961 39 100       86 return unless $k =~ /(?:\)|. )[?|]$/;
962 3         10 $k->{ytag} = 'when';
963 3         7 tag_node($v);
964             }
965              
966 39     39 0 70 sub tag_ysexpr($n) {
  39         164  
  39         55  
967 39         100 $n->{text} =~ s/^\.(?!\d)//;
968 39         105 $n->{ytag} = 'ysexpr';
969             }
970              
971             #------------------------------------------------------------------------------
972             # Event and Node Classes
973             #------------------------------------------------------------------------------
974             {
975             package event;
976             sub new {
977 569     569   1017 my ($class, $line) = @_;
978 569         828 chomp $line;
979 569         890 my $self = bless {}, $class;
980 569         1770 @{$self}{@event_keys} = split /\t/, $line;
  569         2565  
981 569         1685 return $self;
982             }
983             }
984              
985             {
986             package pair;
987             sub new {
988 63     63   133 my ($class, $k, $v) = @_;
989 63         501 bless [$k, $v], $class;
990             }
991 57     57   86 sub key($p) { $p->[0] }
  57         77  
  57         68  
  57         121  
992 0     0   0 sub val($p) { $p->[1] }
  0         0  
  0         0  
  0         0  
993             }
994              
995             {
996             package map;
997             sub new {
998 57     57   114 my ($class, $event, @pairs) = @_;
999 57         158 my $self = bless {
1000             pair => [@pairs],
1001             }, $class;
1002             $refs{$event->{anch}} = $self
1003 57 50       157 if $event->{anch} ne '-';
1004 57         244 $events{Scalar::Util::refaddr($self)} = $event;
1005 57         116 return $self;
1006             }
1007             sub add {
1008 63     63   119 my ($self, $pair) = @_;
1009 63         94 push @{$self->{pair}}, $pair;
  63         213  
1010             }
1011 171     171   569 sub pair { $_[0]->{pair} }
1012             }
1013              
1014             {
1015             package seq;
1016             sub new {
1017 57     57   135 my ($class, $event, @elems) = @_;
1018 57         159 my $self = bless {
1019             elem => [@elems],
1020             }, $class;
1021 57 50       148 if ($event) {
1022             $refs{$event->{anch}} = $self
1023 57 50       164 if $event->{anch} ne '-';
1024 57         220 $events{Scalar::Util::refaddr($self)} = $event;
1025             }
1026 57         131 return $self;
1027             }
1028             sub add {
1029 44     44   102 my ($self, $value) = @_;
1030 44         68 push @{$self->{elem}}, $value;
  44         107  
1031 44         103 return $self;
1032             }
1033 82     82   288 sub elem { $_[0]->{elem} }
1034             }
1035              
1036             {
1037             package val;
1038 10     10   106 use overload '""' => sub { $_[0]->{text} };
  10     1265   22  
  10         133  
  1265         24418  
1039             my %escapes = (
1040             'n' => "\n",
1041             't' => "\t",
1042             '\\' => '\\',
1043             '"' => '"',
1044             );
1045             sub new {
1046 183     183   359 my ($class, $event, $text) = @_;
1047 183   100     791 $text //= $event->{valu} // '';
      100        
1048 183         331 $text =~ s/\\([nt\\\"])/$escapes{$1}/g;
1049 183         491 my $self = bless {
1050             text => $text,
1051             }, $class;
1052 183 100       420 if ($event) {
1053 149         272 delete $event->{valu};
1054             $refs{$event->{anch}} = $self
1055 149 50       345 if $event->{anch} ne '-';
1056 149         513 $events{Scalar::Util::refaddr($self)} = $event;
1057             }
1058 183         376 return $self;
1059             }
1060             }
1061              
1062             {
1063             package ali;
1064             sub new {
1065 0     0     my ($class, $event) = @_;
1066             my $self = bless {
1067             name => $event->{valu},
1068 0           }, $class;
1069 0           delete $event->{valu};
1070 0           $events{Scalar::Util::refaddr($self)} = $event;
1071 0           return $self;
1072             }
1073             }
1074              
1075             1;