File Coverage

blib/lib/Business/UDC/Tokenizer.pm
Criterion Covered Total %
statement 79 83 95.1
branch 38 40 95.0
condition 20 21 95.2
subroutine 8 8 100.0
pod 0 1 0.0
total 145 153 94.7


line stmt bran cond sub pod time code
1             package Business::UDC::Tokenizer;
2              
3 12     12   94738 use base qw(Exporter);
  12         38  
  12         1286  
4 12     12   60 use strict;
  12         37  
  12         260  
5 12     12   48 use warnings;
  12         22  
  12         566  
6              
7 12     12   4866 use Error::Pure qw(err);
  12         87760  
  12         226  
8 12     12   650 use Readonly;
  12         20  
  12         16065  
9              
10             Readonly::Array our @EXPORT_OK => qw(tokenize);
11              
12             our $VERSION = 0.06;
13              
14             sub tokenize {
15 80     80 0 379033 my ($input) = @_;
16 80         115 my @tokens;
17              
18 80         272 pos($input) = 0;
19              
20 80         290 while (pos($input) < length($input)) {
21 225         349 my $start = pos($input);
22              
23 225 100 100     746 if (@tokens && $input =~ /\G( +)(?=\p{L})/gcu) {
24 4         7 next;
25             }
26              
27 221 100 100     624 if (@tokens && $input =~ /\G( +)(?=")/gc) {
28 2         3 next;
29             }
30              
31 219 100 100     685 if (@tokens && $tokens[-1]->{type} eq 'ALPHA_SPEC'
      100        
32             && $input =~ /\G( +)(?=\()/gc) {
33 1         3 next;
34             }
35              
36 218 50 100     651 if (@tokens && $tokens[-1]->{type} eq 'ALPHA_SPEC'
      66        
37             && $input =~ /\G( +)(?=\+)/gc) {
38 0         0 next;
39             }
40              
41 218 100 100     543 if (@tokens && $input =~ /\G( +)\z/gc) {
42 1         2 next;
43             }
44              
45 217 100       615 if ($input =~ /\G(\s)/gc) {
46 3         11 err "Whitespace is not allowed in UDC string.",
47             'position' => $start,
48             'character' => $1,
49             ;
50             }
51              
52 214 100       742 if ($input =~ /\G(\d+(?:\.\d+)*)/gc) {
53 80         224 _push_token(\@tokens, 'NUMBER', $1, $start);
54 80         234 next;
55             }
56              
57 134 100       278 if ($input =~ /\G(\.\d+(?:\.\d+)*)/gc) {
58 9         26 _push_token(\@tokens, 'AUX_DOT', $1, $start);
59 9         22 next;
60             }
61              
62 125 100       266 if ($input =~ /\G(\[)/gc) {
63 4         9 _push_token(\@tokens, 'LBRACK', $1, $start);
64 4         9 next;
65             }
66              
67 121 100       316 if ($input =~ /\G(\])/gc) {
68 5         41 _push_token(\@tokens, 'RBRACK', $1, $start);
69 5         11 next;
70             }
71              
72 116 100       269 if ($input =~ /\G([:+\/])/gc) {
73 30         71 _push_token(\@tokens, 'OP', $1, $start);
74 30         65 next;
75             }
76              
77 86 100       217 if ($input =~ /\G(-\d+(?:\.\d+)*)/gc) {
78 15         46 _push_token(\@tokens, 'FORM', $1, $start);
79 15         39 next;
80             }
81              
82 71 100       254 if ($input =~ /\G(\([^)]+\))/gc) {
83 27         92 _push_token(\@tokens, 'AUX_GROUP', $1, $start, 1);
84 27         69 next;
85             }
86              
87 44 100       106 if ($input =~ /\G("[^"]*")/gc) {
88 4         11 my $value = $1;
89 4 100       16 if ($value =~ /\p{L}/u) {
90 1         3 _push_token(\@tokens, 'ALPHA_SPEC', $value, $start, 1);
91             } else {
92 3         10 _push_token(\@tokens, 'AUX_TIME', $value, $start);
93             }
94 4         12 next;
95             }
96              
97 40 100       101 if ($input =~ /\G(=+(?:[A-Za-z]+|\d+(?:\.\d+)*))/gc) {
98 4         12 _push_token(\@tokens, 'AUX_LANG', $1, $start);
99 4         30 next;
100             }
101              
102 36 100       178 if ($input =~ /\G(\p{L}(?:[\p{L}\p{N}._#,]|\+(?! )|-(?!\d))*(?: +[\p{L}\p{N}._](?:[\p{L}\p{N}._#,]|\+(?! )|-(?!\d))*)*)/gcu) {
103 20         57 _push_token(\@tokens, 'ALPHA_SPEC', $1, $start, 1);
104 20         50 next;
105             }
106              
107 16 100       64 if ($input =~ /\G(\'\d+(?:\.\d+)*)/gc) {
108 13         38 _push_token(\@tokens, 'APOS_AUX', $1, $start);
109 13         34 next;
110             }
111              
112 3         8 my $bad = substr($input, $start, 20);
113 3         16 err "Unrecognized input near '$bad'.",
114             'position' => $start,
115             ;
116             }
117              
118 74         284 return \@tokens;
119             }
120              
121             sub _check_whitespace {
122 163     163   284 my ($value, $start) = @_;
123              
124 163 50       557 if ($value =~ /^(.*?)\s/s) {
125 0         0 my $ws_pos = length($1);
126 0         0 my $char = substr($value, $ws_pos, 1);
127 0         0 err "Whitespace is not allowed in UDC string.",
128             'position' => $start + $ws_pos,
129             'character' => $char,
130             ;
131             }
132              
133 163         252 return;
134             }
135              
136             sub _push_token {
137 211     211   678 my ($tokens_ar, $type, $value, $start, $allow_whitespace) = @_;
138              
139 211 100       419 if (! $allow_whitespace) {
140 163         318 _check_whitespace($value, $start);
141             }
142              
143 211         262 push @{$tokens_ar}, {
  211         733  
144             type => $type,
145             value => $value,
146             pos => $start,
147             };
148              
149 211         366 return;
150             }
151              
152              
153             1;
154              
155             __END__