line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Language::Expr::Compiler::js; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $DATE = '2016-06-29'; # DATE |
4
|
|
|
|
|
|
|
our $VERSION = '0.27'; # VERSION |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
26
|
use 5.010; |
|
1
|
|
|
|
|
3
|
|
7
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
20
|
|
8
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
3
|
use Role::Tiny::With; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
55
|
|
11
|
1
|
|
|
1
|
|
3
|
use parent 'Language::Expr::Compiler::Base'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
6
|
|
12
|
|
|
|
|
|
|
with 'Language::Expr::CompilerRole'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub rule_pair_simple { |
15
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
16
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
17
|
0
|
|
|
|
|
0
|
"'$match->{key}':$match->{value}"; |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub rule_pair_string { |
21
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
22
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
23
|
0
|
|
|
|
|
0
|
"$match->{key}:$match->{value}"; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub rule_or_xor { |
27
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
28
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
29
|
0
|
|
|
|
|
0
|
my @res; |
30
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
31
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
32
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
33
|
0
|
0
|
|
|
|
0
|
last unless $op; |
34
|
0
|
0
|
|
|
|
0
|
if ($op eq '||') { push @res, " || $term" } |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
35
|
0
|
|
|
|
|
0
|
elsif ($op eq '//') { @res = ("(function() { let _x = (", |
36
|
|
|
|
|
|
|
@res, "); return _x==null ? (", |
37
|
|
|
|
|
|
|
$term, ") : _x })()") } |
38
|
0
|
|
|
|
|
0
|
elsif ($op eq '^^') { @res = ("(function() { let _a = (", |
39
|
|
|
|
|
|
|
@res, "); let _b = ($term); ", |
40
|
|
|
|
|
|
|
"return _a&&!_b || !_a&&_b ? _a : _b })()") } |
41
|
|
|
|
|
|
|
} |
42
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub rule_and { |
46
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
47
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
48
|
0
|
|
|
|
|
0
|
my @res; |
49
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
50
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
51
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
52
|
0
|
0
|
|
|
|
0
|
last unless $op; |
53
|
0
|
0
|
|
|
|
0
|
if ($op eq '&&') { push @res, " && $term" } |
|
0
|
|
|
|
|
0
|
|
54
|
|
|
|
|
|
|
} |
55
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub rule_ternary { |
59
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
60
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
61
|
0
|
|
|
|
|
0
|
my $opd = $match->{operand}; |
62
|
0
|
|
|
|
|
0
|
"($opd->[0] ? $opd->[1] : $opd->[2])"; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub rule_bit_or_xor { |
66
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
67
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
68
|
0
|
|
|
|
|
0
|
my @res; |
69
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
70
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
71
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
72
|
0
|
0
|
|
|
|
0
|
last unless $op; |
73
|
0
|
0
|
|
|
|
0
|
if ($op eq '|') { push @res, " | $term" } |
|
0
|
0
|
|
|
|
0
|
|
74
|
0
|
|
|
|
|
0
|
elsif ($op eq '^') { push @res, " ^ $term" } |
75
|
|
|
|
|
|
|
} |
76
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub rule_bit_and { |
80
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
81
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
82
|
0
|
|
|
|
|
0
|
my @res; |
83
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
84
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
85
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
86
|
0
|
0
|
|
|
|
0
|
last unless $op; |
87
|
0
|
0
|
|
|
|
0
|
if ($op eq '&') { push @res, " & $term" } |
|
0
|
|
|
|
|
0
|
|
88
|
|
|
|
|
|
|
} |
89
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub rule_comparison3 { |
93
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
94
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
95
|
0
|
|
|
|
|
0
|
my @res; |
96
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
97
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
98
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
99
|
0
|
0
|
|
|
|
0
|
last unless $op; |
100
|
0
|
0
|
|
|
|
0
|
if ($op eq '<=>') { @res = ("(function() { let _a = (", |
|
0
|
0
|
|
|
|
0
|
|
101
|
|
|
|
|
|
|
@res, "); let _b = ($term); ", |
102
|
|
|
|
|
|
|
"return _a > _b ? 1 : (_a < _b ? -1 : 0) })()") } |
103
|
0
|
|
|
|
|
0
|
elsif ($op eq 'cmp') { @res = ("(function() { let _a = (", |
104
|
|
|
|
|
|
|
@res, ") + ''; let _b = ($term) + ''; ", |
105
|
|
|
|
|
|
|
"return _a > _b ? 1 : (_a < _b ? -1 : 0) })()") } |
106
|
|
|
|
|
|
|
} |
107
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _comparison1 { |
111
|
0
|
|
|
0
|
|
0
|
my ($opd1, $op, $opd2) = @_; |
112
|
0
|
0
|
|
|
|
0
|
if ($op eq 'eq') { return "(function() { let _a = ($opd1) + ''; let _b = ($opd2) + ''; return $opd1 == $opd2 })()" } |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
113
|
0
|
|
|
|
|
0
|
elsif ($op eq 'ne') { return "(function() { let _a = ($opd1) + ''; let _b = ($opd2) + ''; return $opd1 != $opd2 })()" } |
114
|
0
|
|
|
|
|
0
|
elsif ($op eq 'lt') { return "(function() { let _a = ($opd1) + ''; let _b = ($opd2) + ''; return $opd1 < $opd2 })()" } |
115
|
0
|
|
|
|
|
0
|
elsif ($op eq 'le') { return "(function() { let _a = ($opd1) + ''; let _b = ($opd2) + ''; return $opd1 <= $opd2 })()" } |
116
|
0
|
|
|
|
|
0
|
elsif ($op eq 'gt') { return "(function() { let _a = ($opd1) + ''; let _b = ($opd2) + ''; return $opd1 > $opd2 })()" } |
117
|
0
|
|
|
|
|
0
|
elsif ($op eq 'ge') { return "(function() { let _a = ($opd1) + ''; let _b = ($opd2) + ''; return $opd1 >= $opd2 })()" } |
118
|
0
|
|
|
|
|
0
|
else { return "($opd1 $op $opd2)" } |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub rule_comparison { |
122
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
123
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
124
|
0
|
|
|
|
|
0
|
my @opds; |
125
|
0
|
|
|
|
|
0
|
push @opds, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
126
|
0
|
0
|
|
|
|
0
|
return '' unless defined $opds[0]; |
127
|
0
|
|
|
|
|
0
|
my @ops; |
128
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
129
|
0
|
|
|
|
|
0
|
push @opds, $term; |
130
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
131
|
0
|
0
|
|
|
|
0
|
last unless $op; |
132
|
0
|
0
|
|
|
|
0
|
if ($op eq '==' ) { push @ops, '==' } |
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
133
|
0
|
|
|
|
|
0
|
elsif ($op eq '!=' ) { push @ops, '!=' } |
134
|
0
|
|
|
|
|
0
|
elsif ($op eq 'eq' ) { push @ops, 'eq' } |
135
|
0
|
|
|
|
|
0
|
elsif ($op eq 'ne' ) { push @ops, 'ne' } |
136
|
0
|
|
|
|
|
0
|
elsif ($op eq '<' ) { push @ops, '<' } |
137
|
0
|
|
|
|
|
0
|
elsif ($op eq '<=' ) { push @ops, '<=' } |
138
|
0
|
|
|
|
|
0
|
elsif ($op eq '>' ) { push @ops, '>' } |
139
|
0
|
|
|
|
|
0
|
elsif ($op eq '>=' ) { push @ops, '>=' } |
140
|
0
|
|
|
|
|
0
|
elsif ($op eq 'lt' ) { push @ops, 'lt' } |
141
|
0
|
|
|
|
|
0
|
elsif ($op eq 'le' ) { push @ops, 'le' } |
142
|
0
|
|
|
|
|
0
|
elsif ($op eq 'gt' ) { push @ops, 'gt' } |
143
|
0
|
|
|
|
|
0
|
elsif ($op eq 'ge' ) { push @ops, 'ge' } |
144
|
|
|
|
|
|
|
} |
145
|
0
|
0
|
|
|
|
0
|
return $opds[0] unless @ops; |
146
|
0
|
|
|
|
|
0
|
my @res; |
147
|
|
|
|
|
|
|
my $lastopd; |
148
|
0
|
|
|
|
|
0
|
my ($opd1, $opd2); |
149
|
0
|
|
|
|
|
0
|
while (@ops) { |
150
|
0
|
|
|
|
|
0
|
my $op = pop @ops; |
151
|
0
|
0
|
|
|
|
0
|
if (defined($lastopd)) { |
152
|
0
|
|
|
|
|
0
|
$opd2 = $lastopd; |
153
|
0
|
|
|
|
|
0
|
$opd1 = pop @opds; |
154
|
|
|
|
|
|
|
} else { |
155
|
0
|
|
|
|
|
0
|
$opd2 = pop @opds; |
156
|
0
|
|
|
|
|
0
|
$opd1 = pop @opds; |
157
|
|
|
|
|
|
|
} |
158
|
0
|
0
|
|
|
|
0
|
if (@res) { |
159
|
0
|
|
|
|
|
0
|
@res = ("("._comparison1($opd1, $op, $opd2)." ? ", @res, " : false)"); |
160
|
|
|
|
|
|
|
} else { |
161
|
0
|
|
|
|
|
0
|
push @res, _comparison1($opd1, $op, $opd2); |
162
|
|
|
|
|
|
|
} |
163
|
0
|
|
|
|
|
0
|
$lastopd = $opd1; |
164
|
|
|
|
|
|
|
} |
165
|
0
|
|
|
|
|
0
|
join "", @res; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub rule_bit_shift { |
169
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
170
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
171
|
0
|
|
|
|
|
0
|
my @res; |
172
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
173
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
174
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
175
|
0
|
0
|
|
|
|
0
|
last unless $op; |
176
|
0
|
0
|
|
|
|
0
|
if ($op eq '>>') { push @res, " >> $term" } |
|
0
|
0
|
|
|
|
0
|
|
177
|
0
|
|
|
|
|
0
|
elsif ($op eq '<<') { push @res, " << $term" } |
178
|
|
|
|
|
|
|
} |
179
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub rule_add { |
183
|
1
|
|
|
1
|
0
|
3
|
my ($self, %args) = @_; |
184
|
1
|
|
|
|
|
1
|
my $match = $args{match}; |
185
|
1
|
|
|
|
|
2
|
my @res; |
186
|
1
|
|
|
|
|
1
|
push @res, shift @{$match->{operand}}; |
|
1
|
|
|
|
|
2
|
|
187
|
1
|
|
|
|
|
2
|
for my $term (@{$match->{operand}}) { |
|
1
|
|
|
|
|
2
|
|
188
|
1
|
|
50
|
|
|
1
|
my $op = shift @{$match->{op}//=[]}; |
|
1
|
|
|
|
|
4
|
|
189
|
1
|
50
|
|
|
|
2
|
last unless $op; |
190
|
1
|
50
|
|
|
|
4
|
if ($op eq '.') { @res = ("'' + ", @res, " + $term") } |
|
1
|
|
|
|
|
6
|
|
191
|
1
|
50
|
|
|
|
3
|
if ($op eq '+') { push @res, " + $term" } |
|
0
|
|
|
|
|
0
|
|
192
|
1
|
50
|
|
|
|
2
|
if ($op eq '-') { push @res, " - $term" } |
|
0
|
|
|
|
|
0
|
|
193
|
|
|
|
|
|
|
} |
194
|
1
|
|
|
|
|
2
|
join "", grep {defined} @res; |
|
3
|
|
|
|
|
25
|
|
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub rule_mult { |
198
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
199
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
200
|
0
|
|
|
|
|
0
|
my @res; |
201
|
0
|
|
|
|
|
0
|
push @res, shift @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
202
|
0
|
|
|
|
|
0
|
for my $term (@{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
203
|
0
|
|
0
|
|
|
0
|
my $op = shift @{$match->{op}//=[]}; |
|
0
|
|
|
|
|
0
|
|
204
|
0
|
0
|
|
|
|
0
|
last unless $op; |
205
|
0
|
0
|
|
|
|
0
|
if ($op eq '*') { push @res, " * $term" } |
|
0
|
|
|
|
|
0
|
|
206
|
0
|
0
|
|
|
|
0
|
if ($op eq '/') { push @res, " / $term" } |
|
0
|
|
|
|
|
0
|
|
207
|
0
|
0
|
|
|
|
0
|
if ($op eq '%') { push @res, " % $term" } |
|
0
|
|
|
|
|
0
|
|
208
|
0
|
0
|
|
|
|
0
|
if ($op eq 'x') { @res = ("(new Array(1 + $term).join(", @res, "))") } |
|
0
|
|
|
|
|
0
|
|
209
|
|
|
|
|
|
|
} |
210
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub rule_unary { |
214
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
215
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
216
|
0
|
|
|
|
|
0
|
my @res; |
217
|
0
|
|
|
|
|
0
|
push @res, $match->{operand}; |
218
|
0
|
|
0
|
|
|
0
|
for my $op (reverse @{$match->{op}//=[]}) { |
|
0
|
|
|
|
|
0
|
|
219
|
0
|
0
|
|
|
|
0
|
last unless $op; |
220
|
|
|
|
|
|
|
# use paren because --x or ++x is interpreted as pre-decrement/increment |
221
|
0
|
0
|
|
|
|
0
|
if ($op eq '!') { @res = ("!(", @res, ")") } |
|
0
|
|
|
|
|
0
|
|
222
|
0
|
0
|
|
|
|
0
|
if ($op eq '-') { @res = ("-(", @res, ")") } |
|
0
|
|
|
|
|
0
|
|
223
|
0
|
0
|
|
|
|
0
|
if ($op eq '~') { @res = ("~(", @res, ")") } |
|
0
|
|
|
|
|
0
|
|
224
|
|
|
|
|
|
|
} |
225
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub rule_power { |
229
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
230
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
231
|
0
|
|
|
|
|
0
|
my @res; |
232
|
0
|
|
|
|
|
0
|
push @res, pop @{$match->{operand}}; |
|
0
|
|
|
|
|
0
|
|
233
|
0
|
|
|
|
|
0
|
for my $term (reverse @{$match->{operand}}) { |
|
0
|
|
|
|
|
0
|
|
234
|
0
|
|
|
|
|
0
|
@res = ("Math.pow($term, ", @res, ")"); |
235
|
|
|
|
|
|
|
} |
236
|
0
|
|
|
|
|
0
|
join "", grep {defined} @res; |
|
0
|
|
|
|
|
0
|
|
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub rule_subscripting_var { |
240
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
241
|
0
|
|
|
|
|
0
|
$self->rule_subscripting_expr(%args); |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub rule_subscripting_expr { |
245
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
246
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
247
|
0
|
|
|
|
|
0
|
my $opd = $match->{operand}; |
248
|
0
|
|
0
|
|
|
0
|
my @ss = @{$match->{subscript}//=[]}; |
|
0
|
|
|
|
|
0
|
|
249
|
0
|
0
|
|
|
|
0
|
return $opd unless @ss; |
250
|
0
|
|
|
|
|
0
|
my $res; |
251
|
0
|
|
|
|
|
0
|
for my $s (@ss) { |
252
|
0
|
0
|
|
|
|
0
|
$opd = $res if defined($res); |
253
|
0
|
|
|
|
|
0
|
$res = $opd . "[$s]"; |
254
|
|
|
|
|
|
|
} |
255
|
0
|
|
|
|
|
0
|
$res; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub rule_array { |
259
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
260
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
261
|
0
|
|
|
|
|
0
|
"[" . join(", ", @{ $match->{element} }) . "]"; |
|
0
|
|
|
|
|
0
|
|
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub rule_hash { |
265
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
266
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
267
|
0
|
|
|
|
|
0
|
"{" . join(", ", @{ $match->{pair} }). "}"; |
|
0
|
|
|
|
|
0
|
|
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub rule_undef { |
271
|
0
|
|
|
0
|
0
|
0
|
"null"; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub rule_squotestr { |
275
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
276
|
|
|
|
|
|
|
join(" + ", |
277
|
0
|
|
|
|
|
0
|
map { $self->_quote($_->{value}) } |
278
|
0
|
|
|
|
|
0
|
@{ $self->parse_squotestr($args{match}{part}) }); |
|
0
|
|
|
|
|
0
|
|
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub rule_dquotestr { |
282
|
2
|
|
|
2
|
0
|
4
|
my ($self, %args) = @_; |
283
|
|
|
|
|
|
|
my @tmp = |
284
|
|
|
|
|
|
|
map { $_->{type} eq 'VAR' ? |
285
|
|
|
|
|
|
|
$self->rule_var(match=>{var=>$_->{value}}) : |
286
|
|
|
|
|
|
|
$self->_quote($_->{value}) |
287
|
2
|
50
|
|
|
|
8
|
} |
288
|
2
|
|
|
|
|
2
|
@{ $self->parse_dquotestr($args{match}{part}) }; |
|
2
|
|
|
|
|
7
|
|
289
|
2
|
50
|
|
|
|
6
|
if (@tmp > 1) { |
290
|
0
|
|
|
|
|
0
|
"(". join(" + ", @tmp) . ")[0]"; |
291
|
|
|
|
|
|
|
} else { |
292
|
2
|
|
|
|
|
47
|
$tmp[0]; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub rule_bool { |
297
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
298
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
299
|
0
|
0
|
|
|
|
0
|
if ($match->{bool} eq 'true') { "true" } else { "false" } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub rule_num { |
303
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
304
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
305
|
0
|
0
|
|
|
|
0
|
if ($match->{num} eq 'inf') { 'Infinity' } |
|
0
|
0
|
|
|
|
0
|
|
306
|
0
|
|
|
|
|
0
|
elsif ($match->{num} eq 'nan') { 'NaN' } |
307
|
0
|
|
|
|
|
0
|
else { $match->{num}+0 } |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub rule_var { |
311
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
312
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
313
|
0
|
0
|
|
|
|
0
|
if ($self->hook_var) { |
314
|
0
|
|
|
|
|
0
|
my $res = $self->hook_var->($match->{var}); |
315
|
0
|
0
|
|
|
|
0
|
return $res if defined($res); |
316
|
|
|
|
|
|
|
} |
317
|
0
|
|
|
|
|
0
|
return "$match->{var}"; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub rule_func { |
321
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
322
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
323
|
0
|
|
|
|
|
0
|
my $f = $match->{func_name}; |
324
|
0
|
|
|
|
|
0
|
my $args = $match->{args}; |
325
|
0
|
0
|
|
|
|
0
|
if ($self->hook_func) { |
326
|
0
|
|
|
|
|
0
|
my $res = $self->hook_func->($f, @$args); |
327
|
0
|
0
|
|
|
|
0
|
return $res if defined($res); |
328
|
|
|
|
|
|
|
} |
329
|
0
|
|
|
|
|
0
|
my $fmap = $self->func_mapping->{$f}; |
330
|
0
|
0
|
|
|
|
0
|
$f = $fmap if $fmap; |
331
|
0
|
|
|
|
|
0
|
my $fc = substr($f, 0, 1); |
332
|
0
|
0
|
|
|
|
0
|
if ($fc eq '.') { |
|
|
0
|
|
|
|
|
|
333
|
0
|
|
|
|
|
0
|
my $invoc = shift @$args; |
334
|
0
|
|
|
|
|
0
|
return "($invoc)$f(".join(", ", @$args).")"; |
335
|
|
|
|
|
|
|
} elsif ($fc eq ':') { |
336
|
0
|
|
|
|
|
0
|
my $invoc = shift @$args; |
337
|
0
|
|
|
|
|
0
|
my $prop = substr($f, 1, length($f)-1); |
338
|
0
|
|
|
|
|
0
|
return "($invoc).$prop"; |
339
|
|
|
|
|
|
|
} else { |
340
|
0
|
|
|
|
|
0
|
return "$f(".join(", ", @$args).")"; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub _map_grep_usort { |
345
|
0
|
|
|
0
|
|
0
|
my ($self, $which, %args) = @_; |
346
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
347
|
0
|
|
|
|
|
0
|
my $ary = $match->{array}; |
348
|
0
|
|
|
|
|
0
|
my $expr = $match->{expr}; |
349
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
0
|
my $uuid = $self->new_marker('subexpr', $expr); |
351
|
0
|
0
|
|
|
|
0
|
if ($which eq 'map') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
352
|
0
|
|
|
|
|
0
|
return "($ary).map(function(_){ return (TODO-$uuid); })"; |
353
|
|
|
|
|
|
|
} elsif ($which eq 'grep') { |
354
|
0
|
|
|
|
|
0
|
return "($ary).filter(function(_){ return (TODO-$uuid); })"; |
355
|
|
|
|
|
|
|
} elsif ($which eq 'usort') { |
356
|
0
|
|
|
|
|
0
|
return "($ary).sort(function(a, b){ return (TODO-$uuid); })"; |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub rule_func_map { |
361
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
362
|
0
|
|
|
|
|
0
|
$self->_map_grep_usort('map', %args); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub rule_func_grep { |
366
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
367
|
0
|
|
|
|
|
0
|
$self->_map_grep_usort('grep', %args); |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub rule_func_usort { |
371
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
372
|
0
|
|
|
|
|
0
|
$self->_map_grep_usort('usort', %args); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub rule_parenthesis { |
376
|
0
|
|
|
0
|
0
|
0
|
my ($self, %args) = @_; |
377
|
0
|
|
|
|
|
0
|
my $match = $args{match}; |
378
|
0
|
|
|
|
|
0
|
"(" . $match->{answer} . ")"; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
1
|
0
|
|
sub expr_preprocess {} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub expr_postprocess { |
384
|
1
|
|
|
1
|
0
|
3
|
my ($self, %args) = @_; |
385
|
1
|
|
|
|
|
1
|
my $result = $args{result}; |
386
|
1
|
|
|
|
|
3
|
$result; |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# can't use regex here (perl segfaults), at least in 5.10.1, because |
390
|
|
|
|
|
|
|
# we are in one big re::gr regex. |
391
|
|
|
|
|
|
|
sub _quote { |
392
|
2
|
|
|
2
|
|
2
|
my ($self, $str) = @_; |
393
|
2
|
|
|
|
|
1
|
my @c; |
394
|
2
|
|
|
|
|
5
|
for my $c (split '', $str) { |
395
|
2
|
|
|
|
|
2
|
my $o = ord($c); |
396
|
2
|
50
|
33
|
|
|
12
|
if ($c eq '"') { push @c, '\\"' } |
|
0
|
50
|
|
|
|
0
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
397
|
0
|
|
|
|
|
0
|
elsif ($c eq "\\") { push @c, "\\\\" } |
398
|
2
|
|
|
|
|
3
|
elsif ($o >= 32 && $o <= 127) { push @c, $c } |
399
|
0
|
|
|
|
|
0
|
elsif ($o > 255) { push @c, sprintf("\\u%04x", $o) } |
400
|
0
|
|
|
|
|
0
|
else { push @c, sprintf("\\x%02x", $o) } |
401
|
|
|
|
|
|
|
} |
402
|
2
|
|
|
|
|
6
|
'"' . join("", @c) . '"'; |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub compile { |
406
|
1
|
|
|
1
|
1
|
49
|
require Language::Expr::Parser; |
407
|
|
|
|
|
|
|
|
408
|
1
|
|
|
|
|
2
|
my ($self, $expr) = @_; |
409
|
1
|
|
|
|
|
3
|
my $res = Language::Expr::Parser::parse_expr($expr, $self); |
410
|
1
|
|
|
|
|
1
|
for my $m (@{ $self->markers }) { |
|
1
|
|
|
|
|
8
|
|
411
|
0
|
|
|
|
|
0
|
my $type = $m->[0]; |
412
|
0
|
0
|
|
|
|
0
|
next unless $type eq 'subexpr'; |
413
|
0
|
|
|
|
|
0
|
my $uuid = $m->[1]; |
414
|
0
|
|
|
|
|
0
|
my $subexpr = $m->[2]; |
415
|
0
|
|
|
|
|
0
|
my $subres = Language::Expr::Parser::parse_expr($subexpr, $self); |
416
|
0
|
|
|
|
|
0
|
$res =~ s/TODO-$uuid/$subres/g; |
417
|
|
|
|
|
|
|
} |
418
|
1
|
|
|
|
|
2
|
$self->markers([]); |
419
|
1
|
|
|
|
|
10
|
$res; |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
1; |
423
|
|
|
|
|
|
|
# ABSTRACT: Compile Language::Expr expression to JavaScript |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
__END__ |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=pod |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=encoding UTF-8 |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=head1 NAME |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
Language::Expr::Compiler::js - Compile Language::Expr expression to JavaScript |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=head1 VERSION |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
This document describes version 0.27 of Language::Expr::Compiler::js (from Perl distribution Language-Expr), released on 2016-06-29. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head1 SYNOPSIS |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
use Language::Expr::Compiler::js; |
442
|
|
|
|
|
|
|
my $jsc = Language::Expr::Compiler::js->new; |
443
|
|
|
|
|
|
|
print $jsc->compile('map({$_**2}, [1, 2, 3])'); # prints: [1, 2, 3].map(function(_){ Math.pow(_, 2) }) |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# map Expr function to JS function/method/property |
446
|
|
|
|
|
|
|
$jsc->func_mapping->{ceil} = 'Math.ceil'; |
447
|
|
|
|
|
|
|
$jsc->func_mapping->{uc} = '.toUpperCase'; |
448
|
|
|
|
|
|
|
$jsc->func_mapping->{length} = ':length'; |
449
|
|
|
|
|
|
|
print $jsc->compile(q{uc("party like it's ") . ceil(1998.9)}); # prints: "party like it's ".toUpperCase() + Math.ceil(1998.9) |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head1 DESCRIPTION |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
Compiles Language::Expr expression to JavaScript code. Some notes: |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=over 4 |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=item * JavaScript version |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
This compiler emits JavaScript 1.8.1 code (it uses several Array methods like |
460
|
|
|
|
|
|
|
map() and filter() [supported since JavaScript 1.6], 'let' lexical variables |
461
|
|
|
|
|
|
|
[supported since JavaScript 1.7] and native JSON [supported since JavaScript |
462
|
|
|
|
|
|
|
1.8.1]). |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
To test emitted JavaScript code, we use Node.js. |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=item * JavaScript-ness |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
The emitted JS code will follow JavaScript's weak typing and coercion rules, |
469
|
|
|
|
|
|
|
e.g. Expr '1+"2"' will simply be translated to JavaScript '1+"2"' and will |
470
|
|
|
|
|
|
|
result in "12". |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=item * Variables by default simply use JavaScript variables. |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
E.g. $a becomes a, and so on. Be careful not to make variables which are invalid |
475
|
|
|
|
|
|
|
in JavaScript, e.g. $.. or ${foo/bar}. |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
You can customize this behaviour by subclassing rule_var() or by providing a |
478
|
|
|
|
|
|
|
hook_var() (see documentation in L<Language::Expr::Compiler::Base>). |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=item * Functions by default simply use Javascript functions. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Except those mentioned in B<func_mapping> (e.g. rand() becomes Math.rand() if |
483
|
|
|
|
|
|
|
func_mapping->{rand} is 'Math.rand'). You can also map to JavaScript method |
484
|
|
|
|
|
|
|
(using '.meth' syntax) and property (using ':prop' syntax). |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
You can customize this behaviour by subclassing rule_func() or by providing a |
487
|
|
|
|
|
|
|
hook_func() (see documentation in L<Language::Expr::Compiler::Base>). |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=back |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=for Pod::Coverage ^(rule|expr)_.+ |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=head1 METHODS |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=head2 compile($expr) => $js_code |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Convert Language::Expr expression into JavaScript code. Dies if there is syntax |
498
|
|
|
|
|
|
|
error in expression. |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=head1 HOMEPAGE |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Please visit the project's homepage at L<https://metacpan.org/release/Language-Expr>. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head1 SOURCE |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Source repository is at L<https://github.com/perlancar/perl-Language-Expr>. |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=head1 BUGS |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Language-Expr> |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
513
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
514
|
|
|
|
|
|
|
feature. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head1 AUTHOR |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
perlancar <perlancar@cpan.org> |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
This software is copyright (c) 2016 by perlancar@cpan.org. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
525
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=cut |