line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Types::Algebraic; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
784230
|
use strict; |
|
11
|
|
|
|
|
117
|
|
|
11
|
|
|
|
|
358
|
|
4
|
11
|
|
|
11
|
|
389
|
use 5.022; |
|
11
|
|
|
|
|
46
|
|
5
|
11
|
|
|
11
|
|
56
|
use warnings; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
666
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.07'; |
7
|
|
|
|
|
|
|
|
8
|
11
|
|
|
11
|
|
74
|
use Carp qw(croak confess); |
|
11
|
|
|
|
|
34
|
|
|
11
|
|
|
|
|
630
|
|
9
|
11
|
|
|
11
|
|
7452
|
use Data::Dumper; |
|
11
|
|
|
|
|
79249
|
|
|
11
|
|
|
|
|
897
|
|
10
|
11
|
|
|
11
|
|
97
|
use List::Util qw(all); |
|
11
|
|
|
|
|
25
|
|
|
11
|
|
|
|
|
1212
|
|
11
|
11
|
|
|
11
|
|
7984
|
use List::MoreUtils qw(pairwise zip_unflatten); |
|
11
|
|
|
|
|
159840
|
|
|
11
|
|
|
|
|
82
|
|
12
|
11
|
|
|
11
|
|
25200
|
use Keyword::Declare; |
|
11
|
|
|
|
|
1293520
|
|
|
11
|
|
|
|
|
132
|
|
13
|
11
|
|
|
11
|
|
3061
|
use Keyword::Simple; |
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
301
|
|
14
|
11
|
|
|
11
|
|
8500
|
use Moops; |
|
11
|
|
|
|
|
198647
|
|
|
11
|
|
|
|
|
77
|
|
15
|
11
|
|
|
11
|
|
719658
|
use PPR; |
|
11
|
|
|
|
|
32
|
|
|
11
|
|
|
|
|
515
|
|
16
|
11
|
|
|
11
|
|
77
|
use Scalar::Util qw(blessed); |
|
11
|
|
|
|
|
26
|
|
|
11
|
|
|
|
|
3247
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $_RETURN_SENTINEL = \23; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our %_KNOWN_CONSTRUCTORS; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my ($expected, $fail_loc) = ('match statement', 0); |
23
|
|
|
|
|
|
|
our $_TA_REGEX_LIB = qr{ |
24
|
|
|
|
|
|
|
(?(DEFINE) |
25
|
|
|
|
|
|
|
(?<ADTPattern> |
26
|
|
|
|
|
|
|
\( (?&PerlOWS) |
27
|
|
|
|
|
|
|
(?{ $expected = "the name of a constructor", $fail_loc = pos() }) |
28
|
|
|
|
|
|
|
(?&PerlIdentifier) # constructor |
29
|
|
|
|
|
|
|
(?{ $expected = "zero or more constructor arguments", $fail_loc = pos() }) |
30
|
|
|
|
|
|
|
(?:(?&PerlNWS) (?&ADTPatternSegment))* # 0 or more arguments |
31
|
|
|
|
|
|
|
(?&PerlOWS) |
32
|
|
|
|
|
|
|
\) |
33
|
|
|
|
|
|
|
) |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
(?<ADTPatternSegment> |
36
|
|
|
|
|
|
|
(?: |
37
|
|
|
|
|
|
|
\$ (?&PerlIdentifier) | # variable |
38
|
|
|
|
|
|
|
(?&PerlIdentifier) | # constuctor without arguments |
39
|
|
|
|
|
|
|
(?&ADTPattern) # constructor with arguments - requires parentheses |
40
|
|
|
|
|
|
|
) |
41
|
|
|
|
|
|
|
) |
42
|
|
|
|
|
|
|
) |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
$PPR::GRAMMAR |
45
|
|
|
|
|
|
|
}xms; |
46
|
|
|
|
|
|
|
|
47
|
11
|
|
|
11
|
|
1477231
|
class ADT { |
|
11
|
|
|
1
|
|
426
|
|
|
11
|
|
|
1
|
|
113
|
|
|
11
|
|
|
1
|
|
32
|
|
|
11
|
|
|
1
|
|
817
|
|
|
11
|
|
|
1
|
|
7684
|
|
|
11
|
|
|
1
|
|
26687
|
|
|
11
|
|
|
1
|
|
52
|
|
|
11
|
|
|
1
|
|
22016
|
|
|
11
|
|
|
1
|
|
32
|
|
|
11
|
|
|
1
|
|
93
|
|
|
11
|
|
|
1
|
|
1824
|
|
|
11
|
|
|
1
|
|
28
|
|
|
11
|
|
|
11
|
|
668
|
|
|
11
|
|
|
|
|
74
|
|
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
1346
|
|
|
11
|
|
|
|
|
425
|
|
|
11
|
|
|
|
|
130
|
|
|
11
|
|
|
|
|
26
|
|
|
11
|
|
|
|
|
140
|
|
|
11
|
|
|
|
|
59174
|
|
|
11
|
|
|
|
|
32
|
|
|
11
|
|
|
|
|
103
|
|
|
11
|
|
|
|
|
13079
|
|
|
11
|
|
|
|
|
42369
|
|
|
11
|
|
|
|
|
60
|
|
|
11
|
|
|
|
|
438103
|
|
|
11
|
|
|
|
|
35
|
|
|
11
|
|
|
|
|
246
|
|
|
11
|
|
|
|
|
8620
|
|
|
11
|
|
|
|
|
107353
|
|
|
11
|
|
|
|
|
134
|
|
|
11
|
|
|
|
|
10351
|
|
|
11
|
|
|
|
|
34181
|
|
|
11
|
|
|
|
|
103
|
|
|
11
|
|
|
|
|
19496
|
|
|
11
|
|
|
|
|
35813
|
|
|
11
|
|
|
|
|
106
|
|
|
11
|
|
|
|
|
1696223
|
|
|
11
|
|
|
|
|
43
|
|
|
11
|
|
|
|
|
69
|
|
|
11
|
|
|
|
|
28
|
|
|
11
|
|
|
|
|
291
|
|
|
11
|
|
|
|
|
61
|
|
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
494
|
|
|
11
|
|
|
|
|
63
|
|
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
9943
|
|
|
11
|
|
|
|
|
52116
|
|
|
0
|
|
|
|
|
0
|
|
48
|
11
|
|
|
|
|
209
|
has tag => (is => "ro", isa => Str); |
49
|
11
|
|
|
|
|
29294
|
has values => (is => "ro", isa => ArrayRef); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub _equality { |
52
|
74
|
|
|
74
|
|
161
|
my ($type, $x, $y) = @_; |
53
|
|
|
|
|
|
|
|
54
|
74
|
100
|
50
|
|
|
487
|
return 0 unless ref($x) && (ref($x) // '') eq (ref($y) // ''); |
|
|
|
50
|
|
|
|
|
|
|
|
66
|
|
|
|
|
55
|
29
|
100
|
|
|
|
178
|
return 0 unless $x->tag eq $y->tag; |
56
|
24
|
50
|
|
22
|
|
103
|
return List::Util::all { $_ } List::MoreUtils::pairwise { $type eq '==' ? $a == $b : $a eq $b } @{$x->values}, @{$y->values}; |
|
22
|
|
|
|
|
130
|
|
|
22
|
|
|
|
|
77
|
|
|
24
|
|
|
|
|
59
|
|
|
24
|
|
|
|
|
310
|
|
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
0
|
|
|
0
|
|
0
|
sub _equality_num { return _equality('==', @_); } |
60
|
0
|
|
|
0
|
|
0
|
sub _equality_str { return _equality('eq', @_); } |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub _stringify { |
63
|
1044
|
|
|
1044
|
|
3393
|
my $v = shift; |
64
|
1044
|
|
|
|
|
1851
|
return $v->tag . "(" . join(", ", map { "$_" } @{ $v->values }) . ")"; |
|
1582
|
|
|
|
|
4470
|
|
|
1044
|
|
|
|
|
2853
|
|
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
use overload |
68
|
0
|
|
|
0
|
|
0
|
'==' => sub { _equality('==', @_) }, |
|
0
|
|
|
|
|
0
|
|
69
|
0
|
|
|
45
|
|
0
|
'!=' => sub { ! _equality('==', @_) }, |
|
45
|
|
|
|
|
1916
|
|
70
|
0
|
|
|
22
|
|
0
|
'eq' => sub { _equality('eq', @_) }, |
|
22
|
|
|
|
|
10514
|
|
71
|
0
|
|
|
7
|
|
0
|
'ne' => sub { ! _equality('eq', @_) }, |
|
7
|
|
|
|
|
11218
|
|
72
|
1
|
|
|
1
|
|
8
|
'""' => \&_stringify; |
|
1
|
|
|
10
|
|
27
|
|
|
1
|
|
|
|
|
15
|
|
|
10
|
|
|
|
|
90
|
|
|
10
|
|
|
|
|
24
|
|
|
10
|
|
|
|
|
148
|
|
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub _apply_pattern { |
76
|
438
|
|
|
438
|
|
63827
|
my ($value, $pattern) = @_; |
77
|
|
|
|
|
|
|
|
78
|
438
|
100
|
|
|
|
890
|
if ($pattern->{type} eq 'variable') { |
79
|
173
|
|
|
|
|
435
|
return (1, [$value]); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
265
|
50
|
33
|
|
|
650
|
return 0 unless $value && blessed($value) && $value->isa('Types::Algebraic::ADT'); |
|
|
|
33
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
265
|
100
|
|
|
|
1023
|
return 0 unless $pattern->{constructor} eq $value->tag; |
85
|
|
|
|
|
|
|
|
86
|
167
|
|
|
|
|
240
|
my @variables; |
87
|
167
|
|
|
|
|
218
|
for my $pair (List::MoreUtils::zip_unflatten(@{$value->values}, @{$pattern->{arguments}})) { |
|
167
|
|
|
|
|
305
|
|
|
167
|
|
|
|
|
765
|
|
88
|
259
|
|
|
|
|
515
|
my ($rv, $new_vars) = _apply_pattern(@$pair); |
89
|
259
|
100
|
|
|
|
579
|
return 0 unless $rv; |
90
|
215
|
|
|
|
|
450
|
push(@variables, @$new_vars); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
123
|
|
|
|
|
344
|
return (1, \@variables); |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
11
|
|
|
11
|
|
347462
|
keytype ADTConstructor is / (?<tag> (?&PerlIdentifier)) (?<fields> (?: (?&PerlNWS) : (?&PerlIdentifier) )* ) /x; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub _verify_constructor { |
99
|
39
|
|
|
39
|
|
242
|
my ($constructor, $arity) = @_; |
100
|
39
|
|
|
|
|
209
|
my $info = $_KNOWN_CONSTRUCTORS{$constructor}; |
101
|
|
|
|
|
|
|
|
102
|
39
|
50
|
|
|
|
200
|
confess("Unknown constructor '$constructor'. Pattern matching requires types created through Types::Algebraic.") |
103
|
|
|
|
|
|
|
unless $info; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
confess("Constructor '$constructor' expects $info->{arg_count} arguments - but is pattern matched with $arity.") |
106
|
39
|
50
|
|
|
|
266
|
unless $info->{arg_count} == $arity; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _parse_pattern { |
111
|
33
|
|
|
33
|
|
117
|
my ($pattern) = @_; |
112
|
33
|
|
|
|
|
909303
|
$pattern =~ m{ |
113
|
|
|
|
|
|
|
\A |
114
|
|
|
|
|
|
|
\( |
115
|
|
|
|
|
|
|
(?&PerlOWS) |
116
|
|
|
|
|
|
|
(?<tag>(?&PerlIdentifier)) |
117
|
|
|
|
|
|
|
(?<identifiers> (?:(?&PerlNWS) (?&ADTPatternSegment))* ) |
118
|
|
|
|
|
|
|
(?&PerlOWS) |
119
|
|
|
|
|
|
|
\) |
120
|
|
|
|
|
|
|
$Types::Algebraic::_TA_REGEX_LIB |
121
|
|
|
|
|
|
|
}xms; |
122
|
33
|
|
|
|
|
6073
|
my ($tag, $idents) = @+{qw(tag identifiers)}; |
123
|
|
|
|
|
|
|
|
124
|
33
|
|
|
|
|
165
|
my @segments; |
125
|
|
|
|
|
|
|
my @variables; |
126
|
33
|
|
|
|
|
903095
|
while ($idents =~ m/(?&PerlNWS) (?<segment>(?&ADTPatternSegment)) $Types::Algebraic::_TA_REGEX_LIB/xmsg) { |
127
|
29
|
|
|
|
|
4599
|
my $segment = $+{segment}; |
128
|
|
|
|
|
|
|
|
129
|
29
|
100
|
|
|
|
280
|
if ($segment =~ m/^\$/) { |
|
|
100
|
|
|
|
|
|
130
|
21
|
|
|
|
|
174
|
push(@segments, { type => 'variable', value => $segment }); |
131
|
21
|
|
|
|
|
565068
|
push(@variables, $segment); |
132
|
|
|
|
|
|
|
} elsif ($segment =~ m/^\(/) { |
133
|
2
|
|
|
|
|
21
|
my ($parsed, $new_vars) = _parse_pattern($segment); |
134
|
2
|
|
|
|
|
14
|
push(@segments, $parsed); |
135
|
2
|
|
|
|
|
53721
|
push(@variables, @$new_vars); |
136
|
|
|
|
|
|
|
} else { |
137
|
6
|
|
|
|
|
43
|
_verify_constructor($segment, 0); |
138
|
|
|
|
|
|
|
|
139
|
6
|
|
|
|
|
163946
|
push(@segments, { type => 'pattern', constructor => $segment, arguments => [] }); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
33
|
|
|
|
|
5704
|
_verify_constructor($tag, scalar @segments); |
144
|
|
|
|
|
|
|
|
145
|
33
|
|
|
|
|
2034
|
return ({ type => 'pattern', constructor => $tag, arguments => \@segments }, \@variables); |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
sub import { |
149
|
11
|
|
|
11
|
|
282
|
Moops->import; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Keyword::Simple::define 'match', sub { |
152
|
13
|
|
|
13
|
|
151831
|
my ($doc_src) = @_; |
153
|
|
|
|
|
|
|
|
154
|
13
|
|
|
|
|
63
|
($expected, $fail_loc) = ('match statement', 0); |
155
|
13
|
|
|
|
|
39
|
my $curly_open = '{'; |
156
|
|
|
|
|
|
|
|
157
|
13
|
50
|
|
|
|
354325
|
$$doc_src =~ s{ |
158
|
|
|
|
|
|
|
\A |
159
|
|
|
|
|
|
|
(?&PerlNWS) |
160
|
13
|
|
|
|
|
2400
|
(?{ $expected = "parenthesized expression", $fail_loc = pos() }) |
161
|
|
|
|
|
|
|
(?<matched_expression> (?&PerlParenthesesList) ) |
162
|
|
|
|
|
|
|
(?&PerlNWS) |
163
|
13
|
|
|
|
|
217
|
(?{ $expected = "a '$curly_open'", $fail_loc = pos() }) |
164
|
|
|
|
|
|
|
\{ (?&PerlOWS) |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$Types::Algebraic::_TA_REGEX_LIB |
167
|
|
|
|
|
|
|
}{}xms or croak("Invalid match statement.\nExpected: $expected\nFound: ", substr($$doc_src, $fail_loc) =~ /(\S+)/,"\n"); |
168
|
|
|
|
|
|
|
|
169
|
13
|
|
|
|
|
2409
|
my $expr = $+{matched_expression}; |
170
|
|
|
|
|
|
|
|
171
|
13
|
|
|
|
|
67
|
my $res = "{\n"; |
172
|
13
|
|
|
|
|
56
|
my $match_body = $expr . "->match(\n"; |
173
|
|
|
|
|
|
|
|
174
|
13
|
|
|
|
|
133
|
while ($$doc_src =~ m/^(?:with|default)/) { |
175
|
35
|
50
|
|
|
|
957755
|
$$doc_src =~ s{ |
176
|
|
|
|
|
|
|
\A |
177
|
35
|
|
|
|
|
909
|
(?{ $expected = "a with or default statement", $fail_loc = pos() }) |
178
|
|
|
|
|
|
|
(?: |
179
|
|
|
|
|
|
|
with (?&PerlNWS) |
180
|
31
|
|
|
|
|
371
|
(?{ $expected = "a match pattern", $fail_loc = pos() }) |
181
|
|
|
|
|
|
|
(?<with_pattern> (?&ADTPattern)) |
182
|
|
|
|
|
|
|
| |
183
|
|
|
|
|
|
|
(?<default> default) |
184
|
|
|
|
|
|
|
) (?&PerlOWS) |
185
|
35
|
|
|
|
|
12811
|
(?{ $expected = "a code block", $fail_loc = pos() }) |
186
|
|
|
|
|
|
|
(?<block> (?&PerlBlock) ) (?&PerlOWS) |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
$Types::Algebraic::_TA_REGEX_LIB |
189
|
|
|
|
|
|
|
}{}xms or croak("Invalid match statement.\nExpected: $expected\nFound: ", substr($$doc_src, $fail_loc) =~ /(\S+)/,"\n"); |
190
|
|
|
|
|
|
|
|
191
|
35
|
|
|
|
|
13810
|
my ($default, $pattern, $block) = @+{qw(default with_pattern block)}; |
192
|
|
|
|
|
|
|
|
193
|
35
|
100
|
|
|
|
254
|
if ($default) { |
194
|
4
|
|
|
|
|
204
|
$match_body .= "[ sub { $block; return \$Types::Algebraic::_RETURN_SENTINEL; } ],\n"; |
195
|
|
|
|
|
|
|
} else { |
196
|
31
|
|
|
|
|
190
|
my ($parsed, $variables) = _parse_pattern($pattern); |
197
|
|
|
|
|
|
|
|
198
|
31
|
|
|
|
|
217
|
local $Data::Dumper::Indent = 0; |
199
|
31
|
|
|
|
|
102
|
local $Data::Dumper::Terse = 1; |
200
|
|
|
|
|
|
|
|
201
|
31
|
|
|
|
|
311
|
my $flattened_pattern = Data::Dumper::Dumper($parsed); |
202
|
31
|
|
|
|
|
4893
|
my $args = join(',', @$variables); |
203
|
|
|
|
|
|
|
|
204
|
31
|
|
|
|
|
851
|
$match_body .= "[$flattened_pattern, sub { my ($args) = \@_; $block; return \$Types::Algebraic::_RETURN_SENTINEL; } ],\n"; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} |
207
|
13
|
|
|
|
|
53
|
$match_body .= ")"; |
208
|
|
|
|
|
|
|
|
209
|
13
|
|
|
|
|
46
|
my $curly_close = '}'; |
210
|
13
|
50
|
|
|
|
358603
|
$$doc_src =~ s{ |
211
|
|
|
|
|
|
|
\A |
212
|
13
|
|
|
|
|
412
|
(?{ $expected = "a '$curly_close'", $fail_loc = pos() }) |
213
|
|
|
|
|
|
|
\} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
$Types::Algebraic::_TA_REGEX_LIB |
216
|
|
|
|
|
|
|
}{}xms or croak("Invalid match statement.\nExpected: $expected\nFound: ", substr($$doc_src, $fail_loc) =~ /(\S+)/,"\n"); |
217
|
|
|
|
|
|
|
|
218
|
13
|
|
|
|
|
2158
|
$res .= <<"EOF"; |
219
|
|
|
|
|
|
|
if (wantarray) { |
220
|
|
|
|
|
|
|
my \@types_algebraic_match_result = $match_body; |
221
|
|
|
|
|
|
|
if (\@types_algebraic_match_result != 1 || \$types_algebraic_match_result[0] != \$Types::Algebraic::_RETURN_SENTINEL) { return \@types_algebraic_match_result }; |
222
|
|
|
|
|
|
|
} else { |
223
|
|
|
|
|
|
|
my \$types_algebraic_match_result = $match_body; |
224
|
|
|
|
|
|
|
if (\$types_algebraic_match_result && \$types_algebraic_match_result != \$Types::Algebraic::_RETURN_SENTINEL) { return \$types_algebraic_match_result; } |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
EOF |
227
|
13
|
|
|
|
|
102
|
$res .= "}\n"; |
228
|
|
|
|
|
|
|
#say STDERR "\n\n\n$res\n\n-----\n"; |
229
|
13
|
|
|
|
|
31187
|
$$doc_src = $res . $$doc_src; |
230
|
11
|
|
|
|
|
14269
|
}; |
231
|
11
|
|
|
|
|
196
|
|
232
|
0
|
50
|
50
|
12
|
|
0
|
keyword data (Ident $name, '=', ADTConstructor* @constructors :sep(/\|/)) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
11
|
|
|
|
|
232
|
|
|
12
|
|
|
|
|
713363
|
|
|
12
|
|
|
|
|
36
|
|
|
12
|
|
|
|
|
42
|
|
233
|
0
|
|
|
|
|
0
|
my %ARGS; |
|
11
|
|
|
|
|
56
|
|
|
12
|
|
|
|
|
43
|
|
234
|
0
|
|
|
|
|
0
|
for my $constructor (@constructors) { |
|
12
|
|
|
|
|
321
|
|
235
|
|
|
|
|
|
|
my $tag = $constructor->{tag}; |
236
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
12
|
|
|
|
|
500
|
|
|
12
|
|
|
|
|
94
|
|
|
12
|
|
|
|
|
30
|
|
237
|
0
|
|
|
|
|
0
|
my @args; |
|
12
|
|
|
|
|
34
|
|
238
|
0
|
|
0
|
|
|
0
|
while ($constructor->{fields} =~ m/ (?&PerlNWS) : (?<ident> (?&PerlIdentifier) ) $PPR::GRAMMAR/xg ) { |
|
12
|
|
66
|
|
|
328727
|
|
239
|
0
|
|
|
|
|
0
|
push(@args, $+{ident}); |
|
29
|
|
|
|
|
463464
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
0
|
|
|
|
|
0
|
$ARGS{$tag} = scalar @args; |
|
12
|
|
|
|
|
324679
|
|
243
|
|
|
|
|
|
|
|
244
|
0
|
|
|
|
|
0
|
$_KNOWN_CONSTRUCTORS{$tag} = { |
|
12
|
|
|
|
|
55
|
|
245
|
0
|
|
|
|
|
0
|
typename => $name, |
|
12
|
|
|
|
|
33
|
|
246
|
0
|
|
|
|
|
0
|
arg_count => scalar @args, |
|
12
|
|
|
|
|
54
|
|
247
|
0
|
|
|
|
|
0
|
}; |
|
29
|
|
|
|
|
531
|
|
248
|
|
|
|
|
|
|
} |
249
|
0
|
|
|
|
|
0
|
|
|
29
|
|
|
|
|
87
|
|
250
|
0
|
|
|
|
|
0
|
my $args_str = join(", ", map { "$_ => $ARGS{$_}" } keys %ARGS); |
|
29
|
|
|
|
|
786803
|
|
251
|
0
|
|
|
|
|
0
|
|
|
14
|
|
|
|
|
382146
|
|
252
|
|
|
|
|
|
|
my $res = <<CODE; |
253
|
|
|
|
|
|
|
class $name extends Types::Algebraic::ADT { |
254
|
0
|
|
|
|
|
0
|
my %ARGS = ( $args_str ); |
|
29
|
|
|
|
|
4029
|
|
255
|
|
|
|
|
|
|
CODE |
256
|
0
|
|
|
|
|
0
|
|
|
29
|
|
|
|
|
279
|
|
257
|
|
|
|
|
|
|
$res .= <<'CODE'; |
258
|
|
|
|
|
|
|
sub BUILD { |
259
|
|
|
|
|
|
|
my ($self, $args) = @_; |
260
|
|
|
|
|
|
|
my $tag = $args->{tag} || confess("tag is required - please use public interface"); |
261
|
|
|
|
|
|
|
my $values = $args->{values} || confess("values is required - please use public interface"); |
262
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
12
|
|
|
|
|
139
|
|
|
29
|
|
|
|
|
253
|
|
263
|
|
|
|
|
|
|
confess("Unknown constructor $tag") unless exists $ARGS{$tag}; |
264
|
0
|
|
|
|
|
0
|
confess("$tag expects $ARGS{$tag} arguments - given ".scalar @$values) unless @$values == $ARGS{$tag}; |
|
12
|
|
|
|
|
230
|
|
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub match { |
268
|
|
|
|
|
|
|
my $self = shift; |
269
|
0
|
|
|
|
|
0
|
for my $case (@_) { |
|
12
|
|
|
|
|
205
|
|
270
|
|
|
|
|
|
|
if (@$case == 2) { |
271
|
|
|
|
|
|
|
my ($pattern, $f) = @$case; |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
my ($matches, $values) = Types::Algebraic::_apply_pattern($self, $pattern); |
274
|
|
|
|
|
|
|
return $f->(@$values) if $matches; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
# default |
277
|
|
|
|
|
|
|
if (@$case == 1) { |
278
|
|
|
|
|
|
|
return $case->[0]->(@{ $self->values }); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
CODE |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
for my $key (keys %ARGS) { |
286
|
|
|
|
|
|
|
$res .= <<CODE; |
287
|
|
|
|
|
|
|
sub $key { return $name->new( tag => '$key', values => [\@_] ); } |
288
|
|
|
|
|
|
|
CODE |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
#say STDERR $res; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
return $res; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
0
|
|
|
|
|
0
|
} |
|
12
|
|
|
|
|
62
|
|
298
|
0
|
|
|
|
|
0
|
|
|
29
|
|
|
|
|
534
|
|
299
|
0
|
|
|
0
|
|
0
|
sub unimport { |
300
|
0
|
0
|
0
|
|
|
0
|
unkeyword data; |
|
0
|
|
|
|
|
0
|
|
301
|
0
|
|
|
|
|
0
|
Keyword::Simple::undefine 'match'; |
|
0
|
|
|
|
|
0
|
|
302
|
11
|
|
|
11
|
|
308171
|
} |
303
|
11
|
|
|
|
|
2905
|
|
304
|
|
|
|
|
|
|
1; |
305
|
|
|
|
|
|
|
__END__ |
306
|
0
|
|
|
|
|
0
|
|
|
12
|
|
|
|
|
434
|
|
307
|
11
|
|
|
|
|
113
|
=encoding utf-8 |
|
11
|
|
|
|
|
117
|
|
308
|
11
|
|
|
|
|
636
|
|
309
|
11
|
|
|
11
|
|
453234
|
=head1 NAME |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
Types::Algebraic - Algebraic data types in perl |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head1 SYNOPSIS |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
use Types::Algebraic; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
data Maybe = Nothing | Just :v; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
my $sum = 0; |
320
|
|
|
|
|
|
|
my @vs = ( Nothing, Just(5), Just(7), Nothing, Just(6) ); |
321
|
|
|
|
|
|
|
for my $v (@vs) { |
322
|
|
|
|
|
|
|
match ($v) { |
323
|
|
|
|
|
|
|
with (Nothing) { } |
324
|
|
|
|
|
|
|
with (Just $v) { $sum += $v; } |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
say $sum; |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head1 DESCRIPTION |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Types::Algebraic is an implementation of L<algebraic data types|https://en.wikipedia.org/wiki/Algebraic_data_type> in perl. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
These kinds of data types are often seen in functional languages, and allow you to create and consume structured data containers very succinctly. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
The module provides two keywords: L</"data"> for creating a new data type, and L</"match"> to provide pattern matching on the type. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=head1 USAGE |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=head2 Creating a new type with C<data> |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
The C<data> keyword is used for creating a new type. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
The code |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
data Maybe = Nothing | Just :v; |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
creates a new type, of name C<Maybe>, which has 2 I<data constructors>, C<Nothing> (taking no parameters), and C<Just> (taking 1 parameter). |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
You may insantiate values of this type by using one of the constructors with the appropriate number of arguments. |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
my $a = Nothing; |
352
|
|
|
|
|
|
|
my $b = Just 5; |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head2 Unpacking values with C<match> |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
In order to access the data stored within one of these values, you can use the C<match> keyword. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
my $value = Just 7; |
359
|
|
|
|
|
|
|
match ($value) { |
360
|
|
|
|
|
|
|
with (Nothing) { say "There was nothing in there. :("; } |
361
|
|
|
|
|
|
|
with (Just $v) { say "I got the value $v!"; } |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
The cases are matched from the top down, and only the first matching case is run. |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
You can also create a default fallback case, which will always run if reached. |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
data Color = Red | Blue | Green | White | Black; |
369
|
|
|
|
|
|
|
match ($color) { |
370
|
|
|
|
|
|
|
with (Red) { say "Yay, you picked my favorite color!"; } |
371
|
|
|
|
|
|
|
default { say "Bah. You clearly have no taste."; } |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=head2 Nested patterns |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
Note, patterns can be nested, allowing for more complex unpacking: |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
data PairingHeap = Empty | Heap :head :subheaps; |
379
|
|
|
|
|
|
|
data Pair = Pair :left :right; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# Merge two pairing heaps (https://en.wikipedia.org/wiki/Pairing_heap) |
382
|
|
|
|
|
|
|
sub merge { |
383
|
|
|
|
|
|
|
my ($h1, $h2) = @_; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
match (Pair($h1, $h2)) { |
386
|
|
|
|
|
|
|
with (Pair Empty $h) { return $h; } |
387
|
|
|
|
|
|
|
with (Pair $h Empty) { return $h; } |
388
|
|
|
|
|
|
|
with (Pair (Heap $e1 $s1) (Heap $e2 $s2)) { |
389
|
|
|
|
|
|
|
return $e1 < $e2 ? Heap($e1, [$h2, @$s1]) |
390
|
|
|
|
|
|
|
: Heap($e2, [$h1, @$s2]); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=head1 LIMITATIONS |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=over 4 |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=item Currently, match statements can't be nested. |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=back |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=head1 BUGS |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Please report bugs directly on L<the project's GitHub page|https://github.com/Eckankar/Types-Algebraic>. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=head1 AUTHOR |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Sebastian Paaske Tørholm E<lt>sebbe@cpan.orgE<gt> |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=head1 COPYRIGHT |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
Copyright 2020- Sebastian Paaske Tørholm |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=head1 LICENSE |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
418
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 SEE ALSO |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=cut |