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