line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
use 5.010001; |
3
|
22
|
|
|
22
|
|
13758
|
use strict; |
|
22
|
|
|
|
|
68
|
|
4
|
22
|
|
|
22
|
|
102
|
use warnings; |
|
22
|
|
|
|
|
43
|
|
|
22
|
|
|
|
|
382
|
|
5
|
22
|
|
|
22
|
|
99
|
use Log::ger; |
|
22
|
|
|
|
|
35
|
|
|
22
|
|
|
|
|
520
|
|
6
|
22
|
|
|
22
|
|
100
|
|
|
22
|
|
|
|
|
34
|
|
|
22
|
|
|
|
|
127
|
|
7
|
|
|
|
|
|
|
use Mo qw(build default); |
8
|
22
|
|
|
22
|
|
4024
|
extends 'Data::Sah::Compiler'; |
|
22
|
|
|
|
|
51
|
|
|
22
|
|
|
|
|
127
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#use Digest::MD5 qw(md5_hex); |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# human compiler, to produce error messages |
13
|
|
|
|
|
|
|
has hc => (is => 'rw'); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# subclass should provide a default, choices: 'shell', 'c', 'ini', 'cpp' |
16
|
|
|
|
|
|
|
has comment_style => (is => 'rw'); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
has var_sigil => (is => 'rw'); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
has concat_op => (is => 'rw'); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has logical_and_op => (is => 'rw', default => sub {'&&'}); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
has logical_not_op => (is => 'rw', default => sub {'!'}); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
#has logical_or_op => (is => 'rw', default => sub {'||'}); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY |
29
|
|
|
|
|
|
|
our $DATE = '2022-08-20'; # DATE |
30
|
|
|
|
|
|
|
our $DIST = 'Data-Sah'; # DIST |
31
|
|
|
|
|
|
|
our $VERSION = '0.912'; # VERSION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my ($self, %args) = @_; |
34
|
|
|
|
|
|
|
|
35
|
5060
|
|
|
5060
|
0
|
32715
|
my $cd = $self->SUPER::init_cd(%args); |
36
|
|
|
|
|
|
|
$cd->{vars} = {}; |
37
|
5060
|
|
|
|
|
28370
|
|
38
|
5060
|
|
|
|
|
14697
|
my $hc = $self->hc; |
39
|
|
|
|
|
|
|
if (!$hc) { |
40
|
5060
|
|
|
|
|
13283
|
$hc = $self->main->get_compiler("human"); |
41
|
5060
|
100
|
|
|
|
23072
|
$self->hc($hc); |
42
|
4725
|
|
|
|
|
9992
|
} |
43
|
4725
|
|
|
|
|
13198
|
|
44
|
|
|
|
|
|
|
if (my $ocd = $cd->{outer_cd}) { |
45
|
|
|
|
|
|
|
$cd->{vars} = $ocd->{vars}; |
46
|
5060
|
100
|
|
|
|
23883
|
$cd->{modules} = $ocd->{modules}; |
47
|
330
|
|
|
|
|
645
|
$cd->{functions} = $ocd->{functions}; |
48
|
330
|
|
|
|
|
603
|
$cd->{_hc} = $ocd->{_hc}; |
49
|
330
|
|
|
|
|
847
|
$cd->{_hcd} = $ocd->{_hcd}; |
50
|
330
|
|
|
|
|
606
|
$cd->{_subdata_level} = $ocd->{_subdata_level}; |
51
|
330
|
|
|
|
|
526
|
$cd->{use_dpath} = 1 if $ocd->{use_dpath}; |
52
|
330
|
|
|
|
|
467
|
} else { |
53
|
330
|
100
|
|
|
|
826
|
$cd->{vars} = {}; |
54
|
|
|
|
|
|
|
$cd->{modules} = []; |
55
|
4730
|
|
|
|
|
11681
|
$cd->{functions} = {}; |
56
|
4730
|
|
|
|
|
10298
|
$cd->{_hc} = $hc; |
57
|
4730
|
|
|
|
|
9157
|
$cd->{_subdata_level} = 0; |
58
|
4730
|
|
|
|
|
7938
|
} |
59
|
4730
|
|
|
|
|
14964
|
|
60
|
|
|
|
|
|
|
$cd; |
61
|
|
|
|
|
|
|
} |
62
|
5060
|
|
|
|
|
24589
|
|
63
|
|
|
|
|
|
|
my ($self, $args) = @_; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
return if $args->{_args_checked_Prog}++; |
66
|
9784
|
|
|
9784
|
0
|
20087
|
|
67
|
|
|
|
|
|
|
$self->SUPER::check_compile_args($args); |
68
|
9784
|
100
|
|
|
|
28326
|
|
69
|
|
|
|
|
|
|
my $ct = ($args->{code_type} //= 'validator'); |
70
|
4730
|
|
|
|
|
17567
|
if ($ct ne 'validator') { |
71
|
|
|
|
|
|
|
$self->_die({}, "code_type currently can only be 'validator'"); |
72
|
4730
|
|
50
|
|
|
16009
|
} |
73
|
4730
|
50
|
|
|
|
11173
|
for ($args->{return_type}) { |
74
|
0
|
|
|
|
|
0
|
$_ //= 'bool_valid'; |
75
|
|
|
|
|
|
|
# old values that are still supported but now deprecated |
76
|
4730
|
|
|
|
|
9039
|
$_ = "bool_valid" if $_ eq 'bool'; |
77
|
4730
|
|
100
|
|
|
13710
|
$_ = "bool_valid+val" if $_ eq 'bool+val'; |
78
|
|
|
|
|
|
|
$_ = "str_errmsg" if $_ eq 'str'; |
79
|
4730
|
50
|
|
|
|
9112
|
$_ = "str_errmsg+val" if $_ eq 'str+val'; |
80
|
4730
|
50
|
|
|
|
8649
|
$_ = "hash_details" if $_ eq 'full'; |
81
|
4730
|
50
|
|
|
|
8966
|
} |
82
|
4730
|
50
|
|
|
|
8536
|
my $rt = $args->{return_type}; |
83
|
4730
|
50
|
|
|
|
10966
|
if ($rt !~ /\A(bool_valid\+val|bool_valid|str_errmsg\+val|str_errmsg|hash_details)\z/) { |
84
|
|
|
|
|
|
|
$self->_die({}, "Invalid value for return_type, ". |
85
|
4730
|
|
|
|
|
6926
|
"use bool_valid+val|bool_valid|str_errmsg+val|str_errmsg|hash_details"); |
86
|
4730
|
50
|
|
|
|
18451
|
} |
87
|
0
|
|
|
|
|
0
|
$args->{var_prefix} //= "_sahv_"; |
88
|
|
|
|
|
|
|
$args->{sub_prefix} //= "_sahs_"; |
89
|
|
|
|
|
|
|
$args->{data_term} //= $self->var_sigil . $args->{data_name}; |
90
|
4730
|
|
50
|
|
|
19004
|
$args->{data_term_is_lvalue} //= 1; |
91
|
4730
|
|
50
|
|
|
18448
|
$args->{tmp_data_name} //= "tmp_$args->{data_name}"; |
92
|
4730
|
|
33
|
|
|
20113
|
$args->{tmp_data_term} //= $self->var_sigil . $args->{tmp_data_name}; |
93
|
4730
|
|
50
|
|
|
39663
|
$args->{comment} //= 1; |
94
|
4730
|
|
33
|
|
|
19299
|
$args->{err_term} //= $self->var_sigil . "err_$args->{data_name}"; |
95
|
4730
|
|
33
|
|
|
14828
|
$args->{coerce} //= 1; |
96
|
4730
|
|
50
|
|
|
31064
|
} |
97
|
4730
|
|
33
|
|
|
17505
|
|
98
|
4730
|
|
50
|
|
|
36198
|
my ($self, $cd, @args) = @_; |
99
|
|
|
|
|
|
|
return '' unless $cd->{args}{comment}; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my $content = join("", @args); |
102
|
17105
|
|
|
17105
|
1
|
31965
|
$content =~ s/\n+/ /g; |
103
|
17105
|
50
|
|
|
|
33785
|
|
104
|
|
|
|
|
|
|
my $style = $self->comment_style; |
105
|
17105
|
|
|
|
|
29608
|
if ($style eq 'shell') { |
106
|
17105
|
|
|
|
|
30171
|
return join("", "# ", $content, "\n"); |
107
|
|
|
|
|
|
|
} elsif ($style eq 'shell2') { |
108
|
17105
|
|
|
|
|
37807
|
return join("", "## ", $content, "\n"); |
109
|
17105
|
50
|
|
|
|
74418
|
} elsif ($style eq 'cpp') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
110
|
17105
|
|
|
|
|
55481
|
return join("", "// ", $content, "\n"); |
111
|
|
|
|
|
|
|
} elsif ($style eq 'c') { |
112
|
0
|
|
|
|
|
0
|
return join("", "/* ", $content, '*/'); |
113
|
|
|
|
|
|
|
} elsif ($style eq 'ini') { |
114
|
0
|
|
|
|
|
0
|
return join("", "; ", $content, "\n"); |
115
|
|
|
|
|
|
|
} else { |
116
|
0
|
|
|
|
|
0
|
$self->_die($cd, "BUG: Unknown comment style: $style"); |
117
|
|
|
|
|
|
|
} |
118
|
0
|
|
|
|
|
0
|
} |
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
0
|
# enclose expression with parentheses, unless it already is |
121
|
|
|
|
|
|
|
my ($self, $expr, $force) = @_; |
122
|
|
|
|
|
|
|
if ($expr =~ /\A(\s*)(\(.+\)\s*)\z/os) { |
123
|
|
|
|
|
|
|
return $expr if !$force; |
124
|
|
|
|
|
|
|
return "$1($2)"; |
125
|
|
|
|
|
|
|
} else { |
126
|
55497
|
|
|
55497
|
0
|
83684
|
$expr =~ /\A(\s*)(.*)/os; |
127
|
55497
|
100
|
|
|
|
163233
|
return "$1($2)"; |
128
|
30047
|
100
|
|
|
|
90564
|
} |
129
|
6605
|
|
|
|
|
24202
|
} |
130
|
|
|
|
|
|
|
|
131
|
25450
|
|
|
|
|
54842
|
my ($self, $cd, $name, $value) = @_; |
132
|
25450
|
|
|
|
|
110750
|
|
133
|
|
|
|
|
|
|
return if exists $cd->{vars}{$name}; |
134
|
|
|
|
|
|
|
#$log->tracef("TMP: add_var %s", $name); |
135
|
|
|
|
|
|
|
$cd->{vars}{$name} = $value; |
136
|
|
|
|
|
|
|
} |
137
|
13479
|
|
|
13479
|
0
|
22912
|
|
138
|
|
|
|
|
|
|
# naming convention: expr_NOUN(), stmt_VERB(_NOUN)?() |
139
|
13479
|
100
|
|
|
|
29749
|
|
140
|
|
|
|
|
|
|
# XXX requires: expr_list |
141
|
5042
|
|
|
|
|
12088
|
|
142
|
|
|
|
|
|
|
# XXX requires: expr_defined |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# XXX requires: expr_array |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# XXX requires: expr_array_subscript |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# XXX requires: expr_last_elem |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# XXX requires: expr_push |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# XXX requires: expr_pop |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# XXX requires: expr_push_and_pop_dpath_between_expr |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# XXX requires: expr_prefix_dpath |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# XXX requires: expr_set |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# XXX requires: expr_setif |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# XXX requires: expr_set_err_str |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# XXX requires: expr_set_err_full |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# XXX requires: expr_reset_err_str |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# XXX requires: expr_reset_err_full |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# XXX requires: expr_ternary |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# XXX requires: expr_log |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# XXX requires: expr_block |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# XXX requires: expr_anon_sub |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# XXX requires: expr_eval |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# XXX requires: expr_refer_or_call_sub |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# TODO XXX requires: expr_declare_lexical_var |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# XXX requires: stmt_declare_local_var |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# XXX requires: stmt_require_module |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# XXX requires: stmt_require_log_module |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# XXX requires: stmt_assign_hash_value |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# XXX requires: stmt_sub |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# XXX requires: stmt_return |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# XXX requires: sub_defined |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# XXX requires: gen_cached_validator |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
my ($self, $cd, $text) = @_; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
my $hc = $cd->{_hc}; |
203
|
|
|
|
|
|
|
my $hcd = $cd->{_hcd}; |
204
|
|
|
|
|
|
|
#$log->tracef("(Prog) Translating text %s ...", $text); |
205
|
|
|
|
|
|
|
$hc->_xlt($hcd, $text); |
206
|
|
|
|
|
|
|
} |
207
|
19465
|
|
|
19465
|
|
31608
|
|
208
|
|
|
|
|
|
|
# concatenate strings |
209
|
19465
|
|
|
|
|
26005
|
my ($self, @t) = @_; |
210
|
19465
|
|
|
|
|
27560
|
join(" " . $self->concat_op . " ", @t); |
211
|
|
|
|
|
|
|
} |
212
|
19465
|
|
|
|
|
45946
|
|
213
|
|
|
|
|
|
|
# variable |
214
|
|
|
|
|
|
|
my ($self, $v) = @_; |
215
|
|
|
|
|
|
|
$self->var_sigil. $v; |
216
|
|
|
|
|
|
|
} |
217
|
0
|
|
|
0
|
0
|
0
|
|
218
|
0
|
|
|
|
|
0
|
my ($self, $t) = @_; |
219
|
|
|
|
|
|
|
"++$t"; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
my ($self, $v) = @_; |
223
|
607
|
|
|
607
|
0
|
1271
|
"++" . $self->var_sigil. $v; |
224
|
607
|
|
|
|
|
1523
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# expr_postinc |
227
|
|
|
|
|
|
|
# expr_predec |
228
|
0
|
|
|
0
|
0
|
0
|
# expr_postdec |
229
|
0
|
|
|
|
|
0
|
|
230
|
|
|
|
|
|
|
# args: log_result, var_term, err_term. the rest is the same/supplied to |
231
|
|
|
|
|
|
|
# compile(). |
232
|
|
|
|
|
|
|
my ($self, %args) = @_; |
233
|
2428
|
|
|
2428
|
0
|
3880
|
|
234
|
2428
|
|
|
|
|
4640
|
my $cache = $args{cache}; |
235
|
|
|
|
|
|
|
my $log_result = $args{log_result}; |
236
|
|
|
|
|
|
|
my $dt = $args{data_term}; |
237
|
|
|
|
|
|
|
my $vt = delete($args{var_term}) // $dt; |
238
|
|
|
|
|
|
|
my $do_log = $args{debug_log} // $args{debug}; |
239
|
|
|
|
|
|
|
my $rt = $args{return_type} // 'bool_valid'; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
$args{indent_level} = 1; |
242
|
|
|
|
|
|
|
if ($cache) { |
243
|
|
|
|
|
|
|
# ... |
244
|
4724
|
|
|
4724
|
0
|
31284
|
} |
245
|
|
|
|
|
|
|
my $cd = $args{cd} // $self->compile(%args); |
246
|
4724
|
|
|
|
|
8769
|
my $et = $cd->{args}{err_term}; |
247
|
4724
|
|
|
|
|
8764
|
|
248
|
4724
|
|
|
|
|
6704
|
if ($rt !~ /\Abool/) { |
249
|
4724
|
|
33
|
|
|
15309
|
my ($ev) = $et =~ /(\w+)/; # to remove sigil |
250
|
4724
|
|
33
|
|
|
15646
|
$self->add_var($cd, $ev, $rt =~ /\Astr/ ? undef : {}); |
251
|
4724
|
|
50
|
|
|
10375
|
} |
252
|
|
|
|
|
|
|
my $resv = '_sahv_res'; |
253
|
4724
|
|
|
|
|
7122
|
my $rest = $self->var_sigil . $resv; |
254
|
4724
|
50
|
|
|
|
8644
|
|
255
|
|
|
|
|
|
|
my $needs_expr_block = (grep {$_->{phase} eq 'runtime'} @{ $cd->{modules} }) |
256
|
|
|
|
|
|
|
|| $do_log; |
257
|
4724
|
|
33
|
|
|
31309
|
|
258
|
4691
|
|
|
|
|
20776
|
my $code_sub_body = join( |
259
|
|
|
|
|
|
|
"", |
260
|
4691
|
100
|
|
|
|
15347
|
(map {$self->stmt_declare_local_var( |
261
|
2943
|
|
|
|
|
12726
|
$_, $self->literal($cd->{vars}{$_}))."\n"} |
262
|
2943
|
100
|
|
|
|
13342
|
sort keys %{ $cd->{vars} }), |
263
|
|
|
|
|
|
|
#$log->tracef('-> (validator)(%s) ...', $dt);\n"; |
264
|
4691
|
|
|
|
|
7245
|
$self->stmt_declare_local_var($resv, "\n\n" . $cd->{result})."\n\n", |
265
|
4691
|
|
|
|
|
12061
|
|
266
|
|
|
|
|
|
|
# when rt=bool_valid, return true/false result |
267
|
4691
|
|
66
|
|
|
20532
|
#(";\n\n\$log->tracef('<- validator() = %s', \$res)") |
268
|
|
|
|
|
|
|
# x !!($do_log && $rt eq 'bool_valid'), |
269
|
|
|
|
|
|
|
($self->stmt_return($rest)."\n") |
270
|
|
|
|
|
|
|
x !!($rt eq 'bool_valid'), |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# when rt=str_errmsg, return string error message |
273
|
5038
|
|
|
|
|
14009
|
#($log->tracef('<- validator() = %s', ". |
274
|
4691
|
|
|
|
|
21576
|
# "\$err_data);\n\n"; |
275
|
|
|
|
|
|
|
# x !!($do_log && $rt eq 'str_errmsg'), |
276
|
4691
|
|
|
|
|
8042
|
($self->expr_set_err_str($et, $self->literal('')).";", |
277
|
|
|
|
|
|
|
"\n\n".$self->stmt_return($et)."\n") |
278
|
|
|
|
|
|
|
x !!($rt eq 'str_errmsg'), |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# when rt=bool_valid+val, return true/false result as well as |
281
|
|
|
|
|
|
|
# final input value |
282
|
|
|
|
|
|
|
($self->stmt_return($self->expr_array($rest, $dt))."\n") |
283
|
|
|
|
|
|
|
x !!($rt eq 'bool_valid+val'), |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# when rt=str_errmsg+val, return string error message as well as |
286
|
|
|
|
|
|
|
# final input value |
287
|
|
|
|
|
|
|
($self->expr_set_err_str($et, $self->literal('')).";", |
288
|
|
|
|
|
|
|
"\n\n".$self->stmt_return($self->expr_array($et, $dt))."\n") |
289
|
|
|
|
|
|
|
x !!($rt eq 'str_errmsg+val'), |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# when rt=hash_details, return error hash |
292
|
|
|
|
|
|
|
($self->stmt_assign_hash_value($et, $self->literal('value'), $dt), |
293
|
|
|
|
|
|
|
"\n".$self->stmt_return($et)."\n") |
294
|
|
|
|
|
|
|
x !!($rt eq 'hash_details'), |
295
|
|
|
|
|
|
|
); |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
my $code = join( |
298
|
|
|
|
|
|
|
"", |
299
|
|
|
|
|
|
|
($self->stmt_require_log_module."\n") x !!$do_log, |
300
|
|
|
|
|
|
|
(map { $self->stmt_require_module($_)."\n" } |
301
|
|
|
|
|
|
|
grep { $_->{phase} eq 'runtime' } @{ $cd->{modules} }), |
302
|
|
|
|
|
|
|
$self->expr_anon_sub([$vt], $code_sub_body), |
303
|
|
|
|
|
|
|
); |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
if ($needs_expr_block) { |
306
|
|
|
|
|
|
|
$code = $self->expr_block($code); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
if ($log_result && log_is_trace()) { |
310
|
|
|
|
|
|
|
log_trace("validator code:\n%s", |
311
|
|
|
|
|
|
|
($ENV{LINENUM} // 1) ? |
312
|
7185
|
|
|
|
|
15107
|
Data::Sah::Compiler::__linenum($code) : |
313
|
4691
|
|
|
|
|
20142
|
$code); |
|
13526
|
|
|
|
|
25793
|
|
|
4691
|
|
|
|
|
9137
|
|
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
$code; |
317
|
4691
|
100
|
|
|
|
17804
|
} |
318
|
4651
|
|
|
|
|
14113
|
|
319
|
|
|
|
|
|
|
# add compiled clause to ccls, along with extra information useful for joining |
320
|
|
|
|
|
|
|
# later (like error level, code for adding error message, etc). available |
321
|
4691
|
50
|
33
|
|
|
13799
|
# options: |
322
|
|
|
|
|
|
|
# |
323
|
0
|
0
|
0
|
|
|
0
|
# - err_level (str, the default will be taken from current clause's .err_level |
324
|
|
|
|
|
|
|
# if not specified), |
325
|
|
|
|
|
|
|
# |
326
|
|
|
|
|
|
|
# - err_expr (str, a string expression in the target language that evaluates to |
327
|
|
|
|
|
|
|
# an error message, the more general and dynamic alternative to err_msg. |
328
|
4691
|
|
|
|
|
142386
|
# |
329
|
|
|
|
|
|
|
# - err_msg (str, the default will be produced by human compiler if not |
330
|
|
|
|
|
|
|
# supplied, or taken from current clause's .err_msg), |
331
|
|
|
|
|
|
|
# |
332
|
|
|
|
|
|
|
# - subdata (bool, default false, if set to true then this means we are |
333
|
|
|
|
|
|
|
# delving into subdata, e.g. array elements or hash pair values, and appropriate |
334
|
|
|
|
|
|
|
# things must be done to adjust for this [e.g. push_dpath/pop_dpath at the end |
335
|
|
|
|
|
|
|
# so that error message can show the proper data path]. |
336
|
|
|
|
|
|
|
# |
337
|
|
|
|
|
|
|
# - assert (bool, default false, if set to true means this ccl is an assert ccl, |
338
|
|
|
|
|
|
|
# meaning it always returns true and is not translated from an actual clause. it |
339
|
|
|
|
|
|
|
# will not affect number of errors nor produce error messages.) |
340
|
|
|
|
|
|
|
my ($self, $cd, $ccl, $opts) = @_; |
341
|
|
|
|
|
|
|
$opts //= {}; |
342
|
|
|
|
|
|
|
my $clause = $cd->{clause} // ""; |
343
|
|
|
|
|
|
|
my $op = $cd->{cl_op} // ""; |
344
|
|
|
|
|
|
|
#$log->errorf("TMP: adding ccl %s, current ccls=%s", $ccl, $cd->{ccls}); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
my $el = $opts->{err_level} // $cd->{clset}{"$clause.err_level"} // "error"; |
347
|
|
|
|
|
|
|
my $err_expr = $opts->{err_expr}; |
348
|
|
|
|
|
|
|
my $err_msg = $opts->{err_msg}; |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
if (defined $err_expr) { |
351
|
|
|
|
|
|
|
$self->add_var($cd, '_sahv_dpath', []) if $cd->{use_dpath}; |
352
|
|
|
|
|
|
|
$err_expr = $self->expr_prefix_dpath($err_expr) if $cd->{use_dpath}; |
353
|
18651
|
|
|
18651
|
0
|
58019
|
} else { |
354
|
18651
|
|
100
|
|
|
45340
|
unless (defined $err_msg) { $err_msg = $cd->{clset}{"$clause.err_msg"} } |
355
|
18651
|
|
100
|
|
|
49924
|
unless (defined $err_msg) { |
356
|
18651
|
|
100
|
|
|
45073
|
# XXX how to invert on op='none' or op='not'? |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
my @msgpath = @{$cd->{spath}}; |
359
|
18651
|
|
100
|
|
|
69252
|
my $msgpath; |
|
|
|
100
|
|
|
|
|
360
|
18651
|
|
|
|
|
25001
|
my $hc = $cd->{_hc}; |
361
|
18651
|
|
|
|
|
24279
|
my $hcd = $cd->{_hcd}; |
362
|
|
|
|
|
|
|
while (1) { |
363
|
18651
|
100
|
|
|
|
29678
|
# search error message, use more general one if the more |
364
|
133
|
100
|
|
|
|
412
|
# specific one is not available |
365
|
133
|
100
|
|
|
|
383
|
last unless @msgpath; |
366
|
|
|
|
|
|
|
$msgpath = join("/", @msgpath); |
367
|
18518
|
100
|
|
|
|
34536
|
my $ccls = $hcd->{result}{$msgpath}; |
|
6037
|
|
|
|
|
12388
|
|
368
|
18518
|
100
|
|
|
|
32555
|
pop @msgpath; |
369
|
|
|
|
|
|
|
if ($ccls) { |
370
|
|
|
|
|
|
|
local $hcd->{args}{format} = 'inline_err_text'; |
371
|
6037
|
|
|
|
|
8126
|
$err_msg = $hc->format_ccls($hcd, $ccls); |
|
6037
|
|
|
|
|
15905
|
|
372
|
6037
|
|
|
|
|
8268
|
# show path when debugging |
373
|
6037
|
|
|
|
|
8307
|
$err_msg = "(msgpath=$msgpath) $err_msg" |
374
|
6037
|
|
|
|
|
9004
|
if $cd->{args}{debug}; |
375
|
6037
|
|
|
|
|
7347
|
last; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
8518
|
100
|
|
|
|
16249
|
if (!$err_msg) { |
379
|
8236
|
|
|
|
|
16459
|
$err_msg = "ERR (clause=".($cd->{clause} // "").")"; |
380
|
8236
|
|
|
|
|
14928
|
} else { |
381
|
8236
|
|
|
|
|
9968
|
$err_msg = ucfirst($err_msg); |
382
|
8236
|
100
|
|
|
|
16312
|
} |
383
|
5755
|
|
|
|
|
13087
|
} |
384
|
5755
|
|
|
|
|
17891
|
if ($err_msg) { |
385
|
|
|
|
|
|
|
$self->add_var($cd, '_sahv_dpath', []) if $cd->{use_dpath}; |
386
|
|
|
|
|
|
|
$err_expr = $self->literal($err_msg); |
387
|
5755
|
50
|
|
|
|
13816
|
$err_expr = $self->expr_prefix_dpath($err_expr) if $cd->{use_dpath}; |
388
|
5755
|
|
|
|
|
10730
|
} |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
6037
|
100
|
|
|
|
11324
|
my $rt = $cd->{args}{return_type}; |
392
|
282
|
|
100
|
|
|
1161
|
my $et = $cd->{args}{err_term}; |
393
|
|
|
|
|
|
|
my $err_code; |
394
|
5755
|
|
|
|
|
15521
|
if ($rt eq 'hash_details') { |
395
|
|
|
|
|
|
|
$self->add_var($cd, '_sahv_dpath', []) if $cd->{use_dpath}; |
396
|
|
|
|
|
|
|
my $k = $el eq 'warn' ? 'warnings' : 'errors'; |
397
|
18518
|
100
|
|
|
|
31198
|
$err_code = $self->expr_set_err_full($et, $k, $err_expr) if $err_expr; |
398
|
11591
|
100
|
|
|
|
28674
|
} elsif ($rt =~ /\Astr/) { |
399
|
11591
|
|
|
|
|
30285
|
if ($el ne 'warn') { |
400
|
11591
|
100
|
|
|
|
377850
|
$err_code = $self->expr_set_err_str($et, $err_expr) if $err_expr; |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
18651
|
|
|
|
|
33602
|
my $res = { |
405
|
18651
|
|
|
|
|
30088
|
ccl => $ccl, |
406
|
18651
|
|
|
|
|
22053
|
err_level => $el, |
407
|
18651
|
100
|
|
|
|
52793
|
err_code => $err_code, |
|
|
100
|
|
|
|
|
|
408
|
5844
|
50
|
|
|
|
20823
|
(_debug_ccl_note => $cd->{_debug_ccl_note}) x !!$cd->{_debug_ccl_note}, |
409
|
5844
|
100
|
|
|
|
12076
|
subdata => $opts->{subdata}, |
410
|
5844
|
100
|
|
|
|
14846
|
}; |
411
|
|
|
|
|
|
|
push @{ $cd->{ccls} }, $res; |
412
|
5869
|
100
|
|
|
|
13269
|
delete $cd->{uclset}{"$clause.err_level"}; |
413
|
5851
|
100
|
|
|
|
15476
|
delete $cd->{uclset}{"$clause.err_msg"}; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# join ccls to handle .op and insert error messages. opts = op |
417
|
|
|
|
|
|
|
my ($self, $cd, $ccls, $opts) = @_; |
418
|
|
|
|
|
|
|
$opts //= {}; |
419
|
|
|
|
|
|
|
my $op = $opts->{op} // "and"; |
420
|
|
|
|
|
|
|
#$log->errorf("TMP: joining ccl %s", $ccls); |
421
|
|
|
|
|
|
|
#warn "join_ccls"; #TMP |
422
|
|
|
|
|
|
|
|
423
|
18651
|
|
|
|
|
76865
|
my ($min_ok, $max_ok, $min_nok, $max_nok); |
424
|
18651
|
|
|
|
|
26982
|
if ($op eq 'and') { |
|
18651
|
|
|
|
|
35998
|
|
425
|
18651
|
|
|
|
|
35415
|
$max_nok = 0; |
426
|
18651
|
|
|
|
|
75254
|
} elsif ($op eq 'or') { |
427
|
|
|
|
|
|
|
$min_ok = 1; |
428
|
|
|
|
|
|
|
} elsif ($op eq 'none') { |
429
|
|
|
|
|
|
|
$max_ok = 0; |
430
|
|
|
|
|
|
|
} elsif ($op eq 'not') { |
431
|
13822
|
|
|
13822
|
0
|
25137
|
|
432
|
13822
|
|
100
|
|
|
39474
|
} |
433
|
13822
|
|
100
|
|
|
39005
|
my $dmin_ok = defined($min_ok); |
434
|
|
|
|
|
|
|
my $dmax_ok = defined($max_ok); |
435
|
|
|
|
|
|
|
my $dmin_nok = defined($min_nok); |
436
|
|
|
|
|
|
|
my $dmax_nok = defined($max_nok); |
437
|
13822
|
|
|
|
|
19203
|
|
438
|
13822
|
100
|
|
|
|
25958
|
return "" unless @$ccls; |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
439
|
12623
|
|
|
|
|
20166
|
|
440
|
|
|
|
|
|
|
my $rt = $cd->{args}{return_type}; |
441
|
607
|
|
|
|
|
1125
|
my $vp = $cd->{args}{var_prefix}; |
442
|
|
|
|
|
|
|
|
443
|
288
|
|
|
|
|
505
|
my $aop = $self->logical_and_op; |
444
|
|
|
|
|
|
|
my $nop = $self->logical_not_op; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
my $true = $self->true; |
447
|
13822
|
|
|
|
|
20376
|
|
448
|
13822
|
|
|
|
|
16704
|
# insert comment, error message, and $ok/$nok counting. $which is 0 by |
449
|
13822
|
|
|
|
|
16717
|
# default (normal), or 1 (reverse logic, for 'not' or 'none'), or 2 (for |
450
|
13822
|
|
|
|
|
18634
|
# $ok/$nok counting), or 3 (like 2, but for the last clause). |
451
|
|
|
|
|
|
|
my $_ice = sub { |
452
|
13822
|
100
|
|
|
|
23359
|
my ($ccl, $which) = @_; |
453
|
|
|
|
|
|
|
|
454
|
13768
|
|
|
|
|
19896
|
return $self->enclose_paren($ccl->{ccl}) if $ccl->{assert}; |
455
|
13768
|
|
|
|
|
19626
|
|
456
|
|
|
|
|
|
|
my $res = ""; |
457
|
13768
|
|
|
|
|
38399
|
|
458
|
13768
|
|
|
|
|
73442
|
if ($ccl->{_debug_ccl_note}) { |
459
|
|
|
|
|
|
|
if ($cd->{args}{debug_log} // $cd->{args}{debug}) { |
460
|
13768
|
|
|
|
|
62553
|
$res .= $self->expr_log( |
461
|
|
|
|
|
|
|
$cd, $self->literal($ccl->{_debug_ccl_note})) . " $aop\n"; |
462
|
|
|
|
|
|
|
} else { |
463
|
|
|
|
|
|
|
$res .= $self->comment($cd, $ccl->{_debug_ccl_note}); |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
} |
466
|
22751
|
|
|
22751
|
|
33438
|
|
467
|
|
|
|
|
|
|
$which //= 0; |
468
|
22751
|
50
|
|
|
|
46006
|
# clause code |
469
|
|
|
|
|
|
|
my $cc = ($which == 1 ? $nop:"") . $self->enclose_paren($ccl->{ccl}); |
470
|
22751
|
|
|
|
|
33673
|
my ($ec, $oec); |
471
|
|
|
|
|
|
|
my ($ret, $oret); |
472
|
22751
|
100
|
|
|
|
36626
|
if ($which >= 2) { |
473
|
17105
|
50
|
33
|
|
|
55864
|
my @chk; |
474
|
|
|
|
|
|
|
if ($ccl->{err_level} eq 'warn') { |
475
|
0
|
|
|
|
|
0
|
$oret = 1; |
476
|
|
|
|
|
|
|
$ret = 1; |
477
|
17105
|
|
|
|
|
34338
|
} elsif ($ccl->{err_level} eq 'fatal') { |
478
|
|
|
|
|
|
|
$oret = 1; |
479
|
|
|
|
|
|
|
$ret = 0; |
480
|
|
|
|
|
|
|
} else { |
481
|
22751
|
|
100
|
|
|
70941
|
$oret = $self->expr_preinc_var("${vp}ok"); |
482
|
|
|
|
|
|
|
$ret = $self->expr_preinc_var("${vp}nok"); |
483
|
22751
|
100
|
|
|
|
54091
|
push @chk, $self->expr_var("${vp}ok"). " <= $max_ok" |
484
|
22751
|
|
|
|
|
47550
|
if $dmax_ok; |
485
|
22751
|
|
|
|
|
0
|
push @chk, $self->expr_var("${vp}nok")." <= $max_nok" |
486
|
22751
|
100
|
|
|
|
34663
|
if $dmax_nok; |
487
|
1214
|
|
|
|
|
1628
|
if ($which == 3) { |
488
|
1214
|
50
|
|
|
|
3346
|
push @chk, $self->expr_var("${vp}ok"). " >= $min_ok" |
|
|
50
|
|
|
|
|
|
489
|
0
|
|
|
|
|
0
|
if $dmin_ok; |
490
|
0
|
|
|
|
|
0
|
push @chk, $self->expr_var("${vp}nok")." >= $min_nok" |
491
|
|
|
|
|
|
|
if $dmin_nok; |
492
|
0
|
|
|
|
|
0
|
|
493
|
0
|
|
|
|
|
0
|
# we need to clear the error message previously set |
494
|
|
|
|
|
|
|
if ($rt !~ /\Abool/) { |
495
|
1214
|
|
|
|
|
3193
|
my $et = $cd->{args}{err_term}; |
496
|
1214
|
|
|
|
|
6340
|
my $clerrc; |
497
|
1214
|
50
|
|
|
|
5672
|
if ($rt eq 'hash_details') { |
498
|
|
|
|
|
|
|
$clerrc = $self->expr_reset_err_full($et); |
499
|
1214
|
50
|
|
|
|
2654
|
} else { |
500
|
|
|
|
|
|
|
$clerrc = $self->expr_reset_err_str($et); |
501
|
1214
|
100
|
|
|
|
2353
|
} |
502
|
607
|
50
|
|
|
|
2782
|
push @chk, $clerrc; |
503
|
|
|
|
|
|
|
} |
504
|
607
|
50
|
|
|
|
3900
|
} |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
$res .= "($cc ? $oret : $ret)"; |
507
|
|
|
|
|
|
|
$res .= " $aop " . join(" $aop ", @chk) if @chk; |
508
|
607
|
100
|
|
|
|
1820
|
} else { |
509
|
404
|
|
|
|
|
956
|
$ec = $ccl->{err_code}; |
510
|
404
|
|
|
|
|
772
|
$ret = |
511
|
404
|
100
|
|
|
|
1097
|
$ccl->{err_level} eq 'fatal' ? 0 : |
512
|
202
|
|
|
|
|
920
|
# this must not be done because it messes up ok/nok counting |
513
|
|
|
|
|
|
|
#$rt eq 'hash_details' ? 1 : |
514
|
202
|
|
|
|
|
703
|
$ccl->{err_level} eq 'warn' ? 1 : 0; |
515
|
|
|
|
|
|
|
if ($rt =~ /\Abool/ && $ret) { |
516
|
404
|
|
|
|
|
983
|
$res .= $true; |
517
|
|
|
|
|
|
|
} elsif ($rt =~ /\Abool/ || !$ec) { |
518
|
|
|
|
|
|
|
$res .= $self->enclose_paren($cc); |
519
|
|
|
|
|
|
|
} else { |
520
|
1214
|
|
|
|
|
3490
|
$res .= $self->enclose_paren( |
521
|
1214
|
100
|
|
|
|
3892
|
$self->enclose_paren($cc). " ? $true : ($ec,$ret)", |
522
|
|
|
|
|
|
|
"force"); |
523
|
21537
|
|
|
|
|
32257
|
} |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
# insert dpath handling |
527
|
|
|
|
|
|
|
$res = $self->expr_push_and_pop_dpath_between_expr($res) |
528
|
21537
|
100
|
|
|
|
45699
|
if $cd->{use_dpath} && $ccl->{subdata}; |
|
|
100
|
|
|
|
|
|
529
|
21537
|
100
|
100
|
|
|
91254
|
$res; |
|
|
100
|
100
|
|
|
|
|
530
|
36
|
|
|
|
|
62
|
|
531
|
|
|
|
|
|
|
}; |
532
|
14896
|
|
|
|
|
28359
|
|
533
|
|
|
|
|
|
|
my $j = "\n\n$aop\n\n"; |
534
|
6605
|
|
|
|
|
13905
|
if ($op eq 'not') { |
535
|
|
|
|
|
|
|
return $_ice->($ccls->[0], 1); |
536
|
|
|
|
|
|
|
} elsif ($op eq 'and') { |
537
|
|
|
|
|
|
|
return join $j, map { $_ice->($_) } @$ccls; |
538
|
|
|
|
|
|
|
} elsif ($op eq 'none') { |
539
|
|
|
|
|
|
|
return join $j, map { $_ice->($_, 1) } @$ccls; |
540
|
|
|
|
|
|
|
} else { |
541
|
|
|
|
|
|
|
my $jccl = join $j, map {$_ice->($ccls->[$_], $_ == @$ccls-1 ? 3:2)} |
542
|
22751
|
100
|
100
|
|
|
57230
|
0..@$ccls-1; |
543
|
22751
|
|
|
|
|
262574
|
{ |
544
|
|
|
|
|
|
|
local $cd->{ccls} = []; |
545
|
13768
|
|
|
|
|
84668
|
local $cd->{_debug_ccl_note} = "op=$op"; |
546
|
|
|
|
|
|
|
$self->add_ccl( |
547
|
13768
|
|
|
|
|
28453
|
$cd, |
548
|
13768
|
100
|
|
|
|
35856
|
$self->expr_block( |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
549
|
304
|
|
|
|
|
826
|
join( |
550
|
|
|
|
|
|
|
"", |
551
|
12569
|
|
|
|
|
21081
|
$self->stmt_declare_local_var("${vp}ok" , "0"), "\n", |
|
20050
|
|
|
|
|
36131
|
|
552
|
|
|
|
|
|
|
$self->stmt_declare_local_var("${vp}nok", "0"), "\n", |
553
|
288
|
|
|
|
|
712
|
"\n", |
|
576
|
|
|
|
|
1198
|
|
554
|
|
|
|
|
|
|
$self->block_uses_sub ? |
555
|
607
|
100
|
|
|
|
1796
|
$self->stmt_return($jccl) : $jccl, |
|
1214
|
|
|
|
|
3433
|
|
556
|
|
|
|
|
|
|
) |
557
|
|
|
|
|
|
|
), |
558
|
607
|
|
|
|
|
1273
|
); |
|
607
|
|
|
|
|
1545
|
|
559
|
607
|
|
|
|
|
1520
|
$_ice->($cd->{ccls}[0]); |
560
|
607
|
50
|
|
|
|
2653
|
} |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
my ($self, $cd) = @_; |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
if ($cd->{args}{data_term_is_lvalue}) { |
567
|
|
|
|
|
|
|
$cd->{data_term} = $cd->{args}{data_term}; |
568
|
|
|
|
|
|
|
} else { |
569
|
|
|
|
|
|
|
my $v = $cd->{args}{var_prefix} . $cd->{args}{data_name}; |
570
|
|
|
|
|
|
|
push @{ $cd->{vars} }, $v; # XXX unless already there |
571
|
|
|
|
|
|
|
$cd->{data_term} = $self->var_sigil . $v; |
572
|
|
|
|
|
|
|
die "BUG: support for non-perl compiler not yet added here" |
573
|
607
|
|
|
|
|
2057
|
unless $cd->{compiler_name} eq 'perl'; |
574
|
|
|
|
|
|
|
push @{ $cd->{ccls} }, ["(local($cd->{data_term} = $cd->{args}{data_term}), 1)"]; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
my ($self, $cd) = @_; |
579
|
5060
|
|
|
5060
|
1
|
9491
|
|
580
|
|
|
|
|
|
|
# do a human compilation first to collect all the error messages |
581
|
5060
|
50
|
|
|
|
12255
|
|
582
|
5060
|
|
|
|
|
13557
|
unless ($cd->{is_inner}) { |
583
|
|
|
|
|
|
|
my $hc = $cd->{_hc}; |
584
|
0
|
|
|
|
|
0
|
my %hargs = %{$cd->{args}}; |
585
|
0
|
|
|
|
|
0
|
$hargs{format} = 'msg_catalog'; |
|
0
|
|
|
|
|
0
|
|
586
|
0
|
|
|
|
|
0
|
$hargs{schema_is_normalized} = 1; |
587
|
|
|
|
|
|
|
$hargs{schema} = $cd->{nschema}; |
588
|
0
|
0
|
|
|
|
0
|
$hargs{on_unhandled_clause} = 'ignore'; |
589
|
0
|
|
|
|
|
0
|
$hargs{on_unhandled_attr} = 'ignore'; |
|
0
|
|
|
|
|
0
|
|
590
|
|
|
|
|
|
|
$hargs{hash_values} = $cd->{args}{human_hash_values}; |
591
|
|
|
|
|
|
|
$cd->{_hcd} = $hc->compile(%hargs); |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
} |
594
|
5059
|
|
|
5059
|
1
|
10805
|
|
595
|
|
|
|
|
|
|
my ($self, $cd) = @_; |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
my $rt = $cd->{args}{return_type}; |
598
|
5059
|
100
|
|
|
|
11286
|
my $rt_is_hash = $rt =~ /\Ahash/; |
599
|
4729
|
|
|
|
|
8518
|
my $rt_is_str = $rt =~ /\Astr/; |
600
|
4729
|
|
|
|
|
6250
|
|
|
4729
|
|
|
|
|
58448
|
|
601
|
4729
|
|
|
|
|
13703
|
$cd->{use_dpath} //= ( |
602
|
4729
|
|
|
|
|
8284
|
$rt_is_hash || |
603
|
4729
|
|
|
|
|
10354
|
($rt_is_str && $cd->{has_subschema}) |
604
|
4729
|
|
|
|
|
7392
|
); |
605
|
4729
|
|
|
|
|
7476
|
|
606
|
4729
|
|
|
|
|
9493
|
# handle ok/default/coercion/prefilters/req/forbidden clauses and type check |
607
|
4729
|
|
|
|
|
25808
|
|
608
|
|
|
|
|
|
|
my $c = $cd->{compiler}; |
609
|
|
|
|
|
|
|
my $cname = $c->name; |
610
|
|
|
|
|
|
|
my $dt = $cd->{data_term}; |
611
|
|
|
|
|
|
|
my $et = $cd->{args}{err_term}; |
612
|
5056
|
|
|
5056
|
1
|
9053
|
my $clsets = $cd->{clsets}; |
613
|
|
|
|
|
|
|
|
614
|
5056
|
|
|
|
|
9500
|
# handle ok, this is very high priority because !ok=>1 should fail undef |
615
|
5056
|
|
|
|
|
12712
|
# too. we need to handle its .op=not here. |
616
|
5056
|
|
|
|
|
12610
|
for my $i (0..@$clsets-1) { |
617
|
|
|
|
|
|
|
my $clset = $clsets->[$i]; |
618
|
|
|
|
|
|
|
next unless exists $clset->{ok}; |
619
|
|
|
|
|
|
|
my $op = $clset->{"ok.op"} // ""; |
620
|
|
|
|
|
|
|
if ($op && $op ne 'not') { |
621
|
5056
|
|
100
|
|
|
33499
|
$self->_die($cd, "ok can only be combined with .op=not"); |
|
|
|
100
|
|
|
|
|
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
if ($op eq 'not') { |
624
|
|
|
|
|
|
|
local $cd->{_debug_ccl_note} = "!ok #$i"; |
625
|
5056
|
|
|
|
|
7349
|
$self->add_ccl($cd, $self->false); |
626
|
5056
|
|
|
|
|
14781
|
} else { |
627
|
5056
|
|
|
|
|
10581
|
local $cd->{_debug_ccl_note} = "ok #$i"; |
628
|
5056
|
|
|
|
|
8068
|
$self->add_ccl($cd, $self->true); |
629
|
5056
|
|
|
|
|
7241
|
} |
630
|
|
|
|
|
|
|
delete $cd->{uclsets}[$i]{"ok"}; |
631
|
|
|
|
|
|
|
delete $cd->{uclsets}[$i]{"ok.is_expr"}; |
632
|
|
|
|
|
|
|
} |
633
|
5056
|
|
|
|
|
13430
|
|
634
|
4610
|
|
|
|
|
7353
|
# handle default |
635
|
4610
|
100
|
|
|
|
15161
|
HANDLE_DEFAULT: { |
636
|
54
|
|
100
|
|
|
212
|
|
637
|
54
|
50
|
66
|
|
|
179
|
my $default_value_expr; |
638
|
0
|
|
|
|
|
0
|
my $default_value_ccl_note; |
639
|
|
|
|
|
|
|
GEN_DEFAULT_VALUE_RULES: |
640
|
54
|
100
|
|
|
|
140
|
{ |
641
|
27
|
|
|
|
|
94
|
require Data::Sah::DefaultValueCommon; |
642
|
27
|
|
|
|
|
90
|
|
643
|
|
|
|
|
|
|
my @default_value_rules; |
644
|
27
|
|
|
|
|
84
|
for my $i (0..@$clsets-1) { |
645
|
27
|
|
|
|
|
85
|
my $clset = $clsets->[$i]; |
646
|
|
|
|
|
|
|
push @default_value_rules, |
647
|
54
|
|
|
|
|
118
|
@{ $clset->{"x.$cname.default_value_rules"} // [] }, |
648
|
54
|
|
|
|
|
120
|
@{ $clset->{'x.default_value_rules'} // [] }; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
my $rules = Data::Sah::DefaultValueCommon::get_default_value_rules( |
652
|
|
|
|
|
|
|
compiler => $self->name, |
653
|
|
|
|
|
|
|
default_value_rules => \@default_value_rules, |
654
|
5056
|
|
|
|
|
6742
|
); |
|
5056
|
|
|
|
|
7732
|
|
655
|
|
|
|
|
|
|
last unless @$rules; |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
for my $i (reverse 0..$#{$rules}) { |
658
|
5056
|
|
|
|
|
7592
|
my $rule = $rules->[$i]; |
|
5056
|
|
|
|
|
33965
|
|
659
|
|
|
|
|
|
|
|
660
|
5056
|
|
|
|
|
22009
|
$self->add_compile_module( |
661
|
5056
|
|
|
|
|
10801
|
$cd, "Data::Sah::Value::$cname\::$rule->{name}", |
662
|
4610
|
|
|
|
|
8590
|
{category => 'default_value'}, |
663
|
|
|
|
|
|
|
); |
664
|
4610
|
|
100
|
|
|
19785
|
|
665
|
4610
|
|
50
|
|
|
6138
|
if ($rule->{modules}) { |
|
4610
|
|
|
|
|
18629
|
|
666
|
|
|
|
|
|
|
for my $mod (keys %{ $rule->{modules} }) { |
667
|
|
|
|
|
|
|
my $modspec = $rule->{modules}{$mod}; |
668
|
5056
|
|
|
|
|
11662
|
$modspec = {version=>$modspec} unless ref $modspec eq 'HASH'; |
669
|
|
|
|
|
|
|
$self->add_runtime_module($cd, $mod, {category=>'default_value', %$modspec}); |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
} |
672
|
5056
|
100
|
|
|
|
94339
|
} |
673
|
|
|
|
|
|
|
|
674
|
1
|
|
|
|
|
2
|
$default_value_expr = join " // " , map { "($_->{expr_value})" } @$rules; |
|
1
|
|
|
|
|
4
|
|
675
|
1
|
|
|
|
|
1
|
$default_value_ccl_note = "default value rule(s): ". |
676
|
|
|
|
|
|
|
join(", ", map {$_->{name}} @$rules); |
677
|
1
|
|
|
|
|
7
|
} # GEN_DEFAULT_VALUE_RULES |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
for my $i (0..@$clsets-1) { |
680
|
|
|
|
|
|
|
my $clset = $clsets->[$i]; |
681
|
|
|
|
|
|
|
my $def = $clset->{default}; |
682
|
1
|
50
|
|
|
|
5
|
my $defie = $clset->{"default.is_expr"}; |
683
|
0
|
|
|
|
|
0
|
if (defined $def) { |
|
0
|
|
|
|
|
0
|
|
684
|
0
|
|
|
|
|
0
|
local $cd->{_debug_ccl_note} = "default #$i"; |
685
|
0
|
0
|
|
|
|
0
|
my $ct = $defie ? |
686
|
0
|
|
|
|
|
0
|
$self->expr($cd, $def) : $self->literal($def); |
687
|
|
|
|
|
|
|
$self->add_ccl( |
688
|
|
|
|
|
|
|
$cd, |
689
|
|
|
|
|
|
|
$self->expr_list( |
690
|
|
|
|
|
|
|
$self->expr_setif($dt, $ct), |
691
|
1
|
|
|
|
|
3
|
$self->true, |
|
1
|
|
|
|
|
5
|
|
692
|
|
|
|
|
|
|
), |
693
|
1
|
|
|
|
|
3
|
{err_msg => ""}, |
|
1
|
|
|
|
|
5
|
|
694
|
|
|
|
|
|
|
); |
695
|
|
|
|
|
|
|
} |
696
|
5056
|
|
|
|
|
10844
|
delete $cd->{uclsets}[$i]{"default"}; |
697
|
4610
|
|
|
|
|
7445
|
delete $cd->{uclsets}[$i]{"default.is_expr"}; |
698
|
4610
|
|
|
|
|
9599
|
} |
699
|
4610
|
|
|
|
|
7709
|
|
700
|
4610
|
100
|
|
|
|
9589
|
if (defined $default_value_expr) { |
701
|
90
|
|
|
|
|
280
|
local $cd->{_debug_ccl_note} = $default_value_ccl_note; |
702
|
90
|
50
|
|
|
|
280
|
$self->add_ccl( |
703
|
|
|
|
|
|
|
$cd, |
704
|
90
|
|
|
|
|
3002
|
$self->expr_list( |
705
|
|
|
|
|
|
|
$self->expr_setif($dt, $default_value_expr), |
706
|
|
|
|
|
|
|
$self->true, |
707
|
|
|
|
|
|
|
), |
708
|
|
|
|
|
|
|
{err_msg => ""}, |
709
|
|
|
|
|
|
|
); |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
} # HANDLE_DEFAULT |
712
|
|
|
|
|
|
|
|
713
|
4610
|
|
|
|
|
8247
|
# handle req |
714
|
4610
|
|
|
|
|
9304
|
my $has_req; |
715
|
|
|
|
|
|
|
for my $i (0..@$clsets-1) { |
716
|
|
|
|
|
|
|
my $clset = $clsets->[$i]; |
717
|
5056
|
100
|
|
|
|
11383
|
my $req = $clset->{req}; |
718
|
1
|
|
|
|
|
2
|
my $reqie = $clset->{"req.is_expr"}; |
719
|
1
|
|
|
|
|
4
|
my $req_err_msg = $self->_xlt($cd, "Required but not specified"); |
720
|
|
|
|
|
|
|
local $cd->{_debug_ccl_note} = "req #$i"; |
721
|
|
|
|
|
|
|
if ($req && !$reqie) { |
722
|
|
|
|
|
|
|
$has_req++; |
723
|
|
|
|
|
|
|
$self->add_ccl( |
724
|
|
|
|
|
|
|
$cd, $self->expr_defined($dt), |
725
|
|
|
|
|
|
|
{ |
726
|
|
|
|
|
|
|
err_msg => $req_err_msg, |
727
|
|
|
|
|
|
|
err_level => 'fatal', |
728
|
|
|
|
|
|
|
}, |
729
|
|
|
|
|
|
|
); |
730
|
|
|
|
|
|
|
} elsif ($reqie) { |
731
|
5056
|
|
|
|
|
8149
|
$has_req++; |
732
|
5056
|
|
|
|
|
9627
|
my $ct = $self->expr($cd, $req); |
733
|
4610
|
|
|
|
|
7587
|
$self->add_ccl( |
734
|
4610
|
|
|
|
|
8521
|
$cd, "!($ct) || ".$self->expr_defined($dt), |
735
|
4610
|
|
|
|
|
8099
|
{ |
736
|
4610
|
|
|
|
|
13021
|
err_msg => $req_err_msg, |
737
|
4610
|
|
|
|
|
14798
|
err_level => 'fatal', |
738
|
4610
|
100
|
66
|
|
|
14278
|
}, |
|
|
50
|
|
|
|
|
|
739
|
471
|
|
|
|
|
889
|
); |
740
|
471
|
|
|
|
|
1357
|
} |
741
|
|
|
|
|
|
|
delete $cd->{uclsets}[$i]{"req"}; |
742
|
|
|
|
|
|
|
delete $cd->{uclsets}[$i]{"req.is_expr"}; |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
# handle forbidden |
746
|
|
|
|
|
|
|
my $has_fbd; |
747
|
|
|
|
|
|
|
for my $i (0..@$clsets-1) { |
748
|
0
|
|
|
|
|
0
|
my $clset = $clsets->[$i]; |
749
|
0
|
|
|
|
|
0
|
my $fbd = $clset->{forbidden}; |
750
|
0
|
|
|
|
|
0
|
my $fbdie = $clset->{"forbidden.is_expr"}; |
751
|
|
|
|
|
|
|
my $fbd_err_msg = $self->_xlt($cd, "Forbidden but specified"); |
752
|
|
|
|
|
|
|
local $cd->{_debug_ccl_note} = "forbidden #$i"; |
753
|
|
|
|
|
|
|
if ($fbd && !$fbdie) { |
754
|
|
|
|
|
|
|
$has_fbd++; |
755
|
|
|
|
|
|
|
$self->add_ccl( |
756
|
|
|
|
|
|
|
$cd, "!".$self->expr_defined($dt), |
757
|
|
|
|
|
|
|
{ |
758
|
4610
|
|
|
|
|
7255
|
err_msg => $fbd_err_msg, |
759
|
4610
|
|
|
|
|
11413
|
err_level => 'fatal', |
760
|
|
|
|
|
|
|
}, |
761
|
|
|
|
|
|
|
); |
762
|
|
|
|
|
|
|
} elsif ($fbdie) { |
763
|
5056
|
|
|
|
|
7352
|
$has_fbd++; |
764
|
5056
|
|
|
|
|
9530
|
my $ct = $self->expr($cd, $fbd); |
765
|
4610
|
|
|
|
|
7149
|
$self->add_ccl( |
766
|
4610
|
|
|
|
|
8308
|
$cd, "!($ct) || !".$self->expr_defined($dt), |
767
|
4610
|
|
|
|
|
6464
|
{ |
768
|
4610
|
|
|
|
|
9241
|
err_msg => $fbd_err_msg, |
769
|
4610
|
|
|
|
|
11638
|
err_level => 'fatal', |
770
|
4610
|
100
|
66
|
|
|
13380
|
}, |
|
|
50
|
|
|
|
|
|
771
|
27
|
|
|
|
|
50
|
); |
772
|
27
|
|
|
|
|
94
|
} |
773
|
|
|
|
|
|
|
delete $cd->{uclsets}[$i]{"forbidden"}; |
774
|
|
|
|
|
|
|
delete $cd->{uclsets}[$i]{"forbidden.is_expr"}; |
775
|
|
|
|
|
|
|
} |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
if (!$has_req && !$has_fbd) { |
778
|
|
|
|
|
|
|
$cd->{_skip_undef} = 1; |
779
|
|
|
|
|
|
|
$cd->{_ccls_idx1} = @{$cd->{ccls}}; |
780
|
0
|
|
|
|
|
0
|
} |
781
|
0
|
|
|
|
|
0
|
|
782
|
0
|
|
|
|
|
0
|
my $coerce_expr; |
783
|
|
|
|
|
|
|
my $coerce_might_fail; |
784
|
|
|
|
|
|
|
my $coerce_ccl_note; |
785
|
|
|
|
|
|
|
GEN_COERCE_EXPR: |
786
|
|
|
|
|
|
|
{ |
787
|
|
|
|
|
|
|
last unless $cd->{args}{coerce}; |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
require Data::Sah::CoerceCommon; |
790
|
4610
|
|
|
|
|
7135
|
|
791
|
4610
|
|
|
|
|
10000
|
my @coerce_rules; |
792
|
|
|
|
|
|
|
for my $i (0..@$clsets-1) { |
793
|
|
|
|
|
|
|
my $clset = $clsets->[$i]; |
794
|
5056
|
100
|
100
|
|
|
17306
|
push @coerce_rules, |
795
|
4558
|
|
|
|
|
7817
|
@{ $clset->{"x.$cname.coerce_rules"} // [] }, |
796
|
4558
|
|
|
|
|
6539
|
@{ $clset->{'x.coerce_rules'} // [] }; |
|
4558
|
|
|
|
|
12093
|
|
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
5056
|
|
|
|
|
12537
|
my $rules = Data::Sah::CoerceCommon::get_coerce_rules( |
800
|
|
|
|
|
|
|
compiler => $self->name, |
801
|
5056
|
|
|
|
|
0
|
type => $cd->{type}, |
802
|
|
|
|
|
|
|
data_term => $dt, |
803
|
|
|
|
|
|
|
coerce_to => $cd->{coerce_to}, |
804
|
5056
|
50
|
|
|
|
6709
|
coerce_rules => \@coerce_rules, |
|
5056
|
|
|
|
|
11815
|
|
805
|
|
|
|
|
|
|
); |
806
|
5056
|
|
|
|
|
23806
|
last unless @$rules; |
807
|
|
|
|
|
|
|
|
808
|
5056
|
|
|
|
|
18556
|
$coerce_might_fail = 1 if grep { $_->{meta}{might_fail} } @$rules; |
809
|
5056
|
|
|
|
|
11101
|
|
810
|
4610
|
|
|
|
|
7793
|
my $prev_term; |
811
|
|
|
|
|
|
|
for my $i (reverse 0..$#{$rules}) { |
812
|
4610
|
|
100
|
|
|
17844
|
my $rule = $rules->[$i]; |
813
|
4610
|
|
50
|
|
|
6497
|
|
|
4610
|
|
|
|
|
18453
|
|
814
|
|
|
|
|
|
|
$self->add_compile_module( |
815
|
|
|
|
|
|
|
$cd, "Data::Sah::Coerce::$cname\::To_$cd->{type}::$rule->{name}", |
816
|
|
|
|
|
|
|
{category => 'coerce'}, |
817
|
|
|
|
|
|
|
); |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
if ($rule->{modules}) { |
820
|
|
|
|
|
|
|
for my $mod (keys %{ $rule->{modules} }) { |
821
|
5056
|
|
|
|
|
12693
|
my $modspec = $rule->{modules}{$mod}; |
822
|
|
|
|
|
|
|
$modspec = {version=>$modspec} unless ref $modspec eq 'HASH'; |
823
|
5056
|
100
|
|
|
|
302653
|
$self->add_runtime_module($cd, $mod, {category=>'coerce', %$modspec}); |
824
|
|
|
|
|
|
|
} |
825
|
1220
|
100
|
|
|
|
2945
|
} |
|
1384
|
|
|
|
|
4844
|
|
826
|
|
|
|
|
|
|
|
827
|
1220
|
|
|
|
|
1886
|
if ($i == $#{$rules}) { |
828
|
1220
|
|
|
|
|
2122
|
if ($coerce_might_fail) { |
|
1220
|
|
|
|
|
2927
|
|
829
|
1384
|
|
|
|
|
2370
|
$prev_term = $self->expr_array($self->literal(undef), $dt); |
830
|
|
|
|
|
|
|
} else { |
831
|
1384
|
|
|
|
|
8778
|
$prev_term = $dt; |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
} else { |
834
|
|
|
|
|
|
|
$prev_term = "($coerce_expr)"; |
835
|
|
|
|
|
|
|
} |
836
|
1384
|
100
|
|
|
|
5539
|
|
837
|
156
|
|
|
|
|
207
|
my $ec; |
|
156
|
|
|
|
|
340
|
|
838
|
156
|
|
|
|
|
229
|
if ($coerce_might_fail && !$rule->{meta}{might_fail}) { |
839
|
156
|
50
|
|
|
|
382
|
$ec = $self->expr_array($self->literal(undef), $rule->{expr_coerce}); |
840
|
156
|
|
|
|
|
489
|
} else { |
841
|
|
|
|
|
|
|
$ec = "($rule->{expr_coerce})"; |
842
|
|
|
|
|
|
|
} |
843
|
|
|
|
|
|
|
|
844
|
1384
|
100
|
|
|
|
2344
|
$coerce_expr = $self->expr_ternary( |
|
1384
|
|
|
|
|
4158
|
|
845
|
1220
|
100
|
|
|
|
2391
|
"($rule->{expr_match})", |
846
|
60
|
|
|
|
|
163
|
$ec, |
847
|
|
|
|
|
|
|
$prev_term, |
848
|
1160
|
|
|
|
|
2045
|
); |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
$coerce_ccl_note = "coerce rule(s): ". |
851
|
164
|
|
|
|
|
349
|
join(", ", map {$_->{name}} @$rules) . |
852
|
|
|
|
|
|
|
($cd->{coerce_to} ? " # coerce to: $cd->{coerce_to}" : ""); |
853
|
|
|
|
|
|
|
} # GEN_COERCE_EXPR |
854
|
1384
|
|
|
|
|
2306
|
|
855
|
1384
|
100
|
100
|
|
|
3932
|
my $prefilters_expr; |
856
|
164
|
|
|
|
|
333
|
my $prefilters_ccl_note; |
857
|
|
|
|
|
|
|
GEN_PREFILTERS_EXPRS: |
858
|
1220
|
|
|
|
|
3049
|
{ |
859
|
|
|
|
|
|
|
my @filter_names; |
860
|
|
|
|
|
|
|
for my $i (0..@$clsets-1) { |
861
|
1384
|
|
|
|
|
5217
|
my $clset = $clsets->[$i]; |
862
|
|
|
|
|
|
|
push @filter_names, @{ $clset->{prefilters} } |
863
|
|
|
|
|
|
|
if defined $clset->{prefilters}; |
864
|
|
|
|
|
|
|
} |
865
|
|
|
|
|
|
|
last unless @filter_names; |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
require Data::Sah::FilterCommon; |
868
|
1384
|
|
|
|
|
7233
|
my $rules = Data::Sah::FilterCommon::get_filter_rules( |
869
|
1220
|
100
|
|
|
|
2932
|
compiler => $cname, |
870
|
|
|
|
|
|
|
data_term => $dt, |
871
|
|
|
|
|
|
|
filter_names => \@filter_names, |
872
|
5056
|
|
|
|
|
8432
|
); |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
my @exprs; |
875
|
|
|
|
|
|
|
for my $i (0..$#{$rules}) { |
876
|
5056
|
|
|
|
|
8122
|
my $rule = $rules->[$i]; |
|
5056
|
|
|
|
|
6626
|
|
877
|
5056
|
|
|
|
|
10773
|
|
878
|
4610
|
|
|
|
|
7389
|
$self->add_compile_module( |
879
|
6
|
|
|
|
|
15
|
$cd, "Data::Sah::Filter::$cname\::$rule->{name}", |
880
|
4610
|
100
|
|
|
|
13025
|
{category => 'filter'}, |
881
|
|
|
|
|
|
|
); |
882
|
5056
|
100
|
|
|
|
11108
|
if ($rule->{modules}) { |
883
|
|
|
|
|
|
|
for my $mod (keys %{ $rule->{modules} }) { |
884
|
6
|
|
|
|
|
879
|
my $modspec = $rule->{modules}{$mod}; |
885
|
6
|
|
|
|
|
1100
|
$modspec = {version=>$modspec} unless ref $modspec eq 'HASH'; |
886
|
|
|
|
|
|
|
$self->add_runtime_module($cd, $mod, {category=>'filter', %$modspec}); |
887
|
|
|
|
|
|
|
} |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
my $rule_might_fail = $rule->{meta}{might_fail}; |
891
|
6
|
|
|
|
|
4886
|
my $expr; |
892
|
6
|
|
|
|
|
11
|
if ($rule->{meta}{might_fail}) { |
|
6
|
|
|
|
|
19
|
|
893
|
7
|
|
|
|
|
16
|
my $expr_fail; |
894
|
|
|
|
|
|
|
# XXX rather hackish: like when coercion handling, to avoid |
895
|
7
|
|
|
|
|
38
|
# adding another temporary variable, we reuse data term to hold |
896
|
|
|
|
|
|
|
# filtering result (which contains error message string as well |
897
|
|
|
|
|
|
|
# filtered data) then set the data term to the filtered data |
898
|
|
|
|
|
|
|
# again. this might fail in languages or setting that is |
899
|
7
|
50
|
|
|
|
26
|
# stricter (e.g. data term must be of certain type). |
900
|
0
|
|
|
|
|
0
|
if ($rt_is_hash) { |
|
0
|
|
|
|
|
0
|
|
901
|
0
|
|
|
|
|
0
|
$expr_fail = $self->expr_list( |
902
|
0
|
0
|
|
|
|
0
|
$self->expr_set_err_full($et, 'errors', $self->expr_array_subscript($dt, 0)), |
903
|
0
|
|
|
|
|
0
|
$self->false, |
904
|
|
|
|
|
|
|
); |
905
|
|
|
|
|
|
|
} elsif ($rt_is_str) { |
906
|
|
|
|
|
|
|
$expr_fail = $self->expr_list( |
907
|
7
|
|
|
|
|
15
|
$self->expr_set_err_str($et, $self->expr_array_subscript($dt, 0)), |
908
|
7
|
|
|
|
|
10
|
$self->expr_set($dt, $self->expr_array_subscript($dt, 1)), |
909
|
7
|
100
|
|
|
|
18
|
$self->false, |
910
|
3
|
|
|
|
|
4
|
); |
911
|
|
|
|
|
|
|
} else { |
912
|
|
|
|
|
|
|
$expr_fail = $self->false; |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
$expr = $self->expr_list( |
916
|
|
|
|
|
|
|
$self->expr_set($dt, $rule->{expr_filter}), |
917
|
3
|
50
|
|
|
|
7
|
$self->expr_ternary( |
|
|
50
|
|
|
|
|
|
918
|
0
|
|
|
|
|
0
|
$self->expr_defined($self->expr_array_subscript($dt, 0)), |
919
|
|
|
|
|
|
|
$expr_fail, |
920
|
|
|
|
|
|
|
$self->expr_list( |
921
|
|
|
|
|
|
|
$self->expr_set($dt, $self->expr_array_subscript($dt, 1)), |
922
|
|
|
|
|
|
|
$self->true, |
923
|
0
|
|
|
|
|
0
|
) |
924
|
|
|
|
|
|
|
), |
925
|
|
|
|
|
|
|
); |
926
|
|
|
|
|
|
|
} else { |
927
|
|
|
|
|
|
|
$expr = $self->expr_list( |
928
|
|
|
|
|
|
|
$self->expr_set($dt, $rule->{expr_filter}), |
929
|
3
|
|
|
|
|
9
|
$self->true, |
930
|
|
|
|
|
|
|
); |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
push @exprs, $expr; |
933
|
3
|
|
|
|
|
9
|
} # for rules |
934
|
|
|
|
|
|
|
$prefilters_expr = join(" ".$self->logical_and_op." ", @exprs); |
935
|
|
|
|
|
|
|
$prefilters_ccl_note = "prefilters: ". |
936
|
|
|
|
|
|
|
join(", ", map {$_->{name}} @$rules); |
937
|
|
|
|
|
|
|
} # GEN_PREFILTERS_EXPR |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
HANDLE_TYPE_CHECK_OR_BASE_SCHEMA_CHECK: |
940
|
|
|
|
|
|
|
{ |
941
|
|
|
|
|
|
|
if (defined $cd->{base_schema}) { |
942
|
|
|
|
|
|
|
$self->gen_cached_validator($cd, $cd->{base_schema}); |
943
|
|
|
|
|
|
|
} else { |
944
|
|
|
|
|
|
|
$self->_die($cd, "BUG: type handler did not produce _ccl_check_type") |
945
|
4
|
|
|
|
|
16
|
unless defined($cd->{_ccl_check_type}); |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
local $cd->{_debug_ccl_note}; |
948
|
|
|
|
|
|
|
|
949
|
7
|
|
|
|
|
19
|
# handle coercion |
950
|
|
|
|
|
|
|
if ($coerce_expr) { |
951
|
6
|
|
|
|
|
26
|
$cd->{_debug_ccl_note} = $coerce_ccl_note; |
952
|
|
|
|
|
|
|
if ($coerce_might_fail) { |
953
|
6
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
38
|
|
954
|
|
|
|
|
|
|
my $expr_fail; |
955
|
|
|
|
|
|
|
if ($rt_is_hash) { |
956
|
|
|
|
|
|
|
$expr_fail = $self->expr_list( |
957
|
|
|
|
|
|
|
$self->expr_set_err_full($et, 'errors', $self->expr_array_subscript($dt, 0)), |
958
|
5056
|
50
|
|
|
|
8011
|
$self->false, |
|
5056
|
|
|
|
|
9691
|
|
959
|
0
|
|
|
|
|
0
|
); |
960
|
|
|
|
|
|
|
} elsif ($rt_is_str) { |
961
|
|
|
|
|
|
|
$expr_fail = $self->expr_list( |
962
|
5056
|
50
|
|
|
|
11207
|
$self->expr_set_err_str($et, $self->expr_array_subscript($dt, 0)), |
963
|
|
|
|
|
|
|
$self->expr_set($dt, $self->expr_array_subscript($dt, 1)), |
964
|
5056
|
|
|
|
|
17435
|
$self->false, |
965
|
|
|
|
|
|
|
); |
966
|
|
|
|
|
|
|
} else { |
967
|
5056
|
100
|
|
|
|
12499
|
$expr_fail = $self->false; |
968
|
1220
|
|
|
|
|
2543
|
} |
969
|
1220
|
100
|
|
|
|
2591
|
|
970
|
|
|
|
|
|
|
$self->add_ccl( |
971
|
60
|
|
|
|
|
82
|
$cd, |
972
|
60
|
50
|
|
|
|
129
|
$self->expr_list( |
|
|
100
|
|
|
|
|
|
973
|
0
|
|
|
|
|
0
|
$self->expr_set($dt, $coerce_expr), |
974
|
|
|
|
|
|
|
$self->expr_ternary( |
975
|
|
|
|
|
|
|
$self->expr_defined($self->expr_array_subscript($dt, 0)), |
976
|
|
|
|
|
|
|
$expr_fail, |
977
|
|
|
|
|
|
|
$self->expr_list( |
978
|
1
|
|
|
|
|
4
|
$self->expr_set($dt, $self->expr_array_subscript($dt, 1)), |
979
|
|
|
|
|
|
|
$self->true, |
980
|
|
|
|
|
|
|
) |
981
|
|
|
|
|
|
|
), |
982
|
|
|
|
|
|
|
), |
983
|
|
|
|
|
|
|
{ |
984
|
59
|
|
|
|
|
148
|
err_msg => "", |
985
|
|
|
|
|
|
|
err_level => "fatal", |
986
|
|
|
|
|
|
|
}, |
987
|
60
|
|
|
|
|
175
|
); |
988
|
|
|
|
|
|
|
} else { |
989
|
|
|
|
|
|
|
$self->add_ccl( |
990
|
|
|
|
|
|
|
$cd, |
991
|
|
|
|
|
|
|
$self->expr_list( |
992
|
|
|
|
|
|
|
$self->expr_set($dt, $coerce_expr), |
993
|
|
|
|
|
|
|
$self->true, |
994
|
|
|
|
|
|
|
), |
995
|
|
|
|
|
|
|
{ |
996
|
|
|
|
|
|
|
err_msg => "", |
997
|
|
|
|
|
|
|
err_level => "fatal", |
998
|
|
|
|
|
|
|
}, |
999
|
|
|
|
|
|
|
); |
1000
|
|
|
|
|
|
|
} |
1001
|
|
|
|
|
|
|
} # handle coercion |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
# handle prefilters |
1004
|
|
|
|
|
|
|
if (defined $prefilters_expr) { |
1005
|
|
|
|
|
|
|
$cd->{_debug_ccl_note} = $prefilters_ccl_note; |
1006
|
1160
|
|
|
|
|
4607
|
$self->add_ccl( |
1007
|
|
|
|
|
|
|
$cd, |
1008
|
|
|
|
|
|
|
$prefilters_expr, |
1009
|
|
|
|
|
|
|
{ |
1010
|
|
|
|
|
|
|
err_msg => "", |
1011
|
|
|
|
|
|
|
err_level => "fatal", |
1012
|
|
|
|
|
|
|
}, |
1013
|
|
|
|
|
|
|
); |
1014
|
|
|
|
|
|
|
} # handle prefilters |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
# handle type check (if cache=0) or base schema check (if cache=1) |
1017
|
|
|
|
|
|
|
if (defined $cd->{base_schema}) { |
1018
|
|
|
|
|
|
|
$cd->{_debug_ccl_note} = "check base schema '$cd->{base_schema}'"; |
1019
|
|
|
|
|
|
|
$self->add_ccl( |
1020
|
|
|
|
|
|
|
$cd, $self->expr_call_cached_validator($cd, $cd->{base_schema}), |
1021
|
5056
|
100
|
|
|
|
12193
|
{ |
1022
|
6
|
|
|
|
|
13
|
err_msg => sprintf( |
1023
|
6
|
|
|
|
|
26
|
$self->_xlt($cd, "Not of schema %s"), |
1024
|
|
|
|
|
|
|
$self->_xlt( |
1025
|
|
|
|
|
|
|
$cd, |
1026
|
|
|
|
|
|
|
$cd->{base_schema}, |
1027
|
|
|
|
|
|
|
), |
1028
|
|
|
|
|
|
|
), |
1029
|
|
|
|
|
|
|
err_level => 'fatal', |
1030
|
|
|
|
|
|
|
}, |
1031
|
|
|
|
|
|
|
); |
1032
|
|
|
|
|
|
|
} else { |
1033
|
|
|
|
|
|
|
$cd->{_debug_ccl_note} = "check type '$cd->{type}'"; |
1034
|
5056
|
50
|
|
|
|
9970
|
$self->add_ccl( |
1035
|
0
|
|
|
|
|
0
|
$cd, $cd->{_ccl_check_type}, |
1036
|
|
|
|
|
|
|
{ |
1037
|
|
|
|
|
|
|
err_msg => sprintf( |
1038
|
|
|
|
|
|
|
$self->_xlt($cd, "Not of type %s"), |
1039
|
|
|
|
|
|
|
$self->_xlt( |
1040
|
|
|
|
|
|
|
$cd, |
1041
|
|
|
|
|
|
|
$cd->{_hc}->get_th(name=>$cd->{type})->name // |
1042
|
|
|
|
|
|
|
$cd->{type} |
1043
|
|
|
|
|
|
|
), |
1044
|
0
|
|
|
|
|
0
|
), |
1045
|
|
|
|
|
|
|
err_level => 'fatal', |
1046
|
|
|
|
|
|
|
}, |
1047
|
|
|
|
|
|
|
); |
1048
|
|
|
|
|
|
|
} |
1049
|
|
|
|
|
|
|
} # HANDLE_TYPE_CHECK_OR_BASE_SCHEMA_CHECK |
1050
|
5056
|
|
|
|
|
12128
|
} |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
my ($self, $cd) = @_; |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
$self->_die($cd, "Sorry, .op + .is_expr not yet supported ". |
1055
|
|
|
|
|
|
|
"(found in clause $cd->{clause})") |
1056
|
|
|
|
|
|
|
if $cd->{cl_is_expr} && $cd->{cl_op}; |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
if ($cd->{args}{debug}) { |
1059
|
|
|
|
|
|
|
state $json = do { |
1060
|
5056
|
|
66
|
|
|
14680
|
require JSON; |
1061
|
|
|
|
|
|
|
JSON->new->allow_nonref; |
1062
|
|
|
|
|
|
|
}; |
1063
|
|
|
|
|
|
|
my $clset = $cd->{clset}; |
1064
|
|
|
|
|
|
|
my $cl = $cd->{clause}; |
1065
|
|
|
|
|
|
|
my $res = $json->encode({ |
1066
|
|
|
|
|
|
|
map { $_ => $clset->{$_}} |
1067
|
|
|
|
|
|
|
grep {/\A\Q$cl\E(?:\.|\z)/} |
1068
|
|
|
|
|
|
|
keys %$clset }); |
1069
|
|
|
|
|
|
|
$res =~ s/\n+/ /g; |
1070
|
5306
|
|
|
5306
|
1
|
10241
|
# a one-line dump of the clause, suitable for putting in generated |
1071
|
|
|
|
|
|
|
# code's comment |
1072
|
|
|
|
|
|
|
$cd->{_debug_ccl_note} = "clause: $res"; |
1073
|
|
|
|
|
|
|
} else { |
1074
|
5306
|
50
|
66
|
|
|
14664
|
$cd->{_debug_ccl_note} = "clause: $cd->{clause}"; |
1075
|
|
|
|
|
|
|
} |
1076
|
5306
|
50
|
|
|
|
12745
|
|
1077
|
0
|
|
|
|
|
0
|
# we save ccls to save_ccls and empty ccls for each clause, to let clause |
1078
|
0
|
|
|
|
|
0
|
# join and do stuffs to ccls. at after_clause(), we push the clause's result |
1079
|
0
|
|
|
|
|
0
|
# as a single ccl to the original ccls. |
1080
|
|
|
|
|
|
|
|
1081
|
0
|
|
|
|
|
0
|
push @{ $cd->{_save_ccls} }, $cd->{ccls}; |
1082
|
0
|
|
|
|
|
0
|
$cd->{ccls} = []; |
1083
|
|
|
|
|
|
|
} |
1084
|
0
|
|
|
|
|
0
|
|
1085
|
0
|
|
|
|
|
0
|
my ($self, $cd) = @_; |
|
0
|
|
|
|
|
0
|
|
1086
|
|
|
|
|
|
|
|
1087
|
0
|
|
|
|
|
0
|
if ($cd->{args}{debug}) { |
1088
|
|
|
|
|
|
|
delete $cd->{_debug_ccl_note}; |
1089
|
|
|
|
|
|
|
} |
1090
|
0
|
|
|
|
|
0
|
|
1091
|
|
|
|
|
|
|
my $save = pop @{ $cd->{_save_ccls} }; |
1092
|
5306
|
|
|
|
|
13000
|
if (@{ $cd->{ccls} }) { |
1093
|
|
|
|
|
|
|
push @$save, { |
1094
|
|
|
|
|
|
|
ccl => $self->join_ccls($cd, $cd->{ccls}, {op=>$cd->{cl_op}}), |
1095
|
|
|
|
|
|
|
err_level => $cd->{clset}{"$cd->{clause}.err_level"} // "error", |
1096
|
|
|
|
|
|
|
} |
1097
|
|
|
|
|
|
|
} |
1098
|
|
|
|
|
|
|
$cd->{ccls} = $save; |
1099
|
5306
|
|
|
|
|
8716
|
} |
|
5306
|
|
|
|
|
13396
|
|
1100
|
5306
|
|
|
|
|
11604
|
|
1101
|
|
|
|
|
|
|
my ($self, $cd) = @_; |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
# simply join them together with && |
1104
|
5276
|
|
|
5276
|
1
|
9931
|
$cd->{result} = $self->indent( |
1105
|
|
|
|
|
|
|
$cd, |
1106
|
5276
|
50
|
|
|
|
14684
|
$self->join_ccls($cd, $cd->{ccls}, {err_msg => ''}), |
1107
|
0
|
|
|
|
|
0
|
); |
1108
|
|
|
|
|
|
|
} |
1109
|
|
|
|
|
|
|
|
1110
|
5276
|
|
|
|
|
6668
|
my ($self, $cd) = @_; |
|
5276
|
|
|
|
|
10874
|
|
1111
|
5276
|
100
|
|
|
|
6735
|
|
|
5276
|
|
|
|
|
11345
|
|
1112
|
|
|
|
|
|
|
# XXX also handle postfilters here |
1113
|
|
|
|
|
|
|
|
1114
|
3957
|
|
100
|
|
|
17367
|
if (delete $cd->{_skip_undef}) { |
1115
|
|
|
|
|
|
|
my $jccl = $self->join_ccls( |
1116
|
|
|
|
|
|
|
$cd, |
1117
|
5276
|
|
|
|
|
20610
|
[splice(@{ $cd->{ccls} }, $cd->{_ccls_idx1})], |
1118
|
|
|
|
|
|
|
); |
1119
|
|
|
|
|
|
|
local $cd->{_debug_ccl_note} = "skip if undef"; |
1120
|
|
|
|
|
|
|
$self->add_ccl( |
1121
|
174
|
|
|
174
|
0
|
368
|
$cd, |
1122
|
|
|
|
|
|
|
"!".$self->expr_defined($cd->{data_term})." ? ".$self->true." : \n\n". |
1123
|
|
|
|
|
|
|
$self->enclose_paren($jccl), |
1124
|
|
|
|
|
|
|
{err_msg => ''}, |
1125
|
|
|
|
|
|
|
); |
1126
|
174
|
|
|
|
|
681
|
} |
1127
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
# simply join them together with && |
1129
|
|
|
|
|
|
|
$cd->{result} = $self->indent( |
1130
|
|
|
|
|
|
|
$cd, |
1131
|
5025
|
|
|
5025
|
1
|
9680
|
$self->join_ccls($cd, $cd->{ccls}, {err_msg => ''}), |
1132
|
|
|
|
|
|
|
); |
1133
|
|
|
|
|
|
|
} |
1134
|
|
|
|
|
|
|
|
1135
|
5025
|
100
|
|
|
|
13372
|
1; |
1136
|
|
|
|
|
|
|
# ABSTRACT: Base class for programming language compilers |
1137
|
|
|
|
|
|
|
|
1138
|
4554
|
|
|
|
|
7573
|
|
|
4554
|
|
|
|
|
18898
|
|
1139
|
|
|
|
|
|
|
=pod |
1140
|
4554
|
|
|
|
|
18414
|
|
1141
|
|
|
|
|
|
|
=encoding UTF-8 |
1142
|
|
|
|
|
|
|
|
1143
|
4554
|
|
|
|
|
15806
|
=head1 NAME |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
Data::Sah::Compiler::Prog - Base class for programming language compilers |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
=head1 VERSION |
1148
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
This document describes version 0.912 of Data::Sah::Compiler::Prog (from Perl distribution Data-Sah), released on 2022-08-20. |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1152
|
5025
|
|
|
|
|
20431
|
|
1153
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
This class is derived from L<Data::Sah::Compiler>. It is used as base class for |
1156
|
|
|
|
|
|
|
compilers which compile schemas into code (validator) in several programming |
1157
|
|
|
|
|
|
|
languages, Perl (L<Data::Sah::Compiler::perl>) and JavaScript |
1158
|
|
|
|
|
|
|
(L<Data::Sah::Compiler::js>) being two of them. (Other similar programming |
1159
|
|
|
|
|
|
|
languages like PHP and Ruby might also be supported later on if needed). |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
Compilers using this base class are flexible in the kind of code they produce: |
1162
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
=over 4 |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
=item * configurable validator return type |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
Can generate validator that returns a simple bool result, str, or full data |
1168
|
|
|
|
|
|
|
structure (containing errors, warnings, and potentially other information). |
1169
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
=item * configurable data term |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
For flexibility in combining the validator code with other code, e.g. putting |
1173
|
|
|
|
|
|
|
inside subroutine wrapper (see L<Perinci::Sub::Wrapper>) or directly embedded to |
1174
|
|
|
|
|
|
|
your source code (see L<Dist::Zilla::Plugin::Rinci::Validate>). |
1175
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
=back |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
=for Pod::Coverage ^(after_.+|before_.+|add_var|add_ccl|join_ccls|check_compile_args|enclose_paren|init_cd|expr|expr_.+|stmt_.+)$ |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
=head1 HOW IT WORKS |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
The compiler generates code in the following form: |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
EXPR && EXPR2 && ... |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
where C<EXPR> can be a single expression or multiple expressions joined by the |
1187
|
|
|
|
|
|
|
list operator (which Perl and JavaScript support). Each C<EXPR> is typically |
1188
|
|
|
|
|
|
|
generated out of a single schema clause. Some pseudo-example of generated |
1189
|
|
|
|
|
|
|
JavaScript code: |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
(data >= 0) # from clause: min => 0 |
1192
|
|
|
|
|
|
|
&& |
1193
|
|
|
|
|
|
|
(data <= 10) # from clause: max => 10 |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
Another example, a fuller translation of schema C<< [int => {min=>0, max=>10}] |
1196
|
|
|
|
|
|
|
>> to Perl, returning string result (error message) instead of boolean: |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
# from clause: req => 0 |
1199
|
|
|
|
|
|
|
!defined($data) ? 1 : ( |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
# type check |
1202
|
|
|
|
|
|
|
($data =~ /^[+-]?\d+$/ ? 1 : ($err //= "Data is not an integer", 0)) |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
&& |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
# from clause: min => 0 |
1207
|
|
|
|
|
|
|
($data >= 0 ? 1 : ($err //= "Must be at least 0", 0)) |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
&& |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
# from clause: max => 10 |
1212
|
|
|
|
|
|
|
($data <= 10 ? 1 : ($err //= "Must be at most 10", 0)) |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
) |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
The final validator code will add enclosing subroutine and variable declaration, |
1217
|
|
|
|
|
|
|
loading of modules, etc. |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
Note: Current assumptions/hard-coded things for the supported languages: ternary |
1220
|
|
|
|
|
|
|
operator (C<? :>), semicolon as statement separator. |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
=head1 COMPILATION DATA KEYS |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
=over |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
=item * use_dpath => bool |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
Convenience. This is set when code needs to track data path, which is when |
1229
|
|
|
|
|
|
|
C<return_type> argument is set to something other than C<bool> or C<bool+val>, |
1230
|
|
|
|
|
|
|
and when schema has subschemas. Data path is used when generating error message |
1231
|
|
|
|
|
|
|
string, to help point to the item in the data structure (an array element, a |
1232
|
|
|
|
|
|
|
hash value) which fails the validation. This is not needed when we want the |
1233
|
|
|
|
|
|
|
validator to only return true/false, and also not needed when we do not recurse |
1234
|
|
|
|
|
|
|
into subschemas. |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
=item * data_term => ARRAY |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
Input data term. Set to C<< $cd->{args}{data_term} >> or a temporary variable |
1239
|
|
|
|
|
|
|
(if C<< $cd->{args}{data_term_is_lvalue} >> is false). Hooks should use this |
1240
|
|
|
|
|
|
|
instead of C<< $cd->{args}{data_term} >> directly, because aside from the |
1241
|
|
|
|
|
|
|
aforementioned temporary variable, data term can also change, for example if |
1242
|
|
|
|
|
|
|
C<default.temp> or C<prefilters.temp> attribute is set, where generated code |
1243
|
|
|
|
|
|
|
will operate on another temporary variable to avoid modifying the original data. |
1244
|
|
|
|
|
|
|
Or when C<.input> attribute is set, where generated code will operate on |
1245
|
|
|
|
|
|
|
variable other than data. |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
=item * subs => ARRAY |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
Contains pairs of subroutine names and definition code string, e.g. C<< [ |
1250
|
|
|
|
|
|
|
[_sahs_zero => 'sub _sahs_zero { $_[0] == 0 }'], [_sahs_nonzero => 'sub |
1251
|
|
|
|
|
|
|
_sah_s_nonzero { $_[0] != 0 }'] ] >>. For flexibility, you'll need to do this |
1252
|
|
|
|
|
|
|
bit of arranging yourself to get the final usable code you can compile in your |
1253
|
|
|
|
|
|
|
chosen programming language. |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
=item * vars => HASH |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
=item * coerce_to => str |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
Retrieved from the schema's C<x.$COMPILER.coerce_to> attribute. Each type |
1260
|
|
|
|
|
|
|
handler might have its own default value. |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
=back |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
=head1 INTERNAL VARIABLES IN THE GENERATED CODE |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
The generated code maintains the following variables. C<_sahv_> prefix stands |
1267
|
|
|
|
|
|
|
for "Sah validator", it is used to minimize clash with data_term. |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=over |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
=item * _sahv_dpath => ARRAY |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
Analogous to C<spath> in compilation data, this variable stands for "data path" |
1274
|
|
|
|
|
|
|
and is used to track location within data. If a clause is checking each element |
1275
|
|
|
|
|
|
|
of an array (like the 'each_elem' or 'elems' array clause), this variable will |
1276
|
|
|
|
|
|
|
be adjusted accordingly. Error messages thus can be more informative by pointing |
1277
|
|
|
|
|
|
|
more exactly where in the data the problem lies. |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=item * tmp_data_term => ANY |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
As explained in the C<compile()> method, this is used to store temporary value |
1282
|
|
|
|
|
|
|
when checking against clauses. |
1283
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
=item * _sahv_stack => ARRAY |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
This variable is used to store validation result of subdata. It is only used if |
1287
|
|
|
|
|
|
|
the validator is returning a string or full structure, not a single boolean |
1288
|
|
|
|
|
|
|
value. See C<Data::Sah::Compiler::js::TH::hash> for an example. |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
=item * _sahv_x |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
Usually used as temporary variable in short, anonymous functions. |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
=back |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
These usually need not be set/changed by users. |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=head2 hc => OBJ |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
Instance of L<Data::Sah::Compiler::human>, to generate error messages. |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
=head2 comment_style => STR |
1305
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
Specify how comments are written in the target language. Either 'cpp' (C<// |
1307
|
|
|
|
|
|
|
comment>), 'shell' (C<# comment>), 'c' (C</* comment */>), or 'ini' (C<; |
1308
|
|
|
|
|
|
|
comment>). Each programming language subclass will set this, for example, the |
1309
|
|
|
|
|
|
|
perl compiler sets this to 'shell' while js sets this to 'cpp'. |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
=head2 var_sigil => STR |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
=head2 concat_op => STR |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
=head2 logical_and_op => STR |
1316
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
=head2 logical_not_op => STR |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
=head1 METHODS |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
=head2 new() => OBJ |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
=head2 $c->compile(%args) => RESULT |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
Generate a validator (function) for the given schema. |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
Aside from base class' arguments, this class supports these arguments (suffix |
1328
|
|
|
|
|
|
|
C<*> denotes required argument): |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
=over |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
=item * cache |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
Bool, default false. If set to true, will generate validators for base schemas |
1335
|
|
|
|
|
|
|
when possible, compile them into functions in the |
1336
|
|
|
|
|
|
|
C<Data::Sah::_GeneratedValidators::*>, then have the generated validator code |
1337
|
|
|
|
|
|
|
calls these functions. This will result in smaller validator code and shorter |
1338
|
|
|
|
|
|
|
compilation time especially for large/complex schema that is composed from |
1339
|
|
|
|
|
|
|
subschemas. But this will also create a (usually insignificant) additional |
1340
|
|
|
|
|
|
|
overhead of multiple function calls when doing validation using the generated |
1341
|
|
|
|
|
|
|
validator code. |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
Only relevant when L</name> argument is set. When a certain named |
1344
|
|
|
|
|
|
|
function is already defined, avoid generating the function declaration again and |
1345
|
|
|
|
|
|
|
instead call the defined function. |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
=item * data_term |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
Str. A variable name or an expression in the target language that contains the |
1350
|
|
|
|
|
|
|
data, defaults to I<var_sigil> + C<name> if not specified. |
1351
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
=item * data_term_is_lvalue |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
Bool, default true. Whether C<data_term> can be assigned to. |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
=item * tmp_data_name |
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
Str. Normally need not be set manually, as it will be set to "tmp_" . data_name. |
1359
|
|
|
|
|
|
|
Used to store temporary data during clause evaluation. |
1360
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=item * tmp_data_term |
1362
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
Str. Normally need not be set manually, as it will be set to var_sigil . |
1364
|
|
|
|
|
|
|
tmp_data_name. Used to store temporary data during clause evaluation. For |
1365
|
|
|
|
|
|
|
example, in JavaScript, the 'int' and 'float' type pass strings in the type |
1366
|
|
|
|
|
|
|
check. But for further checking with the clauses (like 'min', 'max', |
1367
|
|
|
|
|
|
|
'divisible_by') the string data needs to be converted to number first. Likewise |
1368
|
|
|
|
|
|
|
with prefiltering. This variable holds the temporary value. The clauses compare |
1369
|
|
|
|
|
|
|
against this value. At the end of clauses, the original data_term is restored. |
1370
|
|
|
|
|
|
|
So the output validator code for schema C<< [int => min => 1] >> will look |
1371
|
|
|
|
|
|
|
something like: |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
// type check 'int' |
1374
|
|
|
|
|
|
|
type(data)=='number' && Math.round(data)==data || parseInt(data)==data) |
1375
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
&& |
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
// convert to number |
1379
|
|
|
|
|
|
|
(tmp_data = type(data)=='number' ? data : parseFloat(data), true) |
1380
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
&& |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
// check clause 'min' |
1384
|
|
|
|
|
|
|
(tmp_data >= 1) |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
=item * err_term |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
Str. A variable name or lvalue expression to store error message(s), defaults to |
1389
|
|
|
|
|
|
|
I<var_sigil> + C<err_NAME> (e.g. C<$err_data> in the Perl compiler). |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
=item * var_prefix |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
Str, default "_sahv_". Prefix for variables declared by generated code. |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
=item * sub_prefix |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
Str, default "_sahs_". Prefix for subroutines declared by generated code. |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
=item * code_type |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
Str, default "validator". The kind of code to generate. For now the only valid |
1402
|
|
|
|
|
|
|
(and default) value is 'validator'. Compiler can perhaps generate other kinds of |
1403
|
|
|
|
|
|
|
code in the future. |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
=item * return_type |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
Str, default "bool". Specify what kind of return value the generated code should |
1408
|
|
|
|
|
|
|
produce. Either C<bool_valid>, C<bool_valid+val>, C<str_errmsg>, |
1409
|
|
|
|
|
|
|
C<str_errmsg+val>, or C<hash_details>. |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
C<bool_valid> means generated validator code should just return true/false |
1412
|
|
|
|
|
|
|
depending on whether validation succeeds/fails. |
1413
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
C<bool_valid+val> is like C<bool_valid>, but instead of just C<bool_valid> the |
1415
|
|
|
|
|
|
|
validator code will return a two-element arrayref C<< [bool_valid, val] >> where |
1416
|
|
|
|
|
|
|
C<val> is the final value of data (after setting of default, coercion, etc.) |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
C<str_errmsg> means validation should return an error message string (the first |
1419
|
|
|
|
|
|
|
one encountered) if validation fails and an empty string/undef if validation |
1420
|
|
|
|
|
|
|
succeeds. |
1421
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
C<str_errmsg+val> is like C<str_errmsg>, but instead of just C<str_errmsg> the |
1423
|
|
|
|
|
|
|
validator code will return a two-element arrayref C<< [str_errmsg, val] >> where |
1424
|
|
|
|
|
|
|
C<val> is the final value of data (after setting of default, coercion, etc.) |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
C<hash_details> means validation should return a full hash data structure. From |
1427
|
|
|
|
|
|
|
this structure you can check whether validation succeeds, retrieve all the |
1428
|
|
|
|
|
|
|
collected errors/warnings, etc. |
1429
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
=item * coerce |
1431
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
Bool, default true. If set to false, will not include coercion code. |
1433
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
=item * debug |
1435
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
Bool, default false. This is a general debugging option which should turn on all |
1437
|
|
|
|
|
|
|
debugging-related options, e.g. produce more comments in the generated code, |
1438
|
|
|
|
|
|
|
etc. Each compiler might have more specific debugging options. |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
If turned on, specific debugging options can be explicitly turned off |
1441
|
|
|
|
|
|
|
afterwards, e.g. C<< debug=>1, debug_log=>0 >> will turn on all debugging |
1442
|
|
|
|
|
|
|
options but turn off the C<debug_log> setting. |
1443
|
|
|
|
|
|
|
|
1444
|
|
|
|
|
|
|
Currently turning on C<debug> means: |
1445
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
=over |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
=item - Turning on the other debug_* options, like debug_log |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
=item - Prefixing error message with msgpath |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
=back |
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
=item * debug_log |
1455
|
|
|
|
|
|
|
|
1456
|
|
|
|
|
|
|
Bool, default false. Whether to add logging to generated code. This aids in |
1457
|
|
|
|
|
|
|
debugging generated code specially for more complex validation. |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
=item * comment |
1460
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
Bool, default true. If set to false, generated code will be devoid of comments. |
1462
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
=item * human_hash_values |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
Hash. Optional. Will be passed to C<hash_values> argument during C<compile()> by |
1466
|
|
|
|
|
|
|
human compiler. |
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
=back |
1469
|
|
|
|
|
|
|
|
1470
|
|
|
|
|
|
|
=head2 $c->comment($cd, @args) => STR |
1471
|
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
Generate a comment. For example, in perl compiler: |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
$c->comment($cd, "123"); # -> "# 123\n" |
1475
|
|
|
|
|
|
|
|
1476
|
|
|
|
|
|
|
Will return an empty string if compile argument C<comment> is set to false. |
1477
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
=head1 HOMEPAGE |
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
Please visit the project's homepage at L<https://metacpan.org/release/Data-Sah>. |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
=head1 SOURCE |
1483
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
Source repository is at L<https://github.com/perlancar/perl-Data-Sah>. |
1485
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
=head1 AUTHOR |
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
perlancar <perlancar@cpan.org> |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
=head1 CONTRIBUTING |
1491
|
|
|
|
|
|
|
|
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
To contribute, you can send patches by email/via RT, or send pull requests on |
1494
|
|
|
|
|
|
|
GitHub. |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
Most of the time, you don't need to build the distribution yourself. You can |
1497
|
|
|
|
|
|
|
simply modify the code, then test via: |
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
% prove -l |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
If you want to build the distribution (e.g. to try to install it locally on your |
1502
|
|
|
|
|
|
|
system), you can install L<Dist::Zilla>, |
1503
|
|
|
|
|
|
|
L<Dist::Zilla::PluginBundle::Author::PERLANCAR>, |
1504
|
|
|
|
|
|
|
L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other |
1505
|
|
|
|
|
|
|
Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond |
1506
|
|
|
|
|
|
|
that are considered a bug and can be reported to me. |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1509
|
|
|
|
|
|
|
|
1510
|
|
|
|
|
|
|
This software is copyright (c) 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar <perlancar@cpan.org>. |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
1513
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
1514
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
=head1 BUGS |
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah> |
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
1520
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
1521
|
|
|
|
|
|
|
feature. |
1522
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
=cut |