File Coverage

blib/lib/Dallycot/Parser.pm
Criterion Covered Total %
statement 325 465 69.8
branch 58 100 58.0
condition 15 23 65.2
subroutine 89 109 81.6
pod 0 95 0.0
total 487 792 61.4


line stmt bran cond sub pod time code
1             package Dallycot::Parser;
2             our $AUTHORITY = 'cpan:JSMITH';
3              
4             # ABSTRACT: Parse Dallycot source into an abstract syntax tree
5              
6 22     22   664028 use strict;
  22         44  
  22         697  
7 22     22   89 use warnings;
  22         31  
  22         449  
8              
9 22     22   11266 use utf8;
  22         183  
  22         101  
10 22     22   9741 use experimental qw(switch);
  22         61103  
  22         113  
11              
12 22     22   13518 use Marpa::R2;
  22         3011355  
  22         1167  
13 22     22   16880 use Math::BigRat;
  22         1283894  
  22         123  
14              
15 22     22   32018 use Dallycot::Value::String;
  22         73  
  22         719  
16 22     22   126 use Dallycot::Value::URI;
  22         28  
  22         495  
17              
18 22     22   87 use Scalar::Util qw(blessed);
  22         31  
  22         1061  
19 22     22   12928 use String::Escape qw(unbackslash unquote);
  22         93040  
  22         1854  
20              
21 22     22   9679 use Dallycot::AST::Sequence;
  22         64  
  22         754  
22 22     22   141 use Dallycot::AST::Apply;
  22         52  
  22         494  
23 22     22   96 use Dallycot::Value::URI;
  22         33  
  22         124865  
24              
25             my $grammar = Marpa::R2::Scanless::G->new(
26             { action_object => __PACKAGE__,
27             bless_package => 'Dallycot::AST',
28             default_action => 'copy_arg0',
29             source => do { local ($/) = undef; my $s = <DATA>; \$s; }
30             }
31             );
32              
33             sub new {
34 136     136 0 3011677 my ($class) = @_;
35              
36 136   33     1067 $class = ref($class) || $class;
37 136         1058 return bless {} => $class;
38             }
39              
40 119     119 0 1048 sub grammar { return $grammar; }
41              
42             sub wants_more {
43 119     119 0 2146 my ( $self, $val ) = @_;
44              
45 119 50       454 if ( @_ == 2 ) {
46 119         284 $self->{wants_more} = $val;
47             }
48 119         284 return $self->{wants_more};
49             }
50              
51             sub error {
52 119     119 0 247 my ( $self, $val ) = @_;
53              
54 119 50       560 if ( @_ == 2 ) {
55 119         452 $self->{error} = $val;
56             }
57 119         278 return $self->{error};
58             }
59              
60             sub warnings {
61 119     119 0 230 my ( $self, $warnings ) = @_;
62              
63 119 50       425 if ( @_ == 2 ) {
64 119         283 $self->{warnings} = $warnings;
65             }
66 119 50       428 if (wantarray) {
67 0         0 return @{ $self->{warnings} };
  0         0  
68             }
69             else {
70 119         190 return @{ $self->{warnings} } != 0;
  119         469  
71             }
72             }
73              
74             sub parse {
75 119     119 0 247968 my ( $self, $input ) = @_;
76              
77 119         597 my $re = Marpa::R2::Scanless::R->new( { grammar => $self->grammar } );
78              
79 119         43098 $self->error(undef);
80 119         423 $self->warnings( [] );
81              
82 119         244 my $worked = eval {
83 119         596 $re->read( \$input );
84 119         208672 1;
85             };
86 119 50       711 if ($@) {
    50          
87 0         0 $@ =~ s{Marpa::R2\s+exception\s+at.*$}{}xs;
88 0         0 $self->error($@);
89 0         0 return;
90             }
91             elsif ( !$worked ) {
92 0         0 $self->error("Unable to parse.");
93 0         0 return;
94             }
95 119         696 my $parse = $re->value;
96 119         10369 my $result;
97              
98 119 100       402 if ($parse) {
99 118         366 $result = [$$parse];
100             }
101             else {
102 1         4 $result = [ bless [] => 'Dallycot::AST::Expr' ];
103             }
104              
105             # my @warnings = map {
106             # $_ -> check_for_common_mistakes
107             # } @$result;
108             #
109             # if(@warnings) {
110             # $self -> warnings(\@warnings);
111             # }
112              
113 119         701 $self->wants_more( $re->exhausted );
114              
115 119         12676 return $result;
116             }
117              
118             #--------------------------------------------------------------------
119              
120             sub copy_arg0 {
121 370     370 0 15264 my ( undef, $arg0 ) = @_;
122 370         744 return $arg0;
123             }
124              
125             sub block {
126 131     131 0 3601 my ( undef, @statements ) = @_;
127              
128 131 100       463 if ( @statements > 1 ) {
129 3         27 return Dallycot::AST::Sequence->new(@statements);
130             }
131             else {
132 128         398 return $statements[0];
133             }
134             }
135              
136             sub ns_def {
137 3     3 0 40 my ( undef, $ns, $href ) = @_;
138              
139 3         19 $ns =~ s{^(xml)?ns:}{}x;
140              
141 3 100 66     41 if(blessed($href) && $href -> isa('Dallycot::Value::URI')) {
142 1         15 $href = Dallycot::Value::String->new($href -> value -> as_string);
143             }
144              
145 3         22 return bless [ $ns, $href ] => 'Dallycot::AST::XmlnsDef';
146             }
147              
148             sub add_uses {
149 0     0 0 0 my ( undef, $ns ) = @_;
150              
151 0         0 return bless [$ns] => 'Dallycot::AST::Uses';
152             }
153              
154             sub lambda {
155 8     8 0 277 my ( undef, $expression, $arity ) = @_;
156              
157 8   100     51 $arity //= 1;
158              
159 2         15 return bless [
160             $expression,
161              
162             ( $arity == 0 ? []
163             : $arity == 1 ? ['#']
164 8 100       90 : [ map { '#' . $_ } 1 .. $arity ]
    50          
165             ),
166             [],
167             {}
168             ] => 'Dallycot::AST::Lambda';
169             }
170              
171             sub negate {
172 3     3 0 387 my ( undef, $expression ) = @_;
173              
174 3         12 given ( blessed $expression) {
175 3         20 when ('Dallycot::AST::Negation') {
176 1         4 return $expression->[0];
177             }
178 2         3 default {
179 2         13 return bless [$expression] => 'Dallycot::AST::Negation';
180             }
181             }
182             }
183              
184             sub invert {
185 0     0 0 0 my ( undef, $expression ) = @_;
186              
187 0         0 given ( blessed $expression) {
188 0         0 when ('Dallycot::AST::Invert') {
189 0         0 return $expression->[0];
190             }
191 0         0 default {
192 0         0 return bless [$expression] => 'Dallycot::AST::Invert';
193             }
194             }
195             }
196              
197             sub build_sum_product {
198 28     28 0 88 my ( undef, $sum_class, $negation_class, $left_value, $right_value ) = @_;
199              
200 28         38 my @expressions;
201              
202             # combine left/right as appropriate into a single sum
203 28         117 given ( blessed $left_value ) {
204 28         146 when ($sum_class) {
205 6         7 @expressions = @{$left_value};
  6         20  
206 6         12 given ( blessed $right_value ) {
207 6         8 when ($sum_class) {
208 0         0 push @expressions, @{$right_value};
  0         0  
209             }
210 6         9 default {
211 6         14 push @expressions, $right_value;
212             }
213             }
214             }
215 22         32 default {
216 22         110 given ( blessed $right_value ) {
217 22         48 when ($sum_class) {
218 0         0 @expressions = ( $left_value, @{$right_value} );
  0         0  
219             }
220 22         33 default {
221 22         92 @expressions = ( $left_value, $right_value );
222             }
223             }
224             }
225             }
226              
227             # now go through an consolidate sums and differences
228 28         51 my ( @differences, @sums );
229              
230 28         62 foreach my $expr (@expressions) {
231 68         189 given ( blessed $expr ) {
232 68         86 when ($sum_class) {
233 0         0 foreach my $sub_expr ( @{$expr} ) {
  0         0  
234 0         0 given ( blessed $sub_expr ) {
235 0         0 when ($negation_class) { # adding -(...)
236 0         0 given ( blessed $sub_expr->[0] ) {
237 0         0 when ($sum_class) { # adding -(a+b+...)
238 0         0 push @differences, @{ $sub_expr->[0] };
  0         0  
239             }
240 0         0 default {
241 0         0 push @sums, $sub_expr;
242             }
243             }
244             }
245 0         0 default {
246 0         0 push @sums, $sub_expr;
247             }
248             }
249             }
250             }
251 68         81 when ($negation_class) {
252 10         48 given ( blessed $expr->[0] ) {
253 10         14 when ($sum_class) {
254 0         0 foreach my $sub_expr ( @{ $expr->[0] } ) {
  0         0  
255 0         0 given ( blessed $sub_expr ) {
256 0         0 when ($negation_class) {
257 0         0 push @sums, $sub_expr->[0];
258             }
259 0         0 default {
260 0         0 push @differences, $sub_expr->[0];
261             }
262             }
263             }
264             }
265 10         12 when ($negation_class) {
266 0         0 push @sums, $expr->[0];
267             }
268 10         13 default {
269 10         25 push @differences, $expr->[0];
270             }
271             }
272             }
273 58         63 default {
274 58         140 push @sums, $expr;
275             }
276             }
277             }
278              
279 28         51 given ( scalar(@differences) ) {
280 28         73 when (0) { }
281 8         15 when (1) {
282 6         21 push @sums, bless [ $differences[0] ] => $negation_class
283             }
284 2         4 default {
285 2         9 push @sums, bless [ bless [@differences] => $sum_class ] => $negation_class;
286             }
287             }
288              
289 28         168 return bless \@sums => $sum_class;
290             }
291              
292             sub product {
293 8     8 0 1186 my ( undef, $left_value, $right_value ) = @_;
294              
295 8         21 return build_sum_product( undef, 'Dallycot::AST::Product',
296             'Dallycot::AST::Reciprocal', $left_value, $right_value );
297             }
298              
299             sub divide {
300 2     2 0 206 my ( undef, $numerator, $dividend ) = @_;
301              
302 2         10 return product( undef, $numerator, ( bless [$dividend] => 'Dallycot::AST::Reciprocal' ) );
303             }
304              
305             sub modulus {
306 1     1 0 211 my ( undef, $expr, $mod ) = @_;
307              
308 1         6 given ( blessed $expr) {
309 1         4 when ('Dallycot::AST::Modulus') {
310 0         0 push @{$expr}, $mod;
  0         0  
311 0         0 return $expr;
312             }
313 1         2 default {
314 1         8 return bless [ $expr, $mod ] => 'Dallycot::AST::Modulus';
315             }
316             }
317             }
318              
319             sub sum {
320 20     20 0 1967 my ( undef, $left_value, $right_value ) = @_;
321              
322 20         80 return build_sum_product( undef, 'Dallycot::AST::Sum',
323             'Dallycot::AST::Negation', $left_value, $right_value );
324             }
325              
326             sub subtract {
327 4     4 0 268 my ( undef, $left_value, $right_value ) = @_;
328              
329 4         22 return sum( undef, $left_value, bless [$right_value] => 'Dallycot::AST::Negation' );
330             }
331              
332             my %ops = qw(
333             < Dallycot::AST::StrictlyIncreasing
334             <= Dallycot::AST::Increasing
335             = Dallycot::AST::Equality
336             <> Dallycot::AST::Unique
337             >= Dallycot::AST::Decreasing
338             > Dallycot::AST::StrictlyDecreasing
339             );
340              
341             sub inequality {
342 22     22 0 2294 my ( undef, $left_value, $op, $right_value ) = @_;
343              
344 22 50 66     211 if ( ref $left_value eq $ops{$op} && ref $right_value eq ref $left_value ) {
    100          
    50          
345 0         0 push @{$left_value}, @{$right_value};
  0         0  
  0         0  
346 0         0 return $left_value;
347             }
348             elsif ( ref $left_value eq $ops{$op} ) {
349 6         9 push @{$left_value}, $right_value;
  6         44  
350 6         17 return $left_value;
351             }
352             elsif ( ref $right_value eq $ops{$op} ) {
353 0         0 unshift @{$right_value}, $left_value;
  0         0  
354 0         0 return $right_value;
355             }
356             else {
357 16         91 return bless [ $left_value, $right_value ] => $ops{$op};
358             }
359             }
360              
361             sub all {
362 1     1 0 23 my ( undef, $left_value, $right_value ) = @_;
363              
364 1 50       4 if ( ref $left_value eq 'Dallycot::AST::All' ) {
365 0         0 push @{$left_value}, $right_value;
  0         0  
366 0         0 return $left_value;
367             }
368             else {
369 1         5 return bless [ $left_value, $right_value ] => 'Dallycot::AST::All';
370             }
371             }
372              
373             sub any {
374 1     1 0 23 my ( undef, $left_value, $right_value ) = @_;
375              
376 1 50       4 if ( ref $left_value eq 'Dallycot::AST::Any' ) {
377 0         0 push @{$left_value}, $right_value;
  0         0  
378 0         0 return $left_value;
379             }
380             else {
381 1         5 return bless [ $left_value, $right_value ] => 'Dallycot::AST::Any';
382             }
383             }
384              
385             sub stream {
386 19     19 0 501 my ( undef, $expressions ) = @_;
387              
388 19         188 return bless $expressions => 'Dallycot::AST::BuildList';
389             }
390              
391             sub empty_stream {
392 3     3 0 3407 return bless [] => 'Dallycot::AST::BuildList';
393             }
394              
395             sub compose {
396 3     3 0 105 my ( undef, @functions ) = @_;
397              
398             return
399 3 100       7 bless [ map { ( blessed $_ eq 'Dallycot::AST::Compose' ) ? @{$_} : $_ } @functions ] =>
  6         43  
  1         16  
400             'Dallycot::AST::Compose';
401             }
402              
403             sub compose_map {
404 7     7 0 283 my ( undef, $left_term, $right_term ) = @_;
405              
406 7 100       81 if ( $right_term->isa('Dallycot::AST::BuildMap') ) {
407 1 50       10 if ( $left_term->isa('Dallycot::AST::BuildMap') ) {
408 0         0 push @$left_term, @$right_term;
409 0         0 return $left_term;
410             }
411             else {
412 1         4 unshift @$right_term, $left_term;
413 1         4 return $right_term;
414             }
415             }
416             else {
417 6         36 return bless [ $left_term, $right_term ] => 'Dallycot::AST::BuildMap';
418             }
419             }
420              
421             sub compose_filter {
422 5     5 0 184 my ( undef, @functions ) = @_;
423              
424 5         32 return bless [@functions] => 'Dallycot::AST::BuildFilter';
425             }
426              
427             sub build_string_vector {
428 3     3 0 10410 my ( undef, $lit ) = @_;
429              
430 3         12 my $lang = 'en';
431              
432 3 50       26 if ( $lit =~ s{\@([a-z][a-z](_[A-Z][A-Z])?)$}{}x ) {
433 0         0 $lang = $1;
434             }
435              
436 3         32 $lit =~ s/^<<//;
437 3         20 $lit =~ s/>>$//;
438 8         73 my @matches = map { unbackslash($_) }
  8         22  
439 3         40 map { unbackslash_spaces($_) }
440             split( m{(?<!\\)\s+}x, $lit );
441              
442             return
443 3         26 bless [ map { bless [ $_, $lang ] => 'Dallycot::Value::String'; } @matches ] =>
  8         45  
444             'Dallycot::Value::Vector';
445             }
446              
447             sub unbackslash_spaces {
448 8     8 0 10 my ($text) = @_;
449 8         15 $text =~ s/\\ / /g;
450 8         24 return $text;
451             }
452              
453             sub integer_literal {
454 154     154 0 131452 my ( undef, $lit ) = @_;
455              
456 154         807 return bless [ Math::BigRat->new($lit) ] => 'Dallycot::Value::Numeric';
457             }
458              
459             sub rational_literal {
460 0     0 0 0 my ( undef, $num, $den ) = @_;
461              
462             return bless [
463 0         0 do {
464 0         0 my $rat = Math::BigRat->new( Math::BigInt->new($num), Math::BigInt->new($den) );
465 0         0 $rat->bnorm();
466 0         0 $rat;
467             }
468             ] => 'Dallycot::Value::Numeric';
469             }
470              
471             sub float_literal {
472 4     4 0 10521 my ( undef, $lit ) = @_;
473 4         42 return bless [ Math::BigRat->new($lit) ] => 'Dallycot::Value::Numeric';
474             }
475              
476             sub string_literal {
477 20     20 0 33266 my ( undef, $lit ) = @_;
478              
479 20         58 my $lang = 'en';
480              
481 20 100       204 if ( $lit =~ s{\@([a-z][a-z](_[A-Z][A-Z])?)$}{}x ) {
482 2         10 $lang = $1;
483             }
484              
485 20         132 $lit = unbackslash( unquote($lit) );
486              
487 20         574 return bless [ $lit, $lang ] => 'Dallycot::Value::String';
488             }
489              
490             sub bool_literal {
491 0     0 0 0 my ( undef, $val ) = @_;
492              
493 0         0 return Dallycot::Value::Boolean->new( $val eq 'true' );
494             }
495              
496             sub uri_literal {
497 10     10 0 12937 my ( undef, $lit ) = @_;
498 10         102 return Dallycot::Value::URI -> new(
499             substr( $lit, 1, length($lit) - 2 )
500             );
501             }
502              
503             sub duration_literal {
504 0     0 0 0 my ( undef, $lit ) = @_;
505              
506 0         0 $lit =~ /^P(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/;
507 0         0 my(%args);
508 0         0 @args{qw(years months days hours minutes seconds)} = map {
509 0 0       0 s/[^1-9]//g; $_
  0         0  
510             } map {
511 0         0 defined($_) ? "$_" : 0
512             } $1, $2, $3, $5, $6, $7;
513              
514 0         0 return Dallycot::Value::Duration->new(%args);
515             }
516              
517             sub uri_expression {
518 1     1 0 47 my ( undef, $expression ) = @_;
519              
520 1         11 return bless [$expression] => 'Dallycot::AST::BuildURI';
521             }
522              
523             sub undef_literal {
524 0     0 0 0 return bless [] => 'Dallycot::Value::Undefined';
525             }
526              
527             sub combine_identifiers_options {
528 51     51 0 1400 my ( undef, $bindings, $options ) = @_;
529              
530 51   100     158 $bindings //= [];
531 51   100     213 $options //= [];
532              
533 51 100       156 if ( 'HASH' eq ref $bindings ) {
534             return +{
535 3         9 bindings => $bindings->{'bindings'},
536             bindings_with_defaults => $bindings->{'bindings_with_defaults'},
537 15         119 options => { map {@$_} @$options }
538             };
539             }
540             else {
541             return +{
542 2         12 bindings => $bindings,
543             bindings_with_defaults => [],
544 36         219 options => { map {@$_} @$options }
545             };
546             }
547             }
548              
549             sub relay_options {
550 0     0 0 0 my ( undef, $options ) = @_;
551             return +{
552 0         0 bindings => [],
553 0         0 options => { map {@$_} @$options }
554             };
555             }
556              
557             sub fetch {
558 144     144 0 180911 my ( undef, $ident ) = @_;
559              
560 144 100       630 if($ident =~ /^P(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/) {
561 4         9 my(%args);
562 24         52 @args{qw(years months days hours minutes seconds)} = map {
563 24 100       50 s/[^1-9]//g; $_
  24         57  
564             } map {
565 4         11 defined($_) ? "$_" : 0
566             } $1, $2, $3, $5, $6, $7;
567              
568 4         39 return Dallycot::Value::Duration->new(%args);
569             }
570              
571 140         447 my @bits = split( /:/, $ident );
572              
573 140         704 return bless \@bits => 'Dallycot::AST::Fetch';
574             }
575              
576             sub assign {
577 8     8 0 383 my ( undef, $ident, $expression ) = @_;
578              
579 8 100       49 if($ident =~ /^(xml)?ns:/) {
580 2 50 33     40 if($expression -> isa('Dallycot::Value::String') || $expression -> isa('Dallycot::Value::URI')) {
581 2         8 return ns_def(undef, $ident, $expression);
582             }
583             }
584              
585 6         29 return bless [ $ident, $expression ] => 'Dallycot::AST::Assign';
586             }
587              
588             sub apply {
589 36     36 0 1072 my ( undef, $function, $bindings ) = @_;
590              
591 36         219 return bless [ $function, $bindings->{bindings}, $bindings->{options} ] => 'Dallycot::AST::Apply';
592             }
593              
594             sub apply_sans_params {
595 0     0 0 0 my ( undef, $function ) = @_;
596              
597 0         0 return bless [ $function, [], {} ] => 'Dallycot::AST::Apply';
598             }
599              
600             sub list {
601 94     94 0 53879 my ( undef, @things ) = @_;
602              
603 94         257 return \@things;
604             }
605              
606             sub head {
607 11     11 0 319 my ( undef, $expression ) = @_;
608              
609 11         48 return bless [$expression] => 'Dallycot::AST::Head';
610             }
611              
612             sub tail {
613 13     13 0 307 my ( undef, $expression ) = @_;
614              
615 13         49 return bless [$expression] => 'Dallycot::AST::Tail';
616             }
617              
618             sub cons {
619 5     5 0 121 my ( undef, $scalar, $stream ) = @_;
620              
621 5 100       20 if ( ref $stream eq 'Dallycot::AST::Cons' ) {
    50          
622 2         4 push @{$stream}, $scalar;
  2         6  
623 2         5 return $stream;
624             }
625             elsif ( ref $scalar eq 'Dallycot::AST::Cons' ) {
626 0         0 unshift @{$scalar}, $stream;
  0         0  
627 0         0 return $scalar;
628             }
629             else {
630 3         14 return bless [ $stream, $scalar ] => 'Dallycot::AST::Cons';
631             }
632             }
633              
634             sub list_cons {
635 2     2 0 52 my ( undef, $first_stream, $second_stream ) = @_;
636              
637 2 50       12 if( ref $first_stream eq 'Dallycot::AST::ListCons' ) {
    50          
638 0 0       0 if( ref $second_stream eq 'Dallycot::AST::ListCons' ) {
639 0         0 push @$first_stream, @$second_stream;
640             }
641             else {
642 0         0 push @$first_stream, $second_stream;
643             }
644 0         0 return $first_stream;
645             }
646             elsif( ref $second_stream eq 'Dallycot::AST::ListCons' ) {
647 0         0 unshift @$second_stream, $first_stream;
648 0         0 return $second_stream;
649             }
650             else {
651 2         10 return bless [ $first_stream, $second_stream ] => 'Dallycot::AST::ListCons';
652             }
653             }
654              
655             sub stream_vectors {
656 0     0 0 0 my ( undef, @vectors ) = @_;
657              
658 0         0 return bless [@vectors] => 'Dallycot::AST::ConsVectors';
659             }
660              
661             sub lambda_definition_sans_args {
662 0     0 0 0 my ( undef, $expression ) = @_;
663              
664 0         0 return lambda_definition(
665             undef,
666             { bindings => [],
667             bindings_with_defaults => []
668             },
669             $expression
670             );
671             }
672              
673             sub function_definition_sans_args {
674 0     0 0 0 my ( undef, $identifier, $expression ) = @_;
675              
676 0         0 return function_definition(
677             undef,
678             $identifier,
679             { bindings => [],
680             bindings_with_defaults => []
681             },
682             $expression
683             );
684             }
685              
686             sub function_definition {
687 10     10 0 351 my ( undef, $identifier, $args, $expression ) = @_;
688              
689 10 50       34 if ( ref $args ) {
690 10         111 return bless [ $identifier,
691             bless [ $expression, $args->{bindings}, $args->{bindings_with_defaults}, $args->{options} ] =>
692             'Dallycot::AST::Lambda' ] => 'Dallycot::AST::Assign';
693             }
694             else {
695 0         0 return bless [
696             $identifier,
697             bless [
698             $expression,
699             [ ( $args == 0 ? []
700             : $args == 1 ? ['#']
701 0 0       0 : [ map { '#' . $_ } 1 .. $args ]
    0          
702             ),
703             []
704             ],
705             {}
706             ] => 'Dallycot::AST::Lambda'
707             ] => 'Dallycot::AST::Assign';
708             }
709             }
710              
711             sub lambda_definition {
712 5     5 0 182 my ( undef, $args, $expression ) = @_;
713              
714 5 50       23 if ( ref $args ) {
715             return
716 5         56 bless [ $expression, $args->{bindings}, $args->{bindings_with_defaults}, $args->{options} ] =>
717             'Dallycot::AST::Lambda';
718             }
719             else {
720 0         0 return bless [
721             $expression,
722             [ ( $args == 0 ? []
723             : $args == 1 ? ['#']
724 0 0       0 : [ map { '#' . $_ } 1 .. $args ]
    0          
725             ),
726             []
727             ],
728             {}
729             ] => 'Dallycot::AST::Lambda';
730             }
731             }
732              
733             sub option {
734 5     5 0 465 my ( undef, $identifier, $default ) = @_;
735              
736 5         14 return [ $identifier, $default ];
737             }
738              
739             sub combine_parameters {
740 0     0 0 0 my ( undef, $identifiers, $identifiers_with_defaults ) = @_;
741              
742             return +{
743 0         0 bindings => $identifiers,
744             bindings_with_defaults => $identifiers_with_defaults
745             };
746             }
747              
748             sub parameters_only {
749 15     15 0 442 my ( undef, $bindings ) = @_;
750              
751             return +{
752 15         91 bindings => $bindings,
753             bindings_with_defaults => []
754             };
755             }
756              
757             sub parameters_with_defaults_only {
758 0     0 0 0 my ( undef, $bindings ) = @_;
759             return +{
760 0         0 bindings => [],
761             bindings_with_defaults => $bindings
762             };
763             }
764              
765             sub placeholder {
766 5     5 0 143 return bless [] => 'Dallycot::AST::Placeholder';
767             }
768              
769             sub append_remainder_placeholder {
770 3     3 0 121 my ( undef, $bindings ) = @_;
771 3         5 push @{$bindings}, bless [] => 'Dallycot::AST::FullPlaceholder';
  3         19  
772 3         13 return $bindings;
773             }
774              
775             sub condition_list {
776 4     4 0 98 my ( undef, $conditions, $otherwise ) = @_;
777              
778             return
779 4 50       32 bless [ @$conditions, ( defined($otherwise) ? ( [ undef, $otherwise ] ) : () ) ] =>
780             'Dallycot::AST::Condition';
781             }
782              
783             sub condition {
784 4     4 0 98 my ( undef, $guard, $expression ) = @_;
785              
786 4         9 return [ $guard, $expression ];
787             }
788              
789             sub json_object {
790 7     7 0 161 my ( undef, $prop_list ) = @_;
791              
792             # my @props = map {
793             # _convert_to_json_array($_)
794             # } @$prop_list;
795 7         17 my @props = @$prop_list;
796 7         34 return bless \@props => 'Dallycot::AST::JSONObject';
797             }
798              
799             # sub _convert_to_json_array {
800             # my($ast) = @_;
801             #
802             # if($ast -> isa('Dallycot::AST::Assign') && $ast->[1]->isa('Dallycot::AST::BuildList')) {
803             # bless $ast->[1] => 'Dallycot::AST::JSONArray';
804             # }
805             # return $ast;
806             # }
807              
808             sub json_prop_list {
809 7     7 0 173 my( undef, @props ) = @_;
810              
811 7         18 return \@props;
812             }
813              
814             sub json_prop {
815 15     15 0 321 my( undef, $string, $value ) = @_;
816              
817 15 100 66     207 if(blessed($value) && $value->isa('Dallycot::AST::BuildList')) {
818 3         15 $value = bless $value => 'Dallycot::AST::JSONArray';
819             }
820              
821 15         57 return bless [ $string, $value ] => 'Dallycot::AST::JSONProperty';
822             }
823              
824             sub json_prop_name {
825 15     15 0 13503 my( undef, $string ) = @_;
826              
827 15         58 return substr($string, 1, length($string)-2);
828             }
829              
830             sub json_array {
831 1     1 0 29 my( undef, $values ) = @_;
832              
833 1   50     6 $values //= [];
834              
835 1         6 return bless $values => 'Dallycot::AST::JSONArray';
836             }
837              
838             sub prop_request {
839 11     11 0 324 my ( undef, $node, $req ) = @_;
840              
841 11 100       57 if ( ref $node eq 'Dallycot::AST::PropWalk' ) {
842 1         2 push @{$node}, $req;
  1         3  
843 1         3 return $node;
844             }
845             else {
846 10         72 return bless [ $node, $req ] => 'Dallycot::AST::PropWalk';
847             }
848             }
849              
850             sub forward_prop_request {
851 9     9 0 261 my ( undef, $expression ) = @_;
852              
853 9         55 return bless [$expression] => 'Dallycot::AST::ForwardWalk';
854             }
855              
856             sub reverse_prop_request {
857 2     2 0 62 my ( undef, $expression ) = @_;
858              
859 2         13 return bless [$expression] => 'Dallycot::AST::ReverseWalk';
860             }
861              
862             # implied object is the enclosing node definition
863             sub left_prop {
864 1     1 0 26 my ( undef, $prop, $subject ) = @_;
865              
866 1         5 return bless [ $subject, $prop, undef ] => 'Dallycot::AST::Property';
867             }
868              
869             # implied subject is the enclosing node definition
870             sub right_prop {
871 2     2 0 74 my ( undef, $prop, $object ) = @_;
872              
873 2         13 return bless [ undef, $prop, $object ] => 'Dallycot::AST::Property';
874             }
875              
876             sub build_node {
877 3     3 0 85 my ( undef, $expressions ) = @_;
878              
879 3         17 return bless [@$expressions] => 'Dallycot::AST::BuildNode';
880             }
881              
882             sub prop_literal {
883 10     10 0 427 my ( undef, $lit ) = @_;
884              
885 10         83 return bless [ split( /:/, $lit ) ] => 'Dallycot::AST::PropertyLit';
886             }
887              
888             sub prop_alternatives {
889 1     1 0 21 my ( undef, $left_value, $right_value ) = @_;
890              
891 1 50       5 if ( ref $left_value eq 'Dallycot::AST::AnyProperty' ) {
892 0         0 push @{$left_value}, $right_value;
  0         0  
893 0         0 return $left_value;
894             }
895             else {
896 1         7 return bless [ $left_value, $right_value ] => 'Dallycot::AST::AnyProperty';
897             }
898             }
899              
900             sub prop_closure {
901 1     1 0 20 my ( undef, $prop ) = @_;
902              
903 1         4 return bless [$prop] => 'Dallycot::AST::PropertyClosure';
904             }
905              
906             sub build_vector {
907 12     12 0 358 my ( undef, $expressions ) = @_;
908              
909 12         59 return bless $expressions => 'Dallycot::AST::BuildVector';
910             }
911              
912             sub empty_vector {
913 0     0 0 0 return bless [] => 'Dallycot::Value::Vector';
914             }
915              
916             sub vector_constant {
917 1     1 0 22 my ( undef, $constants ) = @_;
918              
919 1         4 return bless $constants => 'Dallycot::Value::Vector';
920             }
921              
922             sub empty_set {
923 2     2 0 10933 return bless [] => 'Dallycot::Value::Set';
924             }
925              
926             sub build_set {
927 3     3 0 115 my ( undef, $expressions ) = @_;
928              
929 3         13 my @expressions = map { flatten_union($_) } @$expressions;
  7         25  
930              
931 3         27 return bless \@expressions => 'Dallycot::AST::BuildSet';
932             }
933              
934             sub flatten_union {
935 7     7 0 13 my ($thing) = @_;
936              
937 7 100       67 if ( $thing->isa('Dallycot::AST::Union') ) {
938 6         31 return @$thing;
939             }
940             else {
941 1         5 return $thing;
942             }
943             }
944              
945             sub stream_constant {
946 0     0 0 0 my ( undef, $constants ) = @_;
947              
948 0 0       0 if (@$constants) {
949 0         0 my $result = bless [ pop @$constants, undef ] => 'Dallycot::Value::Stream';
950 0         0 while (@$constants) {
951 0         0 $result = bless [ pop @$constants, $result ] => 'Dallycot::Value::Stream';
952             }
953 0         0 return $result;
954             }
955             else {
956 0         0 return bless [] => 'Dallycot::Value::EmptyStream';
957             }
958             }
959              
960             sub _flatten_binary {
961 8     8   52 my ( undef, $class, $left_value, $right_value ) = @_;
962              
963 8 50       55 if ( ref $left_value eq $class ) {
    50          
964 0 0       0 if ( $right_value eq $class ) {
965 0         0 push @{$left_value}, @{$right_value};
  0         0  
  0         0  
966 0         0 return $left_value;
967             }
968             else {
969 0         0 push @{$left_value}, $right_value;
  0         0  
970 0         0 return $left_value;
971             }
972             }
973             elsif ( ref $right_value eq $class ) {
974 0         0 unshift @$right_value, $left_value;
975 0         0 return $right_value;
976             }
977             else {
978 8         80 return bless [ $left_value, $right_value ] => $class;
979             }
980             }
981              
982             sub zip {
983 2     2 0 55 my ( undef, $left_value, $right_value ) = @_;
984              
985 2         10 return _flatten_binary( undef, 'Dallycot::AST::Zip', $left_value, $right_value );
986             }
987              
988             sub set_union {
989 6     6 0 934 my ( undef, $left_value, $right_value ) = @_;
990              
991 6         33 return _flatten_binary( undef, 'Dallycot::AST::Union', $left_value, $right_value );
992             }
993              
994             sub set_intersection {
995 0     0 0 0 my ( undef, $left_value, $right_value ) = @_;
996              
997 0         0 return _flatten_binary( undef, 'Dallycot::AST::Intersection', $left_value, $right_value );
998             }
999              
1000             sub vector_index {
1001 7     7 0 948 my ( undef, $vector, $index ) = @_;
1002              
1003 7 50       25 if ( ref $vector eq 'Dallycot::AST::Index' ) {
1004 0         0 push @{$vector}, $index;
  0         0  
1005 0         0 return $vector;
1006             }
1007             else {
1008 7         33 return bless [ $vector, $index ] => 'Dallycot::AST::Index';
1009             }
1010             }
1011              
1012             sub vector_push {
1013 0     0 0 0 my ( undef, $vector, $scalar ) = @_;
1014              
1015 0 0       0 if ( $vector->[0] eq 'Push' ) {
1016 0         0 push @{$vector}, $scalar;
  0         0  
1017 0         0 return $vector;
1018             }
1019             else {
1020 0         0 return [ Push => ( $vector, $scalar ) ];
1021             }
1022             }
1023              
1024             sub defined_q {
1025 2     2 0 67 my ( undef, $expression ) = @_;
1026              
1027 2         10 return bless [$expression] => 'Dallycot::AST::Defined';
1028             }
1029              
1030             ##
1031             # Eventually, Range will be a type representing all values between
1032             # two endpoints.
1033             #
1034             # Q: how to indicate open/closed endpoints
1035             #
1036             # ( e1 .. e2 )
1037             # [ e1 .. e2 )
1038             # ( e1 .. e2 ]
1039             # [ e1 .. e2 ]
1040             #
1041             sub semi_range {
1042 1     1 0 195 my ( undef, $expression ) = @_;
1043              
1044 1         7 return bless [ $expression, undef ] => 'Dallycot::AST::BuildRange';
1045             }
1046              
1047             sub closed_range {
1048 2     2 0 190 my ( undef, $left_value, $right_value ) = @_;
1049              
1050 2         12 return bless [ $left_value, $right_value ] => 'Dallycot::AST::BuildRange';
1051             }
1052              
1053             sub stream_reduction {
1054 0     0 0 0 my ( undef, $start, $function, $stream ) = @_;
1055              
1056 0         0 return Dallycot::AST::Apply->new(
1057             Dallycot::Value::URI->new('http://www.dallycot.net/ns/core/1.0#last'),
1058             [ Dallycot::AST::Apply->new(
1059             Dallycot::Value::URI->new('http://www.dallycot.net/ns/core/1.0#foldl'),
1060             [ $start, $function, $stream ], {}
1061             )
1062             ],
1063             {}
1064             );
1065             }
1066              
1067             sub stream_reduction1 {
1068 0     0 0 0 my ( undef, $function, $stream ) = @_;
1069              
1070 0         0 return Dallycot::AST::Apply->new(
1071             Dallycot::Value::URI->new('http://www.dallycot.net/ns/core/1.0#last'),
1072             [ Dallycot::AST::Apply->new(
1073             Dallycot::Value::URI->new('http://www.dallycot.net/ns/core/1.0#foldl1'), [ $function, $stream ],
1074             {}
1075             )
1076             ],
1077             {}
1078             );
1079             }
1080              
1081             sub promote_value {
1082 4     4 0 129 my ( undef, $expression, $type ) = @_;
1083              
1084 4 100       23 if ( ref $expression eq 'Dallycot::AST::TypePromotion' ) {
1085 1         4 push @{$expression}, $type;
  1         4  
1086 1         4 return $expression;
1087             }
1088             else {
1089 3         30 return bless [ $expression, $type ] => 'Dallycot::AST::TypePromotion';
1090             }
1091             }
1092              
1093             sub resolve_uri {
1094 0     0 0   my ( undef, $expression ) = @_;
1095              
1096 0           return bless [$expression] => 'Dallycot::AST::Resolve';
1097             }
1098              
1099             1;
1100              
1101             __DATA__
1102              
1103             :start ::= Block
1104              
1105             Block ::= Statement+ separator => STMT_SEP action => block
1106              
1107             Statement ::= NSDef
1108             | Uses action => add_uses
1109             | FuncDef
1110             | Expression
1111              
1112             TypeSpec ::= TypeName
1113             | TypeSpec PIPE TypeName
1114              
1115             TypeName ::= Name
1116             | QCName
1117              
1118              
1119             ExpressionList ::= Expression+ separator => COMMA action => list
1120              
1121             SetExpressionList ::= Expression+ separator => PIPE action => list
1122              
1123             DiscreteBindings ::= Binding* separator => COMMA action => list
1124              
1125             Bindings ::= DiscreteBindings
1126             | DiscreteBindings (COMMA) (TRIPLE_UNDERSCORE) action => append_remainder_placeholder
1127              
1128             Binding ::= Expression
1129             | (UNDERSCORE) action => placeholder
1130              
1131             ConstantValue ::=
1132             Integer action => integer_literal
1133             | Integer (DIV) Integer action => rational_literal
1134             | Float action => float_literal
1135             | String action => string_literal
1136             | Boolean action => bool_literal
1137             | (COLON) Identifier action => prop_literal
1138             | (COLON) QCName action => prop_literal
1139             | ConstantStream action => stream_constant
1140             | ConstantVector action => vector_constant
1141              
1142             ConstantStream ::= (LB) ConstantValues (RB)
1143             | (LB) (RB)
1144              
1145             ConstantVector ::= (LT) ConstantValues (GT)
1146             | (LT) (GT)
1147              
1148             ConstantValues ::= ConstantValue+ separator => COMMA action => list
1149              
1150             Expression ::=
1151             Integer action => integer_literal
1152             | Float action => float_literal
1153             | String action => string_literal
1154             | Boolean action => bool_literal
1155             | Duration action => duration_literal
1156             | Identifier action => fetch
1157             | QCName action => fetch
1158             | JSONObject
1159             | LambdaArg action => fetch
1160             | Node
1161             | Lambda
1162             | (LP) (RP) action => undef_literal
1163             | Expression (LB) Expression (RB) action => vector_index
1164             | ConditionList
1165             | (LP) Block (RP) assoc => group
1166             | (LB) ExpressionList (RB) assoc => group action => stream
1167             | (LB) (RB) action => empty_stream
1168             | StringVector action => build_string_vector
1169             | (LT) ExpressionList (GT) action => build_vector
1170             | (LT) (GT) action => empty_vector
1171             | ('<>') action => empty_vector
1172             | (SET_START) SetExpressionList (SET_END) assoc => group action => build_set
1173             | (SET_START) (SET_END) action => empty_set
1174             | ('<||>') action => empty_set
1175             || Apply
1176             || Expression QUOTE assoc => left action => head
1177             | Expression DOT_DOT_DOT assoc => left action => tail
1178             | Expression ('^^') TypeSpec action => promote_value
1179             || Expression PropRequest assoc => left action => prop_request
1180             || ('?') Expression assoc => right action => defined_q
1181             | (MINUS) Expression assoc => right action => negate
1182             | (TILDE) Expression assoc => right action => invert
1183             | Expression (DOT_DOT) Expression action => closed_range
1184             | Expression (DOT_DOT) action => semi_range
1185             || Expression (Z) Expression action => zip assoc => right
1186             || Expression (MAP) Expression action => compose_map assoc => right
1187             | Expression (FILTER) Expression action => compose_filter assoc => right
1188             || Expression ('<<') Expression ('<<') Expression action => stream_reduction
1189             || Expression ('<<') Expression action => stream_reduction1 assoc => right
1190             || Expression (DOT) Expression action => compose
1191             || Expression (STAR) Expression action => product
1192             | Expression (DIV) Expression action => divide
1193             || Expression (MOD) Expression action => modulus assoc => right
1194             || Expression (PLUS) Expression action => sum
1195             | Expression (MINUS) Expression action => subtract
1196             || Expression (PIPE) Expression action => set_union
1197             || Expression (AMP) Expression action => set_intersection
1198             || Expression (COLON_COLON_GT) Expression action => cons assoc => right
1199             || Expression (LT_COLON_COLON) Expression action => vector_push assoc => left
1200             || Expression (COLON_COLON_COLON) Expression action => list_cons assoc => right
1201             || Expression Inequality Expression action => inequality
1202             || Expression (AND) Expression action => all
1203             || Expression (OR) Expression action => any
1204             || Identifier (COLON_EQUAL) Expression action => assign assoc => right
1205             | Identifier (LP) FunctionParameters (RP) (COLON_GT) Expression action => function_definition assoc => right
1206             | Identifier (LP) (RP) (COLON_GT) Expression action => function_definition_sans_args assoc => right
1207             | Identifier (SLASH) PositiveInteger (COLON_GT) Expression action => function_definition assoc => right
1208              
1209             Duration ~ duration
1210              
1211             Node ::=
1212             NodeDef
1213             | Graph (MOD) UriLit action => modulus
1214             | UriLit
1215             | ('<(') Expression (')>') action => uri_expression
1216              
1217             Graph ::= NodeDef
1218             | NodeDef (COLON_COLON_GT) Graph action => cons assoc => right
1219             | (LC) (RC) action => build_node
1220              
1221             NodeDef ::= (LC) NodePropList (RC) action => build_node
1222             | (STAR) UriLit action => resolve_uri
1223              
1224             NodePropList ::= NodeProp+ action => list
1225              
1226             JSONObject ::= (LC) JSONPropertyList (RC) action => json_object
1227              
1228             JSONPropertyList ::= JSONProperty+ separator => COMMA action => json_prop_list
1229              
1230             JSONProperty ::= JSONString (COLON) JSONValue action => json_prop
1231             | NSDef
1232             | FuncDef
1233             | Assign
1234              
1235             JSONValue ::= JSONObject
1236             | JSONArray
1237             | Expression
1238              
1239             JSONArray ::= (LB) JSONValues (RB) action => json_array
1240             | (LB) (RB) action => json_array
1241              
1242             JSONValues ::= JSONValue+ separator => COMMA action => list
1243              
1244             JSONString ::= jsonstring action => json_prop_name
1245              
1246             NodeProp ::= PropIdentifier (RIGHT_ARROW) Expression action => right_prop
1247             | PropIdentifier (LEFT_ARROW) Expression action => left_prop
1248              
1249             PropRequest ::= (RIGHT_ARROW) PropPattern action => forward_prop_request
1250             | (LEFT_ARROW) PropPattern action => reverse_prop_request
1251              
1252             PropPattern ::= PropIdentifier
1253             | (STAR) PropPattern action => prop_closure
1254             | PropPattern (PIPE) PropPattern action => prop_alternatives
1255             | (LP) PropPattern (RP) assoc => group
1256              
1257             PropIdentifier ::= (COLON) Identifier action => prop_literal
1258             | ATIdentifier action => prop_literal
1259             | (COLON) QCName action => prop_literal
1260             | Expression
1261              
1262             Fetched ::=
1263             Identifier action => fetch
1264             | LambdaArg action => fetch
1265             | QCName action => fetch
1266              
1267             Lambda ::=
1268             (LC) Block (RC) action => lambda
1269             | (LC) Block (RC) (SLASH) NonNegativeInteger action => lambda
1270             | (LP) FunctionParameters (RP) (COLON_GT) Expression action => lambda_definition
1271             | (LP) (RP) (COLON_GT) Expression action => lambda_definition_sans_args
1272              
1273             Apply ::= (LP) Expression (RP) (LP) FunctionArguments (RP) action => apply
1274             | Fetched (LP) FunctionArguments (RP) action => apply
1275             | Apply (LP) FunctionArguments (RP) action => apply
1276              
1277             NSDef ::= NSName (COLON_EQUAL) StringLit action => ns_def
1278             | NSName (COLON_EQUAL) UriLit action => ns_def
1279              
1280             Uses ::= ('uses') StringLit
1281             | ('uses') UriLit
1282              
1283             StringLit ::= String action => string_literal
1284              
1285             ConditionList ::= (LP) Conditions (RP) action => condition_list
1286             | (LP) Conditions Otherwise (RP) action => condition_list
1287              
1288             Conditions ::= Condition+ action => list
1289              
1290             Condition ::= (LP) Expression (RP) (COLON) Expression action => condition
1291              
1292             Otherwise ::= (LP) (RP) (COLON) Expression
1293              
1294             Assign ::= Identifier (COLON_EQUAL) Expression action => assign
1295             | ControlWord (COLON_EQUAL) Expression action => assign
1296              
1297             FuncDef ::= Identifier (LP) FunctionParameters (RP) (COLON_GT) Expression action => function_definition
1298             | Identifier (LP) (RP) (COLON_GT) Expression action => function_definition_sans_args
1299             | Identifier (SLASH) PositiveInteger (COLON_GT) Expression action => function_definition
1300              
1301             FunctionParameters ::= IdentifiersWithPossibleDefaults action => combine_identifiers_options
1302             | OptionDefinitions action => relay_options
1303             | IdentifiersWithPossibleDefaults (COMMA) OptionDefinitions action => combine_identifiers_options
1304              
1305             IdentifiersWithPossibleDefaults ::= IdentifiersWithGlob action => parameters_only
1306             | IdentifiersWithDefaults action => parameters_with_defaults_only
1307             | Identifiers (COMMA) IdentifiersWithDefaults action => combine_parameters
1308              
1309             IdentifiersWithDefaults ::= IdentifierWithDefault+ separator => COMMA action => list
1310              
1311             IdentifierWithDefault ::= Identifier (EQUAL) ConstantValue action => option
1312              
1313             OptionDefinitions ::= OptionDefinition+ separator => COMMA action => list
1314              
1315             OptionDefinition ::= Identifier (RIGHT_ARROW) ConstantValue action => option
1316              
1317             FunctionArguments ::= Bindings action => combine_identifiers_options
1318             | Options action => relay_options
1319             | Bindings (COMMA) Options action => combine_identifiers_options
1320              
1321             Options ::= Option+ separator => COMMA action => list
1322              
1323             Option ::= Identifier (RIGHT_ARROW) Expression action => option
1324              
1325             UriLit ::= Uri action => uri_literal
1326              
1327             # String ::= StringLit action => string_literal
1328              
1329             Boolean ~ boolean
1330              
1331             Inequality ~ inequality
1332              
1333             ATIdentifier ~ '@' identifier
1334              
1335             Identifier ~ identifier | identifier '?' | qcname
1336              
1337             StarIdentifier ~ '*' identifier
1338              
1339             Identifiers ::= Identifier+ separator => COMMA action => list
1340              
1341             IdentifiersWithGlob ::= Identifiers (COMMA) StarIdentifier
1342             | Identifiers
1343              
1344             ControlWord ~ controlWord
1345              
1346             NSName ~ 'xmlns:' identifier | 'ns:' identifier
1347              
1348             Name ~ identifier
1349              
1350             QCName ~ qcname
1351              
1352             Integer ~ integer
1353              
1354             Float ~ float
1355              
1356             PositiveInteger ~ positiveInteger
1357              
1358             NonNegativeInteger ~ zero | positiveInteger
1359              
1360             String ~ qqstring
1361              
1362             StringVector ~ stringVector
1363              
1364             Uri ~ uri
1365              
1366             LambdaArg ~ HASH | HASH positiveInteger
1367              
1368             AMP ~ '&'
1369             AND ~ 'and'
1370             COLON ~ ':'
1371             #COLON_COLON ~ '::'
1372             COLON_COLON_GT ~ '::>'
1373             COLON_COLON_COLON ~ ':::'
1374             COLON_EQUAL ~ ':='
1375             COLON_GT ~ ':>'
1376             COMMA ~ ','
1377             DIV ~ 'div'
1378             DOT ~ '.'
1379             DOT_DOT ~ '..'
1380             DOT_DOT_DOT ~ '...'
1381             DQUOTE ~ '"'
1382             EQUAL ~ '='
1383             FILTER ~ '%'
1384             HASH ~ '#'
1385             GT ~ '>'
1386             GT_GT ~ '>>'
1387             LB ~ '['
1388             LC ~ '{'
1389             SET_START ~ '<|'
1390             LEFT_ARROW ~ '<-'
1391             LP ~ '('
1392             LP_STAR ~ '(*'
1393             LT ~ '<'
1394             LT_COLON_COLON ~ '<::'
1395             LT_LT ~ '<<'
1396             MAP ~ '@'
1397             MINUS ~ '-'
1398             MOD ~ 'mod'
1399             OR ~ 'or'
1400             PIPE ~ '|'
1401             PLUS ~ '+'
1402             QUOTE ~ [']
1403             # '
1404             RB ~ ']'
1405             RC ~ '}'
1406             SET_END ~ '|>'
1407             RIGHT_ARROW ~ '->'
1408             RP ~ ')'
1409             SLASH ~ '/'
1410             STAR ~ '*'
1411             STAR_RP ~ '*)'
1412             TILDE ~ '~'
1413             UNDERSCORE ~ '_'
1414             TRIPLE_UNDERSCORE ~ '___'
1415             Z ~ 'Z'
1416              
1417             STMT_SEP ~ ';'
1418              
1419             <any char> ~ [\d\D\n\r]
1420              
1421             boolean ~ 'true' | 'false'
1422              
1423             digits ~ [_\d] | digits [_\d]
1424              
1425             controlWord ~ '@' <identifier bit>
1426              
1427             inequality ~ '<' | '<=' | '=' | '<>' | '>=' | '>'
1428              
1429             integer ~ negativeInteger | zero | positiveInteger
1430              
1431             negativeInteger ~ '-' positiveInteger
1432              
1433             nonZeroDigit ~ '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
1434              
1435             positiveInteger ~ nonZeroDigit | nonZeroDigit digits | 'inf'
1436              
1437             float ~ negativeFloat | zero '.' zero | positiveFloat
1438              
1439             negativeFloat ~ '-' positiveFloat
1440              
1441             <positiveFloat integer part> ~ nonZeroDigit | nonZeroDigit digits
1442              
1443             <positiveFloat fractional part> ~ digits
1444              
1445             <positiveFloat exponent> ~ [eE] [-+] <integer>
1446              
1447             positiveFloatSansExponent ~ <positiveFloat integer part> '.' zero
1448             | <positiveFloat integer part> '.' <positiveFloat fractional part>
1449             | zero '.' <positiveFloat fractional part>
1450              
1451             positiveFloat ~ positiveFloatSansExponent
1452             | positiveFloatSansExponent <positiveFloat exponent>
1453             | <positiveFloat integer part> <positiveFloat exponent>
1454              
1455             duration ~ 'P' calendarDuration
1456             | 'P' calendarDuration 'T' clockDuration
1457             | 'P' 'T' clockDuration
1458              
1459             calendarDuration ~ yearDuration monthlyDuration
1460             | monthlyDuration
1461              
1462             monthlyDuration ~ monthDuration dayDuration
1463             | dayDuration
1464              
1465             yearDuration ~ zero 'Y'
1466             | positiveInteger 'Y'
1467              
1468             monthDuration ~ zero 'M'
1469             | positiveInteger 'M'
1470              
1471             dayDuration ~ zero 'D'
1472             | positiveInteger 'D'
1473              
1474             clockDuration ~ hourDuration minutelyDuration
1475             | minutelyDuration
1476              
1477             minutelyDuration ~ minuteDuration secondDuration
1478             | secondDuration
1479              
1480             hourDuration ~ zero 'H'
1481             | positiveInteger 'H'
1482              
1483             minuteDuration ~ zero 'M'
1484             | positiveInteger 'M'
1485              
1486             secondDuration ~ zero 'S'
1487             | positiveFloatSansExponent 'S'
1488              
1489             identifier ~ <identifier bit> | identifier '-' <identifier bit>
1490              
1491             <identifier bit> ~ [\w]+
1492              
1493             qcname ~ identifier ':' identifier
1494              
1495             # TODO: add @"lang" to end of string
1496             #
1497             qqstring ~ <qqstring value> | <qqstring value> '@' <qqstring lang>
1498              
1499             jsonstring ~ <qqstring value>
1500              
1501             <qqstring value> ~ DQUOTE qqstringContent DQUOTE | DQUOTE DQUOTE
1502              
1503             qqstringChar ~ [^\"] | '\' <any char>
1504             #"
1505              
1506             <qqstring lang> ~ [a-z][a-z] | [a-z][a-z] '_' [A-Z][A-Z]
1507              
1508             qqstringContent ~ qqstringChar | qqstringContent qqstringChar
1509              
1510             stringVector ~ <stringVector value> | <stringVector value> '@' <qqstring lang>
1511              
1512             <stringVector value> ~ LT_LT stringVectorContent GT_GT | LT_LT GT_GT
1513              
1514             stringVectorContent ~ stringVectorChar | stringVectorContent stringVectorChar
1515              
1516             stringVectorChar ~ [^>] | '>' [^>] | '\' <any char>
1517             #'
1518              
1519             uri ~ '<' uriScheme '://' uriAuthority '/' uriPath '>'
1520             | '<' uriScheme '://' uriAuthority '/' '>'
1521             | '<' uriScheme '://' uriAuthority '>'
1522             | '<' identifier ':' uriPath '>'
1523              
1524             uriScheme ~ [a-z] | uriScheme [-a-z0-9+.]
1525              
1526             uriAuthority ~ uriHostname | uriHostname ':' positiveInteger
1527              
1528             uriPath ~ [^\s]+
1529              
1530             uriHostname ~ <uriHostname bit> '.' <uriHostname bit> | uriHostname '.' <uriHostname bit>
1531              
1532             <uriHostname bit> ~ [-a-z0-9]+
1533              
1534             zero ~ '0'
1535              
1536             :discard ~ whitespace
1537             whitespace ~ [\s]+
1538             # allow comments
1539             :discard ~ <comment>
1540             <comment> ~ LP_STAR <comment body> STAR_RP
1541             <comment body> ~ <comment char>*
1542             #<statement sep char> ~ [;\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}\n\r]
1543             <comment char> ~ [^*)] | '*' [^)] | [^*] ')'