Home > Mobile >  In Raku, how does one create a Code object dynamically from a string?
In Raku, how does one create a Code object dynamically from a string?

Time:06-17


In Raku version v2022.06, I am trying to dynamically create grammar G having two productions …

  1. S → λ
  2. S → aSb

My draft program is …

sub MAIN ( )
  {
  my @variableRightHandSidesMap is Array[Pair] ;
  @variableRightHandSidesMap.push: Pair.new("S", "") ;
  @variableRightHandSidesMap.push: Pair.new("S", "'a' <S> 'b'") ;

  constant Parser := Metamodel::GrammarHOW.new_type( name => 'Parser' ) ;

  my $myTopCode = my token TOP { <S> } ;
  my $mySCode = my token S { '' | 'a' <S> 'b' } ;

  Parser.^add_method( 'TOP', $myTopCode ) ;
  Parser.^add_method( 'S', $mySCode ) ;

  Parser.^compose ;
  say Parser.HOW.^name ;
  say Parser.^methods( :local ) ;
  say ?(Parser.parse: 'aabb') ;
  say ?(Parser.parse: 'aaaaabbbbb') ;
  say ?(Parser.parse: 'aabbb') ;
  say ?(Parser.parse: 'abab') ;
  } # end sub MAIN

Program output is …

Perl6::Metamodel::GrammarHOW
(token TOP { <S> } token S { '' | 'a' <S> 'b' })
True
True
False
False

The draft program shown above has $myTopCode and $mySCode hardcoded.

However, how does one dynamically (programmatically) create the Code objects $myTopCode and $mySCode from the pairs of strings in the @variableRightHandSidesMap ?


Update …

The answers below led me to the following draft, which produces the hoped‑for output (as shown above) …

use MONKEY-SEE-NO-EVAL ;

sub MAIN ( )
  {
  my @variableRightHandSidesMap is Array[Pair] ;
  @variableRightHandSidesMap.push: Pair.new("S", "") ;
  @variableRightHandSidesMap.push: Pair.new("S", "'a' <S> 'b'") ;

  constant Parser := Metamodel::GrammarHOW.new_type( name => 'Parser' ) ;

  my $startVariable = @variableRightHandSidesMap[0].key ; # 'S'
  my $myTopCode = EVAL ( 'my token TOP { <' ~ $startVariable ~ '> }' ) ;
  Parser.^add_method( 'TOP', $myTopCode ) ;

  my Str $sCumulativeRightHandSide = '' ;
  loop ( my $i = 0 ; $i < @variableRightHandSidesMap.elems ; $i   )
    {
    if ( $i > 0 )
      {
      $sCumulativeRightHandSide ~= ( ' | ' ) ;
      }
    if ( @variableRightHandSidesMap[$i].value.chars <= 0 )
      {
      $sCumulativeRightHandSide ~= ( '\'\'' ) ;
      }
    else
      {
      $sCumulativeRightHandSide ~= ( @variableRightHandSidesMap[$i].value ) ;
      } # end else
    } # end loop

  my $mySCode = EVAL ( 'my token ' ~ 'S' ~ ' { ' ~ $sCumulativeRightHandSide ~ ' }' ) ;
  Parser.^add_method( 'S', $mySCode ) ;

  Parser.^compose ;
  say Parser.HOW.^name ;
  say Parser.^methods( :local ) ;
  say ?(Parser.parse: 'aabb') ;
  say ?(Parser.parse: 'aaaaabbbbb') ;
  say ?(Parser.parse: 'aabbb') ;
  say ?(Parser.parse: 'abab') ;
  } # end sub MAIN

Any suggestions (such as those making the above draft more concise) would be appreciated.


CodePudding user response:

Until the RakuAST branch lands, I'd say use EVAL.

You could spelunk in the current Raku grammar to find out how that it is currently done, but there would be no guarantee that that would still work after the RakuAST branch lands.

After the RakuAST branch lands, there would be a stable API to do this.

CodePudding user response:

The EVAL function can be used to parse, compile, and evaluate Raku source. If you want to produce some kind of code object, then make sure the evaluated expression is a code object, e.g. EVAL 'anon regex { x }'.

  • Related