|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use 5.010;  | 
| 
3
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
414
 | 
 use strict;  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
    | 
| 
4
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
103
 | 
 use warnings;  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
461
 | 
    | 
| 
5
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
111
 | 
 #use Log::Any::IfLOG qw($log);  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
704
 | 
    | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use Data::Dmp qw(dmp);  | 
| 
8
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
907
 | 
 use Mo qw(build default);  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3484
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1183
 | 
    | 
| 
9
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
152
 | 
 use POSIX qw(locale_h);  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
116
 | 
    | 
| 
10
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
5850
 | 
 use Text::sprintfn;  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
199
 | 
    | 
| 
11
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
40375
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19783
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10004
 | 
    | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 extends 'Data::Sah::Compiler';  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $DATE = '2022-08-20'; # DATE  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $DIST = 'Data-Sah'; # DIST  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '0.912'; # VERSION  | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # every type extension is registered here  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our %typex; # key = type, val = [clause, ...]  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
23
 | 
10335
 | 
 
 | 
 
 | 
  
10335
  
 | 
  
0
  
 | 
21236
 | 
     my ($self, $cd, $msg) = @_;  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return unless $cd->{args}{format} eq 'msg_catalog';  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
26
 | 
9615
 | 
 
 | 
 
 | 
  
9615
  
 | 
 
 | 
15642
 | 
     my $spath = join("/", @{ $cd->{spath} });  | 
| 
27
 | 
9615
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
25106
 | 
     $cd->{_msg_catalog}{$spath} = $msg;  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
29
 | 
9572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13173
 | 
    | 
| 
 
 | 
9572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19115
 | 
    | 
| 
30
 | 
9572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54086
 | 
     my ($self, $args) = @_;  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->SUPER::check_compile_args($args);  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
34
 | 
5070
 | 
 
 | 
 
 | 
  
5070
  
 | 
  
0
  
 | 
8981
 | 
     my @fmts = ('inline_text', 'inline_err_text', 'markdown', 'msg_catalog');  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $args->{format} //= $fmts[0];  | 
| 
36
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16815
 | 
     unless (grep { $_ eq $args->{format} } @fmts) {  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->_die({}, "Unsupported format, use one of: ".join(", ", @fmts));  | 
| 
38
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12517
 | 
     }  | 
| 
39
 | 
5070
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
13321
 | 
 }  | 
| 
40
 | 
5070
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
8264
 | 
    | 
| 
 
 | 
20280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42023
 | 
    | 
| 
41
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my ($self, %args) = @_;  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $cd = $self->SUPER::init_cd(%args);  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (($cd->{args}{format} // '') eq 'msg_catalog') {  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $cd->{_msg_catalog} //= $cd->{outer_cd}{_msg_catalog};  | 
| 
46
 | 
5070
 | 
 
 | 
 
 | 
  
5070
  
 | 
  
0
  
 | 
43486
 | 
         $cd->{_msg_catalog} //= {};  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
48
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34499
 | 
     $cd;  | 
| 
49
 | 
5070
 | 
  
100
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
22753
 | 
 }  | 
| 
50
 | 
5046
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
22935
 | 
    | 
| 
51
 | 
5046
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
14435
 | 
     my ($self, $cd, $expr) = @_;  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21256
 | 
     # for now we dump expression as is. we should probably parse it first to  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # localize number, e.g. "1.1 + 2" should become "1,1 + 2" in id_ID.  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # XXX for nicer output, perhaps say "the expression X" instead of just "X",  | 
| 
57
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
0
  
 | 
8
 | 
     # especially if X has a variable or rather complex.  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $expr;  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $val) = @_;  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $val unless ref($val);  | 
| 
64
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     dmp($val);  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # translate  | 
| 
68
 | 
12821
 | 
 
 | 
 
 | 
  
12821
  
 | 
  
0
  
 | 
55556
 | 
     my ($self, $cd, $text) = @_;  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
70
 | 
12821
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
36069
 | 
     my $lang = $cd->{args}{lang};  | 
| 
71
 | 
6048
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15952
 | 
    | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #$log->tracef("translating text '%s' to '%s'", $text, $lang);  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $text if $lang eq 'en_US';  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $translations;  | 
| 
76
 | 
98035
 | 
 
 | 
 
 | 
  
98035
  
 | 
 
 | 
132266
 | 
     {  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict  | 
| 
78
 | 
98035
 | 
 
 | 
 
 | 
 
 | 
 
 | 
123533
 | 
         $translations = \%{"Data::Sah::Lang::$lang\::translations"};  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return $translations->{$text} if defined($translations->{$text});  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($cd->{args}{mark_missing_translation}) {  | 
| 
82
 | 
98035
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
260425
 | 
         return "(no $lang text:$text)";  | 
| 
83
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
     } else {  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return $text;  | 
| 
85
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
167
 | 
     }  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4163
 | 
    | 
| 
 
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
86
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 }  | 
| 
 
 | 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
97
 | 
    | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
88
 | 
36
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
104
 | 
 # ($cd, 3, "element") -> "3rd element"  | 
| 
89
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my ($self, $cd, $n, $noun) = @_;  | 
| 
90
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $lang = $cd->{args}{lang};  | 
| 
92
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # we assume _xlt() has been called (and thus the appropriate  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Data::Sah::Lang::* has been loaded)  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($lang eq 'en_US') {  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         require Lingua::EN::Numbers::Ordinate;  | 
| 
98
 | 
62
 | 
 
 | 
 
 | 
  
62
  
 | 
 
 | 
121
 | 
         return Lingua::EN::Numbers::Ordinate::ordinate($n) . " $noun";  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
100
 | 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
86
 | 
         no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         return "Data::Sah::Lang::$lang\::ordinate"->($n, $noun);  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
105
 | 
62
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
113
 | 
     my ($self, $cd, $ccl) = @_;  | 
| 
106
 | 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2620
 | 
     #$log->errorf("TMP: add_ccl %s", $ccl);  | 
| 
107
 | 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1042
 | 
    | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $ccl->{xlt} //= 1;  | 
| 
109
 | 
24
 | 
 
 | 
 
 | 
  
24
  
 | 
 
 | 
191
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
    | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51862
 | 
    | 
| 
110
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     my $clause = $cd->{clause} // "";  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $ccl->{type} //= "clause";  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $do_xlt = 1;  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
115
 | 
9589
 | 
 
 | 
 
 | 
  
9589
  
 | 
 
 | 
16016
 | 
     my $hvals = {  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         modal_verb     => $self->_xlt($cd, "must"),  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         modal_verb_neg => $self->_xlt($cd, "must not"),  | 
| 
118
 | 
9589
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
36037
 | 
    | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # so they can overriden through hash_values  | 
| 
120
 | 
9589
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
23658
 | 
         field          => $self->_xlt($cd, "field"),  | 
| 
121
 | 
9589
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
28623
 | 
         fields         => $self->_xlt($cd, "fields"),  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
123
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11936
 | 
         %{ $cd->{args}{hash_values} // {} },  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $mod="";  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # is .human for desired language specified? if yes, use that instead  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     {  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $lang   = $cd->{args}{lang};  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $dlang  = $cd->{clset_dlang} // "en_US"; # undef if not in clause  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $suffix = $lang eq $dlang ? "" : ".alt.lang.$lang";  | 
| 
133
 | 
9589
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
19681
 | 
         if ($clause) {  | 
| 
 
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56685
 | 
    | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             delete $cd->{uclset}{$_} for  | 
| 
135
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21776
 | 
                 grep {/\A\Q$clause.human\E(\.|\z)/} keys %{$cd->{uclset}};  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if (defined $cd->{clset}{"$clause.human$suffix"}) {  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $ccl->{type} = 'clause';  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $ccl->{fmt}  = $cd->{clset}{"$clause.human$suffix"};  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 goto FILL_FORMAT;  | 
| 
140
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12042
 | 
             }  | 
| 
 
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13820
 | 
    | 
| 
141
 | 
9589
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
25198
 | 
         } else {  | 
| 
142
 | 
9589
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
17760
 | 
             delete $cd->{uclset}{$_} for  | 
| 
143
 | 
9589
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
17677
 | 
                 grep {/\A\.name(\.|\z)/} keys %{$cd->{uclset}};  | 
| 
144
 | 
4548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7876
 | 
             if (defined $cd->{clset}{".name$suffix"}) {  | 
| 
145
 | 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2348
 | 
                 $ccl->{type} = 'noun';  | 
| 
 
 | 
4548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14171
 | 
    | 
| 
146
 | 
4548
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
15396
 | 
                 $ccl->{fmt}  = $cd->{clset}{".name$suffix"};  | 
| 
147
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 $ccl->{vals} = undef;  | 
| 
148
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 goto FILL_FORMAT;  | 
| 
149
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             }  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
152
 | 
5041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7613
 | 
    | 
| 
153
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     goto TRANSLATE unless $clause;  | 
| 
 
 | 
5041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15219
 | 
    | 
| 
154
 | 
5041
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
19432
 | 
    | 
| 
155
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $ie    = $cd->{cl_is_expr};  | 
| 
156
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $im    = $cd->{cl_is_multi};  | 
| 
157
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $op    = $cd->{cl_op} // "";  | 
| 
158
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     my $cv    = $cd->{clset}{$clause};  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $vals  = $ccl->{vals} // [$cv];  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # handle .is_expr  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
163
 | 
9589
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
26176
 | 
     if ($ie) {  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if (!$ccl->{expr}) {  | 
| 
165
 | 
4548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6290
 | 
             $ccl->{fmt} = "($clause -> %s" . ($op ? " op=$op" : "") . ")";  | 
| 
166
 | 
4548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6684
 | 
             $do_xlt = 0;  | 
| 
167
 | 
4548
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
9989
 | 
             $vals = [$self->expr($cd, $vals)];  | 
| 
168
 | 
4548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7964
 | 
         }  | 
| 
169
 | 
4548
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
13728
 | 
         goto ERR_LEVEL;  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # handle .op  | 
| 
173
 | 
4548
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
9740
 | 
    | 
| 
174
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
     if ($op eq 'not') {  | 
| 
175
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         ($hvals->{modal_verb}, $hvals->{modal_verb_neg}) =  | 
| 
176
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             ($hvals->{modal_verb_neg}, $hvals->{modal_verb});  | 
| 
177
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $vals = [map {$self->literal($_)} @$vals];  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ($im && $op eq 'and') {  | 
| 
179
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
         if (@$cv == 2) {  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $vals = [sprintf($self->_xlt($cd, "%s and %s"),  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv->[0]),  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv->[1]))];  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
184
 | 
4544
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
21957
 | 
             $vals = [sprintf($self->_xlt($cd, "all of %s"),  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv))];  | 
| 
186
 | 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
801
 | 
         }  | 
| 
187
 | 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
757
 | 
     } elsif ($im && $op eq 'or') {  | 
| 
 
 | 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
778
 | 
    | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if (@$cv == 2) {  | 
| 
189
 | 
559
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1135
 | 
             $vals = [sprintf($self->_xlt($cd, "%s or %s"),  | 
| 
190
 | 
450
 | 
 
 | 
 
 | 
 
 | 
 
 | 
989
 | 
                              $self->literal($cv->[0]),  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv->[1]))];  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $vals = [sprintf($self->_xlt($cd, "one of %s"),  | 
| 
194
 | 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
300
 | 
                              $self->literal($cv))];  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ($im && $op eq 'none') {  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ($hvals->{modal_verb}, $hvals->{modal_verbneg}) =  | 
| 
198
 | 
558
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1111
 | 
             ($hvals->{modal_verb_neg}, $hvals->{modal_verb});  | 
| 
199
 | 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
999
 | 
         if (@$cv == 2) {  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $vals = [sprintf($self->_xlt($cd, "%s nor %s"),  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv->[0]),  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv->[1]))];  | 
| 
203
 | 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
215
 | 
         } else {  | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $vals = [sprintf($self->_xlt($cd, "any of %s"),  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              $self->literal($cv))];  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
208
 | 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
787
 | 
         $vals = [map {$self->literal($_)} @$vals];  | 
| 
209
 | 
270
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
707
 | 
     }  | 
| 
210
 | 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
578
 | 
    | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   ERR_LEVEL:  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # handle .err_level  | 
| 
214
 | 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
129
 | 
     if ($ccl->{type} eq 'clause' && grep { $_ eq 'constraint' } @{ $cd->{cl_meta}{tags} // [] }) {  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if (($cd->{clset}{"$clause.err_level"}//'error') eq 'warn') {  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if ($op eq 'not') {  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $hvals->{modal_verb}     = $self->_xlt($cd, "should not");  | 
| 
218
 | 
2852
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5001
 | 
                 $hvals->{modal_verb_neg} = $self->_xlt($cd, "should");  | 
| 
 
 | 
3892
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7353
 | 
    | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $hvals->{modal_verb}     = $self->_xlt($cd, "should");  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $hvals->{modal_verb_neg} = $self->_xlt($cd, "should not");  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
224
 | 
4548
 | 
  
100
  
 | 
  
 50
  
 | 
 
 | 
 
 | 
88231
 | 
     }  | 
| 
 
 | 
4277
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
14931
 | 
    | 
| 
 
 | 
4277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12812
 | 
    | 
| 
225
 | 
4187
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
18191
 | 
     delete $cd->{uclset}{"$clause.err_level"};  | 
| 
226
 | 
54
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
139
 | 
    | 
| 
227
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
   TRANSLATE:  | 
| 
228
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($ccl->{xlt}) {  | 
| 
230
 | 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
141
 | 
         if (ref($ccl->{fmt}) eq 'ARRAY') {  | 
| 
231
 | 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
109
 | 
             $ccl->{fmt}  = [map {$self->_xlt($cd, $_)} @{$ccl->{fmt}}];  | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif (!ref($ccl->{fmt})) {  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $ccl->{fmt}  = $self->_xlt($cd, $ccl->{fmt});  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
235
 | 
4548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8975
 | 
     }  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   FILL_FORMAT:  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
239
 | 
9589
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
18355
 | 
     if (ref($ccl->{fmt}) eq 'ARRAY') {  | 
| 
240
 | 
9586
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
28273
 | 
         $ccl->{text} = [map {sprintfn($_, (map {$_//""} ($hvals, @$vals)))}  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
241
 | 
5041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6264
 | 
                             @{$ccl->{fmt}}];  | 
| 
 
 | 
10082
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17114
 | 
    | 
| 
 
 | 
5041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9483
 | 
    | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif (!ref($ccl->{fmt})) {  | 
| 
243
 | 
4545
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10354
 | 
         $ccl->{text} = sprintfn($ccl->{fmt}, (map {$_//""} ($hvals, @$vals)));  | 
| 
244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     delete $ccl->{fmt} unless $cd->{args}{debug};  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   PUSH:  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     push @{$cd->{ccls}}, $ccl;  | 
| 
249
 | 
9589
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
27190
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
250
 | 
10082
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
200869
 | 
     $self->_add_msg_catalog($cd, $ccl);  | 
| 
 
 | 
10082
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34135
 | 
    | 
| 
251
 | 
5041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6581
 | 
 }  | 
| 
 
 | 
5041
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9860
 | 
    | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
253
 | 
4548
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
7875
 | 
 # add a compiled clause (ccl), which will be combined at the end of compilation  | 
| 
 
 | 
10208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26953
 | 
    | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # to be the final result. args is a hashref with these keys:  | 
| 
255
 | 
9589
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
603633
 | 
 #  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # * type* - str (default 'clause'). either 'noun', 'clause', 'list' (bulleted  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   list, a clause followed by a list of items, each of them is also a ccl)  | 
| 
258
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12928
 | 
 #  | 
| 
 
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19868
 | 
    | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # * fmt* - str/2-element array. human text which can be used as the first  | 
| 
260
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26600
 | 
 #   argument to sprintf. string. if type=noun, can be a two-element arrayref to  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   contain singular and plural version of noun.  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # * expr - bool. fmt can handle .is_expr=1. for example, 'len=' => '1+1' can be  | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   compiled into 'length must be 1+1'. other clauses cannot handle expression,  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   e.g. 'between=' => '[2, 2*2]'. this clause will be using the generic message  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   'between must [2, 2*2]'  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # * vals - arrayref (default [clause value]). values to fill fmt with.  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # * items - arrayref. required if type=list. a single ccl or a list of ccls.  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # * xlt - bool (default 1). set to 0 if fmt has been translated, and should not  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   be translated again.  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # add_ccl() is called by clause handlers and handles using .human, translating  | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # fmt, sprintf(fmt, vals) into 'text', .err_level (adding 'must be %s', 'should  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # not be %s'), .is_expr, .op.  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd, @ccls) = @_;  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $op     = $cd->{cl_op} // '';  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $ccl;  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (@ccls == 1) {  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->_add_ccl($cd, $ccls[0]);  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $inner_cd = $self->init_cd(outer_cd => $cd);  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $inner_cd->{args} = $cd->{args};  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $inner_cd->{clause} = $cd->{clause};  | 
| 
289
 | 
9589
 | 
 
 | 
 
 | 
  
9589
  
 | 
  
0
  
 | 
20699
 | 
         for (@ccls) {  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $self->_add_ccl($inner_cd, $_);  | 
| 
291
 | 
9589
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
29394
 | 
         }  | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
293
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14573
 | 
         $ccl = {  | 
| 
294
 | 
9589
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
18738
 | 
             type  => 'list',  | 
| 
295
 | 
9589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22360
 | 
             vals  => [],  | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             items => $inner_cd->{ccls},  | 
| 
297
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             multi => 0,  | 
| 
298
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         };  | 
| 
299
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         if ($op eq 'or') {  | 
| 
300
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $ccl->{fmt} = 'any of the following %(modal_verb)s be true';  | 
| 
301
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         } elsif ($op eq 'and') {  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $ccl->{fmt} = 'all of the following %(modal_verb)s be true';  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } elsif ($op eq 'none') {  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $ccl->{fmt} = 'none of the following %(modal_verb)s be true';  | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # or perhaps, fmt = 'All of the following ...' but set op to 'not'?  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->_add_ccl($cd, $ccl);  | 
| 
308
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     }  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
310
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
311
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 # format ccls to form final result. at the end of compilation, we have a tree of  | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ccls. this method accept a single ccl (of type either noun/clause) or an array  | 
| 
313
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 # of ccls (which it will join together).  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd, $ccls) = @_;  | 
| 
315
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # used internally to determine if the result is a single noun, in which case  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # when format is inline_err_text, we add 'Not of type '. XXX: currently this  | 
| 
318
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     # is the wrong way to count? we shouldn't count children? perhaps count from  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # msg_catalog instead?  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     local $cd->{_fmt_noun_count} = 0;  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     local $cd->{_fmt_etc_count} = 0;  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $f = $cd->{args}{format};  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $res;  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($f eq 'inline_text' || $f eq 'inline_err_text' || $f eq 'msg_catalog') {  | 
| 
326
 | 
10822
 | 
 
 | 
 
 | 
  
10822
  
 | 
  
0
  
 | 
18007
 | 
         $res = $self->_format_ccls_itext($cd, $ccls);  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if ($f eq 'inline_err_text') {  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             #$log->errorf("TMP: noun=%d, etc=%d", $cd->{_fmt_noun_count}, $cd->{_fmt_etc_count});  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if ($cd->{_fmt_noun_count} == 1 && $cd->{_fmt_etc_count} == 0) {  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # a single noun (type name), we should add some preamble  | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $res = sprintf(  | 
| 
332
 | 
10822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20606
 | 
                     $self->_xlt($cd, "Not of type %s"),  | 
| 
333
 | 
10822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18408
 | 
                     $res  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 );  | 
| 
335
 | 
10822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19971
 | 
             } elsif (!$cd->{_fmt_noun_count}) {  | 
| 
336
 | 
10822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12527
 | 
                 # a clause (e.g. "must be >= 10"), already looks like errmsg  | 
| 
337
 | 
10822
 | 
  
 50
  
 | 
  
100
  
 | 
 
 | 
 
 | 
52470
 | 
             } else {  | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
338
 | 
10822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25602
 | 
                 # a noun + clauses (e.g. "integer, must be even"). add preamble  | 
| 
339
 | 
10822
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
23755
 | 
                 $res = sprintf(  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     $self->_xlt(  | 
| 
341
 | 
5755
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
18707
 | 
                         $cd, "Does not satisfy the following schema: %s"),  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     $res  | 
| 
343
 | 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
483
 | 
                 );  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $res = $self->_format_ccls_markdown($cd, $ccls);  | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $res;  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
351
 | 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
325
 | 
    | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd, $ccls) = @_;  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     local $cd->{args}{mark_missing_translation} = 0;  | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $c_comma = $self->_xlt($cd, ", ");  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (ref($ccls) eq 'HASH' && $ccls->{type} =~ /^(noun|clause)$/) {  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if ($ccls->{type} eq 'noun') {  | 
| 
359
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $cd->{_fmt_noun_count}++;  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
361
 | 
10822
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43824
 | 
             $cd->{_fmt_etc_count}++;  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # handle a single noun/clause ccl  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $ccl = $ccls;  | 
| 
365
 | 
21957
 | 
 
 | 
 
 | 
  
21957
  
 | 
 
 | 
30622
 | 
         return ref($ccl->{text}) eq 'ARRAY' ? $ccl->{text}[0] : $ccl->{text};  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif (ref($ccls) eq 'HASH' && $ccls->{type} eq 'list') {  | 
| 
367
 | 
21957
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41254
 | 
         # handle a single list ccl  | 
| 
368
 | 
21957
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39279
 | 
         my $c_openpar  = $self->_xlt($cd, "(");  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $c_closepar = $self->_xlt($cd, ")");  | 
| 
370
 | 
21957
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
106002
 | 
         my $c_colon    = $self->_xlt($cd, ": ");  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
371
 | 
15703
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
28378
 | 
         my $ccl = $ccls;  | 
| 
372
 | 
5731
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8265
 | 
    | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $txt = $ccl->{text}; $txt =~ s/\s+$//;  | 
| 
374
 | 
9972
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15026
 | 
         my @t = ($txt, $c_colon);  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         my $i = 0;  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         for (@{ $ccl->{items} }) {  | 
| 
377
 | 
15703
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18526
 | 
             push @t, $c_comma if $i;  | 
| 
378
 | 
15703
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
64399
 | 
             my $it = $self->_format_ccls_itext($cd, $_);  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if ($it =~ /\Q$c_comma/) {  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 push @t, $c_openpar, $it, $c_closepar;  | 
| 
381
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1095
 | 
             } else {  | 
| 
382
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1027
 | 
                 push @t, $it;  | 
| 
383
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
938
 | 
             }  | 
| 
384
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
742
 | 
             $i++;  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
386
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
853
 | 
         return join("", @t);  | 
| 
 
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1715
 | 
    | 
| 
387
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1136
 | 
     } elsif (ref($ccls) eq 'ARRAY') {  | 
| 
388
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
751
 | 
         # handle an array of ccls  | 
| 
389
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
707
 | 
         return join($c_comma, map {$self->_format_ccls_itext($cd, $_)} @$ccls);  | 
| 
 
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1151
 | 
    | 
| 
390
 | 
612
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1148
 | 
     } else {  | 
| 
391
 | 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1156
 | 
         $self->_die($cd, "Can't format $ccls");  | 
| 
392
 | 
612
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2270
 | 
     }  | 
| 
393
 | 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
546
 | 
 }  | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
395
 | 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
571
 | 
     my ($self, $cd, $ccls) = @_;  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
397
 | 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1056
 | 
     $self->_die($cd, "Sorry, markdown not yet implemented");  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
399
 | 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2126
 | 
    | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd) = @_;  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
402
 | 
5703
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10394
 | 
     my $lang = $cd->{args}{lang};  | 
| 
 
 | 
10523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20076
 | 
    | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     die "Invalid language '$lang', please use letters only"  | 
| 
404
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         unless $lang =~ /\A\w+\z/;  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my @modp;  | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     unless ($lang eq 'en_US') {  | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         push @modp, "Data/Sah/Lang/$lang.pm";  | 
| 
409
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
         for my $cl (@{ $typex{$cd->{type}} // []}) {  | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             my $modp = "Data/Sah/Lang/$lang/TypeX/$cd->{type}/$cl.pm";  | 
| 
411
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $modp =~ s!::!/!g; # $cd->{type} might still contain '::'  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             push @modp, $modp;  | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
415
 | 
5070
 | 
 
 | 
 
 | 
  
5070
  
 | 
 
 | 
11380
 | 
     my $i;  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     for my $modp (@modp) {  | 
| 
417
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11318
 | 
         $i++;  | 
| 
418
 | 
5070
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
19370
 | 
         unless (exists $INC{$modp}) {  | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             if ($i == 1) {  | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 # test to check whether Data::Sah::Lang::$lang exists. if it  | 
| 
421
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7561
 | 
                 # does not, we fallback to en_US.  | 
| 
422
 | 
5070
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
11492
 | 
                 require Module::Installed::Tiny;  | 
| 
423
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
                 if (!Module::Installed::Tiny::module_installed($modp)) {  | 
| 
424
 | 
3
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
3
 | 
                     #$log->debug("$mod cannot be found, falling back to en_US");  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
    | 
| 
425
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                     $cd->{args}{lang} = 'en_US';  | 
| 
426
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                     last;  | 
| 
427
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 }  | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             #$log->trace("Loading $modp ...");  | 
| 
430
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9317
 | 
             require $modp;  | 
| 
431
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12974
 | 
    | 
| 
432
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
             # negative-cache, so we don't have to try again  | 
| 
433
 | 
3
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
             $INC{$modp} = undef;  | 
| 
434
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
3
 | 
         }  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
437
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1651
 | 
    | 
| 
438
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1318
 | 
     my ($self, $cd) = @_;  | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
440
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     # set locale so that numbers etc are printed according to locale (e.g.  | 
| 
441
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     # sprintf("%s", 1.2) prints '1,2' in id_ID).  | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $cd->{_orig_locale} = setlocale(LC_ALL);  | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
444
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # XXX do we need to set everything? LC_ADDRESS, LC_TELEPHONE, LC_PAPER, ...  | 
| 
445
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
561
 | 
     my $res = setlocale(LC_ALL, $cd->{args}{locale} // $cd->{args}{lang});  | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     warn "Unsupported locale $cd->{args}{lang}"  | 
| 
447
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         if $cd->{args}{debug} && !defined($res);  | 
| 
448
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 }  | 
| 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
450
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd) = @_;  | 
| 
451
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
452
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $self->_load_lang_modules($cd);  | 
| 
453
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
454
 | 
5070
 | 
 
 | 
 
 | 
  
5070
  
 | 
  
1
  
 | 
10978
 | 
    | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd) = @_;  | 
| 
456
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
457
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # by default, human clause handler can handle multiple values (e.g.  | 
| 
458
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25926
 | 
     # "div_by&"=>[2, 3] becomes "must be divisible by 2 and 3" instead of having  | 
| 
459
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # to be ["must be divisible by 2", "must be divisible by 3"]. some clauses  | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # that don't can override this value to 0.  | 
| 
461
 | 
5070
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
50501
 | 
     $cd->{CLAUSE_DO_MULTI} = 1;  | 
| 
462
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
463
 | 
5070
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
17453
 | 
    | 
| 
464
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd) = @_;  | 
| 
465
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # reset what we set in before_clause()  | 
| 
467
 | 
5070
 | 
 
 | 
 
 | 
  
5070
  
 | 
  
1
  
 | 
9678
 | 
     delete $cd->{CLAUSE_DO_MULTI};  | 
| 
468
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
469
 | 
5070
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12019
 | 
    | 
| 
470
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd) = @_;  | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
472
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # quantify NOUN (e.g. integer) into 'required integer', 'optional integer',  | 
| 
473
 | 
5324
 | 
 
 | 
 
 | 
  
5324
  
 | 
  
1
  
 | 
10639
 | 
     # or 'forbidden integer'.  | 
| 
474
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
475
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # my $q;  | 
| 
476
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # if (!$cd->{clset}{'required.is_expr'} &&  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #         !(grep {$_ eq 'required'} @{ $cd->{args}{skip_clause} })) {  | 
| 
478
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     if ($cd->{clset}{required}) {  | 
| 
479
 | 
5324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11954
 | 
     #         $q = 'required %s';  | 
| 
480
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     } else {  | 
| 
481
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #         $q = 'optional %s';  | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     }  | 
| 
483
 | 
5321
 | 
 
 | 
 
 | 
  
5321
  
 | 
  
1
  
 | 
10966
 | 
     # } elsif ($cd->{clset}{forbidden} && !$cd->{clset}{'forbidden.is_expr'} &&  | 
| 
484
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #              !(grep { $_ eq 'forbidden' } @{ $cd->{args}{skip_clause} })) {  | 
| 
485
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     $q = 'forbidden %s';  | 
| 
486
 | 
5321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10638
 | 
     # }  | 
| 
487
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # if ($q && @{$cd->{ccls}} && $cd->{ccls}[0]{type} eq 'noun') {  | 
| 
488
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     $q = $self->_xlt($cd, $q);  | 
| 
489
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     for (ref($cd->{ccls}[0]{text}) eq 'ARRAY' ?  | 
| 
490
 | 
5067
 | 
 
 | 
 
 | 
  
5067
  
 | 
  
1
  
 | 
8687
 | 
     #              @{ $cd->{ccls}[0]{text} } : $cd->{ccls}[0]{text}) {  | 
| 
491
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #         $_ = sprintf($q, $_);  | 
| 
492
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     }  | 
| 
493
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # }  | 
| 
494
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
495
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $cd->{result} = $self->format_ccls($cd, $cd->{ccls});  | 
| 
496
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
497
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
498
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my ($self, $cd) = @_;  | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
500
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     setlocale(LC_ALL, $cd->{_orig_locale});  | 
| 
501
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
502
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ($cd->{args}{format} eq 'msg_catalog') {  | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $cd->{result} = $cd->{_msg_catalog};  | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
508
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ABSTRACT: Compile Sah schema to human language  | 
| 
509
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
510
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
511
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =pod  | 
| 
512
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
513
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =encoding UTF-8  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
515
 | 
5067
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13273
 | 
 =head1 NAME  | 
| 
516
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Data::Sah::Compiler::human - Compile Sah schema to human language  | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
519
 | 
5067
 | 
 
 | 
 
 | 
  
5067
  
 | 
  
1
  
 | 
9214
 | 
 =head1 VERSION  | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
521
 | 
5067
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55500
 | 
 This document describes version 0.912 of Data::Sah::Compiler::human (from Perl distribution Data-Sah), released on 2022-08-20.  | 
| 
522
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
523
 | 
5067
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
13954
 | 
 =head1 SYNOPSIS  | 
| 
524
 | 
5043
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11196
 | 
    | 
| 
525
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DESCRIPTION  | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
527
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This class is derived from L<Data::Sah::Compiler>. It generates human language  | 
| 
528
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 text.  | 
| 
529
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
530
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =for Pod::Coverage ^(name|literal|expr|add_ccl|format_ccls|check_compile_args|handle_.+|before_.+|after_.+)$  | 
| 
531
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 ATTRIBUTES  | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
534
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 METHODS  | 
| 
535
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 new() => OBJ  | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
538
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 $c->compile(%args) => RESULT  | 
| 
539
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
540
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Aside from base class' arguments, this class supports these arguments (suffix  | 
| 
541
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C<*> denotes required argument):  | 
| 
542
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
543
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over  | 
| 
544
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
545
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * format => STR (default: C<inline_text>)  | 
| 
546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
547
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Format of text to generate. Either C<inline_text>, C<inline_err_text>, or  | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C<markdown>. Note that you can easily convert Markdown to HTML, there are  | 
| 
549
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 libraries in Perl, JavaScript, etc to do that.  | 
| 
550
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Sample C<inline_text> output:  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  integer, must satisfy all of the following: (divisible by 3, at least 10)  | 
| 
554
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
555
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 C<inline_err_text> is just like C<inline_text>, except geared towards producing  | 
| 
556
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 an error message. Currently, instead of producing "integer" from schema "int",  | 
| 
557
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 it produces "Not of type integer". The rest is identical.  | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
559
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Sample C<markdown> output:  | 
| 
560
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
561
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  integer, must satisfy all of the following:  | 
| 
562
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
563
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  * divisible by 3  | 
| 
564
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  * at least 10  | 
| 
565
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
566
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item * hash_values => hash  | 
| 
567
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
568
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Optional, supply more keys to hash value to C<sprintfn> which will be used  | 
| 
569
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 during compilation.  | 
| 
570
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
571
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
572
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
573
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head3 Compilation data  | 
| 
574
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
575
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This subclass adds the following compilation data (C<$cd>).  | 
| 
576
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
577
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Keys which contain compilation state:  | 
| 
578
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
579
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
580
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
581
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
582
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
583
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Keys which contain compilation result:  | 
| 
584
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
585
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over 4  | 
| 
586
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
587
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
588
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
589
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 HOMEPAGE  | 
| 
590
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
591
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Please visit the project's homepage at L<https://metacpan.org/release/Data-Sah>.  | 
| 
592
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
593
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SOURCE  | 
| 
594
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
595
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Source repository is at L<https://github.com/perlancar/perl-Data-Sah>.  | 
| 
596
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
597
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 AUTHOR  | 
| 
598
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
599
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 perlancar <perlancar@cpan.org>  | 
| 
600
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
601
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 CONTRIBUTING  | 
| 
602
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
603
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
604
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 To contribute, you can send patches by email/via RT, or send pull requests on  | 
| 
605
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 GitHub.  | 
| 
606
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
607
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Most of the time, you don't need to build the distribution yourself. You can  | 
| 
608
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 simply modify the code, then test via:  | 
| 
609
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
610
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  % prove -l  | 
| 
611
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
612
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 If you want to build the distribution (e.g. to try to install it locally on your  | 
| 
613
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 system), you can install L<Dist::Zilla>,  | 
| 
614
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,  | 
| 
615
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other  | 
| 
616
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond  | 
| 
617
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 that are considered a bug and can be reported to me.  | 
| 
618
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
619
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 COPYRIGHT AND LICENSE  | 
| 
620
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
621
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This software is copyright (c) 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar <perlancar@cpan.org>.  | 
| 
622
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
623
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This is free software; you can redistribute it and/or modify it under  | 
| 
624
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the same terms as the Perl 5 programming language system itself.  | 
| 
625
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
626
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 BUGS  | 
| 
627
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
628
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Sah>  | 
| 
629
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
630
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 When submitting a bug or request, please include a test-file or a  | 
| 
631
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 patch to an existing test-file that illustrates the bug or desired  | 
| 
632
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 feature.  | 
| 
633
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
634
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  |