Attached is a patch to assemble.pl that adds very simple macros.  I
fear it's a bit of a hack, but I'm fighting my usual impulse to
rewrite stuff.

Attached is also macros.pasm, a simple usage case.  It goes in t/ for
want of a better place, but it's not a true test yet.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net
--- assemble.new-1      Fri Sep 14 12:26:38 2001
+++ assemble.pl Fri Sep 14 13:31:46 2001
@@ -93,24 +93,77 @@
 
 my $listing="PARROT ASSEMBLY LISTING - ".scalar(localtime)."\n\n";
 
-
+# Macro stack.
+sub MACRO_FILE () { 0 }
+sub MACRO_LINE () { 1 }
+sub MACRO_NAME () { 2 }
+sub MACRO_ARGS () { 3 }
+sub MACRO_BODY () { 4 }
+my (@macro_stack, %macros, @input_buffer);
 
 # read source and assemble
 my $pc=0; my $op_pc=0;
 my ($bytecode,%label,%fixup,%constants,@constants);
 my $line=0;
-while(<>) {
+while(1) {
+    my $sline;
+    if (@input_buffer) {
+        $_ = shift @input_buffer;
+    }
+    else {
+        $_ = <>;
+        last unless defined $_;
     $line++;
     chomp;
-    my $sline=$_;
+        $sline=$_;
     s/^\s*//;
     s/\s*$//;
+    }
+
+    # Comments and blank lines.
     if(/^\#/ || $_ eq "") {
        if($options{'listing'}) {
            $listing.=sprintf("%4d %08x %-44s %s\n", $line, $op_pc, '',$sline);
        }
        next;
     }
+
+    # Macro definition.
+    if (m/^MACRO\s+(\S+)\s*(.*?)\s*$/) {
+      my ($label, $args) = ($1, $2);
+      my @args = split /, */, $args;
+
+      die "macro $label redefined at STDIN line $.\n"
+        if exists $macros{$label};
+
+      $macros{$label} =
+        [ 'STDIN',  # MACRO_FILE
+          $.,       # MACRO_LINE
+          $label,   # MACRO_NAME
+          \@args,   # MACRO_ARGS
+          [ ],      # MACRO_BODY
+        ];
+
+      push @macro_stack, $label;
+      next;
+    }
+
+    # Macro done.
+    if (m/^MEND/) {
+      my $label = pop @macro_stack;
+
+      # XXX: Verify that the macro uses all of its arguments at least once.
+
+      next;
+    }
+
+    # Within a macro.
+    if (@macro_stack) {
+      push @{$macros{$macro_stack[-1]}->[MACRO_BODY]}, $_;
+      next;
+    }
+
+    # Opcodes with labels, or just labels by themselves.
     if(m/^((\S+):)?\s*(.+)?/) {
         my($label,$code)=($2,$3);
         if(defined($label) && $label ne "") {
@@ -173,7 +226,24 @@
                    last;
                }
            }
-            error("No opcode $opcode in <$_>") if(!$found_op);
+
+            # Not an opcode.  Is it a macro?
+            unless ($found_op) {
+              error("No opcode or macro $opcode in <$_>") unless exists 
+$macros{$opcode};
+              error("Macro $opcode parameter count doesn't match definition") unless 
+@args == @{$macros{$opcode}->[MACRO_ARGS]};
+
+              # Expand the macro and push it on the input stream.
+              my @macro_body = @{$macros{$opcode}->[MACRO_BODY]};
+              my @macro_args = @{$macros{$opcode}->[MACRO_ARGS]};
+              foreach my $macro_line (@macro_body) {
+                for (my $arg_index = 0; $arg_index < @args; $arg_index++) {
+                  $macro_line =~ s/\b$macro_args[$arg_index]\b/$args[$arg_index]/g;
+                }
+              }
+
+              push @input_buffer, @macro_body;
+              next;
+            }
         }
         if (@args != $opcodes{$opcode}{ARGS}) {
             error("Wrong arg count--got ".scalar(@args)." needed 
".$opcodes{$opcode}{ARGS});
@@ -219,6 +289,14 @@
     }
 }
 $listing.="\n" if($options{'listing'});
+
+# Macro stuff.  Make sure all the macros were closed.
+my $bad_macros = 0;
+foreach (@macro_stack) {
+  $bad_macros++;
+  warn "macro $_ not closed (defined in $macros{$_}->[MACRO_FILE] at 
+$macros{$_}->[MACRO_LINE])\n";
+}
+die "assembly stopped.\n" if $bad_macros;
 
 my $output;
 
# Todo : Local labels (begin with dollar sign)
# Todo?: NAMESPACE (namespace name)
# Todo?: SUB (sub name)
# Todo : symbol  IS      "string"   ; string
# Todo : symbol  IS      1234       ; integer
# Todo : symbol  IS      3.141      ; float
# Todo : symbol  IS      ???        ; memory (variable)

MACRO   inc_and_print   register
        inc             register
        print           register
        print           "\n"
MEND


start:  set             I1, 0
        set             I2, 10
loop:   inc_and_print   I1
        eq              I1, I2, done, loop
done:   end

Reply via email to