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