The Code Generator

Assembler language programs produced by the code generator are represented by ground terms having the following syntax:
t.Code =
     (Seq { t.Code } ) |
     (Instr s.Instr s.Operand) |
     (Label s.Label) |
     (Block s.Value).
s.Operand = s.Label | s.Value.
s.Label = s.Box.
s.Value = s.Int.
$
s.Instr =
     ADD  | SUB  | DIV  | MUL  | LOAD  | STORE |
     ADDC | SUBC | DIVC | MULC | LOADC |
     JUMPEQ | JUMPNE | JUMPLT | JUMPGT | JUMPLE | JUMPGE
     JUMP | READ | WRITE | HALT |

Assembler language programs may contain labels to be replaced with absolute addresses by the assembler. Assembling a program proceeds in two steps. First, the assembler determines the addresses associated with instructions and variables, and puts each address associated with a label into the box referred to by the label. Second, all labels are replaced with the addresses associated with them, i.e. each reference to a box is replaced with the contents of the box.

The module CmpGen has the following implementation:
//
// File: CmpGen.rf
//

$use StdIO Class Arithm Box;
$use CmpDic;

$func EncProgram     t.Program s.Dic = t.Code;
$func EncSt          t.St s.Dic = t.Code;
$func EncTest        t.Test s.Label s.Dic = t.TestC;
$func UnlessOp       s.Op = s.JumpIf;
$func EncExpr        t.Expr s.Dic = t.ExprC;
$func EncSubExpr     t.Expr sN s.Dic = t.ExprC;
$func LiteralOp      s.Op = s.OpCode;
$func MemoryOp       s.Op = s.OpCode;
$func Assemble       t.Code s.StartAddr = s.FreeAddr;
$func AssembleSeq    e.CodeSeq s.Addr = s.FreeAddr;
$func Dereference    t.Code = t.Target;
$func DereferenceSeq e.CodeSeq = e.CodeSeqD;
$func WriteCodeSeq   e.CodeSeq = ;

// Generates an assembler language program
// from an abstract program.

GenCode  t.Program =
      // Creating an empty dictionary.
  <MakeDic> :: s.Dic,
      // Generating the abstract program.
  <EncProgram t.Program s.Dic> :: t.Code,
      // Allocating memory for the program's instructions.
  <Assemble t.Code 1> :: s.FreeAddr,
      // Allocating memory for the program's variables.
  <AllocateDic s.Dic s.FreeAddr> :: s.EndAddr,
      // Replacing the labels with their addresses.
  <Dereference t.Code> :: t.CodeD,
      // Generating the directive BLOCK.
  <Sub s.EndAddr s.FreeAddr> :: s.BlockLength,
  (Seq t.CodeD (Block s.BlockLength)) :: t.Target,
    = t.Target;

// Encodes a program.

EncProgram  (Program t.St) s.Dic =
  <EncSt t.St s.Dic> :: t.StC,
  <Box> :: s.L,
    = (Seq t.StC (Instr HALT 0) (Label s.L));

// Encodes a statement.

EncSt  (s.KeyWord e.Info) s.Dic =
  (s.KeyWord e.Info) :
  {
  (Assign sX t.Expr) =
    <LookupDic sX s.Dic> :: s.Addr,
    <EncExpr t.Expr s.Dic> :: t.ExprC,
      = (Seq t.ExprC (Instr STORE s.Addr));
  (If t.Test t.Then t.Else) =
    <Box> :: s.L1, <Box> :: s.L2,
    <EncTest t.Test s.L1 s.Dic> :: t.TestC,
    <EncSt t.Then s.Dic> :: t.ThenC,
    <EncSt t.Else s.Dic> :: t.ElseC,
      = (Seq
            t.TestC
            t.ThenC
            (Instr JUMP s.L2)
          (Label s.L1)
            t.ElseC
          (Label s.L2)
        );
  (While t.Test t.Do) =
    <Box> :: s.L1, <Box> :: s.L2,
    <EncTest t.Test s.L2 s.Dic> :: t.TestC,
    <EncSt t.Do s.Dic> :: t.DoC,
      = (Seq
          (Label s.L1)
            t.TestC
            t.DoC
            (Instr JUMP s.L1)
          (Label s.L2)
        );
  (Read s.X) =
    <LookupDic s.X s.Dic> :: s.Addr,
      = (Instr READ s.Addr);
  (Write t.Expr) =
    <EncExpr t.Expr s.Dic> :: t.ExprC,
      = (Seq t.ExprC (Instr WRITE 0));
  (Seq t.St1 t.St2) =
    <EncSt t.St1 s.Dic> :: t.StC1,
    <EncSt t.St2 s.Dic> :: t.StC2,
      = (Seq t.StC1 t.StC2);
  (Skip) =
      = (Seq );
  };

// Encodes a test.

EncTest  (Test s.Op t.Arg1 t.Arg2) s.Label s.Dic =
  <EncExpr (Op Sub t.Arg1 t.Arg2) s.Dic> :: t.ExprC,
  <UnlessOp s.Op> :: s.JumpIf,
    = (Seq t.ExprC (Instr s.JumpIf s.Label));

UnlessOp                     // Generates a jump.
  {
  Eq = JUMPNE; Ne = JUMPEQ;
  Lt = JUMPGE; Gt = JUMPLE;
  Le = JUMPGT; Ge = JUMPLT;
  };

// This function compiles an arithmetic expression.
// Auxiliary variables are created to keep
// the values obtained by evaluating subexpressions.
// The evaluation order of the subexpressions is chosen in
// such a way as to reduce the number of auxiliary variables.

EncExpr  t.Expr s.Dic
  = <EncSubExpr t.Expr 0 s.Dic>;

EncSubExpr  (s.KeyWord e.Info) sN s.Dic =
  (s.KeyWord e.Info) :
  {
  (Const sC) =
      = (Instr LOADC sC);
  (Name sX) =
    <LookupDic sX s.Dic> :: s.Addr,
      = (Instr LOAD s.Addr);
  (Op s.Op t.Expr1 t.Expr2) =
    t.Expr2 :
    {
    (Const sC2) =
      <EncSubExpr t.Expr1 sN s.Dic> :: t.Expr1C,
      <LiteralOp s.Op> :: s.OpCode,
        = (Seq t.Expr1C (Instr s.OpCode sC2));
    (Name sX2) =
      <EncSubExpr t.Expr1 sN s.Dic> :: t.Expr1C,
      <MemoryOp s.Op> :: s.OpCode,
      <LookupDic sX2 s.Dic> :: s.Addr,
        = (Seq t.Expr1C (Instr s.OpCode s.Addr));
    (Op e) =
      <LookupDic sN s.Dic> :: s.Addr,
      <EncSubExpr t.Expr2 sN s.Dic> :: t.Expr2C,
      <Add sN 1> :: sN1,
      <EncSubExpr t.Expr1 sN1 s.Dic> :: t.Expr1C,
      <MemoryOp s.Op> :: s.OpCode,
        = (Seq
            t.Expr2C
            (Instr STORE s.Addr)
            t.Expr1C
            (Instr s.OpCode s.Addr)
          );
    };
  };

LiteralOp                    // Generates the names of
  {                          // the instructions with
  Add = ADDC; Sub = SUBC;    // literal operands.
  Mult = MULTC; Div = DIVC;
  };

MemoryOp                     // Generates the names of
  {                          // the instructions with
  Add = ADD; Sub = SUB;      // address operands.
  Mult = MULT; Div = DIV;
  };

// Allocates memory for the instructions.

Assemble  t.Code s.A0 =
  t.Code :
  {
  (Seq e.CodeSeq) =
      = <AssembleSeq e.CodeSeq s.A0>;
  (Instr s s) =
      = <Add s.A0 1>;
  (Label s.Label) =
    <Store s.Label s.A0>
      = s.A0;
  };

AssembleSeq  e.CodeSeq s.A0 =
  e.CodeSeq :
  {
  t.Code e.Rest =
    <Assemble t.Code s.A0> :: s.A1,
    = <AssembleSeq e.Rest s.A1>;
  =
    = s.A0;
  };

// Replaces the labels with their addresses.

Dereference  t.Code =
  t.Code :
  {
  (Seq e.CodeSeq) =
    (Seq <DereferenceSeq e.CodeSeq>);
  (Instr s.Instr s.Value) =
    {
    <IsInt s.Value>
      = t.Code;
    //<IsBox s.Value>
      = (Instr s.Instr <Get s.Value>);
    };
  (Label s.Label) =
    (Label <Get s.Label>);
  };

DereferenceSeq
  {
  t.Code e.CodeSeq =
      <Dereference t.Code><DereferenceSeq e.CodeSeq>;
  = ;
  };

// Converts the assembler language program to
// the character sequence, and outputs it to
// the standard output device.

WriteCode
  {
  (Seq e.CodeSeq) =
      <WriteCodeSeq e.CodeSeq>;
  (Instr s.Instr s.Value) =
      <Print "   "><Print s.Instr><Print ",">
      <Print s.Value><Print ";\n">;
  (Label s.Label) =
      <Print s.Label><Print ":\n">;
  (Block s.Value) =
      <Print "   BLOCK,"><Print s.Value><Print ";\n">;
  };

WriteCodeSeq
  {
  t.Code e.CodeSeq =
      <WriteCode t.Code><WriteCodeSeq e.CodeSeq>;
  = ;
  };