#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11147096
use warnings;
my $testdata = <<END;
sys ecm cloud-provider /Common/aws-ec2 {
description "The aws-ec2 parameters"
property-template {
account { }
availability-zone {
valid-values { a b c d }
}
instance-type {
valid-values { t2.micro t2.small t2.medium }
}
region {
valid-values { us-east-1 us-west-1 }
}
}
}
END
local $_ = $testdata; # NOTE expr() expect input in $_
my $parse = expr();
use Data::Dump 'dd'; dd $parse;
use List::AllUtils qw( all );
sub fixhash
{
local $_ = shift;
if( ref $_ eq 'ARRAY' )
{
all { ref $_ eq 'HASH' } @$_ and return { map { map fixhash($_), %
+$_ } @$_ };
return [ map fixhash($_), @$_ ];
}
elsif( ref $_ eq 'HASH' )
{
return { map fixhash($_), %$_ };
}
else { return $_ };
}
sub expr
{
/\G\s+/gc;
my $e = [];
$e =
/\G\s+/gc ? $e :
/\G([^{}\s]+) "(.*?)"/gc ? [ @$e, { $1 => $2 } ] :
/\G([^{}\s]+) \{/gc ? [ @$e, { "$1" => (expr(),
/\G\}/gc || die pos($_), ' b missing }', substr $_, pos($_))[0
+] } ] :
/\G([^{}\s]+)/gc ? [ @$e, $1 ] :
return fixhash($e) while 1;
}
Outputs:
[
"sys",
"ecm",
"cloud-provider",
{
"/Common/aws-ec2" => {
"description" => "The aws-ec2 parameters",
"property-template" => {
"account" => {},
"availability-zone" => { "valid-values"
+ => ["a" .. "d"] },
"instance-type" => { "valid-values" =>
+["t2.micro", "t2.small", "t2.medium"] },
"region" => { "valid-values" => ["us-ea
+st-1", "us-west-1"] },
},
},
},
]
UPDATE: cleaned up fixhash and added "incomplete parse" check.
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11147096
use warnings;
my $testdata = <<END;
sys ecm cloud-provider /Common/aws-ec2 {
description "The aws-ec2 parameters"
property-template {
account { }
availability-zone {
valid-values { a b c d }
}
instance-type {
valid-values { t2.micro t2.small t2.medium }
}
region {
valid-values { us-east-1 us-west-1 }
}
}
}
END
sub expr
{
/\G\s+/gc;
my $e = [];
$e =
/\G\s+/gc ? $e :
/\G([^{}\s]+) "(.*?)"/gc ? [ @$e, { $1 => $2 } ] :
/\G([^{}\s]+) \{/gc ? [ @$e, { "$1" => (expr(),
/\G\}/gc || die pos($_), ' missing }', substr $_, pos($_))[0]
+} ] :
/\G([^{}\s]+)/gc ? [ @$e, $1 ] :
return $e while 1;
}
use List::AllUtils qw( all );
sub fixhash
{
local $_ = shift;
return ref $_ eq 'ARRAY'
? ( all { ref $_ eq 'HASH' } @$_ )
? { map fixhash($_), map %$_, @$_ }
: [ map fixhash($_), @$_ ]
: ref $_ eq 'HASH'
? { map fixhash($_), %$_ }
: $_;
}
local $_ = $testdata; # NOTE expr() expects input in $_
my $parse = fixhash expr();
pos($_) < length $_ and die "incomplete parse ", substr $_, pos($_);
use Data::Dump 'dd'; dd $parse;
SECOND UPDATE: eliminating fixhash() by building it into expr()
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11147096
use warnings;
use List::AllUtils qw( all );
my $testdata = <<END;
sys ecm cloud-provider /Common/aws-ec2 {
description "The aws-ec2 parameters"
property-template {
account { }
availability-zone {
valid-values { a b c d }
}
instance-type {
valid-values { t2.micro t2.small t2.medium }
}
region {
valid-values { us-east-1 us-west-1 }
}
}
}
END
sub expr
{
/\G\s+/gc;
my $e = [];
$e =
/\G\s+/gc ? $e :
/\G([^{}\s]+) "(.*?)"/gc ? [ @$e, { $1 => $2 } ] :
/\G([^{}\s]+) \{/gc ? [ @$e, { "$1" => (expr(),
/\G\}/gc || die pos($_), ' missing } ', substr $_, pos($_))[0] }
+ ] :
/\G([^{}\s]+)/gc ? [ @$e, $1 ] :
return ref $e eq 'ARRAY' && ( all { ref $_ eq 'HASH' } @$e ) ?
{ map %$_, @$e } : $e while 1
}
local $_ = $testdata; # NOTE expr() expects input in $_
my $parse = expr();
pos($_) < length $_ and die "incomplete parse ", substr $_, pos($_);
$Data::Dump::LINEWIDTH = 26;
use Data::Dump 'dd'; dd $parse;
Outputs:
[
"sys",
"ecm",
"cloud-provider",
{
"/Common/aws-ec2" => {
"description" => "The aws-ec2 parameters",
"property-template" => {
"account" => {},
"availability-zone" => {
"valid-values" => ["a" .. "d"],
},
"instance-type" => {
"valid-values" => [
"t2.micro",
"t2.small",
"t2.medium",
],
},
"region" => {
"valid-values" => ["us-east-1", "us-w
+est-1"],
},
},
},
},
]
THIRD UPDATE: factoring out a regex and shifting things around a little, maybe making
things slightly clearer.
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11147096
use warnings;
sub expr
{
my $val = [];
$val =
/\G\s+/gc ? $val :
/\G([^{}\s]+)/gc ? do { my $key = $1; [ @$val, /\G \{/gc
? { $key => (expr(), /\G\}/gc || die ' missing } ')[0] }
: /\G "(.*?)"/gc ? { $key => $1 } : $key ] } :
return ref $val eq 'ARRAY' && ( @$val == grep ref $_ eq 'HASH', @$
+val )
? { map %$_, @$val }
: $val while 1
}
sub parse
{
local $_ = join '', @_;
my $parse = expr;
pos($_) == length $_ or die "incomplete parse stopped at ", substr $
+_, pos($_);
return $parse;
}
my $parse = parse( <DATA> );
$Data::Dump::LINEWIDTH = 26;
use Data::Dump 'dd'; dd $parse;
__DATA__
sys ecm cloud-provider /Common/aws-ec2 {
description "The aws-ec2 parameters"
property-template {
account { }
availability-zone {
valid-values { a b c d }
}
instance-type {
valid-values { t2.micro t2.small t2.medium }
}
region {
valid-values { us-east-1 us-west-1 }
}
}
}
|