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   12404 use strict;
  22         73  
4 22     22   99 use warnings;
  22         40  
  22         385  
5 22     22   82 use Log::ger;
  22         58  
  22         520  
6 22     22   112  
  22         39  
  22         100  
7             use Mo qw(build default);
8 22     22   4138 extends 'Data::Sah::Compiler';
  22         43  
  22         129  
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-10-19'; # DATE
30             our $DIST = 'Data-Sah'; # DIST
31             our $VERSION = '0.914'; # VERSION
32              
33             my ($self, %args) = @_;
34              
35 5060     5060 0 33537 my $cd = $self->SUPER::init_cd(%args);
36             $cd->{vars} = {};
37 5060         30020  
38 5060         14009 my $hc = $self->hc;
39             if (!$hc) {
40 5060         14634 $hc = $self->main->get_compiler("human");
41 5060 100       23321 $self->hc($hc);
42 4725         9696 }
43 4725         14625  
44             if (my $ocd = $cd->{outer_cd}) {
45             $cd->{vars} = $ocd->{vars};
46 5060 100       24341 $cd->{modules} = $ocd->{modules};
47 330         626 $cd->{functions} = $ocd->{functions};
48 330         555 $cd->{_hc} = $ocd->{_hc};
49 330         872 $cd->{_hcd} = $ocd->{_hcd};
50 330         596 $cd->{_subdata_level} = $ocd->{_subdata_level};
51 330         581 $cd->{use_dpath} = 1 if $ocd->{use_dpath};
52 330         540 } else {
53 330 100       798 $cd->{vars} = {};
54             $cd->{modules} = [];
55 4730         9811 $cd->{functions} = {};
56 4730         11350 $cd->{_hc} = $hc;
57 4730         10207 $cd->{_subdata_level} = 0;
58 4730         7990 }
59 4730         16243  
60             $cd;
61             }
62 5060         25314  
63             my ($self, $args) = @_;
64              
65             return if $args->{_args_checked_Prog}++;
66 9784     9784 0 15744  
67             $self->SUPER::check_compile_args($args);
68 9784 100       27076  
69             my $ct = ($args->{code_type} //= 'validator');
70 4730         17637 if ($ct ne 'validator') {
71             $self->_die({}, "code_type currently can only be 'validator'");
72 4730   50     18507 }
73 4730 50       11880 for ($args->{return_type}) {
74 0         0 $_ //= 'bool_valid';
75             # old values that are still supported but now deprecated
76 4730         11123 $_ = "bool_valid" if $_ eq 'bool';
77 4730   100     12998 $_ = "bool_valid+val" if $_ eq 'bool+val';
78             $_ = "str_errmsg" if $_ eq 'str';
79 4730 50       10424 $_ = "str_errmsg+val" if $_ eq 'str+val';
80 4730 50       10004 $_ = "hash_details" if $_ eq 'full';
81 4730 50       9418 }
82 4730 50       10738 my $rt = $args->{return_type};
83 4730 50       10672 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         7654 "use bool_valid+val|bool_valid|str_errmsg+val|str_errmsg|hash_details");
86 4730 50       18179 }
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     20083 $args->{data_term_is_lvalue} //= 1;
91 4730   50     18542 $args->{tmp_data_name} //= "tmp_$args->{data_name}";
92 4730   33     20134 $args->{tmp_data_term} //= $self->var_sigil . $args->{tmp_data_name};
93 4730   50     39158 $args->{comment} //= 1;
94 4730   33     22886 $args->{err_term} //= $self->var_sigil . "err_$args->{data_name}";
95 4730   33     16969 $args->{coerce} //= 1;
96 4730   50     31337 }
97 4730   33     17441  
98 4730   50     35595 my ($self, $cd, @args) = @_;
99             return '' unless $cd->{args}{comment};
100              
101             my $content = join("", @args);
102 17105     17105 1 30267 $content =~ s/\n+/ /g;
103 17105 50       32302  
104             my $style = $self->comment_style;
105 17105         32319 if ($style eq 'shell') {
106 17105         32874 return join("", "# ", $content, "\n");
107             } elsif ($style eq 'shell2') {
108 17105         42402 return join("", "## ", $content, "\n");
109 17105 50       70908 } elsif ($style eq 'cpp') {
    0          
    0          
    0          
    0          
110 17105         52273 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 80656 $expr =~ /\A(\s*)(.*)/os;
127 55497 100       153650 return "$1($2)";
128 30047 100       90904 }
129 6605         23633 }
130              
131 25450         56955 my ($self, $cd, $name, $value) = @_;
132 25450         106651  
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 23256  
138             # naming convention: expr_NOUN(), stmt_VERB(_NOUN)?()
139 13479 100       32820  
140             # XXX requires: expr_list
141 5042         13294  
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   32787  
208             # concatenate strings
209 19465         26875 my ($self, @t) = @_;
210 19465         29933 join(" " . $self->concat_op . " ", @t);
211             }
212 19465         45991  
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 1497 "++" . $self->var_sigil. $v;
224 607         1291 }
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 4175  
234 2428         4694 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 30394 }
245             my $cd = $args{cd} // $self->compile(%args);
246 4724         11323 my $et = $cd->{args}{err_term};
247 4724         9652  
248 4724         7827 if ($rt !~ /\Abool/) {
249 4724   33     12277 my ($ev) = $et =~ /(\w+)/; # to remove sigil
250 4724   33     15955 $self->add_var($cd, $ev, $rt =~ /\Astr/ ? undef : {});
251 4724   50     10210 }
252             my $resv = '_sahv_res';
253 4724         9526 my $rest = $self->var_sigil . $resv;
254 4724 50       8501  
255             my $needs_expr_block = (grep {$_->{phase} eq 'runtime'} @{ $cd->{modules} })
256             || $do_log;
257 4724   33     29269  
258 4691         20707 my $code_sub_body = join(
259             "",
260 4691 100       16135 (map {$self->stmt_declare_local_var(
261 2943         13084 $_, $self->literal($cd->{vars}{$_}))."\n"}
262 2943 100       15102 sort keys %{ $cd->{vars} }),
263             #$log->tracef('-> (validator)(%s) ...', $dt);\n";
264 4691         8362 $self->stmt_declare_local_var($resv, "\n\n" . $cd->{result})."\n\n",
265 4691         13324  
266             # when rt=bool_valid, return true/false result
267 4691   66     20792 #(";\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         15003 #($log->tracef('<- validator() = %s', ".
274 4691         20846 # "\$err_data);\n\n";
275             # x !!($do_log && $rt eq 'str_errmsg'),
276 4691         8188 ($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         18006 Data::Sah::Compiler::__linenum($code) :
313 4691         20460 $code);
  13534         26392  
  4691         10423  
314             }
315              
316             $code;
317 4691 100       16381 }
318 4651         13495  
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     14957 # 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         132401 #
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 56689 } else {
354 18651   100     49984 unless (defined $err_msg) { $err_msg = $cd->{clset}{"$clause.err_msg"} }
355 18651   100     51497 unless (defined $err_msg) {
356 18651   100     48168 # XXX how to invert on op='none' or op='not'?
357              
358             my @msgpath = @{$cd->{spath}};
359 18651   100     67503 my $msgpath;
      100        
360 18651         26692 my $hc = $cd->{_hc};
361 18651         26944 my $hcd = $cd->{_hcd};
362             while (1) {
363 18651 100       32881 # search error message, use more general one if the more
364 133 100       644 # specific one is not available
365 133 100       519 last unless @msgpath;
366             $msgpath = join("/", @msgpath);
367 18518 100       32674 my $ccls = $hcd->{result}{$msgpath};
  6037         13035  
368 18518 100       32613 pop @msgpath;
369             if ($ccls) {
370             local $hcd->{args}{format} = 'inline_err_text';
371 6037         7984 $err_msg = $hc->format_ccls($hcd, $ccls);
  6037         13534  
372 6037         9296 # show path when debugging
373 6037         8625 $err_msg = "(msgpath=$msgpath) $err_msg"
374 6037         8322 if $cd->{args}{debug};
375 6037         7755 last;
376             }
377             }
378 8518 100       19454 if (!$err_msg) {
379 8236         17063 $err_msg = "ERR (clause=".($cd->{clause} // "").")";
380 8236         13754 } else {
381 8236         11107 $err_msg = ucfirst($err_msg);
382 8236 100       17848 }
383 5755         13323 }
384 5755         16703 if ($err_msg) {
385             $self->add_var($cd, '_sahv_dpath', []) if $cd->{use_dpath};
386             $err_expr = $self->literal($err_msg);
387 5755 50       13979 $err_expr = $self->expr_prefix_dpath($err_expr) if $cd->{use_dpath};
388 5755         11743 }
389             }
390              
391 6037 100       11823 my $rt = $cd->{args}{return_type};
392 282   100     1146 my $et = $cd->{args}{err_term};
393             my $err_code;
394 5755         15131 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       31505 $err_code = $self->expr_set_err_full($et, $k, $err_expr) if $err_expr;
398 11591 100       28429 } elsif ($rt =~ /\Astr/) {
399 11591         26583 if ($el ne 'warn') {
400 11591 100       377265 $err_code = $self->expr_set_err_str($et, $err_expr) if $err_expr;
401             }
402             }
403              
404 18651         30829 my $res = {
405 18651         26049 ccl => $ccl,
406 18651         23051 err_level => $el,
407 18651 100       48533 err_code => $err_code,
    100          
408 5844 50       19738 (_debug_ccl_note => $cd->{_debug_ccl_note}) x !!$cd->{_debug_ccl_note},
409 5844 100       12284 subdata => $opts->{subdata},
410 5844 100       15611 };
411             push @{ $cd->{ccls} }, $res;
412 5869 100       13198 delete $cd->{uclset}{"$clause.err_level"};
413 5851 100       17212 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         82059 my ($min_ok, $max_ok, $min_nok, $max_nok);
424 18651         25140 if ($op eq 'and') {
  18651         34138  
425 18651         37268 $max_nok = 0;
426 18651         72695 } 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 23925  
432 13822   100     38309 }
433 13822   100     41128 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         20706  
438 13822 100       27115 return "" unless @$ccls;
    100          
    100          
    50          
439 12623         17310  
440             my $rt = $cd->{args}{return_type};
441 607         1075 my $vp = $cd->{args}{var_prefix};
442              
443 288         618 my $aop = $self->logical_and_op;
444             my $nop = $self->logical_not_op;
445              
446             my $true = $self->true;
447 13822         18642  
448 13822         19995 # insert comment, error message, and $ok/$nok counting. $which is 0 by
449 13822         17402 # default (normal), or 1 (reverse logic, for 'not' or 'none'), or 2 (for
450 13822         20185 # $ok/$nok counting), or 3 (like 2, but for the last clause).
451             my $_ice = sub {
452 13822 100       26320 my ($ccl, $which) = @_;
453              
454 13768         24497 return $self->enclose_paren($ccl->{ccl}) if $ccl->{assert};
455 13768         21690  
456             my $res = "";
457 13768         38479  
458 13768         78362 if ($ccl->{_debug_ccl_note}) {
459             if ($cd->{args}{debug_log} // $cd->{args}{debug}) {
460 13768         64958 $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   33866  
467             $which //= 0;
468 22751 50       41060 # clause code
469             my $cc = ($which == 1 ? $nop:"") . $self->enclose_paren($ccl->{ccl});
470 22751         32293 my ($ec, $oec);
471             my ($ret, $oret);
472 22751 100       39470 if ($which >= 2) {
473 17105 50 33     60336 my @chk;
474             if ($ccl->{err_level} eq 'warn') {
475 0         0 $oret = 1;
476             $ret = 1;
477 17105         37735 } elsif ($ccl->{err_level} eq 'fatal') {
478             $oret = 1;
479             $ret = 0;
480             } else {
481 22751   100     74212 $oret = $self->expr_preinc_var("${vp}ok");
482             $ret = $self->expr_preinc_var("${vp}nok");
483 22751 100       50138 push @chk, $self->expr_var("${vp}ok"). " <= $max_ok"
484 22751         54069 if $dmax_ok;
485 22751         0 push @chk, $self->expr_var("${vp}nok")." <= $max_nok"
486 22751 100       38008 if $dmax_nok;
487 1214         1872 if ($which == 3) {
488 1214 50       3789 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         3498 my $et = $cd->{args}{err_term};
496 1214         6399 my $clerrc;
497 1214 50       5539 if ($rt eq 'hash_details') {
498             $clerrc = $self->expr_reset_err_full($et);
499 1214 50       2367 } else {
500             $clerrc = $self->expr_reset_err_str($et);
501 1214 100       2913 }
502 607 50       2687 push @chk, $clerrc;
503             }
504 607 50       3705 }
505             }
506             $res .= "($cc ? $oret : $ret)";
507             $res .= " $aop " . join(" $aop ", @chk) if @chk;
508 607 100       2083 } else {
509 404         945 $ec = $ccl->{err_code};
510 404         813 $ret =
511 404 100       1136 $ccl->{err_level} eq 'fatal' ? 0 :
512 202         817 # this must not be done because it messes up ok/nok counting
513             #$rt eq 'hash_details' ? 1 :
514 202         728 $ccl->{err_level} eq 'warn' ? 1 : 0;
515             if ($rt =~ /\Abool/ && $ret) {
516 404         830 $res .= $true;
517             } elsif ($rt =~ /\Abool/ || !$ec) {
518             $res .= $self->enclose_paren($cc);
519             } else {
520 1214         3593 $res .= $self->enclose_paren(
521 1214 100       4563 $self->enclose_paren($cc). " ? $true : ($ec,$ret)",
522             "force");
523 21537         31398 }
524             }
525              
526             # insert dpath handling
527             $res = $self->expr_push_and_pop_dpath_between_expr($res)
528 21537 100       42735 if $cd->{use_dpath} && $ccl->{subdata};
    100          
529 21537 100 100     94030 $res;
    100 100        
530 36         67  
531             };
532 14896         27156  
533             my $j = "\n\n$aop\n\n";
534 6605         14981 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     58148 0..@$ccls-1;
543 22751         265245 {
544             local $cd->{ccls} = [];
545 13768         79186 local $cd->{_debug_ccl_note} = "op=$op";
546             $self->add_ccl(
547 13768         30127 $cd,
548 13768 100       35760 $self->expr_block(
    100          
    100          
549 304         788 join(
550             "",
551 12569         21576 $self->stmt_declare_local_var("${vp}ok" , "0"), "\n",
  20050         32261  
552             $self->stmt_declare_local_var("${vp}nok", "0"), "\n",
553 288         718 "\n",
  576         1055  
554             $self->block_uses_sub ?
555 607 100       1805 $self->stmt_return($jccl) : $jccl,
  1214         3625  
556             )
557             ),
558 607         1347 );
  607         1760  
559 607         1824 $_ice->($cd->{ccls}[0]);
560 607 50       2402 }
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         1938 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 9259  
580             # do a human compilation first to collect all the error messages
581 5060 50       13637  
582 5060         14840 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 9605  
595             my ($self, $cd) = @_;
596              
597             my $rt = $cd->{args}{return_type};
598 5059 100       13817 my $rt_is_hash = $rt =~ /\Ahash/;
599 4729         8888 my $rt_is_str = $rt =~ /\Astr/;
600 4729         9637  
  4729         54864  
601 4729         15050 $cd->{use_dpath} //= (
602 4729         8878 $rt_is_hash ||
603 4729         8161 ($rt_is_str && $cd->{has_subschema})
604 4729         6725 );
605 4729         6914  
606 4729         8747 # handle ok/default/coercion/prefilters/req/forbidden clauses and type check
607 4729         23789  
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 9516 my $clsets = $cd->{clsets};
613              
614 5056         10544 # handle ok, this is very high priority because !ok=>1 should fail undef
615 5056         12413 # too. we need to handle its .op=not here.
616 5056         11411 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     32586 $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         8696 $self->add_ccl($cd, $self->false);
626 5056         13686 } else {
627 5056         10986 local $cd->{_debug_ccl_note} = "ok #$i";
628 5056         8422 $self->add_ccl($cd, $self->true);
629 5056         7906 }
630             delete $cd->{uclsets}[$i]{"ok"};
631             delete $cd->{uclsets}[$i]{"ok.is_expr"};
632             }
633 5056         13771  
634 4610         8462 # handle default
635 4610 100       18580 HANDLE_DEFAULT: {
636 54   100     202  
637 54 50 66     240 my $default_value_expr;
638 0         0 my $default_value_ccl_note;
639             GEN_DEFAULT_VALUE_RULES:
640 54 100       168 {
641 27         98 require Data::Sah::DefaultValueCommon;
642 27         114  
643             my @default_value_rules;
644 27         89 for my $i (0..@$clsets-1) {
645 27         89 my $clset = $clsets->[$i];
646             push @default_value_rules,
647 54         145 @{ $clset->{"x.$cname.default_value_rules"} // [] },
648 54         157 @{ $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         7464 );
  5056         8688  
655             last unless @$rules;
656              
657             for my $i (reverse 0..$#{$rules}) {
658 5056         7267 my $rule = $rules->[$i];
  5056         33157  
659              
660 5056         21455 $self->add_compile_module(
661 5056         11043 $cd, "Data::Sah::Value::$cname\::$rule->{name}",
662 4610         7522 {category => 'default_value'},
663             );
664 4610   100     21456  
665 4610   50     8389 if ($rule->{modules}) {
  4610         20960  
666             for my $mod (keys %{ $rule->{modules} }) {
667             my $modspec = $rule->{modules}{$mod};
668 5056         12306 $modspec = {version=>$modspec} unless ref $modspec eq 'HASH';
669             $self->add_runtime_module($cd, $mod, {category=>'default_value', %$modspec});
670             }
671             }
672 5056 100       101056 }
673              
674 1         3 $default_value_expr = join " // " , map { "($_->{expr_value})" } @$rules;
  1         3  
675 1         2 $default_value_ccl_note = "default value rule(s): ".
676             join(", ", map {$_->{name}} @$rules);
677 1         8 } # 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         12008 delete $cd->{uclsets}[$i]{"default"};
697 4610         8151 delete $cd->{uclsets}[$i]{"default.is_expr"};
698 4610         7788 }
699 4610         8154  
700 4610 100       9924 if (defined $default_value_expr) {
701 90         269 local $cd->{_debug_ccl_note} = $default_value_ccl_note;
702 90 50       305 $self->add_ccl(
703             $cd,
704 90         3017 $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         8960 # handle req
714 4610         9459 my $has_req;
715             for my $i (0..@$clsets-1) {
716             my $clset = $clsets->[$i];
717 5056 100       11577 my $req = $clset->{req};
718 1         3 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         8664 $has_req++;
732 5056         10135 my $ct = $self->expr($cd, $req);
733 4610         8796 $self->add_ccl(
734 4610         9153 $cd, "!($ct) || ".$self->expr_defined($dt),
735 4610         7184 {
736 4610         12773 err_msg => $req_err_msg,
737 4610         14938 err_level => 'fatal',
738 4610 100 66     17928 },
    50          
739 471         963 );
740 471         1510 }
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         7741 err_msg => $fbd_err_msg,
759 4610         12404 err_level => 'fatal',
760             },
761             );
762             } elsif ($fbdie) {
763 5056         9668 $has_fbd++;
764 5056         10005 my $ct = $self->expr($cd, $fbd);
765 4610         7492 $self->add_ccl(
766 4610         8675 $cd, "!($ct) || !".$self->expr_defined($dt),
767 4610         6690 {
768 4610         11412 err_msg => $fbd_err_msg,
769 4610         13791 err_level => 'fatal',
770 4610 100 66     15566 },
    50          
771 27         51 );
772 27         86 }
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         8308  
791 4610         10269 my @coerce_rules;
792             for my $i (0..@$clsets-1) {
793             my $clset = $clsets->[$i];
794 5056 100 100     17326 push @coerce_rules,
795 4558         9127 @{ $clset->{"x.$cname.coerce_rules"} // [] },
796 4558         6133 @{ $clset->{'x.coerce_rules'} // [] };
  4558         12917  
797             }
798              
799 5056         12618 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       7517 coerce_rules => \@coerce_rules,
  5056         12483  
805             );
806 5056         23873 last unless @$rules;
807              
808 5056         18666 $coerce_might_fail = 1 if grep { $_->{meta}{might_fail} } @$rules;
809 5056         11093  
810 4610         7443 my $prev_term;
811             for my $i (reverse 0..$#{$rules}) {
812 4610   100     17569 my $rule = $rules->[$i];
813 4610   50     8802  
  4610         17665  
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         14980 my $modspec = $rule->{modules}{$mod};
822             $modspec = {version=>$modspec} unless ref $modspec eq 'HASH';
823 5056 100       306089 $self->add_runtime_module($cd, $mod, {category=>'coerce', %$modspec});
824             }
825 1220 100       2921 }
  1392         4001  
826              
827 1220         1793 if ($i == $#{$rules}) {
828 1220         2088 if ($coerce_might_fail) {
  1220         3646  
829 1392         2318 $prev_term = $self->expr_array($self->literal(undef), $dt);
830             } else {
831 1392         7988 $prev_term = $dt;
832             }
833             } else {
834             $prev_term = "($coerce_expr)";
835             }
836 1392 100       5325  
837 156         191 my $ec;
  156         320  
838 156         244 if ($coerce_might_fail && !$rule->{meta}{might_fail}) {
839 156 50       382 $ec = $self->expr_array($self->literal(undef), $rule->{expr_coerce});
840 156         502 } else {
841             $ec = "($rule->{expr_coerce})";
842             }
843              
844 1392 100       2139 $coerce_expr = $self->expr_ternary(
  1392         3356  
845 1220 100       2534 "($rule->{expr_match})",
846 60         151 $ec,
847             $prev_term,
848 1160         2095 );
849             }
850             $coerce_ccl_note = "coerce rule(s): ".
851 172         342 join(", ", map {$_->{name}} @$rules) .
852             ($cd->{coerce_to} ? " # coerce to: $cd->{coerce_to}" : "");
853             } # GEN_COERCE_EXPR
854 1392         2343  
855 1392 100 100     4259 my $prefilters_expr;
856 164         336 my $prefilters_ccl_note;
857             GEN_PREFILTERS_EXPRS:
858 1228         2714 {
859             my @filter_names;
860             for my $i (0..@$clsets-1) {
861 1392         6203 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         6955 my $rules = Data::Sah::FilterCommon::get_filter_rules(
869 1220 100       2831 compiler => $cname,
870             data_term => $dt,
871             filter_names => \@filter_names,
872 5056         9330 );
873              
874             my @exprs;
875             for my $i (0..$#{$rules}) {
876 5056         6911 my $rule = $rules->[$i];
  5056         7022  
877 5056         10724  
878 4610         8331 $self->add_compile_module(
879 6         16 $cd, "Data::Sah::Filter::$cname\::$rule->{name}",
880 4610 100       13989 {category => 'filter'},
881             );
882 5056 100       11966 if ($rule->{modules}) {
883             for my $mod (keys %{ $rule->{modules} }) {
884 6         1300 my $modspec = $rule->{modules}{$mod};
885 6         1069 $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         5728 my $expr;
892 6         12 if ($rule->{meta}{might_fail}) {
  6         18  
893 7         15 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       41 # 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         14 $self->expr_set_err_str($et, $self->expr_array_subscript($dt, 0)),
908 7         9 $self->expr_set($dt, $self->expr_array_subscript($dt, 1)),
909 7 100       25 $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         8 $self->true,
930             );
931             }
932             push @exprs, $expr;
933 3         10 } # 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         22 # handle coercion
950             if ($coerce_expr) {
951 6         27 $cd->{_debug_ccl_note} = $coerce_ccl_note;
952             if ($coerce_might_fail) {
953 6         13  
  7         50  
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       8634 $self->false,
  5056         10485  
959 0         0 );
960             } elsif ($rt_is_str) {
961             $expr_fail = $self->expr_list(
962 5056 50       11709 $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         15196 $self->false,
965             );
966             } else {
967 5056 100       11413 $expr_fail = $self->false;
968 1220         2128 }
969 1220 100       2469  
970             $self->add_ccl(
971 60         79 $cd,
972 60 50       131 $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         5 $self->expr_set($dt, $self->expr_array_subscript($dt, 1)),
979             $self->true,
980             )
981             ),
982             ),
983             {
984 59         144 err_msg => "",
985             err_level => "fatal",
986             },
987 60         214 );
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         3886 $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       12250 {
1022 6         12 err_msg => sprintf(
1023 6         28 $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       10010 $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         13617 }
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     14309 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 11460 # 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     13906 $cd->{_debug_ccl_note} = "clause: $cd->{clause}";
1075             }
1076 5306 50       12737  
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         13285 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         7671 }
  5306         13749  
1100 5306         11849  
1101             my ($self, $cd) = @_;
1102              
1103             # simply join them together with &&
1104 5276     5276 1 13124 $cd->{result} = $self->indent(
1105             $cd,
1106 5276 50       12462 $self->join_ccls($cd, $cd->{ccls}, {err_msg => ''}),
1107 0         0 );
1108             }
1109              
1110 5276         8247 my ($self, $cd) = @_;
  5276         10425  
1111 5276 100       7215  
  5276         13919  
1112             # XXX also handle postfilters here
1113              
1114 3957   100     16463 if (delete $cd->{_skip_undef}) {
1115             my $jccl = $self->join_ccls(
1116             $cd,
1117 5276         21133 [splice(@{ $cd->{ccls} }, $cd->{_ccls_idx1})],
1118             );
1119             local $cd->{_debug_ccl_note} = "skip if undef";
1120             $self->add_ccl(
1121 174     174 0 386 $cd,
1122             "!".$self->expr_defined($cd->{data_term})." ? ".$self->true." : \n\n".
1123             $self->enclose_paren($jccl),
1124             {err_msg => ''},
1125             );
1126 174         692 }
1127              
1128             # simply join them together with &&
1129             $cd->{result} = $self->indent(
1130             $cd,
1131 5025     5025 1 10714 $self->join_ccls($cd, $cd->{ccls}, {err_msg => ''}),
1132             );
1133             }
1134              
1135 5025 100       12633 1;
1136             # ABSTRACT: Base class for programming language compilers
1137              
1138 4554         8498  
  4554         15360  
1139             =pod
1140 4554         19164  
1141             =encoding UTF-8
1142              
1143 4554         16674 =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.914 of Data::Sah::Compiler::Prog (from Perl distribution Data-Sah), released on 2022-10-19.
1150              
1151             =head1 SYNOPSIS
1152 5025         19598  
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