File Coverage

blib/lib/Data/Sah/Compiler/Prog.pm
Criterion Covered Total %
statement 438 496 88.3
branch 185 242 76.4
condition 80 115 69.5
subroutine 24 26 92.3
pod 7 19 36.8
total 734 898 81.7


line stmt bran cond sub pod time code
1              
2             use 5.010001;
3 22     22   13580 use strict;
  22         68  
4 22     22   106 use warnings;
  22         42  
  22         388  
5 22     22   90 use Log::ger;
  22         45  
  22         543  
6 22     22   107  
  22         37  
  22         119  
7             use Mo qw(build default);
8 22     22   4225 extends 'Data::Sah::Compiler';
  22         42  
  22         116  
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-09-30'; # DATE
30             our $DIST = 'Data-Sah'; # DIST
31             our $VERSION = '0.913'; # VERSION
32              
33             my ($self, %args) = @_;
34              
35 5060     5060 0 33116 my $cd = $self->SUPER::init_cd(%args);
36             $cd->{vars} = {};
37 5060         27521  
38 5060         15389 my $hc = $self->hc;
39             if (!$hc) {
40 5060         13882 $hc = $self->main->get_compiler("human");
41 5060 100       20583 $self->hc($hc);
42 4725         9907 }
43 4725         12922  
44             if (my $ocd = $cd->{outer_cd}) {
45             $cd->{vars} = $ocd->{vars};
46 5060 100       22566 $cd->{modules} = $ocd->{modules};
47 330         664 $cd->{functions} = $ocd->{functions};
48 330         517 $cd->{_hc} = $ocd->{_hc};
49 330         747 $cd->{_hcd} = $ocd->{_hcd};
50 330         460 $cd->{_subdata_level} = $ocd->{_subdata_level};
51 330         519 $cd->{use_dpath} = 1 if $ocd->{use_dpath};
52 330         476 } else {
53 330 100       694 $cd->{vars} = {};
54             $cd->{modules} = [];
55 4730         10142 $cd->{functions} = {};
56 4730         9875 $cd->{_hc} = $hc;
57 4730         8632 $cd->{_subdata_level} = 0;
58 4730         8611 }
59 4730         14872  
60             $cd;
61             }
62 5060         26266  
63             my ($self, $args) = @_;
64              
65             return if $args->{_args_checked_Prog}++;
66 9784     9784 0 16371  
67             $self->SUPER::check_compile_args($args);
68 9784 100       28433  
69             my $ct = ($args->{code_type} //= 'validator');
70 4730         14796 if ($ct ne 'validator') {
71             $self->_die({}, "code_type currently can only be 'validator'");
72 4730   50     16151 }
73 4730 50       11548 for ($args->{return_type}) {
74 0         0 $_ //= 'bool_valid';
75             # old values that are still supported but now deprecated
76 4730         8472 $_ = "bool_valid" if $_ eq 'bool';
77 4730   100     12902 $_ = "bool_valid+val" if $_ eq 'bool+val';
78             $_ = "str_errmsg" if $_ eq 'str';
79 4730 50       8564 $_ = "str_errmsg+val" if $_ eq 'str+val';
80 4730 50       7801 $_ = "hash_details" if $_ eq 'full';
81 4730 50       8300 }
82 4730 50       7923 my $rt = $args->{return_type};
83 4730 50       8803 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         6502 "use bool_valid+val|bool_valid|str_errmsg+val|str_errmsg|hash_details");
86 4730 50       16938 }
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     17317 $args->{data_term_is_lvalue} //= 1;
91 4730   50     17218 $args->{tmp_data_name} //= "tmp_$args->{data_name}";
92 4730   33     20799 $args->{tmp_data_term} //= $self->var_sigil . $args->{tmp_data_name};
93 4730   50     37353 $args->{comment} //= 1;
94 4730   33     18715 $args->{err_term} //= $self->var_sigil . "err_$args->{data_name}";
95 4730   33     15763 $args->{coerce} //= 1;
96 4730   50     31110 }
97 4730   33     17116  
98 4730   50     33715 my ($self, $cd, @args) = @_;
99             return '' unless $cd->{args}{comment};
100              
101             my $content = join("", @args);
102 17105     17105 1 29203 $content =~ s/\n+/ /g;
103 17105 50       30557  
104             my $style = $self->comment_style;
105 17105         29293 if ($style eq 'shell') {
106 17105         30074 return join("", "# ", $content, "\n");
107             } elsif ($style eq 'shell2') {
108 17105         36945 return join("", "## ", $content, "\n");
109 17105 50       72751 } elsif ($style eq 'cpp') {
    0          
    0          
    0          
    0          
110 17105         51247 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 78746 $expr =~ /\A(\s*)(.*)/os;
127 55497 100       150490 return "$1($2)";
128 30047 100       88132 }
129 6605         25805 }
130              
131 25450         54110 my ($self, $cd, $name, $value) = @_;
132 25450         107380  
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 23827  
138             # naming convention: expr_NOUN(), stmt_VERB(_NOUN)?()
139 13479 100       27387  
140             # XXX requires: expr_list
141 5042         12447  
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   31750  
208             # concatenate strings
209 19465         27399 my ($self, @t) = @_;
210 19465         27475 join(" " . $self->concat_op . " ", @t);
211             }
212 19465         49593  
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 1255 "++" . $self->var_sigil. $v;
224 607         1355 }
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 3617  
234 2428         5517 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 31495 }
245             my $cd = $args{cd} // $self->compile(%args);
246 4724         9188 my $et = $cd->{args}{err_term};
247 4724         7134  
248 4724         7090 if ($rt !~ /\Abool/) {
249 4724   33     12578 my ($ev) = $et =~ /(\w+)/; # to remove sigil
250 4724   33     14529 $self->add_var($cd, $ev, $rt =~ /\Astr/ ? undef : {});
251 4724   50     9965 }
252             my $resv = '_sahv_res';
253 4724         7236 my $rest = $self->var_sigil . $resv;
254 4724 50       10299  
255             my $needs_expr_block = (grep {$_->{phase} eq 'runtime'} @{ $cd->{modules} })
256             || $do_log;
257 4724   33     27691  
258 4691         21082 my $code_sub_body = join(
259             "",
260 4691 100       14011 (map {$self->stmt_declare_local_var(
261 2943         11993 $_, $self->literal($cd->{vars}{$_}))."\n"}
262 2943 100       12604 sort keys %{ $cd->{vars} }),
263             #$log->tracef('-> (validator)(%s) ...', $dt);\n";
264 4691         6952 $self->stmt_declare_local_var($resv, "\n\n" . $cd->{result})."\n\n",
265 4691         12576  
266             # when rt=bool_valid, return true/false result
267 4691   66     20829 #(";\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         14408 #($log->tracef('<- validator() = %s', ".
274 4691         20779 # "\$err_data);\n\n";
275             # x !!($do_log && $rt eq 'str_errmsg'),
276 4691         7083 ($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         15613 Data::Sah::Compiler::__linenum($code) :
313 4691         18753 $code);
  13534         26076  
  4691         9506  
314             }
315              
316             $code;
317 4691 100       17570 }
318 4651         12111  
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     14271 # 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         141491 #
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 53433 } else {
354 18651   100     49744 unless (defined $err_msg) { $err_msg = $cd->{clset}{"$clause.err_msg"} }
355 18651   100     48207 unless (defined $err_msg) {
356 18651   100     47435 # XXX how to invert on op='none' or op='not'?
357              
358             my @msgpath = @{$cd->{spath}};
359 18651   100     67044 my $msgpath;
      100        
360 18651         27221 my $hc = $cd->{_hc};
361 18651         24633 my $hcd = $cd->{_hcd};
362             while (1) {
363 18651 100       37345 # search error message, use more general one if the more
364 133 100       412 # specific one is not available
365 133 100       325 last unless @msgpath;
366             $msgpath = join("/", @msgpath);
367 18518 100       33974 my $ccls = $hcd->{result}{$msgpath};
  6037         11875  
368 18518 100       28734 pop @msgpath;
369             if ($ccls) {
370             local $hcd->{args}{format} = 'inline_err_text';
371 6037         7472 $err_msg = $hc->format_ccls($hcd, $ccls);
  6037         13815  
372 6037         7664 # show path when debugging
373 6037         8408 $err_msg = "(msgpath=$msgpath) $err_msg"
374 6037         7986 if $cd->{args}{debug};
375 6037         7872 last;
376             }
377             }
378 8518 100       14690 if (!$err_msg) {
379 8236         16136 $err_msg = "ERR (clause=".($cd->{clause} // "").")";
380 8236         13642 } else {
381 8236         10575 $err_msg = ucfirst($err_msg);
382 8236 100       15929 }
383 5755         12016 }
384 5755         18019 if ($err_msg) {
385             $self->add_var($cd, '_sahv_dpath', []) if $cd->{use_dpath};
386             $err_expr = $self->literal($err_msg);
387 5755 50       13017 $err_expr = $self->expr_prefix_dpath($err_expr) if $cd->{use_dpath};
388 5755         10467 }
389             }
390              
391 6037 100       10118 my $rt = $cd->{args}{return_type};
392 282   100     949 my $et = $cd->{args}{err_term};
393             my $err_code;
394 5755         13561 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       39192 $err_code = $self->expr_set_err_full($et, $k, $err_expr) if $err_expr;
398 11591 100       30290 } elsif ($rt =~ /\Astr/) {
399 11591         30154 if ($el ne 'warn') {
400 11591 100       374753 $err_code = $self->expr_set_err_str($et, $err_expr) if $err_expr;
401             }
402             }
403              
404 18651         34334 my $res = {
405 18651         28058 ccl => $ccl,
406 18651         22830 err_level => $el,
407 18651 100       46880 err_code => $err_code,
    100          
408 5844 50       19467 (_debug_ccl_note => $cd->{_debug_ccl_note}) x !!$cd->{_debug_ccl_note},
409 5844 100       11655 subdata => $opts->{subdata},
410 5844 100       16013 };
411             push @{ $cd->{ccls} }, $res;
412 5869 100       12389 delete $cd->{uclset}{"$clause.err_level"};
413 5851 100       15425 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         81496 my ($min_ok, $max_ok, $min_nok, $max_nok);
424 18651         25185 if ($op eq 'and') {
  18651         33402  
425 18651         38594 $max_nok = 0;
426 18651         74372 } 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 23653  
432 13822   100     33430 }
433 13822   100     36562 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         19103  
438 13822 100       28549 return "" unless @$ccls;
    100          
    100          
    50          
439 12623         16534  
440             my $rt = $cd->{args}{return_type};
441 607         1020 my $vp = $cd->{args}{var_prefix};
442              
443 288         436 my $aop = $self->logical_and_op;
444             my $nop = $self->logical_not_op;
445              
446             my $true = $self->true;
447 13822         18577  
448 13822         16554 # insert comment, error message, and $ok/$nok counting. $which is 0 by
449 13822         19694 # default (normal), or 1 (reverse logic, for 'not' or 'none'), or 2 (for
450 13822         16891 # $ok/$nok counting), or 3 (like 2, but for the last clause).
451             my $_ice = sub {
452 13822 100       30200 my ($ccl, $which) = @_;
453              
454 13768         21478 return $self->enclose_paren($ccl->{ccl}) if $ccl->{assert};
455 13768         18967  
456             my $res = "";
457 13768         36770  
458 13768         77714 if ($ccl->{_debug_ccl_note}) {
459             if ($cd->{args}{debug_log} // $cd->{args}{debug}) {
460 13768         62511 $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   32818  
467             $which //= 0;
468 22751 50       41266 # clause code
469             my $cc = ($which == 1 ? $nop:"") . $self->enclose_paren($ccl->{ccl});
470 22751         28408 my ($ec, $oec);
471             my ($ret, $oret);
472 22751 100       37833 if ($which >= 2) {
473 17105 50 33     55867 my @chk;
474             if ($ccl->{err_level} eq 'warn') {
475 0         0 $oret = 1;
476             $ret = 1;
477 17105         32392 } elsif ($ccl->{err_level} eq 'fatal') {
478             $oret = 1;
479             $ret = 0;
480             } else {
481 22751   100     74830 $oret = $self->expr_preinc_var("${vp}ok");
482             $ret = $self->expr_preinc_var("${vp}nok");
483 22751 100       47837 push @chk, $self->expr_var("${vp}ok"). " <= $max_ok"
484 22751         49910 if $dmax_ok;
485 22751         0 push @chk, $self->expr_var("${vp}nok")." <= $max_nok"
486 22751 100       32392 if $dmax_nok;
487 1214         1841 if ($which == 3) {
488 1214 50       3249 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         2987 my $et = $cd->{args}{err_term};
496 1214         6578 my $clerrc;
497 1214 50       5176 if ($rt eq 'hash_details') {
498             $clerrc = $self->expr_reset_err_full($et);
499 1214 50       2077 } else {
500             $clerrc = $self->expr_reset_err_str($et);
501 1214 100       2682 }
502 607 50       2523 push @chk, $clerrc;
503             }
504 607 50       3491 }
505             }
506             $res .= "($cc ? $oret : $ret)";
507             $res .= " $aop " . join(" $aop ", @chk) if @chk;
508 607 100       1878 } else {
509 404         820 $ec = $ccl->{err_code};
510 404         584 $ret =
511 404 100       911 $ccl->{err_level} eq 'fatal' ? 0 :
512 202         751 # this must not be done because it messes up ok/nok counting
513             #$rt eq 'hash_details' ? 1 :
514 202         885 $ccl->{err_level} eq 'warn' ? 1 : 0;
515             if ($rt =~ /\Abool/ && $ret) {
516 404         750 $res .= $true;
517             } elsif ($rt =~ /\Abool/ || !$ec) {
518             $res .= $self->enclose_paren($cc);
519             } else {
520 1214         3086 $res .= $self->enclose_paren(
521 1214 100       3812 $self->enclose_paren($cc). " ? $true : ($ec,$ret)",
522             "force");
523 21537         29738 }
524             }
525              
526             # insert dpath handling
527             $res = $self->expr_push_and_pop_dpath_between_expr($res)
528 21537 100       44991 if $cd->{use_dpath} && $ccl->{subdata};
    100          
529 21537 100 100     86596 $res;
    100 100        
530 36         69  
531             };
532 14896         26842  
533             my $j = "\n\n$aop\n\n";
534 6605         11901 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     53188 0..@$ccls-1;
543 22751         252852 {
544             local $cd->{ccls} = [];
545 13768         81739 local $cd->{_debug_ccl_note} = "op=$op";
546             $self->add_ccl(
547 13768         26249 $cd,
548 13768 100       32127 $self->expr_block(
    100          
    100          
549 304         922 join(
550             "",
551 12569         20656 $self->stmt_declare_local_var("${vp}ok" , "0"), "\n",
  20050         31812  
552             $self->stmt_declare_local_var("${vp}nok", "0"), "\n",
553 288         525 "\n",
  576         1057  
554             $self->block_uses_sub ?
555 607 100       1916 $self->stmt_return($jccl) : $jccl,
  1214         3181  
556             )
557             ),
558 607         1221 );
  607         1430  
559 607         1536 $_ice->($cd->{ccls}[0]);
560 607 50       2199 }
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         2211 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 9121  
580             # do a human compilation first to collect all the error messages
581 5060 50       10206  
582 5060         12322 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 9028  
595             my ($self, $cd) = @_;
596              
597             my $rt = $cd->{args}{return_type};
598 5059 100       13318 my $rt_is_hash = $rt =~ /\Ahash/;
599 4729         6748 my $rt_is_str = $rt =~ /\Astr/;
600 4729         6002  
  4729         61703  
601 4729         13219 $cd->{use_dpath} //= (
602 4729         8367 $rt_is_hash ||
603 4729         8046 ($rt_is_str && $cd->{has_subschema})
604 4729         6878 );
605 4729         6454  
606 4729         9257 # handle ok/default/coercion/prefilters/req/forbidden clauses and type check
607 4729         25191  
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 8212 my $clsets = $cd->{clsets};
613              
614 5056         9195 # handle ok, this is very high priority because !ok=>1 should fail undef
615 5056         13244 # too. we need to handle its .op=not here.
616 5056         10596 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     30993 $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         7735 $self->add_ccl($cd, $self->false);
626 5056         13351 } else {
627 5056         9209 local $cd->{_debug_ccl_note} = "ok #$i";
628 5056         8182 $self->add_ccl($cd, $self->true);
629 5056         7562 }
630             delete $cd->{uclsets}[$i]{"ok"};
631             delete $cd->{uclsets}[$i]{"ok.is_expr"};
632             }
633 5056         12814  
634 4610         8322 # handle default
635 4610 100       12300 HANDLE_DEFAULT: {
636 54   100     205  
637 54 50 66     177 my $default_value_expr;
638 0         0 my $default_value_ccl_note;
639             GEN_DEFAULT_VALUE_RULES:
640 54 100       122 {
641 27         94 require Data::Sah::DefaultValueCommon;
642 27         104  
643             my @default_value_rules;
644 27         91 for my $i (0..@$clsets-1) {
645 27         95 my $clset = $clsets->[$i];
646             push @default_value_rules,
647 54         156 @{ $clset->{"x.$cname.default_value_rules"} // [] },
648 54         141 @{ $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         8479 );
  5056         7304  
655             last unless @$rules;
656              
657             for my $i (reverse 0..$#{$rules}) {
658 5056         6901 my $rule = $rules->[$i];
  5056         34952  
659              
660 5056         21371 $self->add_compile_module(
661 5056         9796 $cd, "Data::Sah::Value::$cname\::$rule->{name}",
662 4610         8755 {category => 'default_value'},
663             );
664 4610   100     20208  
665 4610   50     7214 if ($rule->{modules}) {
  4610         18768  
666             for my $mod (keys %{ $rule->{modules} }) {
667             my $modspec = $rule->{modules}{$mod};
668 5056         11606 $modspec = {version=>$modspec} unless ref $modspec eq 'HASH';
669             $self->add_runtime_module($cd, $mod, {category=>'default_value', %$modspec});
670             }
671             }
672 5056 100       89341 }
673              
674 1         5 $default_value_expr = join " // " , map { "($_->{expr_value})" } @$rules;
  1         5  
675 1         3 $default_value_ccl_note = "default value rule(s): ".
676             join(", ", map {$_->{name}} @$rules);
677 1         18 } # 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       6 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         6 $self->true,
  1         8  
692             ),
693 1         4 {err_msg => ""},
  1         6  
694             );
695             }
696 5056         10255 delete $cd->{uclsets}[$i]{"default"};
697 4610         7716 delete $cd->{uclsets}[$i]{"default.is_expr"};
698 4610         7616 }
699 4610         7851  
700 4610 100       8708 if (defined $default_value_expr) {
701 90         301 local $cd->{_debug_ccl_note} = $default_value_ccl_note;
702 90 50       281 $self->add_ccl(
703             $cd,
704 90         3041 $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         8355 # handle req
714 4610         8633 my $has_req;
715             for my $i (0..@$clsets-1) {
716             my $clset = $clsets->[$i];
717 5056 100       9475 my $req = $clset->{req};
718 1         4 my $reqie = $clset->{"req.is_expr"};
719 1         6 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         7320 $has_req++;
732 5056         9327 my $ct = $self->expr($cd, $req);
733 4610         7445 $self->add_ccl(
734 4610         7456 $cd, "!($ct) || ".$self->expr_defined($dt),
735 4610         6010 {
736 4610         10257 err_msg => $req_err_msg,
737 4610         13104 err_level => 'fatal',
738 4610 100 66     14995 },
    50          
739 471         768 );
740 471         1361 }
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         7729 err_msg => $fbd_err_msg,
759 4610         10713 err_level => 'fatal',
760             },
761             );
762             } elsif ($fbdie) {
763 5056         6542 $has_fbd++;
764 5056         10235 my $ct = $self->expr($cd, $fbd);
765 4610         6264 $self->add_ccl(
766 4610         6695 $cd, "!($ct) || !".$self->expr_defined($dt),
767 4610         5958 {
768 4610         8510 err_msg => $fbd_err_msg,
769 4610         11071 err_level => 'fatal',
770 4610 100 66     13393 },
    50          
771 27         51 );
772 27         98 }
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         6341  
791 4610         9423 my @coerce_rules;
792             for my $i (0..@$clsets-1) {
793             my $clset = $clsets->[$i];
794 5056 100 100     15510 push @coerce_rules,
795 4558         9067 @{ $clset->{"x.$cname.coerce_rules"} // [] },
796 4558         7445 @{ $clset->{'x.coerce_rules'} // [] };
  4558         12399  
797             }
798              
799 5056         10673 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       6078 coerce_rules => \@coerce_rules,
  5056         10058  
805             );
806 5056         23956 last unless @$rules;
807              
808 5056         17680 $coerce_might_fail = 1 if grep { $_->{meta}{might_fail} } @$rules;
809 5056         11084  
810 4610         6707 my $prev_term;
811             for my $i (reverse 0..$#{$rules}) {
812 4610   100     17061 my $rule = $rules->[$i];
813 4610   50     7565  
  4610         17441  
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         13341 my $modspec = $rule->{modules}{$mod};
822             $modspec = {version=>$modspec} unless ref $modspec eq 'HASH';
823 5056 100       310344 $self->add_runtime_module($cd, $mod, {category=>'coerce', %$modspec});
824             }
825 1220 100       2566 }
  1392         4149  
826              
827 1220         2207 if ($i == $#{$rules}) {
828 1220         1946 if ($coerce_might_fail) {
  1220         2776  
829 1392         2619 $prev_term = $self->expr_array($self->literal(undef), $dt);
830             } else {
831 1392         8926 $prev_term = $dt;
832             }
833             } else {
834             $prev_term = "($coerce_expr)";
835             }
836 1392 100       5243  
837 156         189 my $ec;
  156         357  
838 156         242 if ($coerce_might_fail && !$rule->{meta}{might_fail}) {
839 156 50       459 $ec = $self->expr_array($self->literal(undef), $rule->{expr_coerce});
840 156         545 } else {
841             $ec = "($rule->{expr_coerce})";
842             }
843              
844 1392 100       2268 $coerce_expr = $self->expr_ternary(
  1392         3087  
845 1220 100       2192 "($rule->{expr_match})",
846 60         240 $ec,
847             $prev_term,
848 1160         1815 );
849             }
850             $coerce_ccl_note = "coerce rule(s): ".
851 172         368 join(", ", map {$_->{name}} @$rules) .
852             ($cd->{coerce_to} ? " # coerce to: $cd->{coerce_to}" : "");
853             } # GEN_COERCE_EXPR
854 1392         2052  
855 1392 100 100     3466 my $prefilters_expr;
856 164         349 my $prefilters_ccl_note;
857             GEN_PREFILTERS_EXPRS:
858 1228         2876 {
859             my @filter_names;
860             for my $i (0..@$clsets-1) {
861 1392         4728 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 1392         7190 my $rules = Data::Sah::FilterCommon::get_filter_rules(
869 1220 100       3656 compiler => $cname,
870             data_term => $dt,
871             filter_names => \@filter_names,
872 5056         7861 );
873              
874             my @exprs;
875             for my $i (0..$#{$rules}) {
876 5056         6730 my $rule = $rules->[$i];
  5056         6827  
877 5056         9516  
878 4610         6598 $self->add_compile_module(
879 6         16 $cd, "Data::Sah::Filter::$cname\::$rule->{name}",
880 4610 100       10906 {category => 'filter'},
881             );
882 5056 100       10916 if ($rule->{modules}) {
883             for my $mod (keys %{ $rule->{modules} }) {
884 6         875 my $modspec = $rule->{modules}{$mod};
885 6         1002 $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         5002 my $expr;
892 6         9 if ($rule->{meta}{might_fail}) {
  6         17  
893 7         16 my $expr_fail;
894             # XXX rather hackish: like when coercion handling, to avoid
895 7         42 # 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       27 # 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         13 $self->expr_set_err_str($et, $self->expr_array_subscript($dt, 0)),
908 7         11 $self->expr_set($dt, $self->expr_array_subscript($dt, 1)),
909 7 100       17 $self->false,
910 3         3 );
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         6 $self->true,
930             );
931             }
932             push @exprs, $expr;
933 3         8 } # 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         13 unless defined($cd->{_ccl_check_type});
946             }
947             local $cd->{_debug_ccl_note};
948              
949 7         21 # handle coercion
950             if ($coerce_expr) {
951 6         28 $cd->{_debug_ccl_note} = $coerce_ccl_note;
952             if ($coerce_might_fail) {
953 6         15  
  7         51  
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       6528 $self->false,
  5056         10155  
959 0         0 );
960             } elsif ($rt_is_str) {
961             $expr_fail = $self->expr_list(
962 5056 50       11509 $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         17623 $self->false,
965             );
966             } else {
967 5056 100       12257 $expr_fail = $self->false;
968 1220         2124 }
969 1220 100       2551  
970             $self->add_ccl(
971 60         84 $cd,
972 60 50       147 $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         3 $self->expr_set($dt, $self->expr_array_subscript($dt, 1)),
979             $self->true,
980             )
981             ),
982             ),
983             {
984 59         163 err_msg => "",
985             err_level => "fatal",
986             },
987 60         189 );
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         3846 $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       11843 {
1022 6         15 err_msg => sprintf(
1023 6         24 $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       9766 $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         11946 }
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     14806 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 11478 # 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     12114 $cd->{_debug_ccl_note} = "clause: $cd->{clause}";
1075             }
1076 5306 50       11715  
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         13567 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         8141 }
  5306         12528  
1100 5306         11760  
1101             my ($self, $cd) = @_;
1102              
1103             # simply join them together with &&
1104 5276     5276 1 8857 $cd->{result} = $self->indent(
1105             $cd,
1106 5276 50       11199 $self->join_ccls($cd, $cd->{ccls}, {err_msg => ''}),
1107 0         0 );
1108             }
1109              
1110 5276         8146 my ($self, $cd) = @_;
  5276         9618  
1111 5276 100       6960  
  5276         11067  
1112             # XXX also handle postfilters here
1113              
1114 3957   100     15047 if (delete $cd->{_skip_undef}) {
1115             my $jccl = $self->join_ccls(
1116             $cd,
1117 5276         20068 [splice(@{ $cd->{ccls} }, $cd->{_ccls_idx1})],
1118             );
1119             local $cd->{_debug_ccl_note} = "skip if undef";
1120             $self->add_ccl(
1121 174     174 0 318 $cd,
1122             "!".$self->expr_defined($cd->{data_term})." ? ".$self->true." : \n\n".
1123             $self->enclose_paren($jccl),
1124             {err_msg => ''},
1125             );
1126 174         659 }
1127              
1128             # simply join them together with &&
1129             $cd->{result} = $self->indent(
1130             $cd,
1131 5025     5025 1 11326 $self->join_ccls($cd, $cd->{ccls}, {err_msg => ''}),
1132             );
1133             }
1134              
1135 5025 100       12355 1;
1136             # ABSTRACT: Base class for programming language compilers
1137              
1138 4554         6724  
  4554         16171  
1139             =pod
1140 4554         18384  
1141             =encoding UTF-8
1142              
1143 4554         15424 =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.913 of Data::Sah::Compiler::Prog (from Perl distribution Data-Sah), released on 2022-09-30.
1150              
1151             =head1 SYNOPSIS
1152 5025         20479  
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