File Coverage

blib/lib/Language/P/Toy/Opcodes.pm
Criterion Covered Total %
statement 356 645 55.1
branch 50 110 45.4
condition 4 6 66.6
subroutine 63 100 63.0
pod 0 86 0.0
total 473 947 49.9


line stmt bran cond sub pod time code
1             package Language::P::Toy::Opcodes;
2              
3 30     30   12398 use strict;
  30         63  
  30         1275  
4 30     30   171 use warnings;
  30         70  
  30         1227  
5 30     30   167 use Exporter 'import';
  30         66  
  30         926  
6              
7 30     30   16460 use Language::P::Toy::Value::StringNumber;
  30         92  
  30         338  
8 30     30   18237 use Language::P::Toy::Value::Reference;
  30         88  
  30         311  
9 30     30   872 use Language::P::Toy::Value::Array;
  30         67  
  30         240  
10 30     30   29010 use Language::P::Toy::Value::List;
  30         88  
  30         315  
11 30     30   960 use Language::P::ParseTree qw(:all);
  30         70  
  30         45041  
12              
13 30     30   22355 use Language::P::Toy::Opcodes::Regex qw(:opcodes);
  30         715  
  30         186748  
14              
15             our @EXPORT_OK = qw(o);
16              
17             sub o {
18 2607     2607 0 10514 my( $name, %args ) = @_;
19              
20 2607 50       15197 Carp::confess "Invalid opcode '$name'"
21             unless defined $Language::P::Toy::Opcodes::{"o_$name"};
22 2607         2702 my $fun = *{$Language::P::Toy::Opcodes::{"o_$name"}}{CODE};
  2607         8748  
23 2607 50       6184 Carp::confess "Invalid opcode '$name'"
24             unless defined $fun;
25              
26 2607         15933 return { %args,
27             function => $fun,
28             op_name => $name,
29             };
30             }
31              
32             sub _context {
33 5706     5706   7919 my( $op, $runtime ) = @_;
34 5706 100       10899 my $cxt = $op ? $op->{context} : 0;
35              
36 5706 100 66     26996 return $cxt if $cxt && $cxt != CXT_CALLER;
37 3039         8404 return $runtime->{_stack}[$runtime->{_frame} - 2][2];
38             }
39              
40             sub o_noop {
41 0     0 0 0 my( $op, $runtime, $pc ) = @_;
42              
43 0         0 return $pc + 1;
44             }
45              
46             sub o_dup {
47 10     10 0 17 my( $op, $runtime, $pc ) = @_;
48 10         20 my $value = $runtime->{_stack}->[-1];
49              
50 10         13 push @{$runtime->{_stack}}, $value;
  10         18  
51              
52 10         26 return $pc + 1;
53             }
54              
55             sub o_swap {
56 2740     2740 0 4057 my( $op, $runtime, $pc ) = @_;
57 2740         4901 my $t = $runtime->{_stack}->[-1];
58              
59 2740         4634 $runtime->{_stack}->[-1] = $runtime->{_stack}->[-2];
60 2740         3572 $runtime->{_stack}->[-2] = $t;
61              
62 2740         6412 return $pc + 1;
63             }
64              
65             sub o_pop {
66 2854     2854 0 4072 my( $op, $runtime, $pc ) = @_;
67              
68 2854         2813 pop @{$runtime->{_stack}};
  2854         5426  
69              
70 2854         9264 return $pc + 1;
71             }
72              
73             sub o_print {
74 111     111 0 181 my( $op, $runtime, $pc ) = @_;
75 111         141 my $args = pop @{$runtime->{_stack}};
  111         217  
76              
77 111         421 my $fh = $args->get_item( 0 );
78 111         402 for( my $iter = $args->iterator_from( 1 ); $iter->next; ) {
79 113         339 $fh->write( $iter->item );
80             }
81              
82             # HACK
83 111         178 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { integer => 1 } );
  111         727  
84              
85 111         1937 return $pc + 1;
86             }
87              
88             sub o_constant {
89 7614     7614 0 9737 my( $op, $runtime, $pc ) = @_;
90 7614         9368 push @{$runtime->{_stack}}, $op->{value};
  7614         15226  
91              
92 7614         16849 return $pc + 1;
93             }
94              
95             sub o_fresh_string {
96 21     21 0 33 my( $op, $runtime, $pc ) = @_;
97 21         26 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new
  21         122  
98             ( { string => $op->{value} } );
99              
100 21         252 return $pc + 1;
101             }
102              
103             sub o_stringify {
104 0     0 0 0 my( $op, $runtime, $pc ) = @_;
105 0         0 my $v = pop @{$runtime->{_stack}};
  0         0  
106              
107 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { string => $v->as_string } );
  0         0  
108              
109 0         0 return $pc + 1;
110             }
111              
112             sub _make_binary_op {
113 180     180   311 my( $op ) = @_;
114              
115 180     1470 0 26944 eval sprintf <<'EOT',
  1470     6 0 2398  
  1470     0 0 1742  
  1470     0 0 3694  
  1470     6 0 1895  
  1470     2802 0 2603  
  1470         4750  
  1470         2316  
  1470         16531  
  1470         40214  
  6         13  
  6         8  
  6         15  
  6         8  
  6         11  
  6         21  
  6         12  
  6         33  
  6         164  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  6         14  
  6         10  
  6         15  
  6         12  
  6         13  
  6         24  
  6         15  
  6         36  
  6         434  
  2802         5188  
  2802         2716  
  2802         5273  
  2802         3265  
  2802         4222  
  2802         7638  
  2802         9617  
  2802         13963  
  2802         74125  
116             sub %s {
117             my( $op, $runtime, $pc ) = @_;
118             my $vr = pop @{$runtime->{_stack}};
119             my $vl = pop @{$runtime->{_stack}};
120             my $r = $vl->%s %s $vr->%s;
121              
122             push @{$runtime->{_stack}},
123             Language::P::Toy::Value::StringNumber->new( { %s => $r } );
124              
125             return $pc + 1;
126             }
127             EOT
128             $op->{name}, $op->{convert}, $op->{operator}, $op->{convert},
129             $op->{new_type};
130 180 50       1085 die $@ if $@;
131             }
132              
133             sub _make_binary_op_assign {
134 30     30   79 my( $op ) = @_;
135              
136 30     67 0 3697 eval sprintf <<'EOT',
  67         115  
  67         76  
  67         128  
  67         118  
  67         172  
  67         141  
  67         169  
137             sub %s {
138             my( $op, $runtime, $pc ) = @_;
139             my $vr = pop @{$runtime->{_stack}};
140             my $vl = $runtime->{_stack}[-1];
141             my $r = $vl->%s %s $vr->%s;
142              
143             $vl->{%s} = $r;
144              
145             return $pc + 1;
146             }
147             EOT
148             $op->{name}, $op->{convert}, $op->{operator}, $op->{convert},
149             $op->{new_type};
150 30 50       201 die $@ if $@;
151             }
152              
153             _make_binary_op( $_ ) foreach
154             ( { name => 'o_add',
155             convert => 'as_float',
156             operator => '+',
157             new_type => 'float',
158             },
159             { name => 'o_subtract',
160             convert => 'as_float',
161             operator => '-',
162             new_type => 'float',
163             },
164             { name => 'o_multiply',
165             convert => 'as_float',
166             operator => '*',
167             new_type => 'float',
168             },
169             { name => 'o_divide',
170             convert => 'as_float',
171             operator => '/',
172             new_type => 'float',
173             },
174             { name => 'o_modulus',
175             convert => 'as_integer',
176             operator => '%',
177             new_type => 'integer',
178             },
179             { name => 'o_concat',
180             convert => 'as_string',
181             operator => '.',
182             new_type => 'string',
183             },
184             );
185              
186             _make_binary_op_assign( $_ ) foreach
187             ( { name => 'o_concat_assign',
188             convert => 'as_string',
189             operator => '.',
190             new_type => 'string',
191             },
192             );
193              
194             sub o_make_list {
195 8460     8460 0 11158 my( $op, $runtime, $pc ) = @_;
196 8460         12582 my $st = $runtime->{_stack};
197              
198             # create the list
199 8460         24717 my $list = Language::P::Toy::Value::List->new;
200 8460 100       18563 if( $op->{count} ) {
201 8428         28488 for( my $j = $#$st - $op->{count} + 1; $j <= $#$st; ++$j ) {
202 8560         29420 $list->push( $st->[$j] );
203             }
204             # clear the stack
205 8428         19987 $#$st -= $op->{count} - 1;
206 8428         13715 $st->[-1] = $list;
207             } else {
208 32         56 push @$st, $list;
209             }
210              
211 8460         19633 return $pc + 1;
212             }
213              
214             sub o_end {
215 21     21 0 58 my( $op, $runtime, $pc ) = @_;
216              
217 21         63 return -1;
218             }
219              
220             sub o_want {
221 14     14 0 16 my( $op, $runtime, $pc ) = @_;
222 14         28 my $cxt = _context( undef, $runtime );
223 14         15 my $v;
224              
225 14 100       34 if( $cxt == CXT_VOID ) {
    100          
    50          
226 6         20 $v = Language::P::Toy::Value::StringNumber->new;
227             } elsif( $cxt == CXT_SCALAR ) {
228 5         21 $v = Language::P::Toy::Value::StringNumber->new( { string => '' } );
229             } elsif( $cxt == CXT_LIST ) {
230 3         13 $v = Language::P::Toy::Value::StringNumber->new( { integer => 1 } );
231             } else {
232 0         0 die "Unknow context $cxt";
233             }
234 14         121 push @{$runtime->{_stack}}, $v;
  14         27  
235              
236 14         32 return $pc + 1;
237             }
238              
239             sub o_call {
240 2846     2846 0 3974 my( $op, $runtime, $pc ) = @_;
241 2846         3040 my $sub = pop @{$runtime->{_stack}};
  2846         5154  
242              
243 2846         5753 $sub->call( $runtime, $pc, _context( $op, $runtime ) );
244              
245 2846         7105 return 0;
246             }
247              
248             my $empty_list = Language::P::Toy::Value::List->new;
249              
250             sub o_return {
251 2846     2846 0 3891 my( $op, $runtime, $pc ) = @_;
252 2846         5331 my $cxt = _context( undef, $runtime );
253 2846         4603 my $rv = $runtime->{_stack}->[-1];
254 2846         7351 my $rpc = $runtime->call_return;
255              
256 2846 100       5522 if( $cxt == CXT_SCALAR ) {
    100          
    50          
257 2829 100       6979 if( $rv->get_count > 0 ) {
258 2827         3181 push @{$runtime->{_stack}}, $rv->get_item( $rv->get_count - 1 )
  2827         7942  
259             ->as_scalar;
260             } else {
261 2         12 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new;
  2         8  
262             }
263             } elsif( $cxt == CXT_LIST ) {
264 4         5 push @{$runtime->{_stack}}, $rv;
  4         8  
265             } elsif( $cxt == CXT_VOID ) {
266             # it is easier to generate code if a subroutine
267             # always returns a value (even if a dummy one)
268 13         20 push @{$runtime->{_stack}}, $empty_list;
  13         53  
269             }
270              
271 2846         10554 return $rpc + 1;
272             }
273              
274             sub o_glob {
275 3064     3064 0 4055 my( $op, $runtime, $pc ) = @_;
276 3064         8587 my $value = $runtime->symbol_table->get_symbol( $op->{name}, '*',
277             $op->{create} );
278              
279 3064         4106 push @{$runtime->{_stack}}, $value;
  3064         5932  
280              
281 3064         7390 return $pc + 1;
282             }
283              
284             sub o_lexical {
285 10572     10572 0 13731 my( $op, $runtime, $pc ) = @_;
286 10572   66     38638 my $value = $runtime->{_stack}->[$runtime->{_frame} - 3 - $op->{index}]
287             ||= Language::P::Toy::Value::StringNumber->new;
288              
289 10572         12392 push @{$runtime->{_stack}}, $value;
  10572         18937  
290              
291 10572         25799 return $pc + 1;
292             }
293              
294             sub o_lexical_set {
295 10     10 0 16 my( $op, $runtime, $pc ) = @_;
296 10         12 my $value = pop @{$runtime->{_stack}};
  10         22  
297              
298 10         28 $runtime->{_stack}->[$runtime->{_frame} - 3 - $op->{index}] = $value;
299              
300 10         27 return $pc + 1;
301             }
302              
303             sub o_lexical_clear {
304 2640     2640 0 3812 my( $op, $runtime, $pc ) = @_;
305              
306 2640         6292 $runtime->{_stack}->[$runtime->{_frame} - 3 - $op->{index}] = undef;
307              
308 2640         8141 return $pc + 1;
309             }
310              
311             sub o_lexical_pad {
312 22     22 0 31 my( $op, $runtime, $pc ) = @_;
313 22         41 my $pad = $runtime->{_stack}->[$runtime->{_frame} - 1];
314              
315 22         24 push @{$runtime->{_stack}}, $pad->values->[$op->{index}];
  22         73  
316              
317 22         152 return $pc + 1;
318             }
319              
320             sub o_lexical_pad_clear {
321 7     7 0 11 my( $op, $runtime, $pc ) = @_;
322 7         15 my $pad = $runtime->{_stack}->[$runtime->{_frame} - 1];
323              
324 7         20 $pad->values->[$op->{index}] = undef;
325              
326 7         37 return $pc + 1;
327             }
328              
329             sub o_parameter_index {
330 356     356 0 386 my( $op, $runtime, $pc ) = @_;
331 356         997 my $value = $runtime->{_stack}->[$runtime->{_frame} - 3]->get_item( $op->{index} );
332              
333 356         345 push @{$runtime->{_stack}}, $value;
  356         668  
334              
335 356         777 return $pc + 1;
336             }
337              
338             sub o_jump {
339 4240     4240 0 5859 my( $op, $runtime, $pc ) = @_;
340              
341 4240         8965 return $op->{to};
342             }
343              
344             sub o_jump_if_eq_immed {
345 177     177 0 211 my( $op, $runtime, $pc ) = @_;
346 177         154 my $v1 = pop @{$runtime->{_stack}};
  177         236  
347              
348 177 100       487 return $v1 == $op->{value} ? $op->{to} : $pc + 1;
349             }
350              
351             sub o_jump_if_false {
352 0     0 0 0 my( $op, $runtime, $pc ) = @_;
353 0         0 my $v1 = pop @{$runtime->{_stack}};
  0         0  
354              
355 0 0       0 return !$v1->as_boolean_int ? $op->{to} : $pc + 1;
356             }
357              
358             sub o_jump_if_true {
359 20     20 0 31 my( $op, $runtime, $pc ) = @_;
360 20         22 my $v1 = pop @{$runtime->{_stack}};
  20         37  
361              
362 20 100       60 return $v1->as_boolean_int ? $op->{to} : $pc + 1;
363             }
364              
365             sub o_jump_if_null {
366 9     9 0 16 my( $op, $runtime, $pc ) = @_;
367 9         12 my $v1 = pop @{$runtime->{_stack}};
  9         18  
368              
369 9 100       50 return !defined $v1 ? $op->{to} : $pc + 1;
370             }
371              
372             sub _make_cond_jump {
373 360     360   843 my( $op ) = @_;
374              
375 360 100   48 0 43358 eval sprintf <<'EOT',
  48 100   2634 0 106  
  48 100   28 0 75  
  48 100   7 0 115  
  48 100   49 0 68  
  48 0   0 0 94  
  48 0   0 0 168  
  2634 0   0 0 4731  
  2634 0   0 0 2731  
  2634 0   0 0 5423  
  2634 50   2 0 3097  
  2634 0   0 0 7320  
  2634         7341  
  28         45  
  28         35  
  28         58  
  28         30  
  28         46  
  28         82  
  7         14  
  7         9  
  7         19  
  7         10  
  7         13  
  7         22  
  49         84  
  49         67  
  49         97  
  49         64  
  49         78  
  49         162  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         5  
  2         5  
  2         5  
  2         3  
  2         5  
  2         7  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
376             sub %s {
377             my( $op, $runtime, $pc ) = @_;
378             my $vr = pop @{$runtime->{_stack}};
379             my $vl = pop @{$runtime->{_stack}};
380              
381             return $vl->%s %s $vr->%s ? $op->{to} : $pc + 1;
382             }
383             EOT
384             $op->{name}, $op->{convert}, $op->{operator}, $op->{convert};
385             }
386              
387             _make_cond_jump( $_ ) foreach
388             ( { name => 'o_jump_if_i_lt',
389             convert => 'as_integer',
390             operator => '<',
391             },
392             { name => 'o_jump_if_i_le',
393             convert => 'as_integer',
394             operator => '<=',
395             },
396             { name => 'o_jump_if_i_eq',
397             convert => 'as_integer',
398             operator => '==',
399             },
400             { name => 'o_jump_if_i_ge',
401             convert => 'as_integer',
402             operator => '>=',
403             },
404             { name => 'o_jump_if_i_gt',
405             convert => 'as_integer',
406             operator => '>',
407             },
408              
409             { name => 'o_jump_if_f_lt',
410             convert => 'as_float',
411             operator => '<',
412             },
413             { name => 'o_jump_if_f_le',
414             convert => 'as_float',
415             operator => '<=',
416             },
417             { name => 'o_jump_if_f_eq',
418             convert => 'as_float',
419             operator => '==',
420             },
421             { name => 'o_jump_if_f_ge',
422             convert => 'as_float',
423             operator => '>=',
424             },
425             { name => 'o_jump_if_f_gt',
426             convert => 'as_float',
427             operator => '>',
428             },
429              
430             { name => 'o_jump_if_s_eq',
431             convert => 'as_string',
432             operator => 'eq',
433             },
434             { name => 'o_jump_if_s_ne',
435             convert => 'as_string',
436             operator => 'ne',
437             },
438             );
439              
440             sub _make_compare {
441 600     600   1571 my( $op ) = @_;
442              
443 600 100       1649 my $ret = $op->{new_type} eq 'int' ?
444             '$r' :
445             'Language::P::Toy::Value::StringNumber->new( { integer => $r } )';
446              
447 600 0   0 0 94748 eval sprintf <<'EOT',
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 100   177 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  177         268  
  177         203  
  177         266  
  177         186  
  177         233  
  177         433  
  177         186  
  177         258  
  177         392  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
448             sub %s {
449             my( $op, $runtime, $pc ) = @_;
450             my $vr = pop @{$runtime->{_stack}};
451             my $vl = pop @{$runtime->{_stack}};
452             my $r = $vl->%s %s $vr->%s ? 1 : 0;
453              
454             push @{$runtime->{_stack}}, %s;
455              
456             return $pc + 1;
457             }
458             EOT
459             $op->{name}, $op->{convert}, $op->{operator}, $op->{convert},
460             $ret;
461             }
462              
463             _make_compare( $_ ) foreach
464             ( { name => 'o_compare_i_lt_int',
465             convert => 'as_integer',
466             operator => '<',
467             new_type => 'int',
468             },
469             { name => 'o_compare_i_le_int',
470             convert => 'as_integer',
471             operator => '<=',
472             new_type => 'int',
473             },
474             { name => 'o_compare_i_eq_int',
475             convert => 'as_integer',
476             operator => '==',
477             new_type => 'int',
478             },
479             { name => 'o_compare_i_ge_int',
480             convert => 'as_integer',
481             operator => '>=',
482             new_type => 'int',
483             },
484             { name => 'o_compare_i_gt_int',
485             convert => 'as_integer',
486             operator => '>',
487             new_type => 'int',
488             },
489              
490             { name => 'o_compare_i_le_scalar',
491             convert => 'as_integer',
492             operator => '<=',
493             new_type => 'scalar',
494             },
495             { name => 'o_compare_i_eq_scalar',
496             convert => 'as_integer',
497             operator => '==',
498             new_type => 'scalar',
499             },
500             { name => 'o_compare_i_ne_scalar',
501             convert => 'as_integer',
502             operator => '!=',
503             new_type => 'scalar',
504             },
505              
506             { name => 'o_compare_f_lt_int',
507             convert => 'as_float',
508             operator => '<',
509             new_type => 'int',
510             },
511             { name => 'o_compare_f_le_int',
512             convert => 'as_float',
513             operator => '<=',
514             new_type => 'int',
515             },
516             { name => 'o_compare_f_eq_int',
517             convert => 'as_float',
518             operator => '==',
519             new_type => 'int',
520             },
521             { name => 'o_compare_f_ge_int',
522             convert => 'as_float',
523             operator => '>=',
524             new_type => 'int',
525             },
526             { name => 'o_compare_f_gt_int',
527             convert => 'as_float',
528             operator => '>',
529             new_type => 'int',
530             },
531              
532             { name => 'o_compare_f_le_scalar',
533             convert => 'as_float',
534             operator => '<=',
535             new_type => 'scalar',
536             },
537             { name => 'o_compare_f_eq_scalar',
538             convert => 'as_float',
539             operator => '==',
540             new_type => 'scalar',
541             },
542             { name => 'o_compare_f_ne_scalar',
543             convert => 'as_float',
544             operator => '!=',
545             new_type => 'scalar',
546             },
547              
548             { name => 'o_compare_s_eq_int',
549             convert => 'as_string',
550             operator => 'eq',
551             new_type => 'int',
552             },
553             { name => 'o_compare_s_ne_int',
554             convert => 'as_string',
555             operator => 'ne',
556             new_type => 'int',
557             },
558              
559             { name => 'o_compare_s_eq_scalar',
560             convert => 'as_string',
561             operator => 'eq',
562             new_type => 'scalar',
563             },
564             { name => 'o_compare_s_ne_scalar',
565             convert => 'as_string',
566             operator => 'ne',
567             new_type => 'scalar',
568             },
569             );
570              
571             sub o_negate {
572 0     0 0 0 my( $op, $runtime, $pc ) = @_;
573 0         0 my $v = pop @{$runtime->{_stack}};
  0         0  
574              
575 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { float => -$v->as_float } );
  0         0  
576              
577 0         0 return $pc + 1;
578             }
579              
580             sub o_abs {
581 0     0 0 0 my( $op, $runtime, $pc ) = @_;
582 0         0 my $v = pop @{$runtime->{_stack}};
  0         0  
583              
584 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { float => abs $v->as_float } );
  0         0  
585              
586 0         0 return $pc + 1;
587             }
588              
589             sub o_not {
590 8     8 0 13 my( $op, $runtime, $pc ) = @_;
591 8         10 my $v = pop @{$runtime->{_stack}};
  8         18  
592              
593 8         12 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { integer => !$v->as_boolean_int } );
  8         47  
594              
595 8         105 return $pc + 1;
596             }
597              
598             sub o_assign {
599 2738     2738 0 4006 my( $op, $runtime, $pc ) = @_;
600 2738         2706 my $vr = pop @{$runtime->{_stack}};
  2738         6181  
601 2738         4680 my $vl = $runtime->{_stack}[-1];
602              
603 2738         6527 $vl->assign( $vr );
604              
605 2738         7342 return $pc + 1;
606             }
607              
608             sub o_glob_slot_create {
609 3063     3063 0 4361 my( $op, $runtime, $pc ) = @_;
610 3063         3604 my $glob = pop @{$runtime->{_stack}};
  3063         5702  
611 3063         5040 my $slot = $op->{slot};
612              
613 3063         3106 push @{$runtime->{_stack}}, $glob->get_or_create_slot( $slot );
  3063         29913  
614              
615 3063         8476 return $pc + 1;
616             }
617              
618             sub o_glob_slot {
619 1     1 0 3 my( $op, $runtime, $pc ) = @_;
620 1         3 my $glob = pop @{$runtime->{_stack}};
  1         3  
621 1         3 my $slot = $op->{slot};
622              
623 1         2 push @{$runtime->{_stack}}, $glob->get_slot( $slot );
  1         5  
624              
625 1         12 return $pc + 1;
626             }
627              
628             sub o_glob_slot_set {
629 3     3 0 7 my( $op, $runtime, $pc ) = @_;
630              
631 3         4 my $value = pop @{$runtime->{_stack}};
  3         7  
632 3         3 my $glob = pop @{$runtime->{_stack}};
  3         6  
633 3         5 my $slot = $op->{slot};
634              
635 3         11 $glob->set_slot( $slot, $value );
636              
637 3         7 return $pc + 1;
638             }
639              
640             sub o_unlink {
641 0     0 0 0 my( $op, $runtime, $pc ) = @_;
642 0         0 my $args = pop @{$runtime->{_stack}};
  0         0  
643 0         0 my @args;
644              
645 0         0 for( my $it = $args->iterator; $it->next; ) {
646 0         0 my $arg = $it->item;
647              
648 0         0 push @args, $arg->as_string;
649             }
650              
651 0         0 my $ret = unlink @args;
652              
653 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber( { integer => $ret } );
  0         0  
654 0         0 return $pc + 1;
655             }
656              
657             sub o_backtick {
658 0     0 0 0 my( $op, $runtime, $pc ) = @_;
659 0         0 my $arg = pop @{$runtime->{_stack}};
  0         0  
660 0         0 my $command = $arg->as_string;
661              
662             # context
663 0         0 my $ret = `$command`;
664              
665 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { string => $ret } );
  0         0  
666              
667 0         0 return $pc + 1;
668             }
669              
670             sub o_array_element {
671 5     5 0 9 my( $op, $runtime, $pc ) = @_;
672 5         6 my $array = pop @{$runtime->{_stack}};
  5         13  
673 5         8 my $index = pop @{$runtime->{_stack}};
  5         9  
674              
675 5         9 push @{$runtime->{_stack}}, $array->get_item( $index->as_integer );
  5         20  
676              
677 5         15 return $pc + 1;
678             }
679              
680             sub o_hash_element {
681 0     0 0 0 my( $op, $runtime, $pc ) = @_;
682 0         0 my $hash = pop @{$runtime->{_stack}};
  0         0  
683 0         0 my $key = pop @{$runtime->{_stack}};
  0         0  
684              
685 0         0 push @{$runtime->{_stack}}, $hash->get_item( $key->as_string );
  0         0  
686              
687 0         0 return $pc + 1;
688             }
689              
690             sub o_array_size {
691 1     1 0 3 my( $op, $runtime, $pc ) = @_;
692 1         1 my $array = pop @{$runtime->{_stack}};
  1         4  
693              
694 1         2 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new( { integer => $array->get_count - 1 } );
  1         5  
695              
696 1         13 return $pc + 1;
697             }
698              
699             sub o_reference {
700 0     0 0 0 my( $op, $runtime, $pc ) = @_;
701 0         0 my $value = pop @{$runtime->{_stack}};
  0         0  
702              
703 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::Reference->new( { reference => $value } );
  0         0  
704              
705 0         0 return $pc + 1;
706             }
707              
708             sub o_dereference_scalar {
709 0     0 0 0 my( $op, $runtime, $pc ) = @_;
710 0         0 my $ref = pop @{$runtime->{_stack}};
  0         0  
711              
712 0         0 push @{$runtime->{_stack}}, $ref->dereference_scalar;
  0         0  
713              
714 0         0 return $pc + 1;
715             }
716              
717             sub o_dereference_subroutine {
718 8     8 0 12 my( $op, $runtime, $pc ) = @_;
719 8         10 my $ref = pop @{$runtime->{_stack}};
  8         15  
720              
721 8         11 push @{$runtime->{_stack}}, $ref->dereference_subroutine;
  8         29  
722              
723 8         28 return $pc + 1;
724             }
725              
726             sub o_defined {
727 15     15 0 23 my( $op, $runtime, $pc ) = @_;
728 15         18 my $value = pop @{$runtime->{_stack}};
  15         33  
729 15         53 my $defined = $value->is_defined;
730              
731 15 100       22 push @{$runtime->{_stack}}, $defined ?
  15         124  
732             Language::P::Toy::Value::StringNumber->new( { integer => 1 } ) :
733             Language::P::Toy::Value::StringNumber->new( { string => '' } );
734              
735 15         182 return $pc + 1;
736             }
737              
738             sub o_make_closure {
739 6     6 0 9 my( $op, $runtime, $pc ) = @_;
740 6         7 my $sub = pop @{$runtime->{_stack}};
  6         10  
741 6         18 my $clone = Language::P::Toy::Value::Subroutine->new
742             ( { bytecode => $sub->bytecode,
743             stack_size => $sub->stack_size,
744             outer => $sub->outer,
745             lexicals => $sub->lexicals->new_scope,
746             } );
747              
748 6 50       25 if( my $closed_values = $sub->closed ) {
749 6         31 my $outer = $runtime->{_stack}->[$runtime->{_frame} - 1];
750 6         16 my $pad = $clone->lexicals;
751              
752 6         20 foreach my $from_to ( @$closed_values ) {
753 10         39 $pad->values->[$from_to->[1]] = $outer->values->[$from_to->[0]];
754             }
755             }
756              
757 6         42 push @{$runtime->{_stack}}, Language::P::Toy::Value::Reference->new
  6         33  
758             ( { reference => $clone,
759             } );
760              
761 6         59 return $pc + 1;
762             }
763              
764             sub o_localize_glob_slot {
765 10     10 0 120 my( $op, $runtime, $pc ) = @_;
766 10         47 my $glob = $runtime->symbol_table->get_symbol( $op->{name}, '*', 1 );
767 10         42 my $to_save = $glob->get_slot( $op->{slot} );
768 10         123 my $saved = $to_save->localize;
769              
770 10         110 $runtime->{_stack}->[$runtime->{_frame} - 3 - $op->{index}] = $to_save;
771 10         39 $glob->set_slot( $op->{slot}, $saved );
772 10         14 push @{$runtime->{_stack}}, $saved;
  10         24  
773              
774 10         29 return $pc + 1;
775             }
776              
777             sub o_restore_glob_slot {
778 12     12 0 21 my( $op, $runtime, $pc ) = @_;
779 12         40 my $glob = $runtime->symbol_table->get_symbol( $op->{name}, '*', 1 );
780 12         65 my $saved = $runtime->{_stack}->[$runtime->{_frame} - 3 - $op->{index}];
781              
782 12 100       52 $glob->set_slot( $op->{slot}, $saved ) if $saved;
783 12         44 $runtime->{_stack}->[$runtime->{_frame} - 3 - $op->{index}] = undef;
784              
785 12         39 return $pc + 1;
786             }
787              
788             sub o_iterator {
789 3     3 0 7 my( $op, $runtime, $pc ) = @_;
790 3         8 my $list = pop @{$runtime->{_stack}};
  3         9  
791 3         11 my $iter = $list->iterator;
792              
793 3         39 push @{$runtime->{_stack}}, $iter;
  3         9  
794              
795 3         12 return $pc + 1;
796             }
797              
798             sub o_iterator_next {
799 9     9 0 16 my( $op, $runtime, $pc ) = @_;
800 9         11 my $iter = pop @{$runtime->{_stack}};
  9         20  
801              
802 9 100       12 push @{$runtime->{_stack}}, $iter->next ? $iter->item : undef;
  9         30  
803              
804 9         24 return $pc + 1;
805             }
806              
807             1;