All --

I want to commit some bug fixes and new features for Jako. Given the
freeze, though, I want to make sure that if I do it, nobody is
depending upon jakoc as anything other than an illustration for now.
It works great here (much better than the current CVS version), and
will probably work great for you.

But, given that there are more than just fixes, I want to give folks
a chance to say "hold up" before I push the Big Red Button.

Here's what you'll find:

  * Added a primes.jako based on the primes.pasm example from the list.
  * Source code printed in assembler comments.
  * const int n = 5
  * while ( ... ) { ... } continue { ... }
  * Fixed const-reg and reg-const handling code in comparisons
  * Fixed num-int and int-num arg promotion
  * Some fixed ops.

In case anyone really wants to stare at the patch before forming an
opinion, I'm attaching it. I'm also attaching the primes.jako
source and the primes.pasm assembler file. Note that you'll also
need the one-line assemble.pl patch (which allows end-of-line
comments).


Regards,

-- Gregor
 _____________________________________________________________________ 
/     perl -e 'srand(-2091643526); print chr rand 90 for (0..4)'      \

   Gregor N. Purdy                          [EMAIL PROTECTED]
   Focus Research, Inc.                http://www.focusresearch.com/
   8080 Beckett Center Drive #203                   513-860-3570 vox
   West Chester, OH 45069                           513-860-3579 fax
\_____________________________________________________________________/
Index: assemble.pl
===================================================================
RCS file: /home/perlcvs/parrot/assemble.pl,v
retrieving revision 1.34
diff -u -r1.34 assemble.pl
--- assemble.pl 2001/09/21 05:06:23     1.34
+++ assemble.pl 2001/09/21 13:45:14
@@ -188,6 +188,7 @@
 
     1 while $code=~s/\"([^\\\"]*(?:\\.[^\\\"]*)*)\"/constantize($1)/eg;
     $code=~s/,/ /g;
+    $code =~ s/#.*$//; # Strip end-of-line comments
     my($opcode,@args)=split(/\s+/,$code);    
     if(exists($macros{$opcode})) {
        # found a macro
? primes.jako
? jako.patch
Index: Makefile
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/Makefile,v
retrieving revision 1.5
diff -u -r1.5 Makefile
--- Makefile    2001/09/17 12:51:47     1.5
+++ Makefile    2001/09/21 13:45:47
@@ -34,7 +34,10 @@
 hello.pasm: hello.jako jakoc
        jakoc hello.jako > hello.pasm
 
+primes.pasm: primes.jako jakoc
+       jakoc primes.jako > primes.pasm
 
+
 #
 # Assembly:
 #
@@ -54,15 +57,18 @@
 hello.pbc: hello.pasm ../assemble.pl
        cd ..; assemble.pl $(DIR)/hello.pasm > $(DIR)/hello.pbc
 
+primes.pbc: primes.pasm ../assemble.pl
+       cd ..; assemble.pl $(DIR)/primes.pasm > $(DIR)/primes.pbc
+
 
 #
 # Other targets:
 #
 
-all: bench.pbc euclid.pbc fact.pbc fib.pbc hello.pbc
+all: bench.pbc euclid.pbc fact.pbc fib.pbc hello.pbc primes.pbc
 
 clean:
-       rm -f bench.pasm euclid.pasm euclid.pbc fact.pasm fact.pbc fib.pasm fib.pbc 
hello.pasm hello.pbc
+       rm -f bench.pasm euclid.pasm euclid.pbc fact.pasm fact.pbc fib.pasm fib.pbc 
+hello.pasm hello.pbc primes.pasm primes.pbc
 
 over:
        @make clean
@@ -74,6 +80,7 @@
        cd ..; test_prog $(DIR)/fact.pbc
        cd ..; test_prog $(DIR)/fib.pbc
        cd ..; test_prog $(DIR)/hello.pbc
+       cd ..; test_prog $(DIR)/primes.pbc
 
 #
 # End of file.
Index: bench.jako
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/bench.jako,v
retrieving revision 1.2
diff -u -r1.2 bench.jako
--- bench.jako  2001/09/17 12:51:27     1.2
+++ bench.jako  2001/09/21 13:45:47
@@ -23,10 +23,10 @@
 # $Id: bench.jako,v 1.2 2001/09/17 12:51:27 gregor Exp $
 #
 
-var int q =     1;
-var int w =     1;
-var int i =     1;
-var int j =     1;
+var int q = 1;
+var int w = 1;
+var int i = 1;
+var int j = 1;
 
 while (q < 100) {
   w = 1;
@@ -40,6 +40,3 @@
 
   q++;
 }
-
-end
-
Index: euclid.jako
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/euclid.jako,v
retrieving revision 1.5
diff -u -r1.5 euclid.jako
--- euclid.jako 2001/09/17 12:51:27     1.5
+++ euclid.jako 2001/09/21 13:45:47
@@ -24,14 +24,11 @@
 print("Algorithm E (Euclid's algorithm)\n");
 print("  Calculating gcd($m, $n) = ...\n");
 
-r = mod(m, n);
+r = m % n;
 while (r != 0) {
   m = n;
   n = r;
-  r = mod(m, n);
+  r = m % n;
 } 
 
 print("  ... = $n\n");
-
-end;    
-
Index: fact.jako
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/fact.jako,v
retrieving revision 1.3
diff -u -r1.3 fact.jako
--- fact.jako   2001/09/17 12:51:27     1.3
+++ fact.jako   2001/09/21 13:45:47
@@ -12,10 +12,11 @@
 # $Id: fact.jako,v 1.3 2001/09/17 12:51:27 gregor Exp $
 #
 
-var int n = 15;
-var int i =  0;
-var int f =  1;
+const int n = 15;
 
+var   int i =  0;
+var   int f =  1;
+
 print("Algorithm F1 (The factorial function)\n");
 print("    Calculating fact($n) = ...\n");
 
@@ -25,5 +26,3 @@
 }
 
 print("    ... = $f\n");
-
-end;
Index: fib.jako
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/fib.jako,v
retrieving revision 1.3
diff -u -r1.3 fib.jako
--- fib.jako    2001/09/17 12:51:27     1.3
+++ fib.jako    2001/09/21 13:45:47
@@ -1,5 +1,5 @@
 #
-# fibo.jako
+# fib.jako
 #
 # Adapted from fibo.pasm by [EMAIL PROTECTED]
 #
@@ -10,12 +10,13 @@
 # $Id: fib.jako,v 1.3 2001/09/17 12:51:27 gregor Exp $
 #
 
-var int n = 24;
-var int a =  1;
-var int b =  1;
-var int f =  1; 
-var int i =  3;
+const int n = 24;
 
+var   int a =  1;
+var   int b =  1;
+var   int f =  1; 
+var   int i =  3;
+
 print("Algorithm F2 (Fibonacci's function)\n");
 print("  Calculating fib($n) = ...\n");
 
@@ -27,6 +28,3 @@
 }
 
 print("  ... = $f\n");
-
-end;
-
Index: hello.jako
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/hello.jako,v
retrieving revision 1.2
diff -u -r1.2 hello.jako
--- hello.jako  2001/09/17 12:51:27     1.2
+++ hello.jako  2001/09/21 13:45:47
@@ -11,5 +11,3 @@
 #
 
 print("Hello, world!\n");
-end;
-
Index: jakoc
===================================================================
RCS file: /home/perlcvs/parrot/little_languages/jakoc,v
retrieving revision 1.8
diff -u -r1.8 jakoc
--- jakoc       2001/09/20 13:06:53     1.8
+++ jakoc       2001/09/21 13:45:48
@@ -19,13 +19,17 @@
 
 use strict;
 
+use Carp;
 
+
 #
 # Global variables:
 #
 
 my $line;    # Current source line number
+
 my %ident;   # Identifiers
+
 my %regs = ( # Registers
   I => [ undef ], # $regs{I}[0] reserved for integral  temporaries
   N => [ undef ], # $regs{N}[0] reserved for numeric   temporaries
@@ -33,6 +37,13 @@
   S => [ undef ], # $regs{S}[0] reserved for string    temporaries
 );
 
+my %types = (
+ 'I' => 'int',
+ 'N' => 'num',
+ 'P' => 'poly',
+ 's' => 'str',
+);
+
 my $block_count = 0;
 my @block_stack = ();
 
@@ -67,23 +78,156 @@
 
 
 #
+# push_label()
+#
+# Used to push a label onto a stack of labels that will get
+# printed at the beginning of the next emit_code() call.
+#
+# Actually, pushes as many labels as there are arguments.
+#
+
+my @label_stack = ( );
+
+sub push_label
+{
+  push @label_stack, grep { defined $_ and $_ ne '' } @_;
+}
+
+
+#
+# push_comment()
+#
+# Used to push a comment onto a stack of comments that will get
+# printed at the beginning of the next emit_code() call.
+#
+# Actually, pushes as many comments as there are arguments.
+#
+
+my @comment_stack = ( );
+
+sub push_comment
+{
+  push @comment_stack, grep { defined $_ and $_ ne '' } @_;
+}
+
+
+#
+# push_source()
+#
+# Used to push source code comments. This allows them to be
+# indented.
+#
+
+sub push_source
+{
+  my $block_indent = '  ' x scalar(@block_stack);
+  push_comment map { $block_indent . $_ } @_;
+}
+
+
+#
 # emit_code()
 #
 # emit_code LABEL
 # emit_code LABEL OP
 # emit_code LABEL OP ARGS
+# emit_code LABEL OP ARGS COMMENT
 #
 # Label can be undef.
 #
 
+my $last_op = 'noop';
+
 sub emit_code
 {
-  my ($label, $op, @args) = @_;
+  my ($op, $args, $comment) = @_;
+
+  #
+  # Incorporate any comments into the comment stack:
+  #
+
+  push_comment($comment) if defined $comment and $comment ne '';
+  $comment = undef;
+
+  #
+  # Complain about any undefined arguments:
+  #
+
+  if (defined $args and grep { !defined $_ } @$args) {
+    confess "jakoc: Internal error: Undefined arguments to emit_code()!";
+    return;
+  }
+
+  #
+  # Print out all but the last comment, each on a line by itself:
+  #
+  # NOTE: As of 2001-09-20, the assembler doesn't handle lines with
+  # only a label and a comment. So, we write these out separately.
+  #
+
+  $comment = pop @comment_stack if @comment_stack;
+
+  foreach my $comment (@comment_stack) {
+    printf "%-16s %-8s %-25s # %s\n", '', '', '', $comment;
+  }
+
+  @comment_stack = ( );
+
+  #
+  # Print out all but the last label, each on a line by itself:
+  #
+
+  my $label = pop @label_stack if @label_stack;
+
+  foreach my $label (@label_stack) {
+    print "$label:\n";
+  }
+
+  @label_stack = ( );
+
+  #
+  # Print out the label for the actual code line (if any):
+  #
+
+  if (defined $label and $label ne '') {
+    printf "%-16s ", "$label:";
+  } else {
+    printf "%-16s ", '';
+  }
+
+  #
+  # Print out the op mnemonic, if any:
+  #
+
+  if (defined $op) {
+    printf "%-8s", $op;
+    $last_op = $op;
+  } else {
+    printf "%-8s", '';
+  }
+
+  #
+  # Print out the arguments, if any:
+  #
+
+  if (defined $args and @$args) {
+    printf " %-25s", join(", ", @$args);
+  } else {
+    printf " %-25s", '';
+  }
 
-  printf "%-12s ", ((defined $label and $label ne '') ? "$label:" : '');
-  printf "%-8s", $op if defined $op; 
-  print " " if @args;
-  print join(", ", @args) if @args;
+  #
+  # Print out the comment, if any:
+  #
+
+  if (defined $comment) {
+    printf(" # %s", $comment) if defined $comment;
+  }
+
+  #
+  # End the line like a good text generator:
+  #
+
   print "\n";
 }
 
@@ -95,24 +239,88 @@
 sub int_q
 {
   my ($value) = @_;
-
   return $value =~ m/^-?\d+$/;
 }
 
 
 #
+# num_q()
+#
+
+sub num_q
+{
+  my ($value) = @_;
+  return $value =~ m/^-?\d+\.\d+$/;
+}
+
+
+#
+# int_or_num_q()
+#
+
+sub int_or_num_q
+{
+  my ($value) = @_;
+  return $value =~ m/^-?\d+(\.\d+)?$/;
+}
+
+
+#
 # reg_q()
 #
 
 sub reg_q
 {
   my ($value) = @_;
-
   return $value =~ m/^[INPS]\d+$/;
 }
 
 
 #
+# int_reg_q()
+#
+
+sub int_reg_q
+{
+  my ($value) = @_;
+  return $value =~ m/^I\d+$/;
+}
+
+
+#
+# num_reg_q()
+#
+
+sub num_reg_q
+{
+  my ($value) = @_;
+  return $value =~ m/^N\d+$/;
+}
+
+
+#
+# poly_reg_q()
+#
+
+sub poly_reg_q
+{
+  my ($value) = @_;
+  return $value =~ m/^P\d+$/;
+}
+
+
+#
+# str_reg_q()
+#
+
+sub str_reg_q
+{
+  my ($value) = @_;
+  return $value =~ m/^S\d+$/;
+}
+
+
+#
 # op_comp()
 #
 # There are three kinds of entries in the %comp_ops hash:
@@ -126,6 +334,8 @@
 #     we need to reverse the sense of the operator as it appears
 #     in the source.
 #
+# TODO: Handle numeric comparisons, too!
+#
 
 my %comp_ops = (
   'eq' => sub { $_[0] == $_[1] },
@@ -152,37 +362,79 @@
 
 sub op_comp
 {
-  my ($label, $op, $a, $b, $true, $false) = @_;
+  my ($type, $op, $a, $b, $true, $false) = @_;
 
   $op = $comp_ops{$op} unless ref $comp_ops{$op}; # Map, e.g., '>=' to 'ge'
 
+  #
+  # OPTIMIZE const-const comparisons to unconditional branches:
+  #
+
+  if (int_or_num_q($a) and int_or_num_q($b)) {
+    if (&{$comp_ops{$op}}($a, $b)) {
+      emit_code('branch', [$true]);
+    } else {
+      emit_code('branch', [$false]);
+    }
+
+    return;
+  }
+
+  #
+  # CONVERT const-reg and reg-const comparisons to reg-reg comparisons:
+  #
+
+  if (int_q($a)) {
+    emit_code('set', [int_reg_q($b) ? 'I0' : 'N0', $a]);
+    $a = 'I0';
+  } elsif (num_q($a)) {
+    emit_code('set', ['N0', $a]);
+    $a = 'N0';
+  } elsif (!reg_q($a)) {
+    printf STDERR "jako: Syntax error on line %d!\n", $line;
+  }
+
   if (int_q($b)) {
-    if (int_q($a)) {
-      if (&{$comp_ops{$op}}($a, $b)) {
-        emit_code($label, 'branch', $true);
-      } else {
-        emit_code($label, 'branch', $false);
-      }
-    } elsif (reg_q($a)) {
-      emit_code($label, 'set', 'I0', $b);
-      emit_code(undef, $op, $a, 'I0', $true);
-      emit_code('', 'branch', $false);
-    } else {
-      printf STDERR "jako: Syntax error on line %d!\n", $line;
-    }
-  } elsif (reg_q($b)) {
-    if (int_q($a)) {
-      emit_code($label, 'set', 'I0', $a);
-      emit_code(undef, $op, 'I0', $b, $true);
-      emit_code('', 'branch', $false);
-    } elsif (reg_q($a)) {
-      emit_code($label, $op, $a, $b, $true);
-      emit_code('', 'branch', $false);
+    emit_code('set', [int_reg_q($a) ? 'I0' : 'N0', $b]);
+    $b = 'I0';
+  } elsif (num_q($b)) {
+    emit_code('set', ['N0', $b]);
+    $b = 'N0';
+  } elsif (!reg_q($b)) {
+    printf STDERR "jako: Syntax error on line %d!\n", $line;
+  }
+
+  #
+  # CONVERT num-int and int-num comparisons to num-num comparisons:
+  #
+
+  if (substr($a, 0, 1) ne substr($b, 0, 1)) {
+    if ($a =~ m/^I/) {
+      emit_code('iton', ['N0', $a]);
+      $a = 'N0';
+    } elsif ($b =~ m/^I/) {
+      emit_code('iton', ['N0', $b]);
+      $b = 'N0';
     } else {
-      printf STDERR "jako: Syntax error on line %d!\n", $line;
+      confess "jako: Internal compiler error. Expected to have to use iton op.";
+    }
+  }
+
+  #
+  # EMIT code:
+  #
+
+  if (defined $true) {
+    emit_code($op, [$a, $b, $true]);
+    if (defined $false) {
+      emit_code('branch', [$false]);
     }
   } else {
-    printf STDERR "jako: Syntax error on line %d!\n", $line;
+    if (defined $false) {
+      op_comp($type, $comp_ops{"!$op"}, $a, $b, $false, $true);
+    } else {
+      confess "jako: Internal compiler error: op_comp called without any 
+destinations!";
+    }
   }
 }
 
@@ -204,7 +456,8 @@
 sub op_eq
 {
   my ($label, $a, $b, $le, $gt) = @_;
-  op_comp($label, 'eq', $a, $b, $le, $gt);
+  push_label($label);
+  op_comp(undef, 'eq', $a, $b, $le, $gt);
 }
 
 
@@ -225,7 +478,8 @@
 sub op_ne
 {
   my ($label, $a, $b, $le, $gt) = @_;
-  op_comp($label, 'ne', $a, $b, $le, $gt);
+  push_label($label);
+  op_comp(undef, 'ne', $a, $b, $le, $gt);
 }
 
 
@@ -246,7 +500,8 @@
 sub op_le
 {
   my ($label, $a, $b, $le, $gt) = @_;
-  op_comp($label, 'le', $a, $b, $le, $gt);
+  push_label($label);
+  op_comp(undef, 'le', $a, $b, $le, $gt);
 }
 
 
@@ -267,7 +522,8 @@
 sub op_lt
 {
   my ($label, $a, $b, $le, $gt) = @_;
-  op_comp($label, 'lt', $a, $b, $le, $gt);
+  push_label($label);
+  op_comp(undef, 'lt', $a, $b, $le, $gt);
 }
 
 
@@ -288,7 +544,8 @@
 sub op_ge
 {
   my ($label, $a, $b, $le, $gt) = @_;
-  op_comp($label, 'ge', $a, $b, $le, $gt);
+  push_label($label);
+  op_comp(undef, 'ge', $a, $b, $le, $gt);
 }
 
 
@@ -309,7 +566,8 @@
 sub op_gt
 {
   my ($label, $a, $b, $le, $gt) = @_;
-  op_comp($label, 'gt', $a, $b, $le, $gt);
+  push_label($label);
+  op_comp(undef, 'gt', $a, $b, $le, $gt);
 }
 
 
@@ -324,21 +582,22 @@
 #  printf(STDERR "jako: debug: Declaring variable '%s' of type '%s'...\n", $name, 
$type);
 
   if ($ident{$name}) {
-    printf STDERR "jako: Redeclaration of variable '%s' on line %d. Previous 
declaration on line %d.\n",
+    printf STDERR "jako: Redeclaration of identifier '%s' on line %d. Previous 
+declaration on line %d.\n",
       $name, $line, $ident{$name}{LINE};
     return 0;
   } else {
     my $num = scalar @{$regs{$type}};
 
-    $ident{$name}{LINE} = $line;
-    $ident{$name}{TYPE} = $type;
-    $ident{$name}{NUM}  = $num;
-    $ident{$name}{REG}  = "$type$num";
+    $ident{$name}{LINE}  = $line;
+    $ident{$name}{TYPE}  = $type;
+    $ident{$name}{NUM}   = $num;
+    $ident{$name}{REG}   = "$type$num";
+    $ident{$name}{VALUE} = "$type$num";
 
     $regs{$type}[$num]{LINE} = $line;
     $regs{$type}[$num]{NAME} = $name;
 
-    print "# $name: $type$num\n";
+    push_source "var $types{$type} $name;";
 
     return 1;
   }
@@ -346,6 +605,34 @@
 
 
 #
+# declare_const()
+#
+
+sub declare_const
+{
+  my ($name, $type, $value) = @_;
+
+#  printf(STDERR "jako: debug: Declaring constant '%s' of type '%s'...\n", $name, 
+$type);
+
+  if ($ident{$name}) {
+    printf STDERR "jako: Redeclaration of identifier '%s' on line %d. Previous 
+declaration on line %d.\n",
+      $name, $line, $ident{$name}{LINE};
+    return 0;
+  } else {
+    $ident{$name}{LINE}  = $line;
+    $ident{$name}{TYPE}  = $type;
+    $ident{$name}{NUM}   = undef;
+    $ident{$name}{REG}   = undef;
+    $ident{$name}{VALUE} = $value;
+
+    push_source "const $types{$type} $name = $value;";
+
+    return 1;
+  }
+}
+
+
+#
 # assign_var()
 #
 
@@ -354,26 +641,33 @@
   my ($name, $type, $value) = @_;
 
   if ($ident{$name}) {
+    if (!defined $ident{$name}{REG}) {
+      printf(STDERR "jako: Cannot assign to constant '%s' on line %d.\n", $name, 
+$line);
+      return;
+    }
+
+    push_source "$name = $value";
+
     if ($type eq '*') {
       if ($ident{$value}) {
         if ($ident{$name}{TYPE} ne $ident{$value}{TYPE}) {
           if ($ident{$name}{TYPE} eq "N" and $ident{$value}{TYPE} eq "I") {
-            printf "%-12s %-8s %s\n", '', 'iton', "$ident{$name}{REG}, 
$ident{$value}{REG}";
+            emit_code('iton', ["$ident{$name}{REG}, $ident{$value}{REG}"]);
           } elsif ($ident{$name}{TYPE} eq "I" and $ident{$value}{TYPE} eq "N") {
-            printf "%-12s %-8s %s\n", '', 'ntoi', "$ident{$name}{REG}, 
$ident{$value}{REG}";
+            emit_code('ntoi', ["$ident{$name}{REG}, $ident{$value}{REG}"]);
           } else {
             printf(STDERR "jako: Cannot assign type '%s' to type '%s' on line %d.\n", 
                     $ident{$value}{TYPE}, $ident{$name}{TYPE}, $line);
           }
         } else {
-            printf "%-12s %-8s %s\n", '', 'set', "$ident{$name}{REG}, 
$ident{$value}{REG}";
+            emit_code('set', [$ident{$name}{REG}, $ident{$value}{REG}]);
         }
       } else {
         printf(STDERR "jako: Assignment from undefined variable '%s' on line %d.\n",
           $value, $line);
       }
     } elsif ($ident{$name}{TYPE} eq $type) {
-      printf "%-12s %-8s %s\n", '', 'set', "$ident{$name}{REG}, $value";
+      emit_code('set', [$ident{$name}{REG}, $value]);
     } else {
       printf(STDERR "jako: Assignment of %s variable from %s value not allowed on 
line %d.\n", 
         $ident{$name}{TYPE}, $type, $line);
@@ -395,11 +689,15 @@
   my @result;
 
   foreach my $arg (@args) {
+    die "map_args(): Undefined argument in array!\n" unless defined $arg;
+
     $arg =~ s/^\s+//;
     $arg =~ s/\s+$//;
 
     if ($ident{$arg}) {
-      push @result, $ident{$arg}{REG};
+      confess "jakoc: Internal compiler error: Unable to fetch value for identifier 
+'$arg'!"
+        unless defined $ident{$arg}{VALUE};
+      push @result, $ident{$arg}{VALUE};
     } elsif ($arg =~ m/^"/) {
       push @result, $arg;
     } elsif ($arg =~ m/^\d+$/) {
@@ -424,10 +722,12 @@
 {
   my ($name, @args) = @_;
 
+  push_source "$name(...);" unless $name eq 'print';
+
   if ($name eq 'print') {
     @args = map_args(@args);
     foreach my $arg (@args) {
-      printf "%-12s %-8s %s\n", '', $name, $arg;
+      emit_code($name, [$arg]);
     }
   } else {
     printf(STDERR "jako: Unrecognized function '%s' on line %d.\n", $name, $line);
@@ -443,9 +743,11 @@
 {
   my ($dest, $name, @args) = @_;
 
+  push_source "$dest = $name(" . join(", ", @args) . ");";
+
   if ($name eq 'mod') {
     @args = map_args($dest, @args);
-    printf "%-12s %-8s %s\n", '', $name, join(", ", @args);
+    emit_code($name, [@args]);
   } else {
     printf(STDERR "jako: Unrecognized function '%s' on line %d.\n", $name, $line);
   }
@@ -455,34 +757,81 @@
 #
 # begin_block()
 #
+# TODO: bare, until, unless, elsif
+#
+
+my %block_types = (
+  'while'    => { PREFIX => "_W", NEXT => 'CONT', REDO => 'REDO', LAST => 'LAST' },
+  'if'       => { PREFIX => "_I", NEXT => 'TEST', REDO => 'THEN', LAST => 'ELSE' }
+);
 
+my %block_names = ( );
+
 sub begin_block
 {
-  my ($type,$cond) = @_;
-  my %prefix = ( "while" => "_W", "if" => "_I" );
+  my ($name, $type, $cond) = @_;
   $block_count++;
 
-  my $prefix = "$prefix{$type}$block_count";
-  
-  push @block_stack, { TYPE => $type, NEXT => $line, PREFIX => $prefix };
+  my $prefix;
+
+  if (defined $name) {
+    if ($block_names{$name}) {
+      printf STDERR "jakoc: Loop named '%s' already defined at line %d (previously 
+defined at line %d)!\n",
+        $name, $line, $block_names{$name};
+    }
+
+    $prefix = $name;
+  } else {
+    $prefix = "$block_types{$type}{PREFIX}$block_count";
+    $name   = $prefix;
+  }
+
+  $block_names{$name} = $line;
+
+#  my $next   = $block_types{$type}{NEXT};
+#  my $redo   = $block_types{$type}{REDO};
+#  my $last   = $block_types{$type}{LAST};
+
+  if ($cond =~ m/^(\S+)\s*(==|!=|<=|<|>=|>)\s*(.*)$/) {
+    push_source "$name: $type ($1 $2 $3) {";
+
+    push_label "${prefix}_" . uc $type;
+
+    if ($type eq 'while') {
+      push_label "${prefix}_NEXT";
+    } elsif ($type eq 'if') {
+      push_label "${prefix}_TEST";
+    } else {
+      die;
+    }
+
+    if ($type eq 'while') {
+      op_comp($type, $2, map_args($1, $3), undef, "${prefix}_LAST");
+    } elsif ($type eq 'if') {
+      op_comp($type, $2, map_args($1, $3), undef, "${prefix}_ELSE");
+    }
 
-  if ($cond =~ m/^(.*)\s*(==|!=|<=|<|>=|>)\s*(.*)$/) {
-    op_comp("${prefix}_NEXT", $2, map_args($1, $3), "${prefix}_REDO", 
"${prefix}_LAST");
-    emit_code("${prefix}_REDO");
+    push_label "${prefix}_REDO";
   } else {
     printf(STDERR "jako: Syntax error. Unrecognized condition in '%s' on line %d.\n", 
$type, $line);
   }
+
+  push @block_stack, { TYPE => $type, NEXT => $line, PREFIX => $prefix };
 }
 
 
 #
 # end_block()
 #
+# TODO: else (and elsif?) blocks.
+#
 
 sub end_block
 {
+  my ($continue) = @_;
+
   unless (@block_stack) {
-    printf(STDERR "jako: Syntax error. Closing brace without open block on line 
%d.\n", $line);
+    printf(STDERR "jakoc: Syntax error. Closing brace without open block on line 
+%d.\n", $line);
     return;
   }
 
@@ -490,10 +839,29 @@
   my $prefix = $block->{PREFIX};
 
   if ($block->{TYPE} eq 'while') {
-    printf("%-12s %-8s %s\n", '', 'branch', "${prefix}_NEXT", );
-  }
+    if (defined $continue) {
+      if (defined $block->{CONT}) {
+        printf(STDERR "jakoc: Syntax error. No more than one continue block allowed 
+on line %d.\n", $line);
+      } else {
+        $block->{CONT} = $line;
+      }
 
-  printf "%s_LAST:\n", $prefix;
+      push_label "${prefix}_CONT";
+      push_source "} continue {";
+      push @block_stack, $block; # Push it back on for a minute...
+    } else {
+      push_source "}";
+      push_label "${prefix}_CONT" unless defined $block->{CONT};
+      emit_code('branch', ["${prefix}_NEXT"]);
+      push_label "${prefix}_LAST";
+    }
+  } elsif ($block->{TYPE} eq 'if') {
+    push_source "}";
+    emit_code();
+    push_label "${prefix}_ELSE";
+  } else {
+    confess "jakoc: Internal compiler error. End of unknown block type " . 
+$block->{TYPE} . "!";
+  }
 }
 
 
@@ -503,15 +871,36 @@
 
 sub do_loop_control
 {
-  my $which = uc shift;
+  my ($control_op, $loop_label) = @_;
 
   foreach (reverse @block_stack) {
-    if ($_->{TYPE} eq 'while') {
-      my $prefix = $_->{PREFIX};
-      printf("%-12s %-8s %s\n", '', 'branch', "${prefix}_$which", );
-      last;
+    my $type   = $_->{TYPE};
+    my $prefix = $_->{PREFIX};
+
+    next unless $type eq 'while';
+    next unless !defined($loop_label) or $prefix eq $loop_label;
+
+    my $which = $block_types{$type}{uc $control_op};
+
+    push_source "$control_op ${prefix}";
+
+    if (defined $_->{CONT} and $control_op eq 'next') {
+      $which = $block_types{$type}{NEXT};                   # Hard-coded to NEXT in 
+continue { ... }
+      emit_code('branch', ["${prefix}_$which"]);
+    } else {
+      emit_code('branch', ["${prefix}_$which"]);
     }
+
+    return;
   }
+
+  if (defined $loop_label) {
+    printf STDERR "jakoc: No loop '%s' in loop control on line %d!\n", $loop_label, 
+$line;
+  } else {
+    printf STDERR "jakoc: No loop active in loop control on line %d!\n", $line;
+  }
+
+  emit_code('err');
 }
 
 
@@ -521,9 +910,31 @@
 
 sub do_add
 {
-  my ($dest, $a, $b) = @_;
+  my ($dest, $a, $b) = map_args(@_);
 
-  printf("%-12s %-8s %s\n", '', 'add', join(", ", map_args($dest, $a, $b)));
+  if (int_or_num_q($a)) {
+    if (int_or_num_q($b)) {
+      emit_code('set', [$dest, $a + $b]);
+    } elsif (reg_q($b)) {
+      my $temp = int_q($a) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $a]);
+      emit_code('add', [$dest, $temp, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in addition on line %d!\n", $line);
+    }
+  } elsif (reg_q($a)) {
+    if (int_or_num_q($b)) {
+      my $temp = int_q($b) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $b]);
+      emit_code('add', [$dest, $a, $temp]);
+    } elsif (reg_q($b)) {
+      emit_code('add', [$dest, $a, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in addition on line %d!\n", $line);
+    }
+  } else {
+    printf(STDERR "jakoc: Syntax error in addition on line %d!\n", $line);
+  }
 }
 
 
@@ -533,7 +944,15 @@
 
 sub do_inc
 {
-  printf("%-12s %-8s %s\n", '', 'inc', join(", ", map_args(@_)));
+  my ($dest, $amount) = map_args(@_);
+
+  if (defined $amount) {
+    push_source "$dest += $amount";
+    emit_code('inc', [$dest, $amount]);
+  } else {
+    push_source "$dest++";
+    emit_code('inc', [$dest]);
+  }
 }
 
 
@@ -543,9 +962,31 @@
 
 sub do_sub
 {
-  my ($dest, $a, $b) = @_;
+  my ($dest, $a, $b) = map_args(@_);
 
-  printf("%-12s %-8s %s\n", '', 'sub', join(", ", map_args($dest, $a, $b)));
+  if (int_or_num_q($a)) {
+    if (int_or_num_q($b)) {
+      emit_code('set', [$dest, $a - $b]);
+    } elsif (reg_q($b)) {
+      my $temp = int_q($a) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $a]);
+      emit_code('sub', [$dest, $temp, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in subtraction on line %d!\n", $line);
+    }
+  } elsif (reg_q($a)) {
+    if (int_or_num_q($b)) {
+      my $temp = int_q($b) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $b]);
+      emit_code('sub', [$dest, $a, $temp]);
+    } elsif (reg_q($b)) {
+      emit_code('sub', [$dest, $a, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in subtraction on line %d!\n", $line);
+    }
+  } else {
+    printf(STDERR "jakoc: Syntax error in subtraction on line %d!\n", $line);
+  }
 }
 
 
@@ -555,7 +996,15 @@
 
 sub do_dec
 {
-  printf("%-12s %-8s %s\n", '', 'dec', join(", ", map_args(@_)));
+  my ($dest, $amount) = map_args(@_);
+
+  if (defined $amount) {
+    push_source "$dest -= $amount";
+    emit_code('dec', [$dest, $amount]);
+  } else {
+    push_source "$dest--";
+    emit_code('dec', [$dest]);
+  }
 }
 
 
@@ -565,9 +1014,31 @@
 
 sub do_mul
 {
-  my ($dest, $a, $b) = @_;
+  my ($dest, $a, $b) = map_args(@_);
 
-  printf("%-12s %-8s %s\n", '', 'mul', join(", ", map_args($dest, $a, $b)));
+  if (int_or_num_q($a)) {
+    if (int_or_num_q($b)) {
+      emit_code('set', [$dest, $a * $b]);
+    } elsif (reg_q($b)) {
+      my $temp = int_q($a) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $a]);
+      emit_code('mul', [$dest, $temp, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in multiplication on line %d!\n", $line);
+    }
+  } elsif (reg_q($a)) {
+    if (int_or_num_q($b)) {
+      my $temp = int_q($b) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $b]);
+      emit_code('mul', [$dest, $a, $temp]);
+    } elsif (reg_q($b)) {
+      emit_code('mul', [$dest, $a, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in multiplication on line %d!\n", $line);
+    }
+  } else {
+    printf(STDERR "jakoc: Syntax error in multiplication on line %d!\n", $line);
+  }
 }
 
 
@@ -577,9 +1048,31 @@
 
 sub do_div
 {
-  my ($dest, $a, $b) = @_;
+  my ($dest, $a, $b) = map_args(@_);
 
-  printf("%-12s %-8s %s\n", '', 'div', join(", ", map_args($dest, $a, $b)));
+  if (int_or_num_q($a)) {
+    if (int_or_num_q($b)) {
+      emit_code('set', [$dest, $a / $b]);
+    } elsif (reg_q($b)) {
+      my $temp = int_q($a) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $a]);
+      emit_code('div', [$dest, $temp, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in division on line %d!\n", $line);
+    }
+  } elsif (reg_q($a)) {
+    if (int_or_num_q($b)) {
+      my $temp = int_q($b) ? 'I0' : 'N0';
+      emit_code('set', [$temp, $b]);
+      emit_code('div', [$dest, $a, $temp]);
+    } elsif (reg_q($b)) {
+      emit_code('div', [$dest, $a, $b]);
+    } else {
+      printf(STDERR "jakoc: Syntax error in division on line %d!\n", $line);
+    }
+  } else {
+    printf(STDERR "jakoc: Syntax error in division on line %d!\n", $line);
+  }
 }
 
 
@@ -589,9 +1082,8 @@
 
 sub do_mod
 {
-  my ($dest, $a, $b) = @_;
-
-  printf("%-12s %-8s %s\n", '', 'mod', join(", ", map_args($dest, $a, $b)));
+  my ($dest, $a, $b) = map_args(@_);
+  emit_code('mod', [$dest, $a, $b]);
 }
 
 
@@ -601,9 +1093,8 @@
 
 sub do_shift
 {
-  my ($dir, $dest, $a, $amount) = @_;
-
-  printf("%-12s %-8s %s\n", '', "sh$dir", join(", ", map_args($dest, $a, $amount)));
+  my ($dir, $dest, $a, $amount) = map_args(@_);
+  emit_code("sh$dir", [$dest, $a, $amount]);
 }
 
 
@@ -664,20 +1155,28 @@
 #
 # MAIN PROGRAM:
 #
+
+print 
+"###############################################################################\n";
+print "# This Parrot assembler file was produced by the Jako compiler.               
+#\n";
+print "# Initial comments from the source code are reproduced below.                 
+#\n";
+print 
+"###############################################################################\n";
+print "\n";
 
-print "# This file produced by the Jako Compiler\n";
+my $code_lines = 0;
 
 while(<>) {
   $line++;
 
-  if (m/^\s*#/) { print; next; } # Pass comment-only lines through intact.
-  if (m/^\s*$/) { print; next; } # Pass whitespace-only lines through intact.
+  if (m/^\s*#/) { print unless $code_lines; next; } # Pass initial comment-only lines 
+through intact.
+  if (m/^\s*$/) { print unless $code_lines; next; } # Pass initial whitespace-only 
+lines through intact.
 
   chomp;                         # Trim trailing newline
   s/^\s*//;                      # Trim leading whitespace
   s/\s*$//;                      # Trim trailing whitespace
   last if (/^__END__$/);         # Done after __END__ token
 
+  $code_lines++;
+
   s/\s*;\s*$//; # Remove trailing semicolons
 
   #
@@ -716,6 +1215,26 @@
   }
 
   #
+  # Constant declarations:
+  #
+  # const int      foo = 5;
+  # const integer  foo = 5;
+  #
+  # const num      bar = 3.14;
+  # const number   bar = 3.14;
+  #
+  # const str      splee = "Howdy";
+  # const string   splee = "Howdy";
+  #
+
+  if ((m/^const\s+(i)nt(eger)?\s+([A-Za-z][A-Za-z0-9_]*)(\s*=\s*(\d+))$/) ||
+      (m/^const\s+(n)um(ber)?\s+([A-Za-z][A-Za-z0-9_]*)(\s*=\s*(\d+(\.\d+)))$/) ||
+      
+(m/^const\s+(s)tr(ing)?\s+([A-Za-z][A-Za-z0-9_]*)(\s*=\s*(\"[^\\\"]*(?:\\.[^\\\"]*)*\"))$/))
+ { 
+    declare_const($3, uc($1), $5);
+    next;
+  }
+
+  #
   # Variable Assignments:
   #
   # a = 5;
@@ -760,35 +1279,82 @@
     next;
   }
 
+  #
+  # Conditionals:
+  #
+  #   if     (...) {
+  #   unless (...) {
+  #
+  # } elsif  (...) {
+  #
+
+  if (m/^(if|unless)\s*\(\s*(.*)\s*\)\s*{$/) {
+    begin_block(undef, $1, $2);
+    next;
+  }
+
+  if (m/^}\s*(elsif)\s*\(\s*(.*)\s*\)\s*{$/) {
+    begin_block(undef, $1, $2);
+    # TODO
+    next;
+  }
+
   #
-  # Blocks:
+  # Loops:
   #
-  if (m/^(while|if)\s*\(\s*(.*)\s*\)\s*{$/) {
-    begin_block($1,$2);
+  # LABEL: until (...) {
+  #        until (...) {
+  #
+  # LABEL: while (...) {
+  #        while (...) {
+  #
+
+  if (m/^(([A-Za-z][A-Za-z0-9_]*)\s*:\s*)?(until|while)\s*\(\s*(.*)\s*\)\s*{$/) {
+    begin_block($2, $3, $4);
     next;
   }
 
   #
+  # Bare Blocks:
+  #
+  # LABEL: {
+  #
+  # {
+  #
+
+  if (m/^(([A-Za-z][A-Za-z0-9_]*)\s*:\s*)?{$/) {
+    begin_block($2, 'bare', undef);
+    next;
+  }
+
+  #
   # Block Termination:
   #
   # }
+  # } continue {
+  # } else {
   #
 
-  if (m/^}$/) {
-    end_block();
+  if (m/^}(\s*(continue|else)\s*{)?$/) {
+    end_block($2);
     next;
   }
 
   #
   # Loop Control Statements:
+  #
+  # next
+  # next LABEL
+  #
+  # last
+  # last LABEL
   #
-  # next;
-  # last;
-  # redo;
+  # redo
+  # redo LABEL
   #
 
-  if (m/^(next|last|redo)$/) {
-    do_loop_control($1);
+  if (m/^(next|last|redo)(\s+([A-Za-z][A-Za-z0-9_]*))?$/) {
+    do_loop_control($1, $3);
     next;
   }
 
@@ -812,10 +1378,24 @@
   # a++;              # inc_[in]
   #
 
-  if 
(m/([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[+]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
 {
+  if 
+(m/^([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[+]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
+ {
+    #
+    # Regexp Captures:
+    #
+    #  1. LHS sum (variable name)
+    #  2. RHS left addend
+    #  3. RHS left addend (variable name)
+    #  4. RHS left addend (number)
+    #  5. RHS left addend (number's decimal places) -- used for grouping, not capture
+    #  6. RHS right addend
+    #  7. RHS right addend (variable name)
+    #  8. RHS right addend (number)
+    #  9. RHS right addend (number's decimal places) -- used for grouping, not capture
+    #
+
     if (defined $3 or defined $7) {
-      do_add($1, $3, $7);
-    } elsif (defined $3 and defined $8) {
+      do_add($1, $2, $6);
+    } elsif (defined $4 and defined $8) {
       assign_var($1, (defined $5 or defined $9 ? 'N' : 'I'), $4 + $8);
     } else {
       printf(STDERR "jako: Syntax error in addition on line %d: '%s'\n", $line, $_);
@@ -823,7 +1403,7 @@
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9]*)\s*[+]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
+  if (m/^([A-Za-z][A-Za-z0-9]*)\s*[+]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
     if (defined $3) {
       do_add($1, $1, $2);
     } else {
@@ -832,7 +1412,7 @@
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9_]*)\s*[+][+]$/) {
+  if (m/^([A-Za-z][A-Za-z0-9_]*)\s*[+][+]$/) {
     do_inc($1);
     next;
   }
@@ -856,11 +1436,25 @@
   #
   # a--;              # dec_[in]
   #
+
+  if 
+(m/^([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[-]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
+ {
+    #
+    # Regexp Captures:
+    #
+    #  1. LHS difference (variable name)
+    #  2. RHS minuend
+    #  3. RHS minuend (variable name)
+    #  4. RHS minuend (number)
+    #  5. RHS minuend (number's decimal places) -- used for grouping, not capture
+    #  6. RHS subtrahend
+    #  7. RHS subtrahend (variable name)
+    #  8. RHS subtrahend (number)
+    #  9. RHS subtrahend (number's decimal places) -- used for grouping, not capture
+    #
 
-  if 
(m/([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[-]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
 {
     if (defined $3 or defined $7) {
-      do_sub($1, $3, $7);
-    } elsif (defined $3 and defined $8) {
+      do_sub($1, $2, $6);
+    } elsif (defined $4 and defined $8) {
       assign_var($1, (defined $5 or defined $9 ? 'N' : 'I'), $4 - $8);
     } else {
       printf(STDERR "jako: Syntax error in subtraction on line %d: '%s'\n", $line, 
$_);
@@ -868,7 +1462,7 @@
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9]*)\s*[-]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
+  if (m/^([A-Za-z][A-Za-z0-9]*)\s*[-]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
     if (defined $3) {
       do_sub($1, $1, $2);
     } else {
@@ -877,7 +1471,7 @@
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9_]*)\s*[-][-]$/) {
+  if (m/^([A-Za-z][A-Za-z0-9_]*)\s*[-][-]$/) {
     do_dec($1, 1);
     next;
   }
@@ -900,10 +1494,24 @@
   # a *= 3.14;        # mul_n_nc (pseudo-op)
   #
 
-  if 
(m/([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[*]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
 {
+  if 
+(m/^([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[*]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
+ {
+    #
+    # Regexp Captures:
+    #
+    #  1. LHS product (variable name)
+    #  2. RHS multiplicand
+    #  3. RHS multiplicand (variable name)
+    #  4. RHS multiplicand (number)
+    #  5. RHS multiplicand (number's decimal places) -- used for grouping, not capture
+    #  6. RHS multiplier
+    #  7. RHS multiplier (variable name)
+    #  8. RHS multiplier (number)
+    #  9. RHS multiplier (number's decimal places) -- used for grouping, not capture
+    #
+
     if (defined $3 or defined $7) {
-      do_mul($1, $3, $7);
-    } elsif (defined $3 and defined $8) {
+      do_mul($1, $2, $6);
+    } elsif (defined $4 and defined $8) {
       assign_var($1, (defined $5 or defined $9 ? 'N' : 'I'), $4 * $8);
     } else {
       printf(STDERR "jako: Syntax error in multiplication on line %d: '%s'\n", $line, 
$_);
@@ -911,7 +1519,7 @@
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9]*)\s*[*]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
+  if (m/^([A-Za-z][A-Za-z0-9]*)\s*[*]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
     do_mul($1, $1, $2);
     next;
   }
@@ -933,11 +1541,27 @@
   # a /= 5;           # div_i_ic (pseudo-op)
   # a /= 3.14;        # div_n_nc (pseudo-op)
   #
+
+  if 
+(m/^([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)?))\s*[\/]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)?))$/)
+ {
+    #
+    # Regexp Captures:
+    #
+    #  1. LHS quotient (variable name)
+    #  2. RHS dividend
+    #  3. RHS dividend (variable name)
+    #  4. RHS dividend (number)
+    #  5. RHS dividend (number's decimal places) -- used for grouping, not capture
+    #  6. RHS divisor
+    #  7. RHS divisor (variable name)
+    #  8. RHS divisor (number)
+    #  9. RHS divisor (number's decimal places) -- used for grouping, not capture
+    #
 
-  if 
(m/([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))\s*[\/]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/)
 {
+    push_source "$1 = $2 / $6";
+
     if (defined $3 or defined $7) {
-      do_div($1, $3, $7);
-    } elsif (defined $3 and defined $8) {
+      do_div($1, $2, $6);
+    } elsif (defined $4 and defined $8) {
       assign_var($1, (defined $5 or defined $9 ? 'N' : 'I'), $4 / $8);
     } else {
       printf(STDERR "jako: Syntax error in division on line %d: '%s'\n", $line, $_);
@@ -945,7 +1569,7 @@
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9]*)\s*[\/]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
+  if (m/^([A-Za-z][A-Za-z0-9]*)\s*[\/]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+(\.\d+)))$/) {
     do_div($1, $1, $2);
     next;
   }
@@ -964,12 +1588,14 @@
   # a %= 4;
   #
 
-  if 
(m/([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))\s*[%]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/)
 {
+  if 
+(m/^([A-Za-z][A-Za-z0-9]*)\s*=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))\s*[%]\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/)
+ {
+    push_source "$1 = $2 \% $5";
     do_mod($1, $2, $5);
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9]*)\s*[%]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/) {
+  if (m/^([A-Za-z][A-Za-z0-9]*)\s*[%]=\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/) {
+    push_source "$1 \%= $2";
     do_mod($1, $1, $2);
     next;
   }
@@ -983,12 +1609,12 @@
   # TODO: Can't really support shift amount as arg until sh[lr]_i_i ops are 
implemented.
   #
 
-  if 
(m/([A-Za-z][A-Za-z0-9]*)\s*=\s*([A-Za-z][A-Za-z0-9_]*)\s*(<<|>>)\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/)
 {
+  if 
+(m/^([A-Za-z][A-Za-z0-9]*)\s*=\s*([A-Za-z][A-Za-z0-9_]*)\s*(<<|>>)\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/)
+ {
     do_shift($3 eq '<<' ? 'l' : 'r', $1, $2, $4);
     next;
   }
 
-  if (m/([A-Za-z][A-Za-z0-9]*)\s*((<<|>>)=)\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/) {
+  if (m/^([A-Za-z][A-Za-z0-9]*)\s*((<<|>>)=)\s*(([A-Za-z][A-Za-z0-9_]*)|(\d+))$/) {
     do_shift($4 eq '<<' ? 'l' : 'r', $1, $2, $5);
     next;
   }
@@ -998,12 +1624,14 @@
   #
 
   if (m/^end$/) {
-    printf "%-12s %-8s\n", '', 'end';
+    emit_code('end');
     next;
   }
 
   print STDERR "jako: Syntax error on line $line: '$_'.\n";
 }
+
+emit_code('end') unless $last_op eq 'end';
 
 exit 0;
 
#
# primes.jako
#
# A simple program to print out the primes up to 100.
#
# Based on a Parrot assembly example by Leon Brocard <[EMAIL PROTECTED]>
#
# Copyright (C) 2001 Gregor N. Purdy. All rights reserved.
# This program is free software. It is subject to the same
# license as Perl itself.
#
# $Id: $
#

const int n = 100;

var   int i =   2;

print("Algorithm P (Naiive primality test)\n");
print("  Printing primes up to $n...\n");

NUMBER: while (i <= n) {
  var int m;
  var int j = 2;

  m = i / 2;

  FACTOR: while (j <= m) {
    var int x;

    x = i % j;

    if (x == 0) {
        next NUMBER;
    }

    j++;
  }

  print("$i  ");

} continue {
  i++;
}

print("\n");

###############################################################################
# This Parrot assembler file was produced by the Jako compiler.               #
# Initial comments from the source code are reproduced below.                 #
###############################################################################

#
# primes.jako
#
# A simple program to print out the primes up to 100.
#
# Based on a Parrot assembly example by Leon Brocard <[EMAIL PROTECTED]>
#
# Copyright (C) 2001 Gregor N. Purdy. All rights reserved.
# This program is free software. It is subject to the same
# license as Perl itself.
#
# $Id: $
#

                                                    # const int n = 100;
                                                    # var int i;
                 set      I1, 2                     # i = 2
                 print    "Algorithm P (Naiive primality test)\n"
                 print    "  Printing primes up to "
                 print    100                      
                 print    "...\n"                  
NUMBER_WHILE:
NUMBER_NEXT:     set      I0, 100                   # NUMBER: while (i <= n) {
                 gt       I1, I0, NUMBER_LAST      
                                                    #   var int m;
                                                    #   var int j;
NUMBER_REDO:     set      I3, 2                     #   j = 2
                 set      I0, 2                     #   m = i / 2
                 div      I2, I1, I0               
FACTOR_WHILE:
FACTOR_NEXT:     gt       I3, I2, FACTOR_LAST       #   FACTOR: while (j <= m) {
                                                    #     var int x;
FACTOR_REDO:     mod      I4, I1, I3                #     x = i % j
_I3_IF:
_I3_TEST:        set      I0, 0                     #     _I3: if (x == 0) {
                 ne       I4, I0, _I3_ELSE         
_I3_REDO:        branch   NUMBER_CONT               #       next NUMBER
                                                    #     }
_I3_ELSE:        inc      I3                        #     I3++
FACTOR_CONT:     branch   FACTOR_NEXT               #   }
FACTOR_LAST:     print    ""                       
                 print    I1                       
                 print    "  "                     
                                                    # } continue {
NUMBER_CONT:     inc      I1                        #   I1++
                 branch   NUMBER_NEXT               # }
NUMBER_LAST:     print    "\n"                     
                 end                               

Reply via email to