validator: lhs(?) value eofile { if (@{$item[1]}) { $item[1][0] . $item[2] . "}\n"; } else { $item[2] } } | lhs: variable '~~' { "{\nlocal *_ = \\( $item[1] );\n" } variable: { Text::Balanced::extract_variable($text) } value: hash | array | scalar | hash: '{' pair(s? /,/) /,?/ '}' attr(s?) { my $attrs = { map { @$_ } @{ $item[6] } }; my $pairs = $item[3]; my $topic = $arg{topic}; ### $attrs my $for_topic = $topic ? " for $topic" : ""; my ($code, $code2); my $required; if (delete $attrs->{required}) { $code .= <<"_EOC_"; defined or die qq{Value$for_topic required.\\n}; _EOC_ $required = 1; } my $code3 = ''; if (delete $attrs->{nonempty}) { $code3 .= <<"_EOC_"; \%\$_ or die qq{Hash cannot be empty$for_topic.\\n}; _EOC_ } $code2 .= <<"_EOC_" . $code3 . join('', map { $_->[1] } @$pairs); ref and ref eq 'HASH' or die qq{Invalid value$for_topic: Hash expected.\\n}; _EOC_ my @keys = map { $_->[0] } @$pairs; my $cond = join ' or ', map { '$_ eq "' . quotemeta($_) . '"' } @keys; $code2 .= <<"_EOC_"; for (keys \%\$_) { $cond or die qq{Unrecognized key in hash$for_topic: \$_\\n}; } _EOC_ if ($required) { $code .= $code2 } else { $code .= "if (defined) {\n$code2}\n"; } if (my $args = delete $attrs->{to}) { my $var = $args->[0]; $code .= "$var = \$_;\n"; } if (%$attrs) { die "Bad attribute for hash: ", join(" ", keys %$attrs), "\n"; } $code; } pair: key ':' value[ topic => qq{"$item[1]"} . ($arg{topic} ? " for $arg{topic}" : '') ] { my $quoted_key = quotemeta($item[1]); [$item[1], <<"_EOC_" . $item[4] . "}\n"] { local *_ = \\( \$_->{"$quoted_key"} ); _EOC_ } | key: { extract_delimited($text, '"') } { eval $item[1] } | ident ident: /^[A-Za-z]\w*/ scalar: type attr(s?) { my $attrs = { map { @$_ } @{ $item[3] } }; my $topic = $arg{topic}; my $for_topic = $topic ? " for $topic" : ""; my $code; my $code2 = $item[1]; my $required; if (delete $attrs->{required}) { $code .= <<"_EOC_"; defined or die qq{Value$for_topic required.\\n}; _EOC_ $required = 1; } if (my $args = delete $attrs->{match}) { my ($pat, $desc) = @$args; $desc = eval $desc; $code2 .= "$pat or die qq{Invalid value$for_topic: $desc expected.\\n};\n"; } if (delete $attrs->{nonempty}) { $code2 .= "length or die qq{Invalid value$for_topic: Nonempty scalar expected.\\n};\n"; } if (my $args = delete $attrs->{allowed}) { my $values = join ', ', @$args; my $expr = join ' or ', map { "\$_ eq $_" } @$args; $code2 .= "$expr or die qq{Invalid value$for_topic: Allowed values are $values.\\n};\n"; } if ($required) { $code .= $code2; } else { $code .= "if (defined) {\n$code2}\n"; } if (my $args = delete $attrs->{default}) { if ($required) { die "validator: Required scalar cannot take default value at the same time.\n"; } my $default = $args->[0] or die "validator: :default attribute takes one argument.\n"; $code .= <<"_EOC_"; else { \$_ = $default; } _EOC_ } if (my $args = delete $attrs->{to}) { my $var = $args->[0]; $code .= "$var = \$_;\n"; } if (%$attrs) { die "validator: Bad attribute for scalar: ", join(" ", keys %$attrs), "\n"; } #$code . $code2; $code; } array: '[' array_elem ']' attr(s?) { my $attrs = { map { @$_ } @{ $item[5] } }; my $topic = $arg{topic}; my $for_topic = $topic ? " for $topic" : ""; my $required; my ($code, $code2); if ($required = delete $attrs->{required}) { $code .= <<"_EOC_"; defined or die qq{Value$for_topic required.\\n}; _EOC_ #$required = 1; } my $code3 = ''; if (my $args = delete $attrs->{nonempty}) { $code3 .= <<"_EOC_"; \@\$_ or die qq{Array cannot be empty$for_topic.\\n}; _EOC_ } $code2 .= <<"_EOC_"; ref and ref eq 'ARRAY' or die qq{Invalid value$for_topic: Array expected.\\n}; ${code3}for (\@\$_) \{ $item[3]} _EOC_ if ($required) { $code .= $code2; } else { $code .= "if (defined) {\n$code2}\n"; } if (my $args = delete $attrs->{default}) { if ($required) { die "validator: Required array cannot take default value at the same time.\n"; } my $default = $args->[0] or die "validator: :default attribute takes one argument.\n"; $code .= <<"_EOC_"; else { \$_ = $default; } _EOC_ } if (my $args = delete $attrs->{to}) { my $var = $args->[0]; $code .= "$var = \$_;\n"; } if (%$attrs) { die "Bad attribute for array: ", join(" ", keys %$attrs), "\n"; } $code; } | array_elem: { if ($arg{topic}) { $arg{topic} . " " } else { "" } } value[topic => $item[1] . 'array element'] type: 'STRING' { my $topic = $arg{topic}; my $for_topic = $topic ? " for $topic" : ""; <<"_EOC_"; !ref or die qq{Bad value$for_topic: String expected.\\n}; _EOC_ } | 'INT' { my $topic = $arg{topic}; my $for_topic = $topic ? " for $topic" : ""; <<"_EOC_"; /^[-+]?\\d+\$/ or die qq{Bad value$for_topic: Integer expected.\\n}; _EOC_ } | 'BOOL' { my $topic = $arg{topic}; my $for_topic = $topic ? " for $topic" : ""; <<"_EOC_"; JSON::XS::is_bool(\$_) or die qq{Bad value$for_topic: Boolean expected.\\n}; _EOC_ } | 'IDENT' { my $topic = $arg{topic}; my $for_topic = $topic ? " for $topic" : ""; <<"_EOC_"; /^[A-Za-z]\\w*\$/ or die qq{Bad value$for_topic: Identifier expected.\\n}; _EOC_ } | 'ANY' { '' } | attr: ':' ident '(' argument(s /,/) ')' { [ $item[2] => [ @{ $item[5] } ] ] } | ':' ident { [ $item[3] => 1 ] } | argument: /^\d+/ | '[]' | variable | { extract_quotelike($text) } { $item[1] } | { extract_codeblock($text) } { "do $item[1]" } | eofile: /^\Z/