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