my $DSL = <<'DSL';
:default ::= action => ::first
lexeme default = latm => 1
start ::= sexp
sexp ::= list
| atom
list ::= LPAREN elements RPAREN action => Sexp::Tiny::Actions::do_list
elements ::= element* action => Sexp::Tiny::Actions::do_elements
element ::= sexp
atom ::= string
| symbol
string ::= DQUOTE string_chars DQUOTE action => Sexp::Tiny::Actions::d
+o_string
| DQUOTE DQUOTE action => Sexp::Tiny::Actions::do_
+empty_string
string_chars ::= string_char+ action => Sexp::Tiny::Actions::do_join
string_char ::= STRCHAR
DQUOTE ~ '"'
STRCHAR ~ [^"]
symbol ::= symbol_chars action => Sexp::Tiny::Actions::do_symbol
symbol_chars ::= symbol_char+ action => Sexp::Tiny::Actions::do_join
symbol_char ::= SYMCHAR
SYMCHAR ~ [^()\s"]
LPAREN ~ '('
RPAREN ~ ')'
:discard ~ WS
WS ~ [\s]+
DSL
Probably awkward and there are certainly better ways, but it works: (i skip the processing part):
karl@pazuzu:~/src/perl/debug/sexp-tiny$ bin/sexp.pl examples/fossil.da
+t
{ fields => {
"anonymous" => "off",
"canonical-url" => "https://goethebier.space/fossi
+l/p-acme",
"crlf-glob" => "*.bat *.cmd",
"crnl-glob" => "",
"default-user" => "karl",
"hash-policy" => "sha3", "http-port
+" => 8081,
"ignore-glob" => ["*.o", "*.swp", "*.tmp", ".dir
+env/", "_build/", "_opam/"],
"localauth" => "on",
"max-upload" => "20MB", "project-d
+escription" => "Private ACME tooling repository",
"project-name" => "p-acme",
"ssl-ca-location" => "/etc/ssl/certs",
"throttle" => 0,
"timeline-utc" => "on",
},
type => "fossil",
}
But it only works for strings so far:
karl@pazuzu:~/src/perl/debug/sexp-tiny$ echo '(acme (foo "bar"))' | bin/sexp.pl
{ fields => { foo => "bar" }, type => "acme" }
But not with numbers:
karl@pazuzu:~/src/perl/debug/sexp-tiny$ echo '(acme (x 1))' | bin/sexp
+.pl
bin/sexp.pl expected (key value...)
I can only pass them in as string:
karl@pazuzu:~/src/perl/debug/sexp-tiny$ echo '(acme (x "1"))' | bin/se
+xp.pl
{ fields => { x => 1 }, type => "acme" }
How must i modify the grammar to handle numbers (float and int)? I didn't find an example yet.
Update:
I'll try to illustrate how i did it:
#!/usr/bin/env perl
use strict;
use warnings;
use lib 'lib';
use Sexp::Tiny::Grammar ();
use Data::Dump qw(dd);
use Scalar::Util qw(blessed);
my $src = q{(acme (pi "3.14")(nose "cuke"))};
dd $src;
my $r = Sexp::Tiny::Grammar::build_reader();
dd ref($r);
$r->read( \$src );
my $ast_ref = $r->value;
dd $ast_ref;
sub is_qstr {
my ($v) = @_;
return blessed($v) && $v->isa('Sexp::Tiny::String');
}
sub unwrap_qstr {
my ( $v, $path ) = @_;
if ( is_qstr($v) ) {
warn "[unwrap] $path: ", ref($v), qq{ -> plain perl string "},
$v->value, qq{"\n};
return $v->value;
}
return $v;
}
sub maybe_number {
my ( $v, $path ) = @_;
return $v if ref $v;
if ( defined($v)
&& $v =~ /\A[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?\z/ )
{
warn "[numify] $path: \"$v\" -> ", 0 + $v, "\n";
return 0 + $v;
}
return $v;
}
sub normalize {
my ( $node, $path ) = @_;
$node = unwrap_qstr( $node, $path );
if ( ref($node) eq 'ARRAY' ) {
my @out;
for my $i ( 0 .. $#$node ) {
push @out, normalize( $node->[$i], "$path\[$i\]" );
}
return \@out;
}
return maybe_number( $node, $path );
}
my $norm = normalize( $$ast_ref, '$ast' );
dd $norm;
Looks like it's doing what it's supposed to do:
karl@pazuzu:~/src/perl/sexp-tiny$ leftovers/ex02.pl
"(acme (pi \"3.14\")(nose \"cuke\"))"
"Sexp::Tiny::Grammar::Reader"
\[
"acme",
["pi", bless(do{\(my $o = 3.14)}, "Sexp::Tiny::String")],
["nose", bless(do{\(my $o = "cuke")}, "Sexp::Tiny::String")],
]
[unwrap] $ast[1][1]: Sexp::Tiny::String -> plain perl string "3.14"
[numify] $ast[1][1]: "3.14" -> 3.14
[unwrap] $ast[2][1]: Sexp::Tiny::String -> plain perl string "cuke"
["acme", ["pi", 3.14], ["nose", "cuke"]]
my $src = q{(acme (pi 3.14)(nose "cuke"))}; works as well.
|