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);

Reply via email to