our
$DATE
=
'2016-07-03'
;
our
$VERSION
=
'0.29'
;
use
5.010;
sub
rule_pair_simple {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
"$match->{key} => $match->{value}"
;
}
sub
rule_pair_string {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
"$match->{key} => $match->{value}"
;
}
sub
rule_or_xor {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'||'
) {
push
@res
,
" || $term"
}
elsif
(
$op
eq
'//'
) {
push
@res
,
" // $term"
}
elsif
(
$op
eq
'^^'
) {
@res
= (
"("
,
@res
,
" xor $term)"
) }
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_and {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'&&'
) {
@res
= (
"(("
,
@res
,
" && $term) || false)"
) }
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_ternary {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
$opd
=
$match
->{operand};
"$opd->[0] ? $opd->[1] : $opd->[2]"
;
}
sub
rule_bit_or_xor {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'|'
) {
push
@res
,
" | $term"
}
elsif
(
$op
eq
'^'
) {
push
@res
,
" ^ $term"
}
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_bit_and {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'&'
) {
push
@res
,
" & $term"
}
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_comparison3 {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'<=>'
) {
push
@res
,
" <=> $term"
}
elsif
(
$op
eq
'cmp'
) {
push
@res
,
" cmp $term"
}
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_comparison {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@opds
;
push
@opds
,
shift
@{
$match
->{operand}};
return
''
unless
defined
$opds
[0];
my
@ops
;
for
my
$term
(@{
$match
->{operand}}) {
push
@opds
,
$term
;
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'=='
) {
push
@ops
,
'=='
}
elsif
(
$op
eq
'!='
) {
push
@ops
,
'!='
}
elsif
(
$op
eq
'eq'
) {
push
@ops
,
'eq'
}
elsif
(
$op
eq
'ne'
) {
push
@ops
,
'ne'
}
elsif
(
$op
eq
'<'
) {
push
@ops
,
'<'
}
elsif
(
$op
eq
'<='
) {
push
@ops
,
'<='
}
elsif
(
$op
eq
'>'
) {
push
@ops
,
'>'
}
elsif
(
$op
eq
'>='
) {
push
@ops
,
'>='
}
elsif
(
$op
eq
'lt'
) {
push
@ops
,
'lt'
}
elsif
(
$op
eq
'le'
) {
push
@ops
,
'le'
}
elsif
(
$op
eq
'gt'
) {
push
@ops
,
'gt'
}
elsif
(
$op
eq
'ge'
) {
push
@ops
,
'ge'
}
}
return
$opds
[0]
unless
@ops
;
my
@res
;
my
$lastopd
;
my
(
$opd1
,
$opd2
);
while
(
@ops
) {
my
$op
=
pop
@ops
;
if
(
defined
(
$lastopd
)) {
$opd2
=
$lastopd
;
$opd1
=
pop
@opds
;
}
else
{
$opd2
=
pop
@opds
;
$opd1
=
pop
@opds
;
}
if
(
@res
) {
@res
= (
"(($opd1 $op $opd2) ? "
,
@res
,
" : false)"
);
}
else
{
push
@res
,
"($opd1 $op $opd2 ? true:false)"
;
}
$lastopd
=
$opd1
;
}
join
""
,
@res
;
}
sub
rule_bit_shift {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'>>'
) {
push
@res
,
" >> $term"
}
elsif
(
$op
eq
'<<'
) {
push
@res
,
" << $term"
}
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_add {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'.'
) {
push
@res
,
" . $term"
}
if
(
$op
eq
'+'
) {
push
@res
,
" + $term"
}
if
(
$op
eq
'-'
) {
push
@res
,
" - $term"
}
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_mult {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
my
$op
=
shift
@{
$match
->{op}//=[]};
last
unless
$op
;
if
(
$op
eq
'*'
) {
push
@res
,
" * $term"
}
if
(
$op
eq
'/'
) {
push
@res
,
" / $term"
}
if
(
$op
eq
'%'
) {
push
@res
,
" % $term"
}
if
(
$op
eq
'x'
) {
push
@res
,
" x $term"
}
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_unary {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
$match
->{operand};
for
my
$op
(
reverse
@{
$match
->{op}//=[]}) {
last
unless
$op
;
if
(
$op
eq
'!'
) {
@res
= (
"("
,
@res
,
" ? false:true)"
) }
if
(
$op
eq
'-'
) {
@res
= (
"-("
,
@res
,
")"
) }
if
(
$op
eq
'~'
) {
@res
= (
"~("
,
@res
,
")"
) }
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_power {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
@res
;
push
@res
,
shift
@{
$match
->{operand}};
for
my
$term
(@{
$match
->{operand}}) {
push
@res
,
" ** $term"
;
}
join
""
,
grep
{
defined
}
@res
;
}
sub
rule_subscripting_var {
my
(
$self
,
%args
) =
@_
;
$self
->rule_subscripting_expr(
%args
);
}
sub
rule_subscripting_expr {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
$opd
=
$match
->{operand};
my
@ss
= @{
$match
->{subscript}//=[]};
return
$opd
unless
@ss
;
my
$res
;
for
my
$s
(
@ss
) {
$opd
=
$res
if
defined
(
$res
);
$res
=
qq!(do { my (\$v) = ($opd); my (\$s) = ($s); !
.
qq!if (ref(\$v) eq 'HASH') { \$v->{\$s} } !
.
qq!elsif (ref(\$v) eq 'ARRAY') { \$v->[\$s] } else { !
.
qq!die "Invalid subscript \$s for \$v" } })!
;
}
$res
;
}
sub
rule_array {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
"["
.
join
(
", "
, @{
$match
->{element} }) .
"]"
;
}
sub
rule_hash {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
"{"
.
join
(
", "
, @{
$match
->{pair} }).
"}"
;
}
sub
rule_undef {
"undef"
;
}
sub
rule_squotestr {
my
(
$self
,
%args
) =
@_
;
join
(
" . "
,
map
{
$self
->_quote(
$_
->{value}) }
@{
$self
->parse_squotestr(
$args
{match}{part}) });
}
sub
rule_dquotestr {
my
(
$self
,
%args
) =
@_
;
my
@tmp
=
map
{
$_
->{type} eq
'VAR'
?
$self
->rule_var(
match
=>{
var
=>
$_
->{value}}) :
$self
->_quote(
$_
->{value})
}
@{
$self
->parse_dquotestr(
$args
{match}{part}) };
if
(
@tmp
> 1) {
"("
.
join
(
" . "
,
@tmp
) .
")[0]"
;
}
else
{
$tmp
[0];
}
}
sub
rule_bool {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
if
(
$match
->{bool} eq
'true'
) {
"true"
}
else
{
"false"
}
}
sub
rule_num {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
if
(
$match
->{num} eq
'inf'
) {
'"Inf"'
}
elsif
(
$match
->{num} eq
'nan'
) {
'"NaN"'
}
else
{
$match
->{num}+0 }
}
sub
rule_var {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
if
(
$self
->hook_var) {
my
$res
=
$self
->hook_var->(
$match
->{var});
return
$res
if
defined
(
$res
);
}
return
"\$$match->{var}"
;
}
sub
rule_func {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
$f
=
$match
->{func_name};
my
$args
=
$match
->{args};
if
(
$self
->hook_func) {
my
$res
=
$self
->hook_func->(
$f
,
@$args
);
return
$res
if
defined
(
$res
);
}
my
$fmap
=
$self
->func_mapping->{
$f
};
$f
=
$fmap
if
$fmap
;
"$f("
.
join
(
", "
,
@$args
).
")"
;
}
sub
_map_grep_usort {
my
(
$self
,
$which
,
%args
) =
@_
;
my
$match
=
$args
{match};
my
$ary
=
$match
->{array};
my
$expr
=
$match
->{expr};
my
$perlop
=
$which
eq
'map'
?
'map'
:
$which
eq
'grep'
?
'grep'
:
'sort'
;
my
$uuid
=
$self
->new_marker(
'subexpr'
,
$expr
);
"[$perlop({ TODO-$uuid } \@{$ary})]"
;
}
sub
rule_func_map {
my
(
$self
,
%args
) =
@_
;
$self
->_map_grep_usort(
'map'
,
%args
);
}
sub
rule_func_grep {
my
(
$self
,
%args
) =
@_
;
$self
->_map_grep_usort(
'grep'
,
%args
);
}
sub
rule_func_usort {
my
(
$self
,
%args
) =
@_
;
$self
->_map_grep_usort(
'usort'
,
%args
);
}
sub
rule_parenthesis {
my
(
$self
,
%args
) =
@_
;
my
$match
=
$args
{match};
"("
.
$match
->{answer} .
")"
;
}
sub
expr_preprocess {}
sub
expr_postprocess {
my
(
$self
,
%args
) =
@_
;
my
$result
=
$args
{result};
$result
;
}
sub
_quote {
my
(
$self
,
$str
) =
@_
;
my
@c
;
for
my
$c
(
split
''
,
$str
) {
my
$o
=
ord
(
$c
);
if
(
$c
eq
'"'
) {
push
@c
,
'\\"'
}
elsif
(
$c
eq
"\\"
) {
push
@c
,
"\\\\"
}
elsif
(
$c
eq
'$'
) {
push
@c
,
"\\\$"
}
elsif
(
$c
eq
'@'
) {
push
@c
,
'\\@'
}
elsif
(
$o
>= 32 &&
$o
<= 127) {
push
@c
,
$c
}
elsif
(
$o
> 255) {
push
@c
,
sprintf
(
"\\x{%04x}"
,
$o
) }
else
{
push
@c
,
sprintf
(
"\\x%02x"
,
$o
) }
}
'"'
.
join
("
", @c) . '"
';
}
sub
compile {
my
(
$self
,
$expr
) =
@_
;
my
$res
= Language::Expr::Parser::parse_expr(
$expr
,
$self
);
for
my
$m
(@{
$self
->markers }) {
my
$type
=
$m
->[0];
next
unless
$type
eq
'subexpr'
;
my
$uuid
=
$m
->[1];
my
$subexpr
=
$m
->[2];
my
$subres
= Language::Expr::Parser::parse_expr(
$subexpr
,
$self
);
$res
=~ s/TODO-
$uuid
/
$subres
/g;
}
$self
->markers([]);
$res
;
}
sub
eval
{
my
(
$self
,
$expr
) =
@_
;
my
$res
=
eval
"package Language::Expr::Compiler::perl; no strict; "
.
$self
->compile(
$expr
);
die
if
$@;
$res
;
}
1;