I use this to process s-expressions with Marpa::R2:

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::do_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.dat                                                                
{  fields => {
              "anonymous"           => "off",
              "canonical-url"       => "https://goethebier.space/fossil/p-acme",
              "crlf-glob"           => "*.bat *.cmd",
              "crnl-glob"           => "",
              "default-user"        => "karl",
              "hash-policy"         => "sha3",              "http-port"           => 8081,
              "ignore-glob"         => ["*.o", "*.swp", "*.tmp", ".direnv/", "_build/", "_opam/"],
              "localauth"           => "on",
              "max-upload"          => "20MB",              "project-description" => "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/sexp.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.

Replies

  • What a lovely use case.

    Parsing LISP seems to be a good way to get started with a grammar engine like Marpa::R2

    Much appreciated!

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

  • I can't test it but I would start with:

    
    atom ::= string
           | number
           | symbol
    
    number_chars ::= SIGN? DIGITS action => Sexp::Tiny::Actions::do_join
    SIGN           ~ "+" | "-"
    DIGITS         ~ [0-9]
    
    # Exclude the new starting characters for numbers from the "symbol" character class
    SYMCHAR ~ [^()\s"0-9+-]
    
    • Or, if you want to support the exponential notation, too:
      number     ~ sign_maybe digit_many e
                 | sign_maybe digit_any '.' digit_many e_maybe
                 | sign_maybe digit_many e_maybe
                 | sign_maybe non_zero digit_any
      
      empty      ~
      sign_maybe ~ [+-] | empty
      digit      ~ [0-9]
      non_zero   ~ [1-9]
      digit_any  ~ digit*
      digit_many ~ digit+
      e          ~ [Ee] sign_maybe digit_many
      e_maybe    ~ e | empty
      

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]