File Coverage

blib/lib/VIC/Receiver.pm
Criterion Covered Total %
statement 892 1010 88.3
branch 480 714 67.2
condition 81 162 50.0
subroutine 81 87 93.1
pod 1 63 1.5
total 1535 2036 75.3


line stmt bran cond sub pod time code
1             package VIC::Receiver;
2 33     33   203 use strict;
  33         50  
  33         837  
3 33     33   169 use warnings;
  33         52  
  33         694  
4 33     33   15199 use bigint;
  33         92124  
  33         141  
5 33     33   1366906 use POSIX ();
  33         61  
  33         676  
6 33     33   146 use List::Util qw(max);
  33         53  
  33         1877  
7 33     33   17588 use List::MoreUtils qw(any firstidx indexes);
  33         360450  
  33         179  
8              
9             our $VERSION = '0.32';
10             $VERSION = eval $VERSION;
11              
12 33     33   32819 use Pegex::Base;
  33         60  
  33         242  
13             extends 'Pegex::Tree';
14              
15 33     33   50585 use VIC::PIC::Any;
  33         77  
  33         1191  
16              
17             has pic_override => undef;
18             has pic => undef;
19             has simulator => undef;
20             has ast => {
21             block_stack => [],
22             block_mapping => {},
23             block_count => 0,
24             funcs => {},
25             variables => {},
26             tmp_variables => {},
27             conditionals => 0,
28             tmp_stack_size => 0,
29             strings => 0,
30             tables => [],
31             asserts => 0,
32             };
33             has intermediate_inline => undef;
34             has global_collections => {};
35              
36 431     431 0 490 sub stack { reverse @{shift->parser->stack}; }
  431         898  
37              
38 1     1 0 3 sub supported_chips { return VIC::PIC::Any::supported_chips(); }
39              
40 1     1 0 3 sub supported_simulators { return VIC::PIC::Any::supported_simulators(); }
41              
42 19     19 0 39 sub is_chip_supported { return VIC::PIC::Any::is_chip_supported(@_); }
43              
44 0     0 0 0 sub is_simulator_supported { return VIC::PIC::Any::is_simulator_supported(@_); }
45              
46 0     0 0 0 sub list_chip_features { return VIC::PIC::Any::list_chip_features(@_); }
47              
48 0     0 0 0 sub print_pinout { return VIC::PIC::Any::print_pinout(@_); }
49              
50 31     31 0 272 sub current_chip { return $_[0]->pic->type; }
51              
52 31     31 0 168 sub current_simulator { return $_[0]->simulator->type; }
53              
54             sub got_mcu_select {
55 33     33 0 1861 my ($self, $type) = @_;
56             # override the PIC in code if defined
57 33 50       124 $type = $self->pic_override if defined $self->pic_override;
58 33         255 $type = lc $type;
59             # assume supported type else return
60 33         245 $self->pic(VIC::PIC::Any->new($type));
61 33 50 33     642 unless (defined $self->pic and
62             defined $self->pic->type) {
63 0         0 $self->parser->throw_error("$type is not a supported chip");
64             }
65 33         523 $self->ast->{include} = $self->pic->include;
66             # set the defaults in case the headers are not provided by the user
67 33         351 $self->ast->{org} = $self->pic->org;
68 33         243 $self->ast->{chip_config} = $self->pic->get_chip_config;
69 33         222 $self->ast->{code_config} = $self->pic->code_config;
70             # create the default simulator
71 33         339 $self->simulator(VIC::PIC::Any->new_simulator(pic => $self->pic));
72 33         3109 return;
73             }
74              
75             sub got_pragmas {
76 20     20 0 641 my ($self, $list) = @_;
77 20         69 $self->flatten($list);
78 20         366 $self->pic->update_code_config(@$list);
79             # get the updated config
80 20         63 $self->ast->{chip_config} = $self->pic->get_chip_config;
81 20         110 $self->ast->{code_config} = $self->pic->code_config;
82 20 50       151 my ($sim, $stype) = @$list if scalar @$list;
83 20 100 100     148 if ($sim eq 'simulator' and $stype !~ /disable/i) {
    100 66        
84 2         8 $self->simulator(VIC::PIC::Any->new_simulator(
85             type => $stype, pic => $self->pic));
86 2 50       143 if ($self->simulator) {
87 2 50       12 unless ($self->simulator->type eq $stype) {
88 0         0 warn "$stype is not a supported chip. Disabling simulator.";
89 0         0 $self->simulator->disable(1);
90             }
91             } else {
92 0         0 die "$stype is not a supported simulator.";
93             }
94             } elsif ($sim eq 'simulator' and $stype =~ /disable/i) {
95 1 50       5 $self->simulator->disable(1) if $self->simulator;
96             }
97 20         103 return;
98             }
99              
100             sub handle_named_block {
101 94     94 0 223 my ($self, $name, $anon_block, $parent) = @_;
102 94 50       557 my $id = $1 if $anon_block =~ /_anonblock(\d+)/;
103 94 50       231 $id = $self->ast->{block_count} unless defined $id;
104 94         270 my ($expected_label, $expected_param) = ('', '');
105 94 100       642 if ($name eq 'Main') {
    100          
    100          
    100          
    100          
    100          
    50          
106 32         105 $expected_label = "_start";
107             } elsif ($name =~ /^Loop/) {
108 16         137 $expected_label = "_loop_${id}";
109             } elsif ($name =~ /^Action/) {
110 6         20 $expected_label = "_action_${id}";
111 6         19 $expected_param = "action${id}_param";
112             } elsif ($name =~ /^True/) {
113 12         19 $expected_label = "_true_${id}";
114             } elsif ($name =~ /^False/) {
115 4         12 $expected_label = "_false_${id}";
116             } elsif ($name =~ /^ISR/) {
117 5         15 $expected_label = "_isr_${id}";
118 5         16 $expected_param = "isr${id}_param";
119             } elsif ($name eq 'Simulator') {
120 19         44 $expected_label = '_vic_simulator';
121             } else {
122 0         0 $expected_label = lc "_$name$id";
123             }
124 94 100       387 $name .= $id if $name =~ /^(?:Loop|Action|True|False|ISR)/;
125 94         596 $self->ast->{block_mapping}->{$name} = {
126             label => $expected_label,
127             block => $anon_block,
128             params => [],
129             param_prefix => $expected_param,
130             };
131 94         771 $self->ast->{block_mapping}->{$anon_block} = {
132             label => $expected_label,
133             block => $name,
134             params => [],
135             param_prefix => $expected_param,
136             };
137             # make sure the anon-block and named-block refer to the same block
138 94         404 $self->ast->{$name} = $self->ast->{$anon_block};
139              
140 94   33     538 my $stack = $self->ast->{$name} || $self->ast->{$anon_block};
141 94 50 33     826 if (defined $stack and ref $stack eq 'ARRAY') {
142 94         289 my $block_label = $stack->[0];
143             ## this expression is dependent on got_start_block()
144 94         2610 my ($tag, $label, @others) = split /::/, $block_label;
145 94 50       256 $label = $expected_label if $label ne $expected_label;
146 94 50       354 $block_label = "BLOCK::${label}::${name}" if $label;
147             # change the LABEL:: value in the stack for code-generation ease
148             # we want to use the expected label and not the anon one unless it is an
149             # anon-block
150 94         347 $stack->[0] = join("::", $tag, $label, @others);
151 94         1802 my $elabel = "_end$label"; # end label
152 94         160 my $slabel = $label; # start label
153 94 100       242 if (defined $parent) {
154 43 50       272 unless ($parent =~ /BLOCK::/) {
155 43         100 $block_label .= "::$parent";
156 43 50 33     137 if (exists $self->ast->{$parent} and
      33        
157             ref $self->ast->{$parent} eq 'ARRAY' and
158             $parent ne $anon_block) {
159 43         578 my ($ptag, $plabel) = split /::/, $self->ast->{$parent}->[0];
160 43 50       1157 $block_label .= "::$plabel" if $plabel;
161             }
162             }
163 43         184 my $ccount = $self->ast->{conditionals};
164 43 100       373 if ($block_label =~ /True|False/i) {
165 16         100 $elabel = "_end_conditional_$ccount";
166 16         280 $slabel = "_start_conditional_$ccount";
167             }
168 43         281 $block_label .= "::$elabel";
169 43 100       207 $block_label .= "::$expected_param" if length $expected_param;
170 43         63 push @{$self->ast->{$parent}}, $block_label;
  43         112  
171             }
172             # save this for referencing when we need to know what the parent of
173             # this block is in case we need to jump out of the block
174 94         368 $self->ast->{block_mapping}->{$name}->{parent} = $parent;
175 94         420 $self->ast->{block_mapping}->{$anon_block}->{parent} = $parent;
176 94         375 $self->ast->{block_mapping}->{$name}->{end_label} = $elabel;
177 94         375 $self->ast->{block_mapping}->{$anon_block}->{end_label} = $elabel;
178 94         347 $self->ast->{block_mapping}->{$name}->{start_label} = $slabel;
179 94         393 $self->ast->{block_mapping}->{$anon_block}->{start_label} = $slabel;
180 94 100       522 $self->ast->{block_mapping}->{$anon_block}->{loop} = '1' if $block_label =~ /Loop/i;
181 94         479 return $block_label;
182             }
183             }
184              
185             sub got_named_block {
186 78     78 0 2481 my ($self, $list) = @_;
187 78 50       497 $self->flatten($list) if ref $list eq 'ARRAY';
188 78         1605 my ($name, $anon_block, $parent_block) = @$list;
189 78         273 return $self->handle_named_block(@$list);
190             }
191              
192             sub got_anonymous_block {
193 94     94 0 3078 my $self = shift;
194 94         156 my $list = shift;
195 94         171 my ($anon_block, $block_stack, $parent) = @$list;
196             # returns anon_block and parent_block
197 94         311 return [$anon_block, $parent];
198             }
199              
200             sub got_start_block {
201 95     95 0 3840 my ($self, $list) = @_;
202 95         309 my $id = $self->ast->{block_count};
203             # we may not know the block name here
204 95         690 my $block = lc "_anonblock$id";
205 95         2329 push @{$self->ast->{block_stack}}, $block;
  95         234  
206 95         586 $self->ast->{$block} = [ "LABEL::$block" ];
207 95         573 $self->ast->{block_count}++;
208 95         6352 return $block;
209             }
210              
211             sub got_end_block {
212 94     94 0 3823 my ($self, $list) = @_;
213             # we are not capturing anything here
214 94         260 my $stack = $self->ast->{block_stack};
215 94         404 my $block = pop @$stack;
216 94         309 return $stack->[-1];
217             }
218              
219             sub got_name {
220 779     779 0 23347 my ($self, $list) = @_;
221 779 50       1586 if (ref $list eq 'ARRAY') {
222 779         2337 $self->flatten($list);
223 779         7258 return shift(@$list);
224             } else {
225 0         0 return $list;
226             }
227             }
228              
229             sub update_intermediate {
230 381     381 0 1229 my $self = shift;
231 381         829 my $block = $self->ast->{block_stack}->[-1];
232 381 50       10420 push @{$self->ast->{$block}}, @_ if $block;
  381         679  
233 381         1517 return;
234             }
235              
236             sub got_instruction {
237 246     246 0 7595 my ($self, $list) = @_;
238 246         405 my $method = shift @$list;
239 246 50       948 $self->flatten($list) if $list;
240 246         5172 my $tag = 'INS';
241             # check if it is a simulator method
242 246 100 66     638 if ($self->simulator and $self->simulator->can($method)) {
243             # this is a simulator instruction
244 107         1055 $tag = 'SIM';
245             } else {
246 139 100       2076 unless ($self->pic->can($method)) {
247 1         12 my $err = "Unsupported instruction '$method' for chip " . uc $self->pic->type;
248 1         17 return $self->parser->throw_error($err);
249             }
250             }
251 245         1019 my @args = ();
252 245         809 while (scalar @$list) {
253 355         513 my $a = shift @$list;
254 355 100       744 if ($a =~ /BLOCK::(\w+)::(Action|ISR)\w+::.*::(_end_\w+)::(\w+)$/) {
255 11         103 push @args, uc($2) . "::$1::END::$3::PARAM::$4";
256             } else {
257 344         1489 push @args, $a;
258             }
259             }
260 245         1161 $self->update_intermediate("${tag}::${method}::" . join ("::", @args));
261 245         674 return;
262             }
263              
264             sub got_unary_rhs {
265 1     1 0 32 my ($self, $list) = @_;
266 1         4 $self->flatten($list);
267 1         11 return [ reverse @$list ];
268             }
269              
270             sub got_unary_expr {
271 8     8 0 222 my ($self, $list) = @_;
272 8         37 $self->flatten($list);
273 8         98 my $op = shift @$list;
274 8         19 my $varname = shift @$list;
275 8         49 $self->update_intermediate("UNARY::${op}::${varname}");
276 8         22 return;
277             }
278              
279             sub got_assign_expr {
280 85     85 0 2641 my ($self, $list) = @_;
281 85         296 $self->flatten($list);
282 85         1815 my $varname = shift @$list;
283 85         127 my $op = shift @$list;
284 85         218 my $rhsx = $self->got_expr_value($list);
285 85 50       218 my $rhs = ref $rhsx eq 'ARRAY' ? join ("::", @$rhsx) : $rhsx;
286 85 100       193 if ($rhs =~ /PARAM::(\w+)/) {
287             ## ok now we push this as our statement and handle the rest during
288             ## code generation
289             ## this is of the format PARAM::op::block_name::variable
290 5         14 my $block = $1;
291 5         51 $self->update_intermediate("PARAM::${op}::${block}::${varname}");
292             } else {
293 80         486 $self->update_intermediate("SET::${op}::${varname}::${rhs}");
294             }
295 85         251 return;
296             }
297              
298             sub got_array_element {
299 1     1 0 46 my ($self, $list) = @_;
300 1         3 my $var1 = shift @$list;
301 1         5 my $rhsx = $self->got_expr_value($list);
302 1 50       5 if (ref $rhsx eq 'ARRAY') {
303 0         0 XXX $rhsx; # why would this even happen
304             }
305 1         4 my $tvref = $self->ast->{tmp_variables};
306 1         15 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$tvref);
307 1         4 my $vref = $self->ast->{variables}->{$var1};
308 1         5 my @ops = ('OP');
309 1 50 33     8 if (exists $vref->{type} and $vref->{type} eq 'HASH') {
    0 0        
    0 0        
310 1         5 push @ops, $vref->{label}, 'TBLIDX', $rhsx, $vref->{size};
311             } elsif (exists $vref->{type} and $vref->{type} eq 'ARRAY') {
312 0         0 push @ops, $vref->{label}, 'ARRIDX', $rhsx, $vref->{size};
313             } elsif (exists $vref->{type} and $vref->{type} eq 'string') {
314 0         0 push @ops, $vref->{label}, 'STRIDX', $rhsx, $vref->{size};
315             } else {
316             # this must be a byte
317 0         0 return $self->parser->throw_error(
318             "Variable '$var1' is not an array, table or string");
319             }
320 1         25 $tvref->{$tvar} = join("::", @ops);
321             # create a new variable here
322 1         7 my $varname = sprintf "vic_el_%02d", scalar(keys %$tvref);
323 1         6 $varname = $self->got_variable([$varname]);
324 1 50       14 if ($varname) {
325 1         7 $self->update_intermediate("SET::ASSIGN::${varname}::${tvar}");
326 1         5 return $varname;
327             }
328 0 0       0 return $self->parser->throw_error(
329             "Unable to create intermediary variable '$varname'") unless $varname;
330             }
331              
332             sub got_parameter {
333 5     5 0 219 my $self = shift;
334             ## ok the target variable needs a parameter here
335             ## this works only in block scope so we want to check which block we are in
336 5         25 my $block = $self->ast->{block_stack}->[-1];
337 5         152 return "PARAM::$block";
338             }
339              
340             sub got_declaration {
341 3     3 0 131 my ($self, $list) = @_;
342 3         6 my $lhs = shift @$list;
343 3         15 my $rhs;
344 3 50       22 if (scalar @$list == 1) {
345 3         339 $rhs = shift @$list;
346             } else {
347 0         0 $rhs = $list;
348             }
349             # TODO: generate intermediate code here
350 3 50 33     14 if (ref $rhs eq 'HASH' or ref $rhs eq 'ARRAY') {
351 3 50       10 if (not exists $self->ast->{variables}->{$lhs}) {
352 0         0 return $self->parser->throw_error("Variable '$lhs' doesn't exist");
353             }
354 3 100 66     28 if (exists $rhs->{TABLE} or ref $rhs eq 'ARRAY') {
    50          
355 1 50 33     10 my $label = lc "_table_$lhs" if ref $rhs eq 'HASH' and exists $rhs->{TABLE};
356 1 50 33     6 my $szpref = "VIC_TBLSZ_" if ref $rhs eq 'HASH' and exists $rhs->{TABLE};
357 1 50       30 $szpref = "VIC_ARRSZ_" if ref $rhs eq 'ARRAY';
358 1         7 $self->ast->{variables}->{$lhs}->{type} = ref $rhs;
359 1         8 $self->ast->{variables}->{$lhs}->{data} = $rhs;
360 1   33     6 $self->ast->{variables}->{$lhs}->{label} = $label || $lhs;
361 1 50       7 if ($szpref) {
362             $self->ast->{variables}->{$lhs}->{size} = $szpref .
363 1         3 $self->ast->{variables}->{$lhs}->{name};
364             }
365             } elsif (exists $rhs->{string}) {
366             # handle variable that are strings here
367 2         7 $self->ast->{variables}->{$lhs}->{data} = $rhs;
368 2         8 $self->ast->{variables}->{$lhs}->{type} = 'string';
369             $self->ast->{variables}->{$lhs}->{size} = "VIC_STRSZ_" .
370 2         8 $self->ast->{variables}->{$lhs}->{name};
371 2         16 $self->update_intermediate("SET::ASSIGN::${lhs}::${rhs}");
372             } else {
373 0         0 return $self->parser->throw_error("We should not be here");
374             }
375             } else {
376             # var = number | string etc.
377 0 0       0 if ($rhs =~ /^-?\d+$/) {
378             # we just use the got_assign_expr. this should never be called in
379             # reality but is here in case the grammar rules change
380 0         0 $self->update_intermediate("SET::ASSIGN::${lhs}::${rhs}");
381             } else {
382             #VIKAS: check this!
383             # handle strings here
384 0         0 $self->ast->{variables}->{$lhs}->{type} = 'string';
385 0         0 $self->ast->{variables}->{$lhs}->{data} = $rhs;
386 0         0 $self->ast->{variables}->{$lhs}->{label} = $lhs;
387             $self->ast->{variables}->{$lhs}->{size} = "VIC_STRSZ_" .
388 0         0 $self->ast->{variables}->{$lhs}->{name};
389             }
390             }
391 3         15 return;
392             }
393              
394             sub got_conditional_statement {
395 12     12 0 350 my ($self, $list) = @_;
396 12         29 my ($type, $subject, $predicate) = @$list;
397 12 50       39 return unless scalar @$predicate;
398 12 100       41 my $is_loop = ($type eq 'while') ? 1 : 0;
399 12         24 my ($current, $parent) = $self->stack;
400 12         23 my $subcond = 0;
401 12 100       33 $subcond = 1 if $parent =~ /^conditional/;
402 12 50       39 if (ref $predicate ne 'ARRAY') {
403 0         0 $predicate = [ $predicate ];
404             }
405 12         22 my @condblocks = ();
406 12 50       50 if (scalar @$predicate < 3) {
407 12   50     780 my $tb = $predicate->[0] || undef;
408 12   50     347 my $fb = $predicate->[1] || undef;
409 12 50       258 $self->flatten($tb) if $tb;
410 12 50       218 $self->flatten($fb) if $fb;
411 12 50 50     182 my $true_block = $self->handle_named_block('True', @$tb) if $tb and scalar @$tb;
412 12 50       103 push @condblocks, $true_block if $true_block;
413 12 100 50     61 my $false_block = $self->handle_named_block('False', @$fb) if $fb and scalar @$fb;
414 12 100       33 push @condblocks, $false_block if $false_block;
415             } else {
416 0         0 return $self->parser->throw_error("Multiple predicate conditionals not implemented");
417             }
418 12         54 my $inter;
419 12 50       35 if (scalar @condblocks < 3) {
420 12         829 my ($false_label, $true_label, $end_label);
421 12         0 my ($false_name, $true_name);
422 12         27 foreach my $p (@condblocks) {
423 16 100       55 ($false_label, $false_name) = ($1, $2) if $p =~ /BLOCK::(\w+)::(False\d+)::/;
424 16 100       85 ($true_label, $true_name) = ($1, $2) if $p =~ /BLOCK::(\w+)::(True\d+)::/;
425 16 50       75 $end_label = $1 if $p =~ /BLOCK::.*::(_end_conditional\w+)$/;
426             }
427 12 100       29 $false_label = $end_label unless defined $false_label;
428 12 50       85 $true_label = $end_label unless defined $true_label;
429 12         17 my $subj = $subject;
430 12 50       26 $subj = shift @$subject if ref $subject eq 'ARRAY';
431             $inter = join("::",
432             COND => $self->ast->{conditionals},
433 12         29 SUBJ => $subj,
434             FALSE => $false_label,
435             TRUE => $true_label,
436             END => $end_label,
437             LOOP => $is_loop,
438             SUBCOND => $subcond);
439 12         556 my $mapping = $self->ast->{block_mapping};
440 12 50 33     73 if ($true_name and exists $mapping->{$true_name}) {
441 12         24 $mapping->{$true_name}->{loop} = "$is_loop";
442 12         179 my $ab = $mapping->{$true_name}->{block};
443 12         21 $mapping->{$ab}->{loop} = "$is_loop";
444             }
445 12 50 66     195 if ($false_name and exists $mapping->{$false_name}) {
446 4         12 $mapping->{$false_name}->{loop} = "$is_loop";
447 4         73 my $ab = $mapping->{$false_name}->{block};
448 4         11 $mapping->{$ab}->{loop} = "$is_loop";
449             }
450             } else {
451 0         0 return $self->parser->throw_error("Multiple predicate conditionals not implemented");
452             }
453 12         99 $self->update_intermediate($inter);
454 12 100       19 $self->ast->{conditionals}++ unless $subcond;
455 12         694 return;
456             }
457              
458             ##WARNING: do not change this function without looking at its effect on
459             #got_conditional_statement() above which calls this function explicitly
460             # this function is identical to got_expr_value() and hence redundant
461             # we may need to just use the same one although precedence will be different
462             # so maybe not
463             sub got_conditional_subject {
464 16     16 0 1250 my ($self, $list) = @_;
465 16 50       46 if (ref $list eq 'ARRAY') {
466 16         51 $self->flatten($list);
467 16 100       245 if (scalar @$list == 1) {
    50          
    100          
468 4         374 my $var1 = shift @$list;
469 4 100       13 return $var1 if $var1 =~ /^\d+$/;
470 3         7 my $vref = $self->ast->{tmp_variables};
471 3         21 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$vref);
472 3         19 $vref->{$tvar} = "OP::${var1}::EQ::1";
473 3         19 return $tvar;
474             } elsif (scalar @$list == 2) {
475 0         0 my ($op, $var) = @$list;
476 0         0 my $vref = $self->ast->{tmp_variables};
477 0         0 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$vref);
478 0         0 $vref->{$tvar} = "OP::${op}::${var}";
479 0         0 return $tvar;
480             } elsif (scalar @$list == 3) {
481 11         2677 my ($var1, $op, $var2) = @$list;
482 11         30 my $vref = $self->ast->{tmp_variables};
483 11         94 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$vref);
484 11         46 $vref->{$tvar} = "OP::${var1}::${op}::${var2}";
485 11         135 return $tvar;
486             } else {
487             # handle precedence with left-to-right association
488 1         287 my @arr = @$list;
489 1     2   8 my $idx = firstidx { $_ =~ /^GE|GT|LE|LT|EQ|NE$/ } @arr;
  2         7  
490 1         5 while ($idx >= 0) {
491 2         115 my $res = $self->got_conditional_subject([$arr[$idx - 1], $arr[$idx], $arr[$idx + 1]]);
492 2         7 $arr[$idx - 1] = $res;
493 2         307 splice @arr, $idx, 2; # remove the extra elements
494 2     7   43 $idx = firstidx { $_ =~ /^GE|GT|LE|LT|EQ|NE$/ } @arr;
  7         20  
495             }
496 1     2   64 $idx = firstidx { $_ =~ /^AND|OR$/ } @arr;
  2         8  
497 1         4 while ($idx >= 0) {
498 1         61 my $res = $self->got_conditional_subject([$arr[$idx - 1], $arr[$idx], $arr[$idx + 1]]);
499 1         4 $arr[$idx - 1] = $res;
500 1         186 splice @arr, $idx, 2; # remove the extra elements
501 1     1   23 $idx = firstidx { $_ =~ /^AND|OR$/ } @arr;
  1         7  
502             }
503             # YYY $self->ast->{tmp_variables};
504 1         52 return $self->got_conditional_subject([@arr]);
505             }
506             } else {
507 0         0 return $list;
508             }
509             }
510              
511             ##WARNING: do not change this function without looking at its effect on
512             #got_assign_expr() above which calls this function explicitly
513             sub got_expr_value {
514 296     296 0 10366 my ($self, $list) = @_;
515 296 50       645 if (ref $list eq 'ARRAY') {
516 296         698 $self->flatten($list);
517 296 100       2964 if (scalar @$list == 1) {
    100          
    100          
    50          
518 236         21266 my $val = shift @$list;
519 236 100       463 if ($val =~ /MOP::/) {
520 2         8 my $vref = $self->ast->{tmp_variables};
521 2         36 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$vref);
522 2         6 $vref->{$tvar} = $val;
523 2         11 return $tvar;
524             } else {
525 234         1297 return $val;
526             }
527             } elsif (scalar @$list == 2) {
528 8         1359 my ($op, $var) = @$list;
529 8         21 my $vref = $self->ast->{tmp_variables};
530 8         61 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$vref);
531 8         35 $vref->{$tvar} = "OP::${op}::${var}";
532 8         32 return $tvar;
533             } elsif (scalar @$list == 3) {
534 46         10511 my ($var1, $op, $var2) = @$list;
535 46         101 my $vref = $self->ast->{tmp_variables};
536 46         288 my $tvar = sprintf "_vic_tmp_%02d", scalar(keys %$vref);
537 46         184 $vref->{$tvar} = "OP::${var1}::${op}::${var2}";
538 46         120 return $tvar;
539             } elsif (scalar @$list > 3) {
540             # handle precedence with left-to-right association
541 6         1644 my @arr = @$list;
542 6     24   54 my $idx = firstidx { $_ =~ /^MUL|DIV|MOD$/ } @arr;
  24         55  
543 6         24 while ($idx >= 0) {
544 8         446 my $res = $self->got_expr_value([$arr[$idx - 1], $arr[$idx], $arr[$idx + 1]]);
545 8         35 $arr[$idx - 1] = $res;
546 8         1214 splice @arr, $idx, 2; # remove the extra elements
547 8     34   168 $idx = firstidx { $_ =~ /^MUL|DIV|MOD$/ } @arr;
  34         95  
548             }
549 6     12   295 $idx = firstidx { $_ =~ /^ADD|SUB$/ } @arr;
  12         29  
550 6         19 while ($idx >= 0) {
551 8         404 my $res = $self->got_expr_value([$arr[$idx - 1], $arr[$idx], $arr[$idx + 1]]);
552 8         30 $arr[$idx - 1] = $res;
553 8         1145 splice @arr, $idx, 2; # remove the extra elements
554 8     10   157 $idx = firstidx { $_ =~ /^ADD|SUB$/ } @arr;
  10         47  
555             }
556 6     6   288 $idx = firstidx { $_ =~ /^SHL|SHR$/ } @arr;
  6         21  
557 6         17 while ($idx >= 0) {
558 0         0 my $res = $self->got_expr_value([$arr[$idx - 1], $arr[$idx], $arr[$idx + 1]]);
559 0         0 $arr[$idx - 1] = $res;
560 0         0 splice @arr, $idx, 2; # remove the extra elements
561 0     0   0 $idx = firstidx { $_ =~ /^SHL|SHR$/ } @arr;
  0         0  
562             }
563 6     6   313 $idx = firstidx { $_ =~ /^BAND|BXOR|BOR$/ } @arr;
  6         22  
564 6         20 while ($idx >= 0) {
565 0         0 my $res = $self->got_expr_value([$arr[$idx - 1], $arr[$idx], $arr[$idx + 1]]);
566 0         0 $arr[$idx - 1] = $res;
567 0         0 splice @arr, $idx, 2; # remove the extra elements
568 0     0   0 $idx = firstidx { $_ =~ /^BAND|BXOR|BOR$/ } @arr;
  0         0  
569             }
570             # YYY $self->ast->{tmp_variables};
571 6         298 return $self->got_expr_value([@arr]);
572             } else {
573 0         0 return $list;
574             }
575             } else {
576 0         0 return $list;
577             }
578             }
579              
580             sub got_math_operator {
581 42     42 0 1672 my ($self, $op) = @_;
582 42 100       99 return 'ADD' if $op eq '+';
583 26 100       57 return 'SUB' if $op eq '-';
584 20 100       49 return 'MUL' if $op eq '*';
585 10 100       28 return 'DIV' if $op eq '/';
586 4 50       29 return 'MOD' if $op eq '%';
587 0         0 return $self->parser->throw_error("Math operator '$op' is not supported");
588             }
589              
590             sub got_shift_operator {
591 4     4 0 158 my ($self, $op) = @_;
592 4 100       16 return 'SHL' if $op eq '<<';
593 2 50       9 return 'SHR' if $op eq '>>';
594 0         0 return $self->parser->throw_error("Shift operator '$op' is not supported");
595             }
596              
597             sub got_bit_operator {
598 0     0 0 0 my ($self, $op) = @_;
599 0 0       0 return 'BXOR' if $op eq '^';
600 0 0       0 return 'BOR' if $op eq '|';
601 0 0       0 return 'BAND' if $op eq '&';
602 0         0 return $self->parser->throw_error("Bitwise operator '$op' is not supported");
603             }
604              
605             sub got_logic_operator {
606 2     2 0 91 my ($self, $op) = @_;
607 2 100       7 return 'AND' if $op eq '&&';
608 1 50       8 return 'OR' if $op eq '||';
609 0         0 return $self->parser->throw_error("Logic operator '$op' is not supported");
610             }
611              
612             sub got_compare_operator {
613 37     37 0 1413 my ($self, $op) = @_;
614 37 100       134 return 'LE' if $op eq '<=';
615 36 50       105 return 'LT' if $op eq '<';
616 36 50       58 return 'GE' if $op eq '>=';
617 36 100       79 return 'GT' if $op eq '>';
618 35 100       148 return 'EQ' if $op eq '==';
619 4 50       19 return 'NE' if $op eq '!=';
620 0         0 return $self->parser->throw_error("Compare operator '$op' is not supported");
621             }
622              
623             sub got_complement_operator {
624 8     8 0 353 my ($self, $op) = @_;
625 8 50       36 return 'NOT' if $op eq '!';
626 0 0       0 return 'COMP' if $op eq '~';
627 0         0 return $self->parser->throw_error("Complement operator '$op' is not supported");
628             }
629              
630             sub got_assign_operator {
631 88     88 0 5333 my ($self, $op) = @_;
632 88 50       213 if (ref $op eq 'ARRAY') {
633 0         0 $self->flatten($op);
634 0         0 $op = shift @$op;
635             }
636 88 100       289 return 'ASSIGN' if $op eq '=';
637 25 100       59 return 'ADD_ASSIGN' if $op eq '+=';
638 23 100       45 return 'SUB_ASSIGN' if $op eq '-=';
639 21 100       53 return 'MUL_ASSIGN' if $op eq '*=';
640 19 100       37 return 'DIV_ASSIGN' if $op eq '/=';
641 17 100       37 return 'MOD_ASSIGN' if $op eq '%=';
642 15 100       46 return 'BXOR_ASSIGN' if $op eq '^=';
643 13 100       32 return 'BOR_ASSIGN' if $op eq '|=';
644 11 100       42 return 'BAND_ASSIGN' if $op eq '&=';
645 8 100       32 return 'SHL_ASSIGN' if $op eq '<<=';
646 6 100       37 return 'SHR_ASSIGN' if $op eq '>>=';
647 1 50       3 return 'CAT_ASSIGN' if $op eq '.=';
648 0         0 return $self->parser->throw_error("Assignment operator '$op' is not supported");
649             }
650              
651             sub got_unary_operator {
652 8     8 0 347 my ($self, $op) = @_;
653 8 100       47 return 'INC' if $op eq '++';
654 2 50       8 return 'DEC' if $op eq '--';
655 0         0 return $self->parser->throw_error("Increment/Decrement operator '$op' is not supported");
656             }
657              
658             sub got_array {
659 10     10 0 357 my ($self, $arr) = @_;
660 10 50       176 $self->flatten($arr) if ref $arr eq 'ARRAY';
661 10         630 $self->global_collections->{"$arr"} = $arr;
662 10         81 return $arr;
663             }
664              
665             sub got_modifier_constant {
666 12     12 0 413 my ($self, $list) = @_;
667             # we don't flatten since $value can be an array as well
668 12         29 my ($modifier, $value) = @$list;
669 12         36 $modifier = uc $modifier;
670             ## first check if the modifier is an operator
671 12         57 my $method = $self->pic->validate_modifier_operator($modifier);
672 12 50 33     61 $self->flatten($value) if ($method and ref $value eq 'ARRAY');
673 12 50       33 return $self->got_expr_value(["MOP::${modifier}::${value}"]) if $method;
674             ## if not then check if it is a type modifier for use by the simulator
675 12 100 66     45 if ($self->simulator and $self->simulator->supports_modifier($modifier)) {
676 11         434 my $hh = { $modifier => $value };
677 11         34 $self->global_collections->{"$hh"} = $hh;
678 11         108 return $hh;
679             }
680             ## ok check if the modifier is a type modifier for code generation
681             ## this is reallly a bad hack
682 1 50       41 if ($modifier eq 'TABLE') {
    0          
    0          
683 1 50       25 return { TABLE => $value } if ref $value eq 'ARRAY';
684 0         0 return { TABLE => [$value] };
685             } elsif ($modifier eq 'ARRAY') {
686 0 0       0 return $value if ref $value eq 'ARRAY';
687 0         0 return [$value];
688             } elsif ($modifier eq 'STRING') {
689 0 0       0 return { STRING => $value } if ref $value eq 'ARRAY';
690 0         0 return { STRING => [$value] };
691             }
692 0 0       0 $self->parser->throw_error("Modifying operator '$modifier' not supported") unless $method;
693             }
694              
695             sub got_modifier_variable {
696 2     2 0 68 my ($self, $list) = @_;
697 2         3 my ($modifier, $varname);
698 2 50       13 $self->flatten($list) if ref $list eq 'ARRAY';
699 2         19 $modifier = shift @$list;
700 2         5 $varname = shift @$list;
701 2         4 $modifier = uc $modifier;
702 2         8 my $method = $self->pic->validate_modifier_operator($modifier);
703 2 50       6 $self->parser->throw_error("Modifying operator '$modifier' not supported") unless $method;
704 2         10 return $self->got_expr_value(["MOP::${modifier}::${varname}"]);
705             }
706              
707             sub got_validated_variable {
708 207     207 0 13476 my ($self, $list) = @_;
709 207         281 my $varname;
710 207 50       550 if (ref $list eq 'ARRAY') {
711 207         631 $self->flatten($list);
712 207         1911 $varname = shift @$list;
713 207         343 my $suffix = shift @$list;
714 207 50       619 $varname .= $suffix if defined $suffix;
715             } else {
716 0         0 $varname = $list;
717             }
718 207 50       1056 return $varname if $self->pic->validate($varname);
719 0         0 return $self->parser->throw_error("'$varname' is not a valid part of the " . uc $self->pic->type);
720             }
721              
722             sub got_variable {
723 419     419 0 12506 my ($self, $list) = @_;
724 419 50       1456 $self->flatten($list) if ref $list eq 'ARRAY';
725 419         3209 my $varname = shift @$list;
726 419         761 my ($current, $parent) = $self->stack;
727             # if the variable is used from the pragma grammar rule
728             # we do not want to store it yet and definitely not store the size yet
729             # we could remove this if we set the size after the code generation or so
730             # but that may lead to more complexity. this is much easier
731 419 50       889 return $varname if $parent eq 'pragmas';
732             $self->ast->{variables}->{$varname} = {
733             name => uc $varname,
734             scope => $self->ast->{block_stack}->[-1],
735             size => POSIX::ceil($self->pic->address_bits($varname) / 8),
736             type => 'byte',
737             data => undef,
738 419 100       736 } unless exists $self->ast->{variables}->{$varname};
739 419 100       9637 $self->ast->{variables}->{$varname}->{scope} = 'global' if $parent =~ /assert_/;
740 419         1187 return $varname;
741             }
742              
743             sub got_boolean {
744 11     11 0 486 my ($self, $list) = @_;
745 11         14 my $b;
746 11 50       29 if (ref $list eq 'ARRAY') {
747 0         0 $self->flatten($list);
748 0         0 $b = shift @$list;
749             } else {
750 11         17 $b = $list;
751             }
752 11 50       25 return 0 unless defined $b;
753 11 100       64 return 1 if $b =~ /TRUE|true/i;
754 5 50       17 return 1 if $b == 1;
755 5 50       536 return 0 if $b =~ /FALSE|false/i;
756 0         0 return 0; # default boolean is false
757             }
758              
759             sub got_double_quoted_string {
760 43     43 0 1918 my $self = shift;
761 43         60 my $str = pop;
762             ## Ripped from Ingy's pegex-json-pm Pegex::JSON::Data
763             ## Unicode support not implemented yet but available in Pegex::JSON::Data
764 43         299 my %escapes = (
765             '"' => '"',
766             '/' => '/',
767             "\\" => "\\",
768             b => "\b",
769             f => "\x12",
770             n => "\n",
771             r => "\r",
772             t => "\t",
773             0 => "\0",
774             );
775 43         727 $str =~ s/\\(["\/\\bfnrt0])/$escapes{$1}/ge;
  3         9  
776 43         148 return $str;
777             }
778              
779             sub got_string {
780 48     48 0 1181 my $self = shift;
781 48         63 my $str = shift;
782             ##TODO: handle empty strings as initializers
783             # store only unique strings otherwise re-use them
784 48         54 foreach (%{$self->global_collections}) {
  48         111  
785 611         810 my $h = $self->global_collections->{$_};
786 611 100       1698 return $h if ($h->{string} eq $str);
787             }
788 47 100       169 my $is_empty = 1 if $str eq '';
789             my $stref = {
790             string => $str,
791             block => $self->ast->{block_stack}->[-1],
792 47         102 name => sprintf("_vic_str_%02d", $self->ast->{strings}),
793             size => length($str) + 1, # trailing null byte
794             empty => $is_empty, # required for variable allocation later
795             };
796 47         8096 $self->global_collections->{"$stref"} = $stref;
797 47         246 $self->ast->{strings}++;
798 47         1652 return $stref;
799             #return '@' . $str;
800             }
801              
802             sub got_number {
803 400     400 0 23783 my ($self, $list) = @_;
804             # if it is a hexadecimal number we can just convert it to number using int()
805             # since hex is returned here as a string
806 400 100       1980 return hex($list) if $list =~ /0x|0X/;
807 344         784 my $val = int($list);
808 344 50       1409 return $val if $val >= 0;
809             ##TODO: check the negative value
810 0         0 my $bits = (2 ** $self->pic->address_bits) - 1;
811 0         0 $val = sprintf "0x%02X", $val;
812 0         0 return hex($val) & $bits;
813             }
814              
815             # convert the number to appropriate units
816             sub got_number_units {
817 51     51 0 1668 my ($self, $list) = @_;
818 51         170 $self->flatten($list);
819 51         472 my $num = shift @$list;
820 51         98 my $units = shift @$list;
821 51 50       125 return $num unless defined $units;
822 51 100       147 $num *= 1 if $units eq 'us';
823 51 100       399 $num *= 1000 if $units eq 'ms';
824 51 100       2380 $num *= 1e6 if $units eq 's';
825 51 100       2780 $num *= 1 if $units eq 'Hz';
826 51 100       1140 $num *= 1000 if $units eq 'kHz';
827 51 50       938 $num *= 1e6 if $units eq 'MHz';
828             # ignore the '%' sign for now
829 51         180 return $num;
830             }
831              
832             sub got_real_number {
833 5     5 0 317 my ($self, $list) = @_;
834 5 50       12 $list .= '0' if $list =~ /\d+\.$/;
835 5 50       8 $list = "0.$1" if $list =~ /^\.(\d+)$/;
836 5 50       27 $list = "-0.$1" if $list =~ /^-\.(\d+)$/;
837 5         15 return $list;
838             }
839              
840             # remove the dumb stuff from the tree
841 68     68 0 3859 sub got_comment { return; }
842              
843             sub _update_funcs {
844 66     66   115 my ($self, $funcs, $macros) = @_;
845 66 50       195 if (ref $funcs eq 'HASH') {
846 66         205 foreach (keys %$funcs) {
847 21         65 $self->ast->{funcs}->{$_} = $funcs->{$_};
848             }
849             }
850 66 50       235 if (ref $macros eq 'HASH') {
851 66 50       167 return unless ref $macros eq 'HASH';
852 66         165 foreach (keys %$macros) {
853 140         458 $self->ast->{macros}->{$_} = $macros->{$_};
854             }
855             }
856 66         246 1;
857             }
858              
859             sub _update_tables {
860 18     18   29 my ($self, $tables) = @_;
861 18 50       50 if (ref $tables eq 'HASH') {
862 0         0 $tables = [ $tables ];
863             }
864 18 50       49 unless (ref $tables eq 'ARRAY') {
865 0         0 return $self->parser->throw_error(
866             "Code generation error. PIC methods should return strings as a HASH or ARRAY");
867             }
868 18         29 foreach my $s (@$tables) {
869 5 50       14 next unless defined $s->{bytes};
870 5 50       9 next unless defined $s->{name};
871 5         5 push @{$self->ast->{tables}}, $s;
  5         11  
872             }
873 18         37 1;
874             }
875              
876             ## assert handling is special for now
877             sub got_assert_comparison {
878 28     28 0 770 my ($self, $list) = @_;
879 28 50       57 return unless $self->simulator;
880 28 50       176 $self->flatten($list) if ref $list eq 'ARRAY';
881 28 50       558 if (scalar @$list < 3) {
882 0         0 return $self->parser->throw_error("Error in assert statement");
883             }
884 28         1598 return join("@@", @$list);
885             }
886              
887             sub got_assert_statement {
888 28     28 0 853 my ($self, $list) = @_;
889 28 50       113 $self->flatten($list) if ref $list eq 'ARRAY';
890 28         784 my ($method, $cond, $msg) = @$list;
891 28 100       64 $msg = '' unless defined $msg;
892 28         55 $self->ast->{asserts}++;
893 28         1041 $self->update_intermediate("SIM::${method}::${cond}::${msg}");
894 28         71 return;
895             }
896              
897             sub generate_simulator_instruction {
898 130     130 0 224 my ($self, $line) = @_;
899 130         372 my @ins = split /::/, $line;
900 130         196 my $tag = shift @ins;
901 130         160 my $method = shift @ins;
902 130         154 my @code = ();
903 130 50       260 push @code, "\t;; $line" if $self->intermediate_inline;
904 130         517 foreach (@ins) {
905 188 100       594 next unless /HASH|ARRAY/;
906 50 50       127 next unless exists $self->global_collections->{$_};
907 50         234 $_ = $self->global_collections->{$_};
908             }
909 130 100       378 return @code if $self->simulator->disable;
910 129         3269 my $code = $self->simulator->$method(@ins);
911 129 50       835 return $self->parser->throw_error("Error in simulator intermediate code '$line'") unless $code;
912 129 50       274 push @code, $code if $code;
913 129         385 return @code;
914             }
915              
916             sub generate_code_instruction {
917 136     136 0 262 my ($self, $line) = @_;
918 136         520 my @ins = split /::/, $line;
919 136         234 my $tag = shift @ins;
920 136         247 my $method = shift @ins;
921 136         192 my @code = ();
922 136         238 foreach (@ins) {
923 269 100       783 if (exists $self->global_collections->{$_}) {
924 6         25 $_ = $self->global_collections->{$_};
925 6         21 next;
926             }
927 263 100       1150 if (exists $self->ast->{variables}->{$_}) {
928 29         139 my $vhref = $self->ast->{variables}->{$_};
929 29 100       149 if ($vhref->{type} eq 'string') {
930             # send the string variable information to the method
931             # and hope that the method knows how to handle it
932             # this is useful for I/O methods and operator methods
933             # other methods should outright fail to use this and it should
934             # make sense there.#TODO: make better error messages for that.
935 1         2 $_ = $vhref;
936             }
937             }
938             }
939 136         612 my ($code, $funcs, $macros, $tables) = $self->pic->$method(@ins);
940 136 100       402 return $self->parser->throw_error("Error in intermediate code '$line'") unless $code;
941 135 50       414 push @code, "\t;; $line" if $self->intermediate_inline;
942 135 50       690 push @code, $code if $code;
943 135 100 66     507 $self->_update_funcs($funcs, $macros) if ($funcs or $macros);
944 135 100       271 $self->_update_tables($tables) if $tables;
945 135         550 return @code;
946             }
947              
948             sub generate_code_unary_expr {
949 8     8 0 23 my ($self, $line) = @_;
950 8         18 my @code = ();
951 8         34 my $ast = $self->ast;
952 8         59 my ($tag, $op, $varname) = split /::/, $line;
953 8         25 my $method = $self->pic->validate_operator($op);
954 8 50       21 $self->parser->throw_error("Invalid operator '$op' in intermediate code") unless $self->pic->can($method);
955             # check if temporary variable or not
956 8 50       79 if (exists $ast->{variables}->{$varname}) {
957 8   33     39 my $nvar = $ast->{variables}->{$varname}->{name} || $varname;
958 8         24 my ($code, $funcs, $macros, $tables) = $self->pic->$method($nvar);
959 8 50       27 return $self->parser->throw_error("Error in intermediate code '$line'") unless $code;
960 8 50       38 push @code, "\t;; $line" if $self->intermediate_inline;
961 8 50       73 push @code, $code if $code;
962 8 50 33     67 $self->_update_funcs($funcs, $macros) if ($funcs or $macros);
963 8 50       23 $self->_update_tables($tables) if $tables;
964             } else {
965 0         0 return $self->parser->throw_error("Error in intermediate code '$line'");
966             }
967 8         27 return @code;
968             }
969              
970             sub generate_code_operations {
971 70     70 0 213 my ($self, $line, %extra) = @_;
972 70         83 my @code = ();
973 70         232 my ($tag, @args) = split /::/, $line;
974 70         99 my ($op, $var1, $var2);
975 70 100       145 if (scalar @args == 2) {
    100          
    50          
976 9         793 $op = shift @args;
977 9         18 $var1 = shift @args;
978             } elsif (scalar @args == 3) {
979 60         9509 $var1 = shift @args;
980 60         72 $op = shift @args;
981 60         81 $var2 = shift @args;
982             } elsif (scalar @args == 4) {
983 1         252 $var1 = shift @args;
984 1         2 $op = shift @args;
985 1         13 $var2 = shift @args;
986 1         3 my $var3 = shift @args;
987 1         3 $extra{SIZE} = $var3;
988             } else {
989 0         0 return $self->parser->throw_error("Error in intermediate code '$line'");
990             }
991 70 100       155 if (exists $extra{STACK}) {
992 36 50       55 if (defined $var1) {
993 36   66     93 $var1 = $extra{STACK}->{$var1} || $var1;
994             }
995 36 100       51 if (defined $var2) {
996 35   100     73 $var2 = $extra{STACK}->{$var2} || $var2;
997             }
998             }
999 70 100       225 my $method = $self->pic->validate_operator($op) if $tag eq 'OP';
1000 70 100       132 $method = $self->pic->validate_modifier_operator($op) if $tag eq 'MOP';
1001 70 50 33     182 $self->parser->throw_error("Invalid operator '$op' in intermediate code") unless
1002             ($method and $self->pic->can($method));
1003 70 50       418 push @code, "\t;; $line" if $self->intermediate_inline;
1004 70         251 my ($code, $funcs, $macros, $tables) = $self->pic->$method($var1, $var2, %extra);
1005 70 50       152 return $self->parser->throw_error("Error in intermediate code '$line'") unless $code;
1006 70 50       147 push @code, $code if $code;
1007 70 100 66     208 $self->_update_funcs($funcs, $macros) if ($funcs or $macros);
1008 70 50       107 $self->_update_tables($tables) if $tables;
1009 70         202 return @code;
1010             }
1011              
1012             sub find_tmpvar_dependencies {
1013 70     70 0 111 my ($self, $tvar) = @_;
1014 70         116 my $tcode = $self->ast->{tmp_variables}->{$tvar};
1015 70         360 my ($tag, @args) = split /::/, $tcode;
1016 70 100       143 return unless $tag eq 'OP';
1017 68         86 my @deps = ();
1018 68         74 my $sz = scalar @args;
1019 68 100 66     137 if ($sz == 2) {
    50          
1020 7         627 my ($op, $var) = @args;
1021 7 50       16 if (exists $self->ast->{tmp_variables}->{$var}) {
1022 0         0 push @deps, $var;
1023 0         0 my @rdeps = $self->find_tmpvar_dependencies($var);
1024 0 0       0 push @deps, @rdeps if @rdeps;
1025             }
1026             } elsif ($sz == 3 or $sz == 4) {
1027 61         9811 my ($var1, $op, $var2) = @args;
1028 61 100       127 if (exists $self->ast->{tmp_variables}->{$var1}) {
1029 15         62 push @deps, $var1;
1030 15         75 my @rdeps = $self->find_tmpvar_dependencies($var1);
1031 15 100       31 push @deps, @rdeps if @rdeps;
1032             }
1033 61 100       220 if (exists $self->ast->{tmp_variables}->{$var2}) {
1034 13         48 push @deps, $var2;
1035 13         48 my @rdeps = $self->find_tmpvar_dependencies($var2);
1036 13 100       34 push @deps, @rdeps if @rdeps;
1037             }
1038             } else {
1039 0         0 return $self->parser->throw_error("Error in intermediate code '$tcode'");
1040             }
1041 68 50       307 return wantarray ? @deps : \@deps;
1042             }
1043              
1044             sub find_var_dependencies {
1045 42     42 0 60 my ($self, $tvar) = @_;
1046 42         70 my $tcode = $self->ast->{tmp_variables}->{$tvar};
1047 42         196 my ($tag, @args) = split /::/, $tcode;
1048 42 100       103 return unless $tag eq 'OP';
1049 40         54 my @deps = ();
1050 40         55 my $sz = scalar @args;
1051 40 100 66     79 if ($sz == 2) {
    50          
1052 6         530 my ($op, $var) = @args;
1053 6 50       32 if (exists $self->ast->{variables}->{$var}) {
1054 6         37 push @deps, $var;
1055             }
1056             } elsif ($sz == 3 or $sz == 4) {
1057 34         5773 my ($var1, $op, $var2) = @args;
1058 34 100       78 if (exists $self->ast->{variables}->{$var1}) {
1059 27         138 push @deps, $var1;
1060             }
1061 34 100       104 if (exists $self->ast->{variables}->{$var2}) {
1062 12         51 push @deps, $var2;
1063             }
1064             } else {
1065 0         0 return $self->parser->throw_error("Error in intermediate code '$tcode'");
1066             }
1067 40 50       193 return wantarray ? @deps : \@deps;
1068             }
1069              
1070             sub do_i_use_stack {
1071 34     34 0 70 my ($self, @deps) = @_;
1072 34 100       67 return 0 unless @deps;
1073 32         53 my @bits = map { $self->pic->address_bits($_) } @deps;
  43         86  
1074 32 50       106 return 0 if max(@bits) == $self->pic->wreg_size;
1075 0         0 return 1;
1076             }
1077              
1078             sub generate_code_assign_expr {
1079 88     88 0 146 my ($self, $line) = @_;
1080 88         173 my @code = ();
1081 88         190 my $ast = $self->ast;
1082 88         520 my ($tag, $op, $varname, $rhs) = split /::/, $line;
1083 88 50       187 push @code, ";;; $line\n" if $self->intermediate_inline;
1084 88 50       427 if (exists $ast->{variables}->{$varname}) {
1085 88 100       191 if (exists $ast->{tmp_variables}->{$rhs}) {
1086 31         57 my $tmp_code = $ast->{tmp_variables}->{$rhs};
1087 31         70 my @deps = $self->find_tmpvar_dependencies($rhs);
1088 31         80 my @vdeps = $self->find_var_dependencies($rhs);
1089 31 100       65 push @deps, $rhs if @deps;
1090 31 50       60 if ($self->intermediate_inline) {
1091 0 0       0 push @code, "\t;; TMP_VAR DEPS - $rhs, ". join (',', @deps) if @deps;
1092 0 0       0 push @code, "\t;; VAR DEPS - ". join (',', @vdeps) if @vdeps;
1093 0         0 foreach (sort @deps) {
1094 0         0 my $tcode = $ast->{tmp_variables}->{$_};
1095 0         0 push @code, "\t;; $_ = $tcode";
1096             }
1097 0         0 push @code, "\t;; $line";
1098             }
1099 31 100       119 if (scalar @deps) {
1100 6         17 $ast->{tmp_stack_size} = max(scalar(@deps), $ast->{tmp_stack_size});
1101             ## it is assumed that the dependencies and intermediate code are
1102             #arranged in expected order
1103             # TODO: bits check
1104 6         134 my $counter = 0;
1105 6         23 my %tmpstack = map { $_ => 'VIC_STACK + ' . $counter++ } sort(@deps);
  30         1390  
1106 6         326 foreach (sort @deps) {
1107 30         47 my $tcode = $ast->{tmp_variables}->{$_};
1108 30         34 my $result = $tmpstack{$_};
1109 30 100       54 $result = uc $varname if $_ eq $rhs;
1110 30 50       77 my @newcode = $self->generate_code_operations($tcode,
1111             STACK => \%tmpstack, RESULT => $result) if $tcode;
1112 30 50       68 push @code, "\t;; $_ = $tcode" if $self->intermediate_inline;
1113 30 50       146 push @code, @newcode if @newcode;
1114             }
1115             } else {
1116             # no tmp-var dependencies
1117 25 50       67 my $use_stack = $self->do_i_use_stack(@vdeps) unless scalar @deps;
1118 25 50       187 unless ($use_stack) {
1119 25         590 my @newcode = $self->generate_code_operations($tmp_code,
1120             RESULT => uc $varname);
1121 25 50       82 push @code, @newcode if @newcode;
1122             } else {
1123             # TODO: stack
1124 0         0 XXX @vdeps;
1125             }
1126             }
1127             } else {
1128 57   33     173 my $nvar = $ast->{variables}->{$varname}->{name} || $varname;
1129 57 100       194 if ($rhs =~ /HASH|ARRAY/) {
1130 2 50       6 if (exists $self->global_collections->{$rhs}) {
1131 2         19 $rhs = $self->global_collections->{$rhs};
1132             }
1133             }
1134 57 50       125 if (exists $self->ast->{variables}->{$varname}) {
1135 57         254 my $vhref = $self->ast->{variables}->{$varname};
1136 57 100       252 if ($vhref->{type} eq 'string') {
1137             # send the string variable information to the method
1138             # and hope that the method knows how to handle it
1139             # this is useful for I/O methods and operator methods
1140             # other methods should outright fail to use this and it should
1141             # make sense there.#TODO: make better error messages for that.
1142 3         5 $nvar = $vhref;
1143             }
1144             }
1145 57         137 my $method = $self->pic->validate_operator($op);
1146 57 50       131 $self->parser->throw_error("Invalid operator '$op' in intermediate code") unless $self->pic->can($method);
1147 57         347 my ($code, $funcs, $macros, $tables) = $self->pic->$method($nvar, $rhs);
1148 57 50       173 return $self->parser->throw_error("Error in intermediate code '$line'") unless $code;
1149 57 50       176 push @code, "\t;; $line" if $self->intermediate_inline;
1150 57 50       306 push @code, $code if $code;
1151 57 100 66     233 $self->_update_funcs($funcs, $macros) if ($funcs or $macros);
1152 57 100       166 $self->_update_tables($tables) if $tables;
1153             }
1154             } else {
1155 0         0 return $self->parser->throw_error(
1156             "Error in intermediate code '$line': $varname doesn't exist");
1157             }
1158 88         311 return @code;
1159             }
1160              
1161             sub find_nearest_loop {
1162 7     7 0 10 my ($self, $mapping, $child) = @_;
1163 7 50       22 return unless exists $mapping->{$child};
1164 7 50       14 if (exists $mapping->{$child}->{loop}) {
1165 7 100       18 return $child if $mapping->{$child}->{loop} eq '1';
1166             }
1167 3 50       5 return unless $mapping->{$child}->{parent};
1168 3         8 return $self->find_nearest_loop($mapping, $mapping->{$child}->{parent});
1169             }
1170              
1171             sub generate_code_blocks {
1172 43     43 0 104 my ($self, $line, $block) = @_;
1173 43         71 my @code = ();
1174 43         103 my $ast = $self->ast;
1175 43         158 my $mapping = $ast->{block_mapping};
1176 43   33     171 my $mapped_block = $mapping->{$block}->{block} || $block;
1177 43         210 my ($tag, $label, $child, $parent, $parent_label, $end_label) = split/::/, $line;
1178 43 50 33     318 return if ($child eq $block or $child eq $mapped_block or $child eq $parent);
      33        
1179 43 50       184 return if exists $ast->{generated_blocks}->{$child};
1180 43 50       111 push @code, "\t;; $line" if $self->intermediate_inline;
1181 43         577 my @newcode = $self->generate_code($ast, $child);
1182 43     192   345 my @bindexes = indexes { $_ eq 'BREAK' } @newcode;
  192         241  
1183 43     192   233 my @cindexes = indexes { $_ eq 'CONTINUE' } @newcode;
  192         240  
1184 43 100 66     549 if ($child =~ /^(?:True|False)/ and @newcode) {
    100 66        
    50 33        
1185 16         45 my $cond_end = "\tgoto $end_label;; go back to end of conditional\n";
1186             # handle break
1187 16 100       35 if (@bindexes) {
1188             #find top most parent loop
1189 2         6 my $el = $self->find_nearest_loop($mapping, $child);
1190 2 50       6 $el = $mapping->{$el}->{end_label} if $el;
1191 2         3 my $break_end;
1192 2 50       5 unless ($el) {
1193 0         0 $break_end = "\t;; break from existing block since $child not part of any loop\n";
1194 0         0 $break_end .= "\tgoto $end_label;; break from the conditional\n";
1195             } else {
1196 2         5 $break_end = "\tgoto $el;; break from the conditional\n";
1197             }
1198 2         6 $newcode[$_] = $break_end foreach @bindexes;
1199             }
1200             # handle continue
1201 16 100       30 if (@cindexes) {
1202             #find top most parent loop
1203 2         5 my $sl = $self->find_nearest_loop($mapping, $child);
1204 2 50       5 $sl = $mapping->{$sl}->{start_label} if $sl;
1205 2 50       7 my $cont_start = "\tgoto $sl;; go back to start of conditional\n" if $sl;
1206 2 50       4 $cont_start = "\tnop ;; $child or $parent have no start_label" unless $sl;
1207 2         5 $newcode[$_] = $cont_start foreach @cindexes;
1208             }
1209             # add the end _label
1210             # if the current block is a loop, the end label is the start label
1211 16 100 66     83 if (exists $mapping->{$child}->{loop} and $mapping->{$child}->{loop} eq '1') {
1212 4   33     10 my $slabel = $mapping->{$child}->{start_label} || $end_label;
1213 4 50       15 my $start_code = "\tgoto $slabel ;; go back to start of conditional\n" if $slabel;
1214 4 50       6 $start_code = $cond_end unless $start_code;
1215 4         8 push @newcode, $start_code;
1216             } else {
1217 12         22 push @newcode, $cond_end;
1218             }
1219 16         41 push @newcode, ";;;; end of $label";
1220             # hack into the function list
1221 16         63 $ast->{funcs}->{$label} = [@newcode];
1222             } elsif ($child =~ /^(?:Action|ISR)/ and @newcode) {
1223 11         46 my $cond_end = "\tgoto $end_label ;; go back to end of block\n";
1224 11 50       45 if (@bindexes) {
1225             # we just break from the current block since we are not in any
1226             # sub-block
1227 0         0 my $break_end = "\tgoto $end_label ;; break from the block\n";
1228 0         0 $newcode[$_] = $break_end foreach @bindexes;
1229             }
1230 11 50       41 if (@cindexes) {
1231             # continue gets ignored
1232 0         0 my $cont_start = ";; continue is a NOP for $child block";
1233 0         0 $newcode[$_] = $cont_start foreach @cindexes;
1234             }
1235 11         42 push @newcode, $cond_end, ";;;; end of $label";
1236             # hack into the function list
1237 11         71 $ast->{funcs}->{$label} = [@newcode];
1238             } elsif ($child =~ /^Loop/ and @newcode) {
1239 16         72 my $cond_end = "\tgoto $end_label;; go back to end of block\n";
1240 16 50       55 if (@bindexes) {
1241             # we just break from the current block since we are not in any
1242             # sub-block and are in a Loop already
1243 0         0 my $break_end = "\tgoto $end_label ;; break from the block\n";
1244 0         0 $newcode[$_] = $break_end foreach @bindexes;
1245             }
1246 16 50       61 if (@cindexes) {
1247             # continue goes to start of the loop
1248 0         0 my $cont_start = "\tgoto $label ;; go back to start of loop\n";
1249 0         0 $newcode[$_] = $cont_start foreach @cindexes;
1250             }
1251 16         49 push @code, @newcode;
1252 16         62 push @code, "\tgoto $label ;;;; end of $label\n";
1253 16         41 push @code, "$end_label:\n";
1254             } else {
1255 0 0       0 push @code, @newcode if @newcode;
1256             }
1257 43 50       190 $ast->{generated_blocks}->{$child} = 1 if @newcode;
1258             # parent equals block if it is the topmost of the stack
1259             # if the child is not a loop construct it will need a goto back to
1260             # the parent construct. if a child is a loop construct it will
1261             # already have a goto back to itself
1262 43 50 33     415 if (defined $parent and exists $ast->{$parent} and
      33        
      33        
1263             ref $ast->{$parent} eq 'ARRAY' and $parent ne $mapped_block) {
1264 0         0 my ($ptag, $plabel) = split /::/, $ast->{$parent}->[0];
1265 0 0       0 push @code, "\tgoto $plabel;; $plabel" if $plabel;
1266             }
1267 43         212 return @code;
1268             }
1269              
1270             sub generate_code_conditionals {
1271 9     9 0 31 my ($self, @condblocks) = @_;
1272 9         15 my @code = ();
1273 9         32 my $ast = $self->ast;
1274 9         48 my ($start_label, $end_label, $is_loop);
1275 9         12 my $blockcount = scalar @condblocks;
1276 9         14 my $index = 0;
1277 9         17 foreach my $line (@condblocks) {
1278 12 50       26 push @code, "\t;; $line" if $self->intermediate_inline;
1279 12         106 my %hh = split /::/, $line;
1280 12         28 my $subj = $hh{SUBJ};
1281 12 100       37 $index++ if $hh{SUBCOND};
1282             # for multiple if-else-if-else we adjust the labels
1283             # for single ones we do not
1284 12 100       149 $start_label = "_start_conditional_$hh{COND}" unless defined $start_label;
1285 12 100       23 $is_loop = $hh{LOOP} unless defined $is_loop;
1286 12 100       32 $end_label = $hh{END} unless defined $end_label;
1287             # we now modify the TRUE/FALSE/END labels
1288 12 100       29 if ($blockcount > 1) {
1289 4         290 my $el = "$hh{END}_$index"; # new label
1290 4 100       67 $hh{FALSE} = $el if $hh{FALSE} eq $hh{END};
1291 4 50       10 $hh{TRUE} = $el if $hh{TRUE} eq $hh{END};
1292 4         6 $hh{END} = $el;
1293             }
1294 12 100       551 if ($subj =~ /^\d+?$/) { # if subject is a literal
    50          
    50          
1295 1 50       4 push @code, "\t;; $line" if $self->intermediate_inline;
1296 1 50       10 if ($subj eq 0) {
1297             # is false
1298 0 0       0 push @code, "\tgoto $hh{FALSE}" if $hh{FALSE};
1299             } else {
1300             # is true
1301 1 50       24 push @code, "\tgoto $hh{TRUE}" if $hh{TRUE};
1302             }
1303 1 50       5 push @code, "\tgoto $hh{END}" if $hh{END};
1304 1 50       6 push @code, "$hh{END}:\n" if $hh{END};
1305             } elsif (exists $ast->{variables}->{$subj}) {
1306             ## we will never get here actually since we have eliminated this
1307             #possibility
1308 0         0 XXX \%hh;
1309             } elsif (exists $ast->{tmp_variables}->{$subj}) {
1310 11         54 my $tmp_code = $ast->{tmp_variables}->{$subj};
1311 11         34 my @deps = $self->find_tmpvar_dependencies($subj);
1312 11         26 my @vdeps = $self->find_var_dependencies($subj);
1313 11 100       27 push @deps, $subj if @deps;
1314 11 50       24 if ($self->intermediate_inline) {
1315 0 0       0 push @code, "\t;; TMP_VAR DEPS - $subj, ". join (',', @deps) if @deps;
1316 0 0       0 push @code, "\t;; VAR DEPS - ". join (',', @vdeps) if @vdeps;
1317 0         0 push @code, "\t;; $subj = $tmp_code";
1318             }
1319 11 100       59 if (scalar @deps) {
1320 2         14 $ast->{tmp_stack_size} = max(scalar(@deps), $ast->{tmp_stack_size});
1321             ## it is assumed that the dependencies and intermediate code are
1322             #arranged in expected order
1323             # TODO: bits check
1324 2         65 my $counter = 0;
1325 2         8 my %tmpstack = map { $_ => 'VIC_STACK + ' . $counter++ } sort(@deps);
  6         262  
1326 2         114 $counter = 0; # reset
1327 2         8 foreach (sort @deps) {
1328 6         13 my $tcode = $ast->{tmp_variables}->{$_};
1329 6         20 my %extra = (%hh, COUNTER => $counter++);
1330 6 100       340 $extra{RESULT} = $tmpstack{$_} if $_ ne $subj;
1331 6 50       33 my @newcode = $self->generate_code_operations($tcode,
1332             STACK => \%tmpstack, %extra) if $tcode;
1333 6 50       51 push @code, @newcode if @newcode;
1334             }
1335             } else {
1336             # no tmp-var dependencies
1337 9         21 my $use_stack = $self->do_i_use_stack(@vdeps);
1338 9 50       62 unless ($use_stack) {
1339 9         222 my @newcode = $self->generate_code_operations($tmp_code, %hh);
1340 9 50       41 push @code, @newcode if @newcode;
1341 9 50       48 return $self->parser->throw_error("Error in intermediate code '$tmp_code'")
1342             unless @newcode;
1343             } else {
1344             # TODO: stack
1345 0         0 XXX \%hh;
1346             }
1347             }
1348             } else {
1349 0         0 return $self->parser->throw_error("Error in intermediate code '$line'");
1350             }
1351             }
1352 9 50       35 unshift @code, "$start_label:" if defined $start_label;
1353 9 100 66     35 push @code, "$end_label:" if defined $end_label and $blockcount > 1;
1354 9         586 return @code;
1355             }
1356              
1357             sub generate_code {
1358 94     94 0 244 my ($self, $ast, $block_name) = @_;
1359 94         144 my @code = ();
1360 94 0       281 return wantarray ? @code : [] unless defined $ast;
    50          
1361 94 50       274 return wantarray ? @code : [] unless exists $ast->{$block_name};
    100          
1362 93 100       276 $ast->{generated_blocks} = {} unless defined $ast->{generated_blocks};
1363 93         263 push @code, ";;;; generated code for $block_name";
1364 93         197 my $blocks = $ast->{$block_name};
1365 93         301 while (@$blocks) {
1366 510         736 my $line = shift @$blocks;
1367 510 100       810 next unless defined $line;
1368 507 100       2720 if ($line =~ /^BLOCK::\w+/) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
1369 43   50     166 my $blockparams = $ast->{block_mapping}->{$block_name}->{params} || [];
1370 43         239 push @code, $self->generate_code_blocks($line, $block_name, $blockparams);
1371             } elsif ($line =~ /^INS::\w+/) {
1372 136         377 push @code, $self->generate_code_instruction($line);
1373             } elsif ($line =~ /^UNARY::\w+/) {
1374 8         29 push @code, $self->generate_code_unary_expr($line);
1375             } elsif ($line =~ /^SET::\w+/) {
1376 83         206 push @code, $self->generate_code_assign_expr($line);
1377             } elsif ($line =~ /^PARAM::(\w+)::(\w+)::(\w+)/) {
1378 5 50       17 if (exists $ast->{block_mapping}->{$block_name}) {
1379 5         13 my $op = $1;
1380 5         27 my $pblock = $2;
1381 5         13 my $pvar = $3;
1382 5         13 my $mapping = $ast->{block_mapping}->{$pblock};
1383 5         9 my $param_idx = scalar @{$mapping->{params}};
  5         28  
1384 5   33     19 my $paramvar = $mapping->{param_prefix} || lc($block_name . '_param');
1385 5         13 $paramvar .= $param_idx;
1386 5         15 push @{$mapping->{params}}, $paramvar;
  5         12  
1387             # map the param index back to the other mapping too
1388 5 50 33     32 if ($pblock ne $block_name and $mapping->{block} eq $block_name) {
1389 5         11 my $mapping2 = $ast->{block_mapping}->{$block_name};
1390 5         18 $mapping2->{params} = $mapping->{params};
1391             }
1392 5         25 my $pline = "SET::${op}::${pvar}::${paramvar}";
1393             #YYY [$pblock, $pvar, $block_name, $param_idx, $pline, $paramvar];
1394 5         28 push @code, $self->generate_code_assign_expr($pline);
1395             } else {
1396 0         0 $self->parser->throw_error("Intermediate code '$line' in block "
1397             . "$block_name cannot be handled");
1398             }
1399             } elsif ($line =~ /^LABEL::(\w+)/) {
1400 93         354 my $lbl = $1;
1401 93 50       334 push @code, ";; $line" if $self->intermediate_inline;
1402 93 100       806 push @code, "$lbl:\n" if $lbl ne '_vic_simulator';
1403             } elsif ($line =~ /^COND::(\d+)::/) {
1404 9         23 my $cblock = $1;
1405 9         17 my @condblocks = ( $line );
1406 9         32 for my $i (1 .. scalar @$blocks) {
1407 17 100       2488 next unless $blocks->[$i - 1] =~ /^COND::${cblock}::/;
1408 3         483 push @condblocks, $blocks->[$i - 1];
1409 3         467 delete $blocks->[$i - 1];
1410             }
1411 9         850 push @code, $self->generate_code_conditionals(reverse @condblocks);
1412             } elsif ($line =~ /^SIM::\w+/) {
1413 130         291 push @code, $self->generate_simulator_instruction($line);
1414             } else {
1415 0         0 $self->parser->throw_error("Intermediate code '$line' cannot be handled");
1416             }
1417             }
1418 92 50       453 return wantarray ? @code : [@code];
1419             }
1420              
1421             sub final {
1422 32     32 1 1034 my ($self, $got) = @_;
1423 32         257 my $ast = $self->ast;
1424 32 50       139 return $self->parser->throw_error("Missing '}'") if scalar @{$ast->{block_stack}};
  32         143  
1425 32 50       143 return $self->parser->throw_error("Main not defined") unless defined $ast->{Main};
1426             # generate main code first so that any addition to functions, macros,
1427             # variables during generation can be handled after
1428 32         202 my @main_code = $self->generate_code($ast, 'Main');
1429 31         139 push @main_code, "_end_start:\n", "\tgoto \$\t;;;; end of Main";
1430 31         291 my $main_code = join("\n", @main_code);
1431             # variables are part of macros and need to go first
1432 31         94 my $variables = '';
1433 31         95 my $vhref = $ast->{variables};
1434 31 100       144 $variables .= "GLOBAL_VAR_UDATA udata\n" if keys %$vhref;
1435 31         69 my @global_vars = ();
1436 31         58 my @tables = ();
1437 31         56 my @init_vars = ();
1438 31         135 foreach my $var (sort(keys %$vhref)) {
1439 42         101 my $name = $vhref->{$var}->{name};
1440 42   50     104 my $typ = $vhref->{$var}->{type} || 'byte';
1441 42         85 my $data = $vhref->{$var}->{data};
1442 42   66     161 my $label = $vhref->{$var}->{label} || $name;
1443 42         86 my $szvar = $vhref->{$var}->{size};
1444 42 100       192 if ($typ eq 'string') {
    50          
    100          
1445             ##this may need to be stored in a different location
1446 2 50       4 $data = '' unless defined $data;
1447             ## different PICs may have different string handling
1448 2         7 my ($scode, $szdecl)= $self->pic->store_string($data, $label, $szvar);
1449 2         5 push @tables, $scode;
1450 2 50       18 $variables .= $szdecl if $szdecl;
1451             } elsif ($typ eq 'ARRAY') {
1452 0 0       0 $data = [] unless defined $data;
1453 0         0 push @init_vars, $self->pic->store_array($data, $label,
1454             scalar(@$data), $szvar);
1455             } elsif ($typ eq 'HASH') {
1456 1 50       10 $data = {} unless defined $data;
1457 1 50       41 next unless defined $data->{TABLE};
1458 1         3 my $table = $data->{TABLE};
1459 1         6 my ($code, $szdecl) = $self->pic->store_table($table, $label,
1460             scalar(@$table), $szvar);
1461 1         3 push @tables, $code;
1462 1 50       4 push @init_vars, $szdecl if $szdecl;
1463             } else {# $typ == 'byte' or any other
1464             # should we care about scope ?
1465 39         210 $variables .= "$name res $vhref->{$var}->{size}\n";
1466 39 100 100     252 if (($vhref->{$var}->{scope} eq 'global') or
1467             ($ast->{code_config}->{variable}->{export})) {
1468 20         43 push @global_vars, $name;
1469             }
1470             }
1471             }
1472 31 100       175 if ($ast->{tmp_stack_size}) {
1473 3         19 $variables .= "VIC_STACK res $ast->{tmp_stack_size}\t;; temporary stack\n";
1474             }
1475 31 100       874 if (scalar @global_vars) {
1476             # export the variables
1477 5         31 $variables .= "\tglobal ". join (", ", @global_vars) . "\n";
1478             }
1479 31 100       124 if (scalar @init_vars) {
1480 1         3 $variables .= "\nGLOBAL_VAR_IDATA idata\n"; # initialized variables
1481 1         3 $variables .= join("\n", @init_vars);
1482             }
1483 31         78 my $macros = '';
1484 31         114 foreach my $mac (sort(keys %{$ast->{macros}})) {
  31         206  
1485 80 100       282 $variables .= "\n" . $ast->{macros}->{$mac} . "\n", next if $mac =~ /_var$/;
1486 55         221 $macros .= $ast->{macros}->{$mac};
1487 55         97 $macros .= "\n";
1488             }
1489 31         141 my $isr_checks = '';
1490 31         64 my $isr_code = '';
1491 31         56 my $funcs = '';
1492 31         55 foreach my $fn (sort(keys %{$ast->{funcs}})) {
  31         131  
1493 45         82 my $fn_val = $ast->{funcs}->{$fn};
1494             # the default ISR checks to be done first
1495 45 100       215 if ($fn =~ /^isr_\w+$/) {
    100          
1496 5 50       17 if (ref $fn_val eq 'ARRAY') {
1497 0         0 $isr_checks .= join("\n", @$fn_val);
1498             } else {
1499 5         23 $isr_checks .= $fn_val . "\n";
1500             }
1501             # the user ISR code to be handled next
1502             } elsif ($fn =~ /^_isr_\w+$/) {
1503 5 50       24 if (ref $fn_val eq 'ARRAY') {
1504 5         22 $isr_code .= join("\n", @$fn_val);
1505             } else {
1506 0         0 $isr_code .= $fn_val . "\n";
1507             }
1508             } else {
1509 35 100       91 if (ref $fn_val eq 'ARRAY') {
1510 22         90 $funcs .= join("\n", @$fn_val);
1511             } else {
1512 13         40 $funcs .= "$fn:\n";
1513 13 50       58 $funcs .= $fn_val unless ref $fn_val eq 'ARRAY';
1514             }
1515 35         81 $funcs .= "\n";
1516             }
1517             }
1518 31         73 foreach my $tbl (@{$ast->{tables}}) {
  31         99  
1519 5         12 my $dt = $tbl->{bytes};
1520 5         10 my $dn = $tbl->{name};
1521             }
1522 31 100       132 $funcs .= join ("\n", @tables) if scalar @tables;
1523 31         134 $funcs .= $self->pic->store_bytes($ast->{tables});
1524 31 100       130 if (length $isr_code) {
1525 5         23 my $isr_entry = $self->pic->isr_entry;
1526 5         16 my $isr_exit = $self->pic->isr_exit;
1527 5         42 my $isr_var = $self->pic->isr_var;
1528 5         14 $isr_checks .= "\tgoto _isr_exit\n";
1529 5         35 $isr_code = "\tgoto _start\n$isr_entry\n$isr_checks\n$isr_code\n$isr_exit\n";
1530 5         18 $variables .= "\n$isr_var\n";
1531             }
1532 31         95 my ($sim_include, $sim_setup_code) = ('', '');
1533             # we need to generate simulator code if either the Simulator block is
1534             # present or if any asserts are present
1535 31 100 66     147 if ($self->simulator and not $self->simulator->disable and
      100        
      100        
1536             ($ast->{Simulator} or $ast->{asserts})) {
1537 19         767 my $stype = $self->simulator->type;
1538 19         175 $sim_include .= ";;;; generated code for $stype header file\n";
1539 19         49 $sim_include .= '#include <' . $self->simulator->include .">\n";
1540 19         186 my @setup_code = $self->generate_code($ast, 'Simulator');
1541 19         94 my $init_code = $self->simulator->init_code;
1542 19 50       112 $sim_setup_code .= $init_code . "\n" if defined $init_code;
1543 19 100       160 $sim_setup_code .= join("\n", @setup_code) if scalar @setup_code;
1544 19 100       81 if ($self->simulator->should_autorun) {
1545 12         404 $sim_setup_code .= $self->simulator->get_autorun_code;
1546             }
1547             }
1548             # final get of the chip config in case it has been modified
1549 31         1120 $self->ast->{chip_config} = $self->pic->get_chip_config;
1550 31         784 my $pic = <<"...";
1551             ;;;; generated code for PIC header file
1552             #include <$ast->{include}>
1553             $sim_include
1554             ;;;; generated code for variables
1555             $variables
1556             ;;;; generated code for macros
1557             $macros
1558              
1559             $ast->{chip_config}
1560              
1561             \torg $ast->{org}
1562              
1563             $sim_setup_code
1564              
1565             $isr_code
1566              
1567             $main_code
1568              
1569             ;;;; generated code for functions
1570             $funcs
1571             ;;;; generated code for end-of-file
1572             \tend
1573             ...
1574 31         287 return $pic;
1575             }
1576              
1577             1;
1578              
1579             =encoding utf8
1580              
1581             =head1 NAME
1582              
1583             VIC::Receiver
1584              
1585             =head1 SYNOPSIS
1586              
1587             The Pegex::Receiver class for handling the grammar.
1588              
1589             =head1 DESCRIPTION
1590              
1591             INTERNAL CLASS.
1592              
1593             =head1 AUTHOR
1594              
1595             Vikas N Kumar
1596              
1597             =head1 COPYRIGHT
1598              
1599             Copyright (c) 2014. Vikas N Kumar
1600              
1601             This program is free software; you can redistribute it and/or modify it
1602             under the same terms as Perl itself.
1603              
1604             See http://www.perl.com/perl/misc/Artistic.html
1605              
1606             =cut