Re: parse lisp style config
by Fletch (Bishop) on Nov 27, 2024 at 11:55 UTC
|
| [reply] |
|
|
I tried the script, with Marpa::R2, It can be done, I wonder with carful recursion to achieve it is more satisfing.
| [reply] |
Re: parse lisp style config
by GrandFather (Saint) on Nov 27, 2024 at 23:37 UTC
|
Are you doing this as a learning exercise, or have you an intended use in mind where this is just a tool? If the latter, why not use one of the "standard" tools such as YAML, JSON, ini, ...?
Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
| [reply] |
|
|
| [reply] |
|
|
I found those standard tools have their peculiarities and complains always, I just want to express an struture, and not too hard to parse, I heard lisp's way is easy to parse and extendable, I'm trying and practising, though using only brancket may be not a good idea which leads to express array in a funny way, I will find out, now I'm using toml as easy reading config format.
| [reply] |
|
|
|
|
|
|
|
Re: parse lisp style config
by Corion (Patriarch) on Nov 27, 2024 at 10:55 UTC
|
Why is (ignore ...) converted to an array but (config ...) converted to a hash(ref) ?
Is it because (ignore is followed by a list of lists but (config is followed by a list of pairs?
Knowing how to differentiate between the two is important.
| [reply] [d/l] [select] |
|
|
Because its siblings are incremental from zero, They are arrays, if not they are hash.It can be no ambiguity and iterateable, if key is 0, It see himself as an array to expect 1 2 and so on.
| [reply] |
|
|
| [reply] |
|
|
Re: parse lisp style config
by vincentaxhe (Scribe) on Nov 28, 2024 at 08:58 UTC
|
package MyPairList;
use strict;
use warnings;
use feature 'state';
use Data::Dumper;
use List::Util qw(all none);
use Exporter 'import';
our @EXPORT = qw(pair_list_parse);
sub pair_list_parse {
my $string = shift;
sub get_tokens {
my $string = shift;
my @tokens = $string =~ /([()]|[^()\s]+)/g;
}
my @tokens = get_tokens $string;
sub parse_list {
state @tokens = @_;
my @list;
while (my $token = shift @tokens) {
if ($token eq '(') {
push @list, parse_list();
} elsif ($token eq ')') {
return \@list;
} else {
push @list, $token;
}
}
return \@list;
}
my $list = parse_list @tokens;
sub parse_nested_list {
my ($list) = @_;
my %hash;
if (none {ref $_} @$list) {
if (@$list == 1) {
return $list->[0];
} elsif (@$list == 2) {
return { $list->[0] => $list->[1] };
} else {
return $list
}
} elsif (all {ref $_} @$list) {
return [ map { parse_nested_list($_) } @$list ];
} else {
my ($key, $ref, @remain) = @$list;
die 'wrong key ', Dumper $key if ref $key;
die 'wrong value ', Dumper $ref unless ref $ref;
die 'redundant value', Dumper \@remain if @remain;
$hash{$key} = parse_nested_list($ref);
}
return \%hash;
}
return parse_nested_list($list)
}
1;
I finish a little script, partly achieve my goal, firstly parsed to a nested list, then parsed to pairs or arrays.use '( (a) (b))' to represent list which avoid ambiguity. list is ok, and it keep order. Iterate list first, because it can not get deep value only be chained hash ref.A little example as below.
(config(
(a A)
(b B)
((good) (bad))
(d e f)
(a (a1 (a2 A)))
)
)
parsed to
$VAR1 = [
{
'config' => [
{
'a' => 'A'
},
{
'b' => 'B'
},
[
'good',
'bad'
],
[
'd',
'e',
'f'
],
{
'a' => {
'a1' => {
'a2' => 'A'
}
}
}
]
}
];
Like perl do, the parse result is normally what people mean | [reply] [d/l] [select] |
|
|
It seems the code uses nested named subs. Named subs don't nest in Perl as they do in some other languages. Try to avoid nesting named subs.
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
| [reply] [d/l] |
|
|
> seems the code uses nested named subs.
I was baffled at first, but you're right, the OP wasn't using proper indentation.
Worth noting that "newer" Perl versions have lexical subs via my sub name {...} constructs.
See perlsub#Lexical-Subroutines
Personally I'm still stuck to my $code_ref = sub {...} approaches.
| [reply] [d/l] [select] |
|
|
nested subs provide some more abstraction ability, I use it always, and I need it, as long as perl still respect scope rule. Though I did not know on which case it can break bad.
| [reply] |
|
|
|
|
|
|
|
|
|
Re: parse lisp style config
by stevieb (Canon) on Nov 27, 2024 at 10:49 UTC
|
| [reply] |
|
|
#!/bin/perl
use strict;
use warnings;
use Data::Dumper;
use File::Slurper qw(read_text);
sub parse_nested_brackets {
my ($string) = @_;
my $index = 0;
return parse_nested_brackets_recursive($string, \$index);
}
sub parse_nested_brackets_recursive {
my ($string, $index_ref) = @_;
my %hash;
my ($key, $value);
while ($$index_ref < length($string)) {
my $char = substr($string, $$index_ref, 1);
$$index_ref++;
if ($char eq '(') {
my $nested_hash = parse_nested_brackets_recursive($string,
+ $index_ref);
if (defined $key) {
$hash{$key} = $nested_hash;
$key = undef;
} else {
%hash = (%hash, %$nested_hash);
}
} elsif ($char eq ')') {
if (defined $key && defined $value) {
$hash{$key} = $value;
$key = undef;
$value = undef;
}
return \%hash;
} elsif ($char =~ /\s/) {
next;
} else {
if (!defined $key) {
$key = $char;
} elsif (!defined $value) {
$value = $char;
} else {
$value .= $char;
}
}
}
return \%hash;
}
my $string = read_text shift;
my $hash = parse_nested_brackets($string);
print Dumper($hash);
It's not able to parse string to array. | [reply] [d/l] |