The following patch fixes the following bugs with macros:
1) Macros with zero parameters were disallowed
2) Local branches inside macros were not being given unique names on a
per-invocation basis. This made it impossible to write the following
code:
--- cut here ---
answer macro R
eq R,42,$done
print 42
$done:
endm
answer 41
answer 42
end
--- cut here ---
Because when the macro was expanded twice, two separate expansions of
$done existed in the generated code, which caused an error. The
presented patch fixes this naively by appending 'LOCAL_$gensym' onto the
label name, creating '$LOCAL_0_done' in the first expansion,
'$LOCAL_1_done' for the second expansion, and so on. A slightly cleaner
solution would be to alter the parser to allow labels of the form
'LOCAL_0_$done' so that an author would stand a much lower risk of
colliding with compiler-generated labels.
On the upside, this patch allows me to continue to add new instructions
to the compiler without fear of label collision.
--
--Jeff
<[EMAIL PROTECTED]>
#! perl -w
use Parrot::Test tests => 6;
output_is( <<'CODE', <<OUTPUT, "macro, zero parameters" );
answer macro
print 42
print "\n"
endm
answer
end
CODE
42
OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one unused parameter, literal term" );
answer macro A
print 42
endm
answer 42
print "\n"
end
CODE
42
OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one unused parameter, register term" );
answer macro A
print 42
endm
set I0, 43
answer I0
print "\n"
end
CODE
42
OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one used parameter, literal" );
answer macro A
print A
endm
answer 42
print "\n"
end
CODE
42
OUTPUT
#
# Can't test because I can't capture errors
#
#output_is( <<'CODE', <<OUTPUT, "macro, one parameter in call, not in def" );
#answer macro
# print A
#endm
# answer 42
# print "\n"
#end
#CODE
#42
#OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one used parameter, register" );
answer macro A
print A
endm
set I0,42
answer I0
print "\n"
end
CODE
42
OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one used parameter, called twice" );
answer macro A
print A
print "\n"
inc A
endm
set I0,42
answer I0
answer I0
end
CODE
42
43
OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one used parameter, label" );
answer macro A
ne I0,42,$done
print A
print "\n"
$done:
endm
set I0,42
answer I0
end
CODE
42
OUTPUT
output_is( <<'CODE', <<OUTPUT, "macro, one used parameter run twice, label" );
answer macro A
ne I0,42,$done
print A
print "\n"
$done:
endm
set I0,42
answer I0
answer I0
end
CODE
42
OUTPUT
diff -ru parrot_orig/Parrot/Assembler.pm parrot/Parrot/Assembler.pm
--- parrot_orig/Parrot/Assembler.pm Sat Nov 3 19:04:08 2001
+++ parrot/Parrot/Assembler.pm Sat Nov 10 21:35:01 2001
@@ -581,6 +581,7 @@
=cut
sub process_program_lines {
+ my $gensym = 0;
while( my $lineinfo = shift( @program ) ) {
($file, $line, $pline, $sline) = @$lineinfo;
@@ -609,7 +610,7 @@
# found a macro, expand it and append its lines to the front of
# the program lines array.
- my @expanded_lines = expand_macro( $opcode, @args );
+ my @expanded_lines = expand_macro( $opcode, $gensym++, @args );
unshift( @program, @expanded_lines );
$lineinfo->[2] = '';
unshift( @program, $lineinfo );
@@ -687,7 +688,7 @@
=cut
sub has_asm_directive {
- return $_[0] =~ /^[_a-zA-Z]\w*\s+macro\s+.+$/i ||
+ return $_[0] =~ /^[_a-zA-Z]\w*\s+macro(?:\s+.+)?$/i ||
$_[0] =~ /^[_a-zA-Z]\w*\s+equ\s+.+$/i;
}
@@ -710,11 +711,16 @@
$equate{$name} = $data;
return 1;
}
- elsif( $line =~ /^([_a-zA-Z]\w*)\s+macro\s+(.+)$/i ) {
+ elsif( $line =~ /^([_a-zA-Z]\w*)\s+macro(?:\s+(.+))?$/i ) {
# a macro definition
my ($name, $args) = ($1, $2);
my $cur_macro = $name;
- $macros{$name} = [ [split( /,\s*/, $args)], [] ];
+ if(defined $args) {
+ $macros{$name} = [ [split( /,\s*/, $args)], [] ];
+ }
+ else {
+ $macros{$name} = [ [], [] ];
+ }
while( 1 ) {
if( !scalar( @program ) ) {
error( "The end of the macro '$name' was never seen", $file, $line);
@@ -830,8 +836,9 @@
=cut
sub expand_macro {
- my ($opcode, @args) = @_;
+ my ($opcode, $gensym, @args) = @_;
+ my $local_prefix = sprintf("LOCAL_%d_",$gensym);
my (@margs) = @{ $macros{$opcode}[0] };
my (@macro);
@@ -840,6 +847,11 @@
foreach (@{ $macros{ $opcode }[1] } ) {
push( @macro, [@$_] );
+ }
+ for(@macro) {
+ $_->[2]=~/\$/ and do {
+ $_->[2]=~s/\$/\$$local_prefix/;
+ };
}
my $nargs = scalar(@args);