sub
new {
my
$class
=
shift
;
bless
{
width
=> 4,
comma
=>
" : "
,
level
=> 0,
tabwidth
=> 8,
},
$class
;
}
sub
DESTROY {
}
sub
undent {
local
$/ =
"\n"
;
my
(
$o
,
$text
) =
@_
;
my
(
$comma
) =
$o
->{comma};
my
$package
=
caller
;
$package
=
caller
(1)
if
$package
eq
'Inline::denter'
;
%{
$o
->{xref}} = ();
@{
$o
->{objects}} = ();
@{
$o
->{context}} = ();
my
$glob
=
''
;
chomp
$text
;
@{
$o
->{lines}} =
split
$/,
$text
;
$o
->{level} = 0;
$o
->{line} ||= 1;
$o
->_setup_line;
while
(not
$o
->{done}) {
if
(
$o
->{level} == 0 and
$o
->{content} =~ /^(\w+)\s
*$comma
\s*(.*)$/) {
$o
->{content} = $2;
no
strict
'refs'
;
push
@{
$o
->{objects}},
"$1"
;
}
push
@{
$o
->{objects}},
$o
->_undent_data;
}
return
@{
$o
->{objects}};
}
sub
_undent_data {
my
$o
=
shift
;
my
(
$obj
,
$class
) = (
''
,
''
);
my
@refs
;
my
%refs
;
while
(
$o
->{content} =~ s/^\\(?:\((\w+)\))?((\%|\@|\$|\\).*)/$2/) {
push
@refs
, $1;
$refs
{$1} =
scalar
@refs
;
}
if
(
$o
->{content} =~ /^([\%\@\$])
(\w(?:\w|::)*)?
\s*$/x
) {
my
$foo
;
$obj
= ($1 eq
'%'
) ? {} : ($1 eq
'@'
) ? [] : \
$foo
;
$class
= $2 ||
''
;
if
($1 eq
'%'
) {
%$obj
=
$o
->_undent_hash;
}
elsif
($1 eq
'@'
) {
@$obj
=
$o
->_undent_array;
}
else
{
$$obj
=
$o
->_undent_scalar;
}
bless
$obj
,
$class
if
length
$class
;
}
elsif
(
$o
->{content} =~ /^\?\s*$/) {
$obj
=
$o
->_undent_undef;
}
else
{
$obj
=
$o
->_undent_value;
}
while
(
@refs
) {
my
$ref
=
pop
@refs
;
my
$copy
=
$obj
;
$obj
= \
$copy
;
$o
->{xref}{
$ref
} =
$obj
if
$ref
;
}
return
$obj
;
}
sub
_undent_value {
my
$o
=
shift
;
my
$value
=
''
;
if
(
$o
->{content} =~ /^\<\<(\w+)(\-?)\s*$/) {
my
(
$marker
,
$chomp
) = ($1, $2);
my
$line
=
$o
->{line};
$o
->_next_line;
while
(not
$o
->{done} and
$o
->{lines}[0] ne
$marker
) {
$value
.=
$o
->{lines}[0] .
"\n"
;
$o
->_next_line;
}
croak M03_no_value_end_marker(
$marker
,
$line
)
if
$o
->{done};
chomp
$value
if
$chomp
;
}
elsif
(
$o
->{content} =~ /^\"/) {
croak
$o
->M04_mismatched_quotes
unless
$o
->{content} =~ /^\".*\"\s*$/;
(
$value
=
$o
->{content}) =~ s/^\"|\"\s*$//g;
}
else
{
$value
=
$o
->{content};
}
$o
->_next_line;
$o
->_setup_line;
return
$value
;
}
sub
_undent_hash {
my
@values
;
my
$o
=
shift
;
my
$level
=
$o
->{level} + 1;
$o
->_next_line;
$o
->_setup_line;
while
(
$o
->{level} ==
$level
) {
my
(
$key
,
$value
) =
split
$o
->{comma},
$o
->{content};
croak
$o
->M05_invalid_key_value
unless
(
defined
$key
and
defined
$value
);
$o
->{content} =
$value
;
push
@values
,
$o
->_get_key(
$key
),
$o
->_undent_data;;
}
croak
$o
->M06_invalid_indent_level
if
$o
->{level} >
$level
;
return
@values
;
}
sub
_get_key {
my
(
$o
,
$key
) =
@_
;
return
$key
unless
$key
=~ /^\<\<(\w+)(\-?)/;
my
(
$marker
,
$chomp
) = ($1, $2);
$key
=
''
;
my
$line
=
$o
->{line};
$o
->_next_line;
while
(not
$o
->{done} and
$o
->{lines}[0] ne
$marker
) {
$key
.=
$o
->{lines}[0] .
"\n"
;
$o
->_next_line;
}
croak M02_no_key_end_marker(
$marker
,
$line
)
if
$o
->{done};
chomp
$key
if
$chomp
;
$o
->_next_line;
$o
->_setup_line;
return
$key
;
}
sub
_undent_array {
my
@values
;
my
$o
=
shift
;
my
$level
=
$o
->{level} + 1;
$o
->_next_line;
$o
->_setup_line;
while
(
$o
->{level} ==
$level
) {
push
@values
,
$o
->_undent_data;
}
croak
$o
->M06_invalid_indent_level
if
$o
->{level} >
$level
;
return
@values
;
}
sub
_undent_scalar {
my
$values
;
my
$o
=
shift
;
my
$level
=
$o
->{level} + 1;
$o
->_next_line;
$o
->_setup_line;
croak
$o
->M06_invalid_indent_level
if
$o
->{level} !=
$level
;
croak
$o
->M07_invalid_scalar_value
if
$o
->{content} =~ /^[\%\@\$\\]/;
return
$o
->_undent_undef
if
$o
->{content} =~ /^\?/;
return
$o
->_undent_value;
}
sub
_undent_undef {
my
$o
=
shift
;
$o
->_next_line;
$o
->_setup_line;
return
undef
;
}
sub
_next_line {
my
$o
=
shift
;
$o
->{done}++,
$o
->{level} = -1,
return
unless
@{
$o
->{lines}};
local
$_
=
shift
@{
$o
->{lines}};
$o
->{line}++;
}
sub
_setup_line {
my
$o
=
shift
;
$o
->{done}++,
$o
->{level} = -1,
return
unless
@{
$o
->{lines}};
my
(
$width
,
$tabwidth
) = @{
$o
}{
qw(width tabwidth)
};
while
(1) {
local
$_
=
$o
->{lines}[0];
$o
->next_line,
next
if
/^(\s*$|\
while
(s{^( *)(\t+)}
{
' '
x (
length
($1) +
length
($2) *
$tabwidth
-
length
($1) %
$tabwidth
)}e){}
croak
$o
->M01_invalid_indent_width
unless
/^(( {
$width
})*)(\S.*)$/;
$o
->{level} =
length
($1) /
$width
;
$o
->{content} = $3;
last
;
}
}
sub
indent {
my
$o
=
shift
;
my
$package
=
caller
;
$package
=
caller
(1)
if
$package
eq
'Inline::denter'
;
my
$stream
=
''
;
$o
->{key} =
''
;
while
(
@_
) {
local
$_
=
shift
;
$stream
.=
$o
->indent_name(
$_
,
shift
),
next
if
(/^\
*$package
\::\w+$/);
$stream
.=
$o
->indent_data(
$_
);
}
return
$stream
;
}
sub
indent_data {
my
$o
=
shift
;
local
$_
=
shift
;
return
$o
->indent_undef(
$_
)
if
not
defined
;
return
$o
->indent_value(
$_
)
if
(not
ref
);
return
$o
->indent_hash(
$_
)
if
(
ref
eq
'HASH'
and not /=/ or /=HASH/);
return
$o
->indent_array(
$_
)
if
(
ref
eq
'ARRAY'
and not /=/ or /=ARRAY/);
return
$o
->indent_scalar(
$_
)
if
(
ref
eq
'SCALAR'
and not /=/ or /=SCALAR/);
return
$o
->indent_ref(
$_
)
if
(
ref
eq
'REF'
);
return
"$_\n"
;
}
sub
indent_value {
my
(
$o
,
$data
) =
@_
;
my
$stream
;
if
(
$data
=~ /\n/) {
my
$marker
=
'EOV'
;
$marker
++
while
$data
=~ /^
$marker
$/m;
my
$chomp
= (
$data
=~ s/\n\Z//) ?
''
:
'-'
;
$stream
=
"<<$marker$chomp\n"
;
$stream
.=
$o
->{key},
$o
->{key} =
''
if
$o
->{key};
$stream
.=
"$data\n$marker\n"
;
}
elsif
(
$data
=~ /^[\s\%\@\$\\?\"]|\s$/ or
$data
=~ /\Q
$o
->{comma}\E/ or
$data
=~ /[\x00-\x1f]/ or
$data
eq
''
) {
$stream
=
qq{"$data"\n}
;
$stream
.=
$o
->{key},
$o
->{key} =
''
if
$o
->{key};
}
else
{
$stream
=
"$data\n"
;
$stream
.=
$o
->{key},
$o
->{key} =
''
if
$o
->{key};
}
return
$stream
;
}
sub
indent_hash {
my
(
$o
,
$data
) =
@_
;
my
$stream
=
$o
->_print_ref(
$data
,
'%'
,
'HASH'
);
return
$$stream
if
ref
$stream
;
my
$indent
= ++
$o
->{level} *
$o
->{width};
for
my
$key
(
sort
keys
%$data
) {
my
$key_out
=
$key
;
if
(
$key
=~ /\n/ or
$key
=~ /\Q
$o
->{comma}\E/) {
my
$marker
=
'EOK'
;
$marker
++
while
$key
=~ /^
$marker
$/m;
my
$chomp
= ((
$o
->{key} =
$key
) =~ s/\n\Z//m) ?
''
:
'-'
;
$o
->{key} .=
"\n$marker\n"
;
$key_out
=
"<<$marker$chomp"
;
}
elsif
(
$data
=~ /^[\s\%\@\$\\?\"]|\s$/) {
$key_out
=
qq{"$key"}
;
}
$stream
.=
' '
x
$indent
.
$key_out
.
$o
->{comma};
$stream
.=
$o
->indent_data(
$data
->{
$key
});
}
$o
->{level}--;
return
$stream
;
}
sub
indent_array {
my
(
$o
,
$data
) =
@_
;
my
$stream
=
$o
->_print_ref(
$data
,
'@'
,
'ARRAY'
);
return
$$stream
if
ref
$stream
;
my
$indent
= ++
$o
->{level} *
$o
->{width};
for
my
$datum
(
@$data
) {
$stream
.=
' '
x
$indent
;
$stream
.=
$o
->indent_data(
$datum
);
}
$o
->{level}--;
return
$stream
;
}
sub
indent_scalar {
my
(
$o
,
$data
) =
@_
;
my
$stream
=
$o
->_print_ref(
$data
,
q{$}
,
'SCALAR'
);
return
$$stream
if
ref
$stream
;
my
$indent
= (
$o
->{level} + 1) *
$o
->{width};
$stream
.=
' '
x
$indent
;
$stream
.=
$o
->indent_data(
$$data
);
return
$stream
;
}
sub
indent_ref {
my
(
$o
,
$data
) =
@_
;
my
$stream
=
$o
->_print_ref(
$data
,
'\\'
,
'SCALAR'
);
return
$$stream
if
ref
$stream
;
chomp
$stream
;
return
$stream
.
$o
->indent_data(
$$data
);
}
sub
indent_undef {
my
(
$o
,
$data
) =
@_
;
my
$stream
=
"?\n"
;
$stream
.=
$o
->{key},
$o
->{key} =
''
if
$o
->{key};
return
$stream
;
}
sub
indent_name {
my
(
$o
,
$name
,
$value
) =
@_
;
$name
=~ s/^.*:://;
my
$stream
=
$name
.
$o
->{comma};
$stream
.=
$o
->indent_data(
$value
);
return
$stream
;
}
sub
_print_ref {
my
(
$o
,
$data
,
$symbol
,
$type
) =
@_
;
$data
=~ /^(([\w:]+)=)?
$type
\(0x([0-9a-f]+)\)$/
or croak
"Invalid reference: $data\n"
;
my
$stream
=
$symbol
;
$stream
.= $2
if
defined
$2;
$o
->{xref}{$3}++;
croak
"Inline::denter does not handle duplicate references"
if
$o
->{xref}{$3} > 1;
$stream
.=
"\n"
;
$stream
.=
$o
->{key},
$o
->{key} =
''
if
$o
->{key};
return
$stream
;
}
sub
M01_invalid_indent_width {
my
$o
=
shift
;
"Invalid indent width detected at line $o->{line}\n"
;
}
sub
M02_no_key_end_marker {
my
(
$marker
,
$line
) =
@_
;
"No terminating marker '$marker' found for key at line $line\n"
;
}
sub
M03_no_value_end_marker {
my
(
$marker
,
$line
) =
@_
;
"No terminating marker '$marker' found for value at line $line\n"
;
}
sub
M04_mismatched_quotes {
my
$o
=
shift
;
"Mismatched double quotes for value at line $o->{line}\n"
;
}
sub
M05_invalid_key_value {
my
$o
=
shift
;
"Missing or invalid hash key/value pair at $o->{line}\n"
;
}
sub
M06_invalid_indent_level {
my
$o
=
shift
;
"Invalid indentation level at $o->{line}\n"
;
}
sub
M07_invalid_scalar_value {
my
$o
=
shift
;
"Invalid value for scalar ref context at $o->{line}\n"
;
}
1;