Hacker News new | past | comments | ask | show | jobs | submit login

Raku allows strings inside of strings. Of course it does this by way of embedded closures.

    "abc{ "def" }"
Which allows it to be arbitrarily deep.

    "a{ "b{ "c{ "d{ "e{ "f" }g" }h" }i" }j" }k"
    → "abcdefghijk"
This can be handy to generate the correct string.

    my $count = 3;
    "I went to $count place{ "s" if $count ≠ 1 } today"



Interesting, thanks for pointing out a use case. But I don't think backreferences will help with that, it needs to be parsed by something more powerful than a regex.

And that example reminds me that Bash can do something similar:

    echo "$(echo "$(echo "$(echo "hi")")")"


The Rakudo implementation actually uses Raku regexes to parse Raku. To be fair though it is a lot easier to do that with the redesigned regexes that Raku has.

Basically you can use backreferences for that if you also allow the regex to be recursive.

    my $regex = /
      :ratchet
      $<q> = (<["']>) # the beginning quote

      {}:my $q = ~$<q>; # put it into a more normal lexical var

        # capture between " and {
        $<l> = ( [ <!before $q> <-[{}]> ]* )

        [
          [
            :sigspace
            「{」
                <self=&?BLOCK>? # recurse
            「}」
          ]

          {$q = ~$<q>}

          # capture between } and "
          $<r> = ( [ <!before $q> <-[{}]> ]* )
        ]?

      "$q" # match the end quote

      # pass the combined string parts upwards
      { make ($<l> // '') ~ ($<self>.ast // '') ~ ($<r> // '') }
    /;

    「'a{ "b{ "c{ "d{ 'e{ "f" }g' }h" }i" }j" }k'」 ~~ /^ <r=$regex> $ { make $<r>.ast }/;

    say $/.ast;
    # abcdefghijk
Note that `Regex` is a subtype of `Block`. That is why `&?BLOCK` can be used as a reference to the regex itself.

`<foo=bar>` is a way to call `bar`, but also save it under the name of `foo`. `$<foo> = …` is a way to capture `…` and save it under the name of `foo`.

---

It is a lot nicer and modular when you use regexes as part of a grammar:

    # use Grammar::Tracer;
    grammar String::Grammar {
      token TOP { <strings> }

      rule strings {
        # at least one string
        # if there are more than one they are separated by ~
        <string> + % 「~」
      }

      token string {
        $<q> = <["']>

        # set a dynamic variable to the quote character
        {}:my $*quote = ~$<q>;

        <string-part>*

        "$<q>"
      }

      # multiple tokens that act like one
      # which is nicer than using |
      proto token string-part {*}
      multi token string-part:<non> {
        [ <-[{}]> <!after $*quote> ]+
      }
      multi token string-part:<block> {
        <block>
      }

      rule block {
        「{」 ~ 「}」 <strings>?
      }
    }

    class String::Actions {
      method TOP     ($/) { make     $<strings>.ast }
      method strings ($/) { make [~] @<string>».ast }
      method string  ($/) { make [~] @<string-part>».ast }
      method block   ($/) { make     $<strings>.ast }

      method string-part:<non>   ($/) { make ~$/ }
      method string-part:<block> ($/) { make $<block>.ast }
    }

    say String::Grammar.parse(
        「"a{ "b{ "c{ "d{ "e{ "f" }g" ~ "zz" }h" }i" }j" }k"」,
        :actions( String::Actions ),
    ).ast;
    # abcdefgzzhijk
A `token` is just a `regex` with `:ratchet` mode turned on. (prevents backtracking) A `rule` is just a `token` with `:sigspace` also turned on. (makes it easier to deal with optional whitespace.)

Every instance of `<foo>` is basically a method call.

`make` is about generating an `.ast` to pass up and out of the parse. In this case the only thing the actions class does is return what would be the resulting string if it were compiled in Raku.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: