In Raku version v2022.06, I am trying to dynamically create grammar G having two productions …
- S → λ
- 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 }'
.