$Config::Grammar::Dynamic::VERSION
=
$Config::Grammar::VERSION
;
sub
_deepcopy {
my
$what
=
shift
;
return
$what
unless
ref
$what
;
for
(
ref
$what
) {
/^ARRAY$/ and
return
[
map
{
$_
eq
$what
?
$_
: _deepcopy(
$_
) }
@$what
];
/^HASH$/ and
return
{
map
{
$_
=>
$what
->{
$_
} eq
$what
?
$what
->{
$_
} : _deepcopy(
$what
->{
$_
}) }
keys
%$what
};
/^CODE$/ and
return
$what
;
/^Regexp$/ and
return
$what
;
}
die
"Cannot _deepcopy reference type @{[ref $what]}"
;
}
sub
_next_level($$$)
{
my
$self
=
shift
;
my
$name
=
shift
;
if
(
defined
$self
->{section}) {
$self
->{section} .=
"/$name"
;
}
else
{
$self
->{section} =
$name
;
}
my
$s
=
$self
->_search_section(
$name
);
return
0
unless
defined
$s
;
if
(not
defined
$self
->{grammar}{
$s
}) {
$self
->_make_error(
"Config::Grammar internal error (no grammar for $s)"
);
return
0;
}
push
@{
$self
->{grammar_stack}},
$self
->{grammar};
if
(
$s
=~ m|^/(.*)/$|) {
$self
->{grammar}{
$name
} = _deepcopy(
$self
->{grammar}{
$s
});
$self
->{grammar}{_sections} ||= [];
unshift
@{
$self
->{grammar}{_sections}},
$name
;
}
if
(
$self
->{grammar}{_recursive}
and
grep
{
$_
eq
$s
} @{
$self
->{grammar}{_recursive}}) {
$self
->{grammar}{
$name
}{_sections} ||= [];
$self
->{grammar}{
$name
}{_recursive} ||= [];
push
@{
$self
->{grammar}{
$name
}{_sections}},
$s
;
push
@{
$self
->{grammar}{
$name
}{_recursive}},
$s
;
my
$grammarcopy
= _deepcopy(
$self
->{grammar}{
$name
});
if
(
exists
$self
->{grammar}{
$name
}{
$s
}) {
%{
$self
->{grammar}{
$name
}{
$s
}} = (
%$grammarcopy
, %{
$self
->{grammar}{
$name
}{
$s
}} );
}
else
{
$self
->{grammar}{
$name
}{
$s
} =
$grammarcopy
;
}
}
$self
->{grammar} =
$self
->{grammar}{
$name
};
my
%inherited
;
if
(
$self
->{grammar}{_inherited}) {
for
my
$var
(@{
$self
->{grammar}{_inherited}}) {
next
unless
exists
$self
->{cfg}{
$var
};
my
$value
=
$self
->{cfg}{
$var
};
next
unless
defined
$value
;
next
if
ref
$value
;
$inherited
{
$var
} =
$value
;
}
}
my
$order
;
if
(
defined
$self
->{grammar}{_order}) {
if
(
defined
$self
->{cfg}{_order_count}) {
$order
= ++
$self
->{cfg}{_order_count};
}
else
{
$order
=
$self
->{cfg}{_order_count} = 0;
}
}
if
(
defined
$self
->{cfg}{
$name
}) {
$self
->_make_error(
'section or variable already exists'
);
return
0;
}
$self
->{cfg}{
$name
} = {
%inherited
};
push
@{
$self
->{cfg_stack}},
$self
->{cfg};
$self
->{cfg} =
$self
->{cfg}{
$name
};
$self
->{cfg}{_inherited} = \
%inherited
;
if
(
defined
$self
->{grammar}{_varlist}) {
$self
->{cfg}{_varlist} = [];
}
$self
->{grammar}{_is_section} = 1;
$self
->{cfg}{_is_section} = 1;
$self
->{cfg}{_grammar} =
$name
;
$self
->{cfg}{_order} =
$order
if
defined
$order
;
$self
->{level}++;
if
(
defined
$self
->{grammar}{_dyn}) {
&{
$self
->{grammar}{_dyn}}(
$s
,
$name
,
$self
->{grammar});
}
return
1;
}
sub
_findmissing($$$;$) {
my
$old
=
shift
;
my
$new
=
shift
;
my
$listname
=
shift
;
my
$docfunc
=
shift
;
my
@doc
;
if
(
$old
->{
$listname
}) {
my
%newlist
;
if
(
$new
->{
$listname
}) {
@newlist
{@{
$new
->{
$listname
}}} =
undef
;
}
for
my
$v
(@{
$old
->{
$listname
}}) {
next
if
exists
$newlist
{
$v
};
if
(
$docfunc
) {
push
@doc
,
&$docfunc
(
$old
,
$v
)
}
else
{
push
@doc
,
"=item $v"
;
}
}
}
return
@doc
;
}
sub
_findnew($$$;$) {
my
$old
=
shift
;
my
$new
=
shift
;
my
$listname
=
shift
;
my
$docfunc
=
shift
;
return
_findmissing(
$new
,
$old
,
$listname
,
$docfunc
);
}
sub
_listseq($$);
sub
_listseq($$) {
my
(
$k
,
$l
) =
@_
;
my
$length
=
@$k
;
return
0
unless
@$l
==
$length
;
for
(
my
$i
=0;
$i
<
$length
;
$i
++) {
return
0
unless
$k
->[
$i
] eq
$l
->[
$i
];
}
return
1;
}
sub
_diffgrammars($$);
sub
_diffgrammars($$) {
my
$old
=
shift
;
my
$new
=
shift
;
my
@doc
;
my
@vdoc
;
@vdoc
= _findmissing(
$old
,
$new
,
'_vars'
);
push
@doc
,
"The following variables are not valid anymore:"
,
"=over"
,
@vdoc
,
"=back"
if
@vdoc
;
@vdoc
= _findnew(
$old
,
$new
,
'_vars'
, \
&_describevar
);
push
@doc
,
"The following new variables are valid:"
,
"=over"
,
@vdoc
,
"=back"
if
@vdoc
;
@vdoc
= _findmissing(
$old
,
$new
,
'_sections'
);
push
@doc
,
"The following subsections are not valid anymore:"
,
"=over"
,
@vdoc
,
"=back"
if
@vdoc
;
@vdoc
= _findnew(
$old
,
$new
,
'_sections'
,
sub
{
my
(
$tree
,
$sec
) =
@_
;
my
@tdoc
;
_genpod(
$tree
->{
$sec
}, 0, \
@tdoc
);
return
@tdoc
;
});
push
@doc
,
"The following new subsections are defined:"
,
"=over"
,
@vdoc
,
"=back"
if
@vdoc
;
for
(@{
$old
->{_sections}}) {
next
unless
exists
$new
->{
$_
};
@vdoc
= _diffgrammars(
$old
->{
$_
},
$new
->{
$_
});
push
@doc
,
"Syntax changes for subsection B<$_>"
,
"=over"
,
@vdoc
,
"=back"
if
@vdoc
;
}
return
@doc
;
}
sub
_describevar {
my
$tree
=
shift
;
my
$var
=
shift
;
my
$mandatory
= (
$tree
->{_mandatory} and
grep
{
$_
eq
$var
} @{
$tree
->{_mandatory}} ) ?
" I<(mandatory setting)>"
:
""
;
my
@doc
;
push
@doc
,
"=item B<$var>"
.
$mandatory
;
push
@doc
,
$tree
->{
$var
}{_doc}
if
$tree
->{
$var
}{_doc} ;
my
$inherited
= (
$tree
->{_inherited} and
grep
{
$_
eq
$var
} @{
$tree
->{_inherited}});
push
@doc
,
"This variable I<inherits> its value from the parent section if nothing is specified here."
if
$inherited
;
push
@doc
,
"This variable I<dynamically> modifies the grammar based on its value."
if
$tree
->{
$var
}{_dyn};
push
@doc
,
"Default value: $var = $tree->{$var}{_default}"
if
(
$tree
->{
$var
}{_default});
push
@doc
,
"Example: $var = $tree->{$var}{_example}"
if
(
$tree
->{
$var
}{_example});
return
@doc
;
}
sub
_genpod($$$);
sub
_genpod($$$)
{
my
(
$tree
,
$level
,
$doc
) =
@_
;
my
%dyndoc
;
if
(
$tree
->{_vars}){
push
@{
$doc
},
"The following variables can be set in this section:"
;
push
@{
$doc
},
"=over"
;
foreach
my
$var
(@{
$tree
->{_vars}}){
push
@{
$doc
}, _describevar(
$tree
,
$var
);
}
push
@{
$doc
},
"=back"
;
}
if
(
$tree
->{_text}){
push
@{
$doc
}, (
$tree
->{_text}{_doc} or
"Unspecified Text content"
);
if
(
$tree
->{_text}{_example}){
my
$ex
=
$tree
->{_text}{_example};
chomp
$ex
;
$ex
=
map
{
" $_"
}
split
/\n/,
$ex
;
push
@{
$doc
},
"Example:\n\n$ex\n"
;
}
}
if
(
$tree
->{_table}){
push
@{
$doc
}, (
$tree
->{_table}{_doc} or
"This section can contain a table "
.
"with the following structure:"
);
push
@{
$doc
},
"=over"
;
for
(
my
$i
=0;
$i
<
$tree
->{_table}{_columns};
$i
++){
push
@{
$doc
},
"=item column $i"
;
push
@{
$doc
}, (
$tree
->{_table}{
$i
}{_doc} or
"Unspecific Content"
);
push
@{
$doc
},
"Example: $tree->{_table}{$i}{_example}"
if
(
$tree
->{_table}{
$i
}{_example})
}
push
@{
$doc
},
"=back"
;
}
if
(
$tree
->{_sections}){
if
(
$level
> 0) {
push
@{
$doc
},
"The following sections are valid on level $level:"
;
push
@{
$doc
},
"=over"
;
}
foreach
my
$section
(@{
$tree
->{_sections}}){
my
$mandatory
= (
$tree
->{_mandatory} and
grep
{
$_
eq
$section
} @{
$tree
->{_mandatory}} ) ?
" I<(mandatory section)>"
:
""
;
push
@{
$doc
}, (
$level
> 0) ?
"=item B<"
.(
"+"
x
$level
).
"$section>$mandatory"
:
"=head2 *** $section ***$mandatory"
;
if
(
$tree
eq
$tree
->{
$section
}) {
push
@{
$doc
},
"This subsection has the same syntax as its parent."
;
next
;
}
push
@{
$doc
}, (
$tree
->{
$section
}{_doc})
if
$tree
->{
$section
}{_doc};
push
@{
$doc
},
"The grammar of this section is I<dynamically> modified based on its name."
if
$tree
->{
$section
}{_dyn};
if
(
$tree
->{_recursive} and
grep
{
$_
eq
$section
} @{
$tree
->{_recursive}}) {
push
@{
$doc
},
"This section is I<recursive>: it can contain subsection(s) with the same syntax."
;
}
_genpod (
$tree
->{
$section
},
$level
+1,
$doc
);
next
unless
$tree
->{
$section
}{_dyn} and
$tree
->{
$section
}{_dyndoc};
push
@{
$doc
},
"Dynamical grammar changes for example instances of this section:"
;
push
@{
$doc
},
"=over"
;
for
my
$name
(
sort
keys
%{
$tree
->{
$section
}{_dyndoc}}) {
my
$newtree
= _deepcopy(
$tree
->{
$section
});
push
@{
$doc
},
"=item B<$name>: $tree->{$section}{_dyndoc}{$name}"
;
&{
$tree
->{
$section
}{_dyn}}(
$section
,
$name
,
$newtree
);
my
@tdoc
= _diffgrammars(
$tree
->{
$section
},
$newtree
);
if
(
@tdoc
) {
push
@{
$doc
},
@tdoc
;
}
else
{
push
@{
$doc
},
"No changes that can be automatically described."
;
}
push
@{
$doc
},
"(End of dynamical grammar changes for example instance C<$name>.)"
;
}
push
@{
$doc
},
"=back"
;
push
@{
$doc
},
"(End of dynamical grammar changes for example instances of section C<$section>.)"
;
}
push
@{
$doc
},
"=back"
if
$level
> 0
}
if
(
$tree
->{_vars}) {
for
my
$var
(@{
$tree
->{_vars}}) {
next
unless
$tree
->{
$var
}{_dyn} and
$tree
->{
$var
}{_dyndoc};
push
@{
$doc
},
"Dynamical grammar changes for example values of variable C<$var>:"
;
push
@{
$doc
},
"=over"
;
for
my
$val
(
sort
keys
%{
$tree
->{
$var
}{_dyndoc}}) {
my
$newtree
= _deepcopy(
$tree
);
push
@{
$doc
},
"=item B<$val>: $tree->{$var}{_dyndoc}{$val}"
;
&{
$tree
->{
$var
}{_dyn}}(
$var
,
$val
,
$newtree
);
my
@tdoc
= _diffgrammars(
$tree
,
$newtree
);
if
(
@tdoc
) {
push
@{
$doc
},
@tdoc
;
}
else
{
push
@{
$doc
},
"No changes that can be automatically described."
;
}
push
@{
$doc
},
"(End of dynamical grammar changes for variable C<$var> example value C<$val>.)"
;
}
push
@{
$doc
},
"=back"
;
push
@{
$doc
},
"(End of dynamical grammar changes for example values of variable C<$var>.)"
;
}
}
};
sub
makepod($) {
my
$self
=
shift
;
my
$tree
=
$self
->{grammar};
my
@doc
;
_genpod(
$tree
,0,\
@doc
);
return
join
(
"\n\n"
,
@doc
).
"\n"
;
}
sub
_set_variable($$$)
{
my
$self
=
shift
;
my
$key
=
shift
;
my
$value
=
shift
;
my
$gn
=
$self
->_search_variable(
$key
);
defined
$gn
or
return
0;
my
$varlistref
;
if
(
defined
$self
->{grammar}{_varlist}) {
$varlistref
=
$self
->{cfg}{_varlist};
}
if
(
defined
$self
->{grammar}{
$gn
}) {
my
$g
=
$self
->{grammar}{
$gn
};
if
(
defined
$g
->{_re}) {
$value
=~ /^
$g
->{_re}$/ or
do
{
if
(
defined
$g
->{_re_error}) {
$self
->_make_error(
$g
->{_re_error});
}
else
{
$self
->_make_error(
"syntax error in value of '$key'"
);
}
return
0;
}
}
if
(
defined
$g
->{_sub}){
my
$error
= &{
$g
->{_sub}}(
$value
,
$varlistref
);
if
(
defined
$error
){
$self
->_make_error(
$error
);
return
0;
}
}
if
(
defined
$g
->{_dyn}) {
&{
$g
->{_dyn}}(
$key
,
$value
,
$self
->{grammar});
}
}
$self
->{cfg}{
$key
} =
$value
;
push
@{
$varlistref
},
$key
if
ref
$varlistref
;
return
1;
}
sub
parse($$)
{
my
$self
=
shift
;
my
$file
=
shift
;
my
$args
=
shift
;
$self
->{encoding} =
$args
->{encoding}
if
ref
$args
eq
'HASH'
;
$self
->{cfg} = {};
$self
->{level} = 0;
$self
->{cfg_stack} = [];
$self
->{grammar_stack} = [];
$self
->{file_stack} = [];
$self
->{line_stack} = [];
local
$self
->{grammar} = _deepcopy(
$self
->{grammar});
$self
->_parse_file(
$file
) or
return
undef
;
$self
->_goto_level(0,
undef
) or
return
undef
;
$self
->_fill_defaults;
$self
->_check_mandatory(
$self
->{grammar},
$self
->{cfg},
undef
)
or
return
undef
;
return
$self
->{cfg};
}