line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Hub::Parse::Hash; |
2
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
3
|
1
|
|
|
1
|
|
6
|
use Hub qw/:lib :console/; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
7
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '4.00043'; |
6
|
|
|
|
|
|
|
our @EXPORT = qw//; |
7
|
|
|
|
|
|
|
our @EXPORT_OK = qw/ |
8
|
|
|
|
|
|
|
HASH_FORMAT_MAJOR_VERSION |
9
|
|
|
|
|
|
|
HASH_FORMAT_MINOR_VERSION |
10
|
|
|
|
|
|
|
hparse |
11
|
|
|
|
|
|
|
hprint |
12
|
|
|
|
|
|
|
/; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Version |
15
|
1
|
|
|
1
|
|
6
|
use constant HASH_FORMAT_MAJOR_VERSION => 2; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
63
|
|
16
|
1
|
|
|
1
|
|
6
|
use constant HASH_FORMAT_MINOR_VERSION => 1; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4022
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Constants |
19
|
|
|
|
|
|
|
our $NEWLINE = "\n"; |
20
|
|
|
|
|
|
|
our $SPACE = ' '; |
21
|
|
|
|
|
|
|
our $INDENT = ' '; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Literal constants |
24
|
|
|
|
|
|
|
our $LIT_OPEN = '{'; |
25
|
|
|
|
|
|
|
our $LIT_CLOSE = '}'; |
26
|
|
|
|
|
|
|
our $LIT_HASH = '%'; |
27
|
|
|
|
|
|
|
our $LIT_ARRAY = '@'; |
28
|
|
|
|
|
|
|
our $LIT_SCALAR = '$'; |
29
|
|
|
|
|
|
|
our $LIT_ASSIGN = '=>'; |
30
|
|
|
|
|
|
|
our $LIT_COMMENT = '#'; |
31
|
|
|
|
|
|
|
our $LIT_COMMENT_BEGIN = '#{'; |
32
|
|
|
|
|
|
|
our $LIT_COMMENT_END = '#}'; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Used in regular expressions |
35
|
|
|
|
|
|
|
our $PAT_OPEN = $LIT_OPEN; |
36
|
|
|
|
|
|
|
our $PAT_CLOSE = $LIT_CLOSE; |
37
|
|
|
|
|
|
|
our $PAT_HASH = $LIT_HASH; |
38
|
|
|
|
|
|
|
our $PAT_ARRAY = $LIT_ARRAY; |
39
|
|
|
|
|
|
|
our $PAT_SCALAR = "\\$LIT_SCALAR"; |
40
|
|
|
|
|
|
|
our $PAT_ASSIGN = $LIT_ASSIGN; |
41
|
|
|
|
|
|
|
our $PAT_ASSIGN_STRUCT = '[\$\%\@]'; |
42
|
|
|
|
|
|
|
our $PAT_ASSIGN_BLOCK = '<<'; |
43
|
|
|
|
|
|
|
our $PAT_COMMENT = $LIT_COMMENT; |
44
|
|
|
|
|
|
|
our $PAT_COMMENT_BEGIN = $LIT_COMMENT_BEGIN; |
45
|
|
|
|
|
|
|
our $PAT_COMMENT_END = $LIT_COMMENT_END; |
46
|
|
|
|
|
|
|
#our $PAT_LVAL = '[\w\d\.\_\-\s]'; |
47
|
|
|
|
|
|
|
our $PAT_LVAL = '[^\{\=]'; |
48
|
|
|
|
|
|
|
our $PAT_PROTECTED = '[\%\@\$\{\}\>\#]'; |
49
|
|
|
|
|
|
|
our $PAT_PROTECTED2 = '[\%\@\$\{\}\>\=\#]'; # backward compat |
50
|
|
|
|
|
|
|
our $PAT_BLOCK_END = '[a-zA-Z0-9_-]'; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
53
|
|
|
|
|
|
|
# hparse - Parse text into perl data structures |
54
|
|
|
|
|
|
|
# hparse \$text, [options] |
55
|
|
|
|
|
|
|
# options: |
56
|
|
|
|
|
|
|
# -as_array=1 # Treat text as an array list (and return an array ref) |
57
|
|
|
|
|
|
|
# -hint=hint # Usually a filename, used in debug/error output |
58
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub hparse { |
61
|
0
|
|
|
0
|
1
|
|
my ($opts, $text) = Hub::opts(\@_, { |
62
|
|
|
|
|
|
|
'hint' => '', |
63
|
|
|
|
|
|
|
'as_array' => 0, |
64
|
|
|
|
|
|
|
}); |
65
|
0
|
0
|
|
|
|
|
croak "Provide a scalar reference" unless ref($text) eq 'SCALAR'; |
66
|
0
|
0
|
|
|
|
|
my $root = $$opts{'into'} ? $$opts{'into'} : (); |
67
|
0
|
0
|
0
|
|
|
|
$root ||= $$opts{'as_array'} ? [] : Hub::mkinst('SortedHash'); |
68
|
0
|
|
|
|
|
|
my $ptr = $root; |
69
|
0
|
|
|
|
|
|
my $block_comment = 0; |
70
|
0
|
|
|
|
|
|
my $block_text = 0; |
71
|
0
|
|
|
|
|
|
my @parents = (); |
72
|
0
|
|
|
|
|
|
local $. = 0; |
73
|
|
|
|
|
|
|
|
74
|
0
|
|
|
|
|
|
for (split /\r?\n\r?/, $$text) { |
75
|
0
|
|
|
|
|
|
$.++; |
76
|
|
|
|
|
|
|
|
77
|
0
|
0
|
|
|
|
|
if ($block_comment) { |
78
|
|
|
|
|
|
|
# End of a block comment? |
79
|
0
|
0
|
|
|
|
|
/\s*$PAT_COMMENT_END/ and do { |
80
|
0
|
0
|
|
|
|
|
next if (ref($ptr) eq 'SCALAR'); |
81
|
0
|
|
|
|
|
|
_trace($., "comment-e", $_); |
82
|
0
|
|
|
|
|
|
$block_comment = 0; |
83
|
0
|
|
|
|
|
|
next; |
84
|
|
|
|
|
|
|
}; |
85
|
0
|
|
|
|
|
|
_trace($., "comment+", $_); |
86
|
0
|
|
|
|
|
|
next; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
0
|
0
|
|
|
|
|
if ($block_text) { |
90
|
|
|
|
|
|
|
# End of a text block? |
91
|
0
|
0
|
|
|
|
|
/\s*$block_text\s*/ and do { |
92
|
0
|
|
|
|
|
|
_trace($., "txtblk-e", $_); |
93
|
0
|
|
|
|
|
|
$block_text = 0; |
94
|
0
|
|
|
|
|
|
$ptr = pop @parents; |
95
|
0
|
|
|
|
|
|
next; |
96
|
|
|
|
|
|
|
}; |
97
|
0
|
|
|
|
|
|
_trace($., "txtblk+", $_); |
98
|
0
|
0
|
|
|
|
|
$$ptr .= $$ptr ? $NEWLINE . _unescape($_) : _unescape($_); |
99
|
0
|
|
|
|
|
|
next; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Begin of a new hash structure |
103
|
0
|
0
|
|
|
|
|
/^\s*$PAT_HASH($PAT_LVAL*)\s*$PAT_OPEN?\s*$/ and do { |
104
|
0
|
|
|
|
|
|
_trace($., "hash", $_); |
105
|
0
|
|
|
|
|
|
push @parents, $ptr; |
106
|
|
|
|
|
|
|
# my %h; tie %h, 'Hub::Knots::SortedHash'; |
107
|
0
|
|
|
|
|
|
my $h = Hub::mkinst('SortedHash'); |
108
|
0
|
|
|
|
|
|
my $var_name = _trim_whitespace(\$1); |
109
|
0
|
0
|
|
|
|
|
isa($ptr, 'HASH') and $ptr->{$var_name} = $h; |
110
|
0
|
0
|
|
|
|
|
isa($ptr, 'ARRAY') and push @$ptr, $h; |
111
|
0
|
|
|
|
|
|
$ptr = $h; |
112
|
0
|
|
|
|
|
|
next; |
113
|
|
|
|
|
|
|
}; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# Begin of a new array structure |
116
|
0
|
0
|
|
|
|
|
/^\s*$PAT_ARRAY($PAT_LVAL*)\s*$PAT_OPEN?\s*$/ and do { |
117
|
0
|
|
|
|
|
|
_trace($., "array", $_); |
118
|
0
|
|
|
|
|
|
push @parents, $ptr; |
119
|
0
|
|
|
|
|
|
my $a = []; |
120
|
0
|
|
|
|
|
|
my $var_name = _trim_whitespace(\$1); |
121
|
0
|
0
|
|
|
|
|
isa($ptr, 'HASH') and $ptr->{$var_name} = $a; |
122
|
0
|
0
|
|
|
|
|
isa($ptr, 'ARRAY') and push @$ptr, $a; |
123
|
0
|
|
|
|
|
|
$ptr = $a; |
124
|
0
|
|
|
|
|
|
next; |
125
|
|
|
|
|
|
|
}; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# Begin of a new scalar structure |
128
|
0
|
0
|
|
|
|
|
/^\s*$PAT_SCALAR($PAT_LVAL*)\s*$PAT_OPEN?\s*$/ and do { |
129
|
0
|
|
|
|
|
|
_trace($., "scalar", $_); |
130
|
0
|
|
|
|
|
|
push @parents, $ptr; |
131
|
0
|
0
|
|
|
|
|
if (isa($ptr, 'HASH')) { |
|
|
0
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
my $var_name = _trim_whitespace(\$1); |
133
|
0
|
|
|
|
|
|
$ptr->{$var_name} = ''; |
134
|
0
|
|
|
|
|
|
$ptr = \$ptr->{$var_name}; |
135
|
|
|
|
|
|
|
} elsif (isa($ptr, 'ARRAY')) { |
136
|
0
|
|
|
|
|
|
push @$ptr, ''; |
137
|
0
|
|
|
|
|
|
$ptr = \$ptr->[$#$ptr]; |
138
|
|
|
|
|
|
|
} |
139
|
0
|
|
|
|
|
|
next; |
140
|
|
|
|
|
|
|
}; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# A one-line hash member value |
143
|
0
|
0
|
|
|
|
|
/^\s*($PAT_LVAL+)\s*$PAT_ASSIGN\s*(.*)/ and do { |
144
|
0
|
|
|
|
|
|
my $lval = $1; |
145
|
0
|
|
|
|
|
|
my $rval = $2; |
146
|
0
|
|
|
|
|
|
my $var_name = _trim_whitespace(\$lval); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# Structure assignment |
149
|
0
|
0
|
|
|
|
|
$rval =~ /($PAT_ASSIGN_STRUCT)\s*$PAT_OPEN?\s*$/ and do { |
150
|
0
|
|
|
|
|
|
_trace($., "assign-$1", $_); |
151
|
0
|
0
|
|
|
|
|
unless (isa($ptr, 'HASH')) { |
152
|
0
|
|
|
|
|
|
warn "Cannot assign structure to '$ptr'", |
153
|
|
|
|
|
|
|
_get_hint($., $_, $$opts{'hint'}); |
154
|
0
|
|
|
|
|
|
next; |
155
|
|
|
|
|
|
|
} |
156
|
0
|
|
|
|
|
|
push @parents, $ptr; |
157
|
0
|
0
|
|
|
|
|
if ($1 eq $LIT_HASH) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
158
|
0
|
|
|
|
|
|
my $h = Hub::mkinst('SortedHash'); |
159
|
0
|
|
|
|
|
|
$ptr->{$var_name} = $h; |
160
|
0
|
|
|
|
|
|
$ptr = $h; |
161
|
|
|
|
|
|
|
} elsif ($1 eq $LIT_ARRAY) { |
162
|
0
|
|
|
|
|
|
my $a = []; |
163
|
0
|
|
|
|
|
|
$ptr->{$var_name} = $a; |
164
|
0
|
|
|
|
|
|
$ptr = $a; |
165
|
|
|
|
|
|
|
} elsif ($1 eq $LIT_SCALAR) { |
166
|
0
|
|
|
|
|
|
$ptr->{$var_name} = ''; |
167
|
0
|
|
|
|
|
|
$ptr = \$ptr->{$var_name}; |
168
|
|
|
|
|
|
|
} else { |
169
|
0
|
|
|
|
|
|
warn "Unexpected structure assignment", |
170
|
|
|
|
|
|
|
_get_hint($., $_, $$opts{'hint'}); |
171
|
|
|
|
|
|
|
} |
172
|
0
|
|
|
|
|
|
next; |
173
|
|
|
|
|
|
|
}; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Block assignment |
176
|
0
|
0
|
|
|
|
|
$rval =~ /$PAT_ASSIGN_BLOCK\s*($PAT_BLOCK_END+)\s*$/ and do { |
177
|
0
|
|
|
|
|
|
_trace($., "txtblk", $_); |
178
|
0
|
|
|
|
|
|
push @parents, $ptr; |
179
|
0
|
0
|
|
|
|
|
if (isa($ptr, 'HASH')) { |
|
|
0
|
|
|
|
|
|
180
|
0
|
|
|
|
|
|
$ptr->{$var_name} = ''; |
181
|
0
|
|
|
|
|
|
$ptr = \$ptr->{$var_name}; |
182
|
|
|
|
|
|
|
} elsif (isa($ptr, 'ARRAY')) { |
183
|
0
|
|
|
|
|
|
push @$ptr, ''; |
184
|
0
|
|
|
|
|
|
$ptr = \$ptr->[$#$ptr]; |
185
|
|
|
|
|
|
|
} |
186
|
0
|
|
|
|
|
|
$block_text = $1; |
187
|
0
|
|
|
|
|
|
next; |
188
|
|
|
|
|
|
|
}; |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# Value assignment |
191
|
0
|
|
|
|
|
|
_trace($., "assign", $_); |
192
|
0
|
0
|
|
|
|
|
unless (isa($ptr, 'HASH')) { |
193
|
0
|
|
|
|
|
|
warn "Cannot assign variable to '$ptr'", _get_hint($., $_, $$opts{'hint'}); |
194
|
0
|
0
|
|
|
|
|
isa($ptr, 'ARRAY') and push @$ptr, $_; |
195
|
0
|
0
|
|
|
|
|
isa($ptr, 'SCALAR') and $$ptr .= $_; |
196
|
0
|
|
|
|
|
|
next; |
197
|
|
|
|
|
|
|
} |
198
|
0
|
|
|
|
|
|
$ptr->{$var_name} = $rval; |
199
|
0
|
|
|
|
|
|
next; |
200
|
|
|
|
|
|
|
}; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Close a structure |
203
|
0
|
0
|
|
|
|
|
/^\s*$PAT_CLOSE\s*$/ and do { |
204
|
0
|
|
|
|
|
|
_trace($., "close", $_); |
205
|
0
|
|
|
|
|
|
$ptr = pop @parents; |
206
|
0
|
0
|
|
|
|
|
unless (defined $ptr) { |
207
|
0
|
|
|
|
|
|
warn "No parent" . _get_hint($., $_, $$opts{'hint'}); |
208
|
|
|
|
|
|
|
} |
209
|
0
|
|
|
|
|
|
next; |
210
|
|
|
|
|
|
|
}; |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# If this is a brand new structure then this could be a hanging brace. |
213
|
0
|
0
|
|
|
|
|
/^\s*$PAT_OPEN\s*/ and do { |
214
|
0
|
0
|
0
|
|
|
|
if ((isa($ptr, 'HASH') && !keys(%$ptr)) |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
215
|
|
|
|
|
|
|
|| (isa($ptr, 'ARRAY') && !@$ptr) |
216
|
|
|
|
|
|
|
|| (ref($ptr) eq 'SCALAR' && !$$ptr)) { |
217
|
0
|
|
|
|
|
|
_trace($., "hanging", $_); |
218
|
0
|
|
|
|
|
|
next; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
}; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# A block comment |
223
|
0
|
0
|
|
|
|
|
/^\s*$PAT_COMMENT_BEGIN/ and do { |
224
|
0
|
0
|
|
|
|
|
next if (ref($ptr) eq 'SCALAR'); |
225
|
0
|
|
|
|
|
|
_trace($., "comment-b", $_); |
226
|
0
|
|
|
|
|
|
$block_comment = 1; |
227
|
0
|
|
|
|
|
|
next; |
228
|
|
|
|
|
|
|
}; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# A one-line comment |
231
|
0
|
0
|
|
|
|
|
/^\s*$PAT_COMMENT/ and do { |
232
|
0
|
0
|
|
|
|
|
if ($. == 1) { |
233
|
0
|
|
|
|
|
|
_trace($., "crown", $_); |
234
|
0
|
|
|
|
|
|
my @parts = split '\s'; |
235
|
0
|
0
|
0
|
|
|
|
if (@parts >= 3 && $parts[0] =~ /^Hash(File|Format)$/) { |
236
|
0
|
|
|
|
|
|
my ($major, $minor) = split '\.', $parts[2]; |
237
|
0
|
0
|
|
|
|
|
if ($major > HASH_FORMAT_MAJOR_VERSION) { |
238
|
0
|
|
|
|
|
|
die "Hash format version '$major' is too new", |
239
|
|
|
|
|
|
|
_get_hint($., $_, $$opts{'hint'}); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} else { |
243
|
0
|
|
|
|
|
|
_trace($., "comment", $_); |
244
|
|
|
|
|
|
|
} |
245
|
0
|
0
|
|
|
|
|
next unless (ref($ptr) eq 'SCALAR'); |
246
|
|
|
|
|
|
|
}; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# A one-line array item |
249
|
0
|
0
|
|
|
|
|
ref($ptr) eq 'ARRAY' and do { |
250
|
0
|
|
|
|
|
|
_trace($., "array+", $_); |
251
|
0
|
|
|
|
|
|
s/^\s+//g; |
252
|
0
|
0
|
|
|
|
|
next unless $_; # Could be a blank line (arrays of hashes) |
253
|
0
|
|
|
|
|
|
push @$ptr, $_; |
254
|
0
|
|
|
|
|
|
next; |
255
|
|
|
|
|
|
|
}; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# Part of a scalar |
258
|
0
|
0
|
|
|
|
|
ref($ptr) eq 'SCALAR' and do { |
259
|
0
|
|
|
|
|
|
_trace($., "scalar+", $_); |
260
|
0
|
0
|
|
|
|
|
$$ptr .= $$ptr ? $NEWLINE . _unescape($_) : _unescape($_); |
261
|
|
|
|
|
|
|
# $$ptr .= $$ptr ? $NEWLINE . $_ : $_; |
262
|
0
|
|
|
|
|
|
next; |
263
|
|
|
|
|
|
|
}; |
264
|
|
|
|
|
|
|
|
265
|
0
|
|
|
|
|
|
_trace($., "?", $_); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
0
|
0
|
|
|
|
|
warn "Unclosed structure" . _get_hint($., 'EOF', $$opts{'hint'}) if @parents > 1; |
269
|
0
|
|
|
|
|
|
return $root; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
273
|
|
|
|
|
|
|
# hprint - Format nested data structure as string |
274
|
|
|
|
|
|
|
# hprint [options] |
275
|
|
|
|
|
|
|
# |
276
|
|
|
|
|
|
|
# options: |
277
|
|
|
|
|
|
|
# |
278
|
|
|
|
|
|
|
# -as_ref => 1 Return a reference (default 0) |
279
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub hprint { |
282
|
0
|
|
|
0
|
1
|
|
my ($opts, $ref) = Hub::opts(\@_, {'as_ref' => 0}); |
283
|
0
|
0
|
|
|
|
|
croak "Provide a reference" unless ref($ref); |
284
|
0
|
|
|
|
|
|
my $result = _hprint($ref); |
285
|
0
|
0
|
|
|
|
|
return $$opts{'as_ref'} ? $result : ref($result) eq 'SCALAR' ? $$result : ''; |
|
|
0
|
|
|
|
|
|
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
289
|
|
|
|
|
|
|
# _hprint - Implementation of hprint |
290
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub _hprint { |
293
|
0
|
0
|
|
0
|
|
|
my $ref = shift or croak "Provide a reference"; |
294
|
0
|
|
0
|
|
|
|
my $name = shift || ''; |
295
|
0
|
|
0
|
|
|
|
my $level = shift || 0; |
296
|
0
|
|
|
|
|
|
my $parent = shift; |
297
|
0
|
|
|
|
|
|
my $result_str = ''; |
298
|
0
|
|
|
|
|
|
my $result = \$result_str; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# Tame beastly names |
301
|
0
|
0
|
0
|
|
|
|
if ($name && $name !~ /^$PAT_LVAL+$/) { |
302
|
0
|
|
|
|
|
|
$name = Hub::safestr($name); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
0
|
0
|
0
|
|
|
|
if (isa($ref, 'HASH') || isa($ref, 'ARRAY')) { |
|
|
0
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Structure declaration and name |
308
|
0
|
0
|
|
|
|
|
if ($level > 0) { |
309
|
0
|
0
|
|
|
|
|
my $symbol = isa($ref, 'HASH') ? $LIT_HASH : $LIT_ARRAY; |
310
|
0
|
0
|
0
|
|
|
|
if (defined $parent && isa($parent, 'HASH')) { |
311
|
0
|
|
|
|
|
|
$$result .= _get_indent($level) |
312
|
|
|
|
|
|
|
.$name.$SPACE.$LIT_ASSIGN.$SPACE.$symbol.$LIT_OPEN.$NEWLINE; |
313
|
|
|
|
|
|
|
} else { |
314
|
0
|
|
|
|
|
|
$$result .= _get_indent($level) .$symbol.$name.$LIT_OPEN.$NEWLINE; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# Contents |
319
|
0
|
0
|
|
|
|
|
if (isa($ref, 'HASH')) { |
|
|
0
|
|
|
|
|
|
320
|
0
|
|
|
|
|
|
$level++; |
321
|
0
|
|
|
|
|
|
for (keys %$ref) { |
322
|
0
|
0
|
|
|
|
|
if (ref($$ref{$_})) { |
323
|
0
|
|
|
|
|
|
$$result .= ${_hprint($$ref{$_}, $_, $level, $ref)}; |
|
0
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
} else { |
325
|
0
|
|
|
|
|
|
$$result .= ${_hprint(\$$ref{$_}, $_, $level, $ref)}; |
|
0
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
} |
328
|
0
|
|
|
|
|
|
$level--; |
329
|
|
|
|
|
|
|
} elsif (isa($ref, 'ARRAY')) { |
330
|
0
|
|
|
|
|
|
$level++; |
331
|
0
|
|
|
|
|
|
for (@$ref) { |
332
|
0
|
|
|
|
|
|
$$result .= ref($_) ? |
333
|
0
|
|
|
|
|
|
${_hprint($_, '', $level, $ref)} : |
334
|
0
|
0
|
|
|
|
|
${_hprint(\$_, '', $level, $ref)}; |
335
|
|
|
|
|
|
|
} |
336
|
0
|
|
|
|
|
|
$level--; |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# Close the structure |
340
|
0
|
0
|
|
|
|
|
$$result .= _get_indent($level) . $LIT_CLOSE.$NEWLINE |
341
|
|
|
|
|
|
|
if $level > 0; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
} elsif (ref($ref) eq 'SCALAR') { |
344
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
my $value = $$ref; |
346
|
0
|
0
|
|
|
|
|
$value = '' unless defined $value; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# Scalar |
349
|
0
|
0
|
0
|
|
|
|
if (index($value, "\n") > -1 || $value =~ /^\s+/) { |
350
|
0
|
|
|
|
|
|
$$result .= _get_indent($level); |
351
|
0
|
0
|
0
|
|
|
|
if (defined $parent && isa($parent, 'HASH')) { |
352
|
0
|
|
|
|
|
|
$$result .= $name.$SPACE.$LIT_ASSIGN.$SPACE.$LIT_SCALAR.$LIT_OPEN.$NEWLINE; |
353
|
|
|
|
|
|
|
} else { |
354
|
0
|
|
|
|
|
|
$$result .= $LIT_SCALAR.$name.$LIT_OPEN.$NEWLINE; |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
# Write a scalar block to protect data |
357
|
0
|
|
|
|
|
|
$$result .= _escape($value).$NEWLINE; |
358
|
0
|
|
|
|
|
|
$$result .= _get_indent($level) .$LIT_CLOSE.$NEWLINE; |
359
|
|
|
|
|
|
|
} else { |
360
|
|
|
|
|
|
|
# One-line scalar (key/value) |
361
|
0
|
0
|
|
|
|
|
if ($name) { |
362
|
0
|
|
|
|
|
|
$$result .= _get_indent($level) . |
363
|
|
|
|
|
|
|
$name.$SPACE.$LIT_ASSIGN.$SPACE.$value.$NEWLINE; |
364
|
|
|
|
|
|
|
} else { |
365
|
0
|
|
|
|
|
|
$$result .= _get_indent($level) .$value.$NEWLINE; |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
} else { |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# Catch-all |
372
|
0
|
|
|
|
|
|
$$result .= _get_indent($level) . $LIT_COMMENT.$SPACE; |
373
|
0
|
0
|
0
|
|
|
|
$$result .= $name.$SPACE.$LIT_ASSIGN.$SPACE if (defined $name && $name); |
374
|
0
|
|
|
|
|
|
$$result .= $ref.'('.ref($ref).')'.$NEWLINE; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
} |
377
|
0
|
|
|
|
|
|
return $result; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub _trim_whitespace { |
381
|
0
|
|
|
0
|
|
|
my $result = ${$_[0]}; |
|
0
|
|
|
|
|
|
|
382
|
0
|
|
|
|
|
|
$result =~ s/^\s+|\s+$//g; |
383
|
0
|
|
|
|
|
|
return $result; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
387
|
|
|
|
|
|
|
# _escape - Esacape patterns which would be interpred as control characters |
388
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub _escape { |
391
|
0
|
|
|
0
|
|
|
my $result = $_[0]; |
392
|
0
|
|
|
|
|
|
$result =~ s/(?
|
393
|
0
|
|
|
|
|
|
return $result; |
394
|
|
|
|
|
|
|
}#_escape |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
397
|
|
|
|
|
|
|
# _unescape - Remove protective backslashes |
398
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub _unescape { |
401
|
0
|
|
|
0
|
|
|
my $result = $_[0]; |
402
|
0
|
|
|
|
|
|
$result =~ s/\\($PAT_PROTECTED2)/$1/g; |
403
|
0
|
|
|
|
|
|
return $result; |
404
|
|
|
|
|
|
|
}#_unescape |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
407
|
|
|
|
|
|
|
# _get_indent - Get the indent for formatting nested sructures |
408
|
|
|
|
|
|
|
# _get_indent $level |
409
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
sub _get_indent { |
412
|
0
|
|
|
0
|
|
|
my $indent = $INDENT; |
413
|
0
|
0
|
|
|
|
|
return $_[0] > 1 ? $indent x= ($_[0] - 1): ''; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
417
|
|
|
|
|
|
|
# _trace - Debug output |
418
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
419
|
|
|
|
|
|
|
|
420
|
0
|
|
|
0
|
|
|
sub _trace { |
421
|
|
|
|
|
|
|
# warn sprintf("%4d", $_[0]), ": ", Hub::fw(10, $_[1]), " $_[2]\n"; |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
425
|
|
|
|
|
|
|
# _get_hint - Context information for error messages |
426
|
|
|
|
|
|
|
# _get_hint $line_num, $line_text |
427
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub _get_hint { |
430
|
0
|
|
|
0
|
|
|
my $result = ''; |
431
|
0
|
0
|
|
|
|
|
if (defined $_[2]) { |
432
|
0
|
|
|
|
|
|
$result = " ($_[2])"; |
433
|
|
|
|
|
|
|
} |
434
|
0
|
|
|
|
|
|
my $str = substr($_[1], 0, 40); |
435
|
0
|
|
|
|
|
|
$str =~ s/^\s+//g; |
436
|
0
|
|
|
|
|
|
$result .= " at line $_[0]: '$str'"; |
437
|
0
|
|
|
|
|
|
return $result; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
441
|
|
|
|
|
|
|
1; |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
__END__ |