Parser-code-generation
Parser code generation
Introduction
This document has examples of parser code generation for different programming languages.
Raku class code
FunctionalParsers
In this section we generate Raku parser code that uses the functions of this package, "FunctionalParsers".
use FunctionalParsers;
use FunctionalParsers::EBNF;
my $ebnfCode = q:to/END/;
<digit> = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ;
<number> = <digit> , { <digit> } <@ &{ $_.flat.join } ;
<top> = <number> ;
END
my $res = parse-ebnf($ebnfCode, <CODE>, target => 'Raku::Class');
$res.head.tail;
# class FP {
# method pDIGIT(@x) { alternatives(symbol('0'), symbol('1'), symbol('2'), symbol('3'), symbol('4'), symbol('5'), symbol('6'), symbol('7'), symbol('8'), symbol('9'))(@x) }
# method pNUMBER(@x) { apply(&{ $_.flat.join }, sequence({self.pDIGIT($_)}, many({self.pDIGIT($_)})))(@x) }
# method pTOP(@x) { {self.pNUMBER($_)}(@x) }
# has &.parser is rw = -> @x { self.pTOP(@x) };
# }
Let us get (evaluated) class of the code above:
my $class = parse-ebnf($ebnfCode, <EVAL>, target => 'Raku::Class', parser-name => 'MyFP');
# (MyFP)
Here we create an instance of the obtained class and parse with it:
$class.new.parser.('3234'.comb);
# ((() 3234) ((4) 323) ((3 4) 32) ((2 3 4) 3))
Here we generate the parser class code again, but here we place it into an "as-is" Markdown cell:
parse-ebnf($ebnfCode, <CODE>, target => 'Raku::Class').head.tail;
class FP {
method pDIGIT(@x) { alternatives(symbol('0'), symbol('1'), symbol('2'), symbol('3'), symbol('4'), symbol('5'), symbol('6'), symbol('7'), symbol('8'), symbol('9'))(@x) }
method pNUMBER(@x) { apply(&{ $_.flat.join }, sequence({self.pDIGIT($_)}, many({self.pDIGIT($_)})))(@x) }
method pTOP(@x) { {self.pNUMBER($_)}(@x) }
has &.parser is rw = -> @x { self.pTOP(@x) };
}
Grammar
In this sub-section we generate code for Raku's built-in grammars.
Here we generate grammar class and place it into an "as-is" Markdown cell:
parse-ebnf($ebnfCode, <CODE>, target => 'Raku::Grammar', parser-name => 'MyFP').head.tail;
grammar MyFP {
rule pDIGIT { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' }
rule pNUMBER { <pDIGIT> <pDIGIT>* }
rule TOP { <pNUMBER> }
}
Here we generate the grammar code and evaluate it:
my $gr = parse-ebnf($ebnfCode, <EVAL>, target => 'Raku::Grammar', parser-name => 'MyGr');
# (MyGr)
Here we parse with the grammar:
$gr.parse('944'.comb);
# 「9 4 4」
# pNUMBER => 「9 4 4」
# pDIGIT => 「9 」
# pDIGIT => 「4 」
# pDIGIT => 「4」
Wolfram Language code
In this section we generate Wolfram Language (WL) parsers code. (WL is also known as "Mathematica".)
FunctionalParsers
In this sub-section generate code for the WL paclet "AntonAntonov/FunctionalParsers" (which corresponds to this Raku package "FunctionalParsers".)
Here we transform the EBNF code above to have WL function:
my $ebnfCodeWL = $ebnfCode.subst('$_.flat.join', 'StringJoin@*Flatten');
# <digit> = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ;
# <number> = <digit> , { <digit> } <@ &{ StringJoin@*Flatten } ;
# <top> = <number> ;
Here we generate WL code:
parse-ebnf($ebnfCodeWL, <CODE>, target => 'WL::Code').head.tail.subst(/';' \s* /,";\n"):g;
pDIGIT = ParseAlternativeComposition[ParseSymbol["0"], ParseSymbol["1"], ParseSymbol["2"], ParseSymbol["3"], ParseSymbol["4"], ParseSymbol["5"], ParseSymbol["6"], ParseSymbol["7"], ParseSymbol["8"], ParseSymbol["9"]];
pNUMBER = ParseApply[ StringJoin@*Flatten , ParseSequentialComposition[pDIGIT, ParseMany[pDIGIT]]];
pTOP = pNUMBER;
GrammarRules
In this sub-section we generate code for WL's built-in GrammarRules.
parse-ebnf($ebnfCodeWL, <CODE>, target => 'WL::Grammar').head.tail;
# GrammarRules[{"pTOP" -> "pNUMBER"}, {"pDIGIT" -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9","pNUMBER" -> FixedOrder["pDIGIT", "pDIGIT"..] :> StringJoin@*Flatten }]