File Coverage

blib/lib/Business/UDC/Grammar.pm
Criterion Covered Total %
statement 62 87 71.2
branch 42 62 67.7
condition 11 26 42.3
subroutine 17 22 77.2
pod 0 13 0.0
total 132 210 62.8


line stmt bran cond sub pod time code
1             package Business::UDC::Grammar;
2              
3 11     11   96182 use base qw(Exporter);
  11         21  
  11         1216  
4 11     11   55 use strict;
  11         38  
  11         222  
5 11     11   51 use warnings;
  11         15  
  11         651  
6              
7 11     11   90 use List::Util 1.33 qw(any);
  11         291  
  11         1492  
8 11     11   5571 use Readonly;
  11         39697  
  11         11123  
9              
10             Readonly::Array our @EXPORT_OK => qw(can_be_standalone can_follow_operator
11             can_follow_primary can_follow_term can_precede_number
12             can_start_expression_with describe_token_type group_subtype is_modifier_token
13             is_operator_token is_primary_token is_valid_operator operator_info);
14             Readonly::Hash our %DESC => (
15             ALPHA_SPEC => 'direct alphabetical specification',
16             APOS_AUX => 'apostrophe auxiliary',
17             AUX_DOT => 'dot auxiliary',
18             AUX_GROUP => 'parenthesized auxiliary',
19             AUX_LANG => 'language auxiliary',
20             AUX_TIME => 'quoted time auxiliary',
21             FORM => 'special auxiliary subdivision',
22             LBRACK => 'left subgroup bracket',
23             NUMBER => 'main UDC number',
24             OP => 'operator',
25             PARTIAL_FORM => 'partial form for range shorthand',
26             PARTIAL_NUMBER => 'partial number for range shorthand',
27             RBRACK => 'right subgroup bracket',
28             );
29             Readonly::Hash our %TOKEN_RULES => (
30             ALPHA_SPEC => {
31             standalone => 0,
32             primary => 0,
33             modifier => 1,
34             },
35             APOS_AUX => {
36             standalone => 0,
37             primary => 0,
38             modifier => 1,
39             },
40             AUX_DOT => {
41             standalone => 0,
42             primary => 0,
43             modifier => 1,
44             },
45             AUX_GROUP => {
46             standalone => 1,
47             primary => 1,
48             modifier => 1,
49             },
50             AUX_LANG => {
51             standalone => 1,
52             primary => 1,
53             modifier => 1,
54             },
55             AUX_TIME => {
56             standalone => 1,
57             primary => 1,
58             modifier => 1,
59             },
60             FORM => {
61             standalone => 0,
62             primary => 0,
63             modifier => 1,
64             },
65             NUMBER => {
66             standalone => 1,
67             primary => 1,
68             modifier => 0,
69             },
70             OP => {
71             standalone => 0,
72             primary => 0,
73             modifier => 0,
74             },
75             PARTIAL_FORM => {
76             standalone => 0,
77             primary => 0,
78             modifier => 0,
79             },
80             PARTIAL_NUMBER => {
81             standalone => 0,
82             primary => 0,
83             modifier => 0,
84             },
85             );
86             Readonly::Hash our %OPERATORS => (
87             '+' => {
88             name => 'addition',
89             precedence => 10,
90             associativity => 'left',
91             right_types => [qw(AUX_GROUP AUX_LANG AUX_TIME NUMBER)],
92             },
93             ':' => {
94             name => 'relation',
95             precedence => 20,
96             associativity => 'left',
97             right_types => [qw(AUX_GROUP AUX_LANG AUX_TIME NUMBER)],
98             },
99             '/' => {
100             name => 'consecutive_extension',
101             precedence => 15,
102             associativity => 'left',
103             right_types => [qw(APOS_AUX AUX_DOT AUX_GROUP AUX_LANG AUX_TIME FORM NUMBER)],
104             },
105             );
106              
107             our $VERSION = 0.04;
108              
109             sub can_be_standalone {
110 0     0 0 0 my $type = shift;
111              
112 0 0       0 if (! $TOKEN_RULES{$type}) {
113 0         0 return 0;
114             }
115              
116 0 0       0 if ($type eq 'AUX_GROUP') {
117 0         0 return 1;
118             }
119              
120 0 0       0 return $TOKEN_RULES{$type}{'standalone'} ? 1 : 0;
121             }
122              
123             sub can_follow_operator {
124 22     22 0 41 my ($op, $type) = @_;
125              
126 22 50       33 if (! is_valid_operator($op)) {
127 0         0 return 0;
128             }
129              
130 22 50       135 my %allowed = map { $_ => 1 } @{$OPERATORS{$op}{'right_types'} || []};
  121         786  
  22         47  
131              
132 22 100       265 return $allowed{$type} ? 1 : 0;
133             }
134              
135             sub can_follow_primary {
136 69     69 0 133 my ($type, $value, $primary_type, $primary_value) = @_;
137              
138 69 100       99 if (! is_modifier_token($type)) {
139 29         386 return 0;
140             }
141              
142 40 100       595 if ($type eq 'FORM') {
    100          
    100          
    100          
    100          
    100          
    50          
143 10 50 33     36 if (defined $primary_type
144 50     50   63 && any { $primary_type eq $_ } qw(APOS_AUX AUX_DOT AUX_GROUP AUX_LANG AUX_TIME FORM NUMBER SUBGROUP)) {
145              
146 10         19 return 1;
147             }
148 0         0 return 0;
149             } elsif ($type eq 'AUX_DOT') {
150 3 50 33     12 if (defined $primary_type
151 7     7   12 && any { $primary_type eq $_ } qw(NUMBER AUX_GROUP SUBGROUP)) {
152              
153 3         6 return 1;
154             }
155 0         0 return 0;
156             } elsif ($type eq 'AUX_GROUP') {
157 13         21 my $subtype = group_subtype($value);
158 13 100       26 if ($subtype eq 'AUX_FORM') {
159 3         7 return 1;
160             }
161              
162             # XXX
163 10         23 return 1;
164             } elsif ($type eq 'AUX_TIME') {
165 1         3 return 1;
166             } elsif ($type eq 'AUX_LANG') {
167 1         2 return 1;
168             } elsif ($type eq 'ALPHA_SPEC') {
169 5 50 33     17 if (defined $primary_type
170 16     16   21 && any { $primary_type eq $_ } qw(AUX_GROUP AUX_LANG AUX_TIME FORM NUMBER SUBGROUP)) {
171              
172 5         11 return 1;
173             }
174 0         0 return 0;
175             } elsif ($type eq 'APOS_AUX') {
176 7 50 33     23 if (defined $primary_type
177 39     39   67 && any { $primary_type eq $_ } qw(APOS_AUX AUX_DOT AUX_GROUP AUX_LANG AUX_TIME FORM NUMBER SUBGROUP)) {
178              
179 7         16 return 1;
180             }
181 0         0 return 0;
182             }
183              
184 0         0 return 0;
185             }
186              
187             sub can_follow_term {
188 0     0 0 0 my $type = shift;
189              
190 0         0 return is_operator_token($type);
191             }
192              
193             sub can_precede_number {
194 4     4 0 10 my ($type, $value) = @_;
195              
196 4 50       9 return 0 unless defined $type;
197              
198 4 100       10 if ($type eq 'SUBGROUP') {
199 1         2 return 1;
200             }
201              
202 3 50       7 if ($type eq 'AUX_GROUP') {
203 3         8 my $subtype = group_subtype($value);
204 3 100       13 return $subtype eq 'AUX_FORM' ? 1 : 0;
205             }
206              
207 0 0 0     0 if ($type eq 'AUX_TIME' || $type eq 'AUX_LANG') {
208 0         0 return 0;
209             }
210              
211 0         0 return 0;
212             }
213              
214             sub can_start_expression_with {
215 0     0 0 0 my $type = shift;
216              
217 0         0 return can_be_standalone($type);
218             }
219              
220             sub describe_token_type {
221 0     0 0 0 my $type = shift;
222              
223 0   0     0 return $DESC{$type} || 'unknown token';
224             }
225              
226             sub group_subtype {
227 16     16 0 19 my $value = shift;
228              
229 16 50       27 if (! defined $value) {
230 0         0 return 'UNKNOWN';
231             }
232              
233             # Common auxiliaries of form: (0...)
234 16 100       49 return 'AUX_FORM' if $value =~ /^\(0(?:[^)]*)\)$/;
235              
236             # Place and other special auxiliaries typically begin with non-zero digit
237 11 50       57 return 'AUX_OTHER' if $value =~ /^\([1-9][^)]*\)$/;
238              
239 0         0 return 'UNKNOWN';
240             }
241              
242             sub is_modifier_token {
243 69     69 0 69 my $type = shift;
244              
245 69 100 100     137 return ($TOKEN_RULES{$type} && $TOKEN_RULES{$type}{'modifier'}) ? 1 : 0;
246             }
247              
248             sub is_operator_token {
249 30     30 0 36 my $type = shift;
250              
251 30 100 66     103 return (defined $type && $type eq 'OP') ? 1 : 0;
252             }
253              
254             sub is_primary_token {
255 70     70 0 86 my $type = shift;
256              
257 70 100 66     277 return ($TOKEN_RULES{$type} && $TOKEN_RULES{$type}{'primary'}) ? 1 : 0;
258             }
259              
260             sub is_valid_operator {
261 46     46 0 67 my $op = shift;
262              
263 46 50       106 return exists $OPERATORS{$op} ? 1 : 0;
264             }
265              
266             sub operator_info {
267 0     0 0   my $op = shift;
268              
269 0           return $OPERATORS{$op};
270             }
271              
272             1;
273              
274             __END__