#!/usr/bin/perl
use
5.010001;
use
POSIX
qw(setlocale LC_ALL)
;
POSIX::setlocale(LC_ALL,
"C"
);
my
$calculator_grammar
= Marpa::R3::Scanless::G->new(
{
bless_package
=>
'My_Nodes'
,
source
=> \(
<<'END_OF_SOURCE'),
:default ::= action => ::array bless => ::lhs
:start ::= Script
Script ::= Expression+ separator => comma bless => script
comma ~ [,]
Expression ::=
Number bless => primary
| ('(') Expression (')') assoc => group bless => parens
|| Expression ('**') Expression assoc => right bless => power
|| Expression ('*') Expression bless => multiply
| Expression ('/') Expression bless => divide
|| Expression ('+') Expression bless => add
| Expression ('-') Expression bless => subtract
Number ~ [\d]+
:discard ~ whitespace
whitespace ~ [\s]+
# allow comments
:discard ~ <hash comment>
<hash comment> ~ <terminated hash comment> | <unterminated
final hash comment>
<terminated hash comment> ~ '#' <hash comment body> <vertical space char>
<unterminated final hash comment> ~ '#' <hash comment body>
<hash comment body> ~ <hash comment char>*
<vertical space char> ~ [\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
<hash comment char> ~ [^\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
END_OF_SOURCE
}
);
my
$show_rules_output
=
$calculator_grammar
->show_rules();
$show_rules_output
.=
$calculator_grammar
->l0_show_rules(1);
Marpa::R3::Test::is(
$show_rules_output
,
<<'END_OF_SHOW_RULES_OUTPUT', 'Scanless show_rules()' );
G1 R0 Script ::= Expression +
G1 R1 Expression ::= Expression
G1 R2 Expression ::= Expression
G1 R3 Expression ::= Expression
G1 R4 Expression ::= Expression
G1 R5 Expression ::= Number
G1 R6 Expression ::= '(' Expression ')'
G1 R7 Expression ::= Expression '**' Expression
G1 R8 Expression ::= Expression '*' Expression
G1 R9 Expression ::= Expression '/' Expression
G1 R10 Expression ::= Expression '+' Expression
G1 R11 Expression ::= Expression '-' Expression
G1 R12 [:start] ::= Script
L0 R0 comma ::= [,]
L0 R1 '(' ::= [\(]
L0 R2 ')' ::= [\)]
L0 R3 '**' ::= [\*] [\*]
L0 R4 '*' ::= [\*]
L0 R5 '/' ::= [\/]
L0 R6 '+' ::= [\+]
L0 R7 '-' ::= [\-]
L0 R8 Number ::= [\d] +
L0 R9 [:discard] ::= whitespace
L0 R10 whitespace ::= [\s] +
L0 R11 [:discard] ::= <hash comment>
L0 R12 <hash comment> ::= <terminated hash comment>
L0 R13 <hash comment> ::= <unterminated final hash comment>
L0 R14 <terminated hash comment> ::= [\#] <hash comment body> <vertical space char>
L0 R15 <unterminated final hash comment> ::= [\#] <hash comment body>
L0 R16 <hash comment body> ::= <hash comment char> *
L0 R17 <vertical space char> ::= [\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
L0 R18 <hash comment char> ::= [^\x{A}\x{B}\x{C}\x{D}\x{2028}\x{2029}]
L0 R19 [:start_lex] ::= Number
L0 R20 [:start_lex] ::= [:discard]
L0 R21 [:start_lex] ::= '('
L0 R22 [:start_lex] ::= ')'
L0 R23 [:start_lex] ::= '**'
L0 R24 [:start_lex] ::= '*'
L0 R25 [:start_lex] ::= '/'
L0 R26 [:start_lex] ::= '+'
L0 R27 [:start_lex] ::= '-'
L0 R28 [:start_lex] ::= comma
END_OF_SHOW_RULES_OUTPUT
do_test(
'Calculator 1'
,
$calculator_grammar
,
'42*2+7/3, 42*(2+7)/3, 2**7-3, 2**(7-3)'
=>
qr/\A 86[.]3\d+ \s+ 126 \s+ 125 \s+ 16\z/
xms);
do_test(
'Calculator 2'
,
$calculator_grammar
,
'42*3+7, 42 * 3 + 7, 42 * 3+7'
=>
qr/ \s* 133 \s+ 133 \s+ 133 \s* /
xms);
do_test(
'Calculator 3'
,
$calculator_grammar
,
'15329 + 42 * 290 * 711, 42*3+7, 3*3+4* 4'
=>
qr/ \s* 8675309 \s+ 133 \s+ 25 \s* /
xms);
my
$priority_grammar
=
<<'END_OF_GRAMMAR';
:default ::= action => ::array
:start ::= statement
statement ::= (<say keyword>) expression bless => statement
| expression bless => statement
expression ::=
number bless => primary
| variable bless => variable
|| sign expression bless => unary_sign
|| expression ('+') expression bless => add
number ~ [\d]+
variable ~ [[:alpha:]] <optional word characters>
<optional word characters> ~ [[:alnum:]]*
# Marpa::R3::Display
# name: SLIF lexeme rule synopsis
:lexeme ~ <say keyword> priority => 1
# Marpa::R3::Display::End
<say keyword> ~ 'say'
sign ~ [+-]
:discard ~ whitespace
whitespace ~ [\s]+
END_OF_GRAMMAR
do_test(
'Priority test 1'
,
Marpa::R3::Scanless::G->new(
{
bless_package
=>
'My_Nodes'
,
source
=> \
$priority_grammar
,
}
),
'say + 42'
=>
qr/ 42 /
xms
);
(
my
$priority_grammar2
=
$priority_grammar
) =~ s/priority \s+ => \s+ 1$/
priority
=> -1/xms;
do_test(
'Priority test 2'
,
Marpa::R3::Scanless::G->new(
{
bless_package
=>
'My_Nodes'
,
source
=> \
$priority_grammar2
,
}
),
'say + 42'
=>
qr/ 41 /
xms
);
sub
do_test {
my
(
$name
,
$grammar
,
$input
,
$output_re
,
$args
) =
@_
;
my
$recce
= Marpa::R3::Scanless::R->new( {
grammar
=>
$grammar
} );
$recce
->
read
(\
$input
);
my
$value_ref
=
$recce
->value();
if
( not
defined
$value_ref
) {
die
"No parse was found, after reading the entire input\n"
;
}
my
$parse
= {
variables
=> {
say
=> -1 } };
my
$value
= ${
$value_ref
}->doit(
$parse
);
Test::More::like(
$value
,
$output_re
,
$name
);
}
sub
My_Nodes::script::doit {
my
(
$self
,
$parse
) =
@_
;
return
join
q{ }
,
map
{
$_
->doit(
$parse
) } @{
$self
};
}
sub
My_Nodes::statement::doit {
my
(
$self
,
$parse
) =
@_
;
return
$self
->[0]->doit(
$parse
);
}
sub
My_Nodes::add::doit {
my
(
$self
,
$parse
) =
@_
;
my
(
$a
,
$b
) = @{
$self
};
return
$a
->doit(
$parse
) +
$b
->doit(
$parse
);
}
sub
My_Nodes::subtract::doit {
my
(
$self
,
$parse
) =
@_
;
my
(
$a
,
$b
) = @{
$self
};
return
$a
->doit(
$parse
) -
$b
->doit(
$parse
);
}
sub
My_Nodes::multiply::doit {
my
(
$self
,
$parse
) =
@_
;
my
(
$a
,
$b
) = @{
$self
};
return
$a
->doit(
$parse
) *
$b
->doit(
$parse
);
}
sub
My_Nodes::divide::doit {
my
(
$self
,
$parse
) =
@_
;
my
(
$a
,
$b
) = @{
$self
};
return
$a
->doit(
$parse
) /
$b
->doit(
$parse
);
}
sub
My_Nodes::unary_sign::doit {
my
(
$self
,
$parse
) =
@_
;
my
(
$sign
,
$expression
) = @{
$self
};
my
$unsigned_result
=
$expression
->doit(
$parse
);
return
$sign
eq
'+'
?
$unsigned_result
: -
$unsigned_result
;
}
sub
My_Nodes::variable::doit {
my
(
$self
,
$parse
) =
@_
;
my
$name
=
$self
->[0];
Marpa::R3::Context::bail(
qq{variable "$name" does not exist}
)
if
not
exists
$parse
->{variables}->{
$name
};
return
$parse
->{variables}->{
$name
};
}
sub
My_Nodes::primary::doit {
my
(
$self
,
$parse
) =
@_
;
return
$self
->[0];
}
sub
My_Nodes::parens::doit {
my
(
$self
,
$parse
) =
@_
;
return
$self
->[0]->doit(
$parse
);
}
sub
My_Nodes::power::doit {
my
(
$self
,
$parse
) =
@_
;
my
(
$a
,
$b
) = @{
$self
};
return
$a
->doit(
$parse
)*
*$b
->doit(
$parse
);
}