home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / turbopas / tutorpas.arc / TUTOR5.DOC < prev    next >
Encoding:
Text File  |  1988-08-20  |  52.1 KB  |  1,769 lines

  1. O
  2. PA A
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.                             LET'S BUILD A COMPILER!
  30.  
  31.                                        By
  32.  
  33.                             Jack W. Crenshaw, Ph.D.
  34.  
  35.                                  19 August 1988
  36.  
  37.  
  38.                            Part V: CONTROL CONSTRUCTS
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69. PA A
  70.  
  71.  
  72.  
  73.  
  74.  
  75.        *****************************************************************
  76.        *                                                               *
  77.        *                        COPYRIGHT NOTICE                       *
  78.        *                                                               *
  79.        *   Copyright (C) 1988 Jack W. Crenshaw. All rights reserved.   *
  80.        *                                                               *
  81.        *****************************************************************
  82.  
  83.  
  84.        INTRODUCTION
  85.  
  86.        In  the  first  four  installments  of  this  series, we've  been
  87.        concentrating on the parsing of math  expressions  and assignment
  88.        statements.  In  this  installment,  we'll  take off on a new and
  89.        exciting  tangent:  that   of  parsing  and  translating  control
  90.        constructs such as IF statements.
  91.  
  92.        This subject is dear to my heart, because it represents a turning
  93.        point  for  me.    I  had  been  playing  with  the   parsing  of
  94.        expressions, just as  we  have  done  in this series, but I still
  95.        felt that I was a LONG way from being able  to  handle a complete
  96.        language.  After all, REAL  languages have branches and loops and
  97.        subroutines and all that.  Perhaps you've shared some of the same
  98.        thoughts.    Awhile  back,  though,  I  had  to  produce  control
  99.        constructs for a structured assembler preprocessor I was writing.
  100.        Imagine my surprise to  discover  that it was far easier than the
  101.        expression  parsing  I  had  already  been through.   I  remember
  102.        thinking, "Hey! This is EASY!" After we've finished this session,
  103.        I'll bet you'll be thinking so, too.
  104.  
  105.  
  106.        THE PLAN
  107.  
  108.        In what follows, we'll be starting over again with a bare cradle,
  109.        and as we've done twice before now, we'll build things up  one at
  110.        a time.  We'll also  be retaining the concept of single-character
  111.        tokens that has served us so well to date.   This  means that the
  112.        "code" will look a little funny, with 'i' for IF, 'w'  for WHILE,
  113.        etc.  But it helps us  get  the concepts down pat without fussing
  114.        over  lexical  scanning.    Fear  not  ...  eventually we'll  see
  115.        something looking like "real" code.
  116.  
  117.        I also don't  want  to  have  us  get bogged down in dealing with
  118.        statements other than branches, such as the assignment statements
  119.        we've  been  working  on.  We've already demonstrated that we can
  120.        handle them, so there's no point carrying them  around  as excess
  121.        baggage during this exercise.  So what I'll do instead is  to use
  122.        an  anonymous  statement,  "other", to take the place of the non-
  123.        control statements and serve as a place-holder for them.  We have
  124.        to generate some kind of object code for them  (we're  back  into
  125.        compiling, not interpretation), so for want of anything else I'll
  126.        just echo the character input.ABAB
  127.                                      - 2 -A*A*
  128.  
  129. PA A
  130.  
  131.  
  132.  
  133.  
  134.  
  135.        OK, then, starting with  yet  another  copy  of the cradle, let's
  136.        define the procedure:
  137.  
  138.  
  139.        {--------------------------------------------------------------}
  140.        { Recognize and Translate an "Other" }
  141.  
  142.        procedure Other;
  143.        begin
  144.           EmitLn(GetName);
  145.        end;
  146.        {--------------------------------------------------------------}
  147.  
  148.  
  149.        Now include a call to it in the main program, thus:
  150.  
  151.  
  152.        {--------------------------------------------------------------}
  153.        { Main Program }
  154.  
  155.        begin
  156.           Init;
  157.           Other;
  158.        end.
  159.        {--------------------------------------------------------------}
  160.  
  161.  
  162.        Run  the program and see what you get.  Not very exciting, is it?
  163.        But hang in there, it's a start, and things will get better.
  164.  
  165.        The first thing we need is the ability to deal with more than one
  166.        statement, since a single-line branch  is pretty limited.  We did
  167.        that in the last session on interpreting, but this time let's get
  168.        a little more formal.  Consider the following BNF:
  169.  
  170.                  <program> ::= <block> END
  171.  
  172.                  <block> ::= [ <statement> ]*
  173.  
  174.        This says that, for our purposes here, a program is defined  as a
  175.        block, followed by an END statement.  A block, in  turn, consists
  176.        of zero or more statements.  We only have one kind  of statement,
  177.        so far.
  178.  
  179.        What signals the end of a block?  It's  simply any construct that
  180.        isn't an "other"  statement.    For  now, that means only the END
  181.        statement.
  182.  
  183.        Armed with these ideas, we can proceed to build  up  our  parser.
  184.        The code for a program (we  have  to call it DoProgram, or Pascal
  185.        will complain, is:ANAN
  186.                                      - 3 -A*A*
  187.  
  188. PA A
  189.  
  190.  
  191.  
  192.  
  193.  
  194.        {--------------------------------------------------------------}
  195.        { Parse and Translate a Program }
  196.  
  197.        procedure DoProgram;
  198.        begin
  199.           Block;
  200.           if Look <> 'e' then Expected('End');
  201.           EmitLn('END')
  202.        end;
  203.        {--------------------------------------------------------------}
  204.  
  205.  
  206.        Notice  that  I've  arranged to emit  an  "END"  command  to  the
  207.        assembler, which sort of  punctuates  the  output code, and makes
  208.        sense considering that we're parsing a complete program here.
  209.  
  210.        The code for Block is:
  211.  
  212.  
  213.        {--------------------------------------------------------------}
  214.        { Recognize and Translate a Statement Block }
  215.  
  216.        procedure Block;
  217.        begin
  218.           while not(Look in ['e']) do begin
  219.              Other;
  220.           end;
  221.        end;
  222.        {--------------------------------------------------------------}
  223.  
  224.  
  225.        (From the form of the procedure, you just KNOW we're going  to be
  226.        adding to it in a bit!)
  227.  
  228.        OK, enter these routines into your program.  Replace the  call to
  229.        Block in the main program, by  a  call  to DoProgram.  Now try it
  230.        and  see  how  it works.  Well, it's still not  much,  but  we're
  231.        getting closer.
  232.  
  233.  
  234.        SOME GROUNDWORK
  235.  
  236.        Before we begin to define the various control constructs, we need
  237.        to  lay a bit more groundwork.  First, a word of warning: I won't
  238.        be using the same syntax  for these constructs as you're familiar
  239.        with  from Pascal or C.  For example, the Pascal syntax for an IF
  240.        is:
  241.  
  242.  
  243.             IF <condition> THEN <statement>
  244.  
  245.  
  246.        (where the statement, of course, may be compound).A6A6
  247.                                      - 4 -A*A*
  248.  
  249. PA A
  250.  
  251.  
  252.  
  253.  
  254.  
  255.        The C version is similar:
  256.  
  257.  
  258.             IF ( <condition> ) <statement>
  259.  
  260.  
  261.        Instead, I'll be using something that looks more like Ada:
  262.  
  263.  
  264.             IF <condition> <block> ENDIF
  265.  
  266.  
  267.        In  other  words,  the IF construct has  a  specific  termination
  268.        symbol.  This avoids  the  dangling-else of Pascal and C and also
  269.        precludes the need for the brackets {} or begin-end.   The syntax
  270.        I'm showing you here, in fact, is that of the language  KISS that
  271.        I'll be detailing in  later  installments.   The other constructs
  272.        will also be  slightly  different.    That  shouldn't  be  a real
  273.        problem for you.  Once you see how it's done, you'll realize that
  274.        it  really  doesn't  matter  so  much  which  specific syntax  is
  275.        involved.  Once the syntax is defined, turning it  into  code  is
  276.        straightforward.
  277.  
  278.        Now, all of the  constructs  we'll  be  dealing with here involve
  279.        transfer of control, which at the assembler-language  level means
  280.        conditional  and/or  unconditional branches.   For  example,  the
  281.        simple IF statement
  282.  
  283.  
  284.                  IF <condition> A ENDIF B ....
  285.  
  286.        must get translated into
  287.  
  288.                  Branch if NOT condition to L
  289.                  A
  290.             L:   B
  291.                  ...
  292.  
  293.  
  294.        It's clear, then, that we're going to need  some  more procedures
  295.        to  help  us  deal with these branches.  I've defined two of them
  296.        below.  Procedure NewLabel generates unique labels.  This is done
  297.        via the simple expedient of calling every label  'Lnn',  where nn
  298.        is a label number starting from zero.   Procedure  PostLabel just
  299.        outputs the labels at the proper place.
  300.  
  301.        Here are the two routines:A?A?
  302.  
  303.                                      - 5 -A*A*
  304.  
  305. PA A
  306.  
  307.  
  308.  
  309.  
  310.  
  311.        {--------------------------------------------------------------}
  312.        { Generate a Unique Label }
  313.  
  314.        function NewLabel: string;
  315.        var S: string;
  316.        begin
  317.           Str(LCount, S);
  318.           NewLabel := 'L' + S;
  319.           Inc(LCount);
  320.        end;
  321.  
  322.  
  323.        {--------------------------------------------------------------}
  324.        { Post a Label To Output }
  325.  
  326.        procedure PostLabel(L: string);
  327.        begin
  328.           WriteLn(L, ':');
  329.        end;
  330.        {--------------------------------------------------------------}
  331.  
  332.  
  333.        Notice that we've added  a  new  global  variable, LCount, so you
  334.        need to change the VAR declarations at the top of the  program to
  335.        look like this:
  336.  
  337.  
  338.        var Look  : char;              { Lookahead Character }
  339.            Lcount: integer;           { Label Counter }
  340.  
  341.  
  342.        Also, add the following extra initialization to Init:
  343.  
  344.  
  345.           LCount := 0;
  346.  
  347.        (DON'T forget that, or your labels can look really strange!)
  348.  
  349.  
  350.        At this point I'd also like to show you a  new  kind of notation.
  351.        If  you  compare  the form of the IF statement above with the as-
  352.        sembler code that must be produced, you can see  that  there  are
  353.        certain  actions  associated  with each of the  keywords  in  the
  354.        statement:
  355.  
  356.  
  357.             IF:  First, get the condition and issue the code for it.
  358.                  Then, create a unique label and emit a branch if false.
  359.  
  360.             ENDIF: Emit the label.
  361.  
  362.  
  363.        These actions can be shown very concisely if we write  the syntax
  364.        this way:A*A*
  365.                                      - 6 -
  366.  
  367. PA A
  368.  
  369.  
  370.  
  371.  
  372.  
  373.             IF
  374.             <condition>    { Condition;
  375.                              L = NewLabel;
  376.                              Emit(Branch False to L); }
  377.             <block>
  378.             ENDIF          { PostLabel(L) }
  379.  
  380.  
  381.        This is an example  of  syntax-directed  translation.  We've been
  382.        doing it all along ... we've just never written it down  this way
  383.        before.  The stuff in curly brackets represents the ACTIONS to be
  384.        taken.  The nice part about this representation is  that  it  not
  385.        only shows what  we  have  to  recognize, but also the actions we
  386.        have to perform, and in which  order.   Once we have this syntax,
  387.        the code almost writes itself.
  388.  
  389.        About  the  only thing left to do is to be a  bit  more  specific
  390.        about what we mean by "Branch if false."
  391.  
  392.        I'm assuming that there will  be  code  executed  for <condition>
  393.        that  will  perform  Boolean algebra and compute some result.  It
  394.        should also set the condition flags corresponding to that result.
  395.        Now, the usual convention  for  a Boolean variable is to let 0000
  396.        represent "false," and  anything  else (some use FFFF, some 0001)
  397.        represent "true."
  398.  
  399.        On the 68000  the  condition  flags  are set whenever any data is
  400.        moved or calculated.  If the  data  is a 0000 (corresponding to a
  401.        false condition, remember), the zero flag will be set.   The code
  402.        for "Branch on zero" is BEQ.  So for our purposes here,
  403.  
  404.  
  405.                       BEQ  <=> Branch if false
  406.                       BNE  <=> Branch if true
  407.  
  408.  
  409.        It's the nature of the beast that most  of  the  branches  we see
  410.        will  be  BEQ's  ...  we'll  be branching AROUND the code  that's
  411.        supposed to be executed when the condition is true.
  412.  
  413.  
  414.        THE IF STATEMENT
  415.  
  416.        With that bit of explanation out of the way, we're  finally ready
  417.        to begin coding the IF-statement parser.  In  fact,  we've almost
  418.        already  done  it!   As usual, I'll be using our single-character
  419.        approach, with the character 'i' for IF, and 'e'  for  ENDIF  (as
  420.        well  as END ... that dual nature causes  no  confusion).    I'll
  421.        also, for now, skip completely  the character for the branch con-
  422.        dition, which we still have to define.
  423.  
  424.        The code for DoIf is:ABAB
  425.                                      - 7 -A*A*
  426.  
  427. PA A
  428.  
  429.  
  430.  
  431.  
  432.  
  433.        {--------------------------------------------------------------}
  434.        { Recognize and Translate an IF Construct }
  435.  
  436.        procedure Block; Forward;
  437.  
  438.  
  439.        procedure DoIf;
  440.        var L: string;
  441.        begin
  442.           Match('i');
  443.           L := NewLabel;
  444.           Condition;
  445.           EmitLn('BEQ ' + L);
  446.           Block;
  447.           Match('e');
  448.           PostLabel(L);
  449.        end;
  450.        {--------------------------------------------------------------}
  451.  
  452.  
  453.        Add this routine to your program, and change  Block  to reference
  454.        it as follows:
  455.  
  456.  
  457.        {--------------------------------------------------------------}
  458.        { Recognize and Translate a Statement Block }
  459.  
  460.        procedure Block;
  461.        begin
  462.           while not(Look in ['e']) do begin
  463.              case Look of
  464.               'i': DoIf;
  465.               'o': Other;
  466.              end;
  467.           end;
  468.        end;
  469.        {--------------------------------------------------------------}
  470.  
  471.  
  472.        Notice the reference to procedure Condition.    Eventually, we'll
  473.        write a routine that  can  parse  and  translate any Boolean con-
  474.        dition we care to give it.  But  that's  a  whole  installment by
  475.        itself (the next one, in fact).    For  now, let's just make it a
  476.        dummy that emits some text.  Write the following routine:AQAQ
  477.  
  478.                                      - 8 -A*A*
  479.  
  480. PA A
  481.  
  482.  
  483.  
  484.  
  485.  
  486.        {--------------------------------------------------------------}
  487.        { Parse and Translate a Boolean Condition }
  488.        { This version is a dummy }
  489.  
  490.        Procedure Condition;
  491.        begin
  492.           EmitLn('<condition>');
  493.        end;
  494.        {--------------------------------------------------------------}
  495.  
  496.  
  497.        Insert this procedure in your program just before DoIf.   Now run
  498.        the program.  Try a string like
  499.  
  500.             aibece
  501.  
  502.        As you can see,  the  parser seems to recognize the construct and
  503.        inserts the object code at the  right  places.   Now try a set of
  504.        nested IF's, like
  505.  
  506.             aibicedefe
  507.  
  508.        It's starting to look real, eh?
  509.  
  510.        Now that we  have  the  general  idea  (and the tools such as the
  511.        notation and the procedures NewLabel and PostLabel), it's a piece
  512.        of cake to extend the parser to include other  constructs.    The
  513.        first (and also one of the  trickiest)  is to add the ELSE clause
  514.        to IF.  The BNF is
  515.  
  516.  
  517.             IF <condition> <block> [ ELSE <block>] ENDIF
  518.  
  519.  
  520.        The tricky part arises simply  because there is an optional part,
  521.        which doesn't occur in the other constructs.
  522.  
  523.        The corresponding output code should be
  524.  
  525.  
  526.                  <condition>
  527.                  BEQ L1
  528.                  <block>
  529.                  BRA L2
  530.             L1:  <block>
  531.             L2:  ...
  532.  
  533.  
  534.        This leads us to the following syntax-directed translation:A3A3
  535.  
  536.                                      - 9 -A*A*
  537.  
  538. PA A
  539.  
  540.  
  541.  
  542.  
  543.  
  544.             IF
  545.             <condition>    { L1 = NewLabel;
  546.                              L2 = NewLabel;
  547.                              Emit(BEQ L1) }
  548.             <block>
  549.             ELSE           { Emit(BRA L2);
  550.                              PostLabel(L1) }
  551.             <block>
  552.             ENDIF          { PostLabel(L2) }
  553.  
  554.  
  555.        Comparing this with the case for an ELSE-less IF gives us  a clue
  556.        as to how to handle both situations.   The  code  below  does it.
  557.        (Note that I  use  an  'l'  for  the ELSE, since 'e' is otherwise
  558.        occupied):
  559.  
  560.  
  561.        {--------------------------------------------------------------}
  562.        { Recognize and Translate an IF Construct }
  563.  
  564.        procedure DoIf;
  565.        var L1, L2: string;
  566.        begin
  567.           Match('i');
  568.           Condition;
  569.           L1 := NewLabel;
  570.           L2 := L1;
  571.           EmitLn('BEQ ' + L1);
  572.           Block;
  573.           if Look = 'l' then begin
  574.              Match('l');
  575.              L2 := NewLabel;
  576.              EmitLn('BRA ' + L2);
  577.              PostLabel(L1);
  578.              Block;
  579.           end;
  580.           Match('e');
  581.           PostLabel(L2);
  582.        end;
  583.        {--------------------------------------------------------------}
  584.  
  585.  
  586.        There you have it.  A complete IF parser/translator, in  19 lines
  587.        of code.
  588.  
  589.        Give it a try now.  Try something like
  590.  
  591.           aiblcede
  592.  
  593.        Did it work?  Now, just  to  be  sure we haven't broken the ELSE-
  594.        less case, try
  595.  
  596.           aibeceA6A6
  597.                                     - 10 -A*A*
  598.  
  599. PA A
  600.  
  601.  
  602.  
  603.  
  604.  
  605.        Now try some nested IF's.  Try anything you like,  including some
  606.        badly formed statements.   Just  remember that 'e' is not a legal
  607.        "other" statement.
  608.  
  609.  
  610.        THE WHILE STATEMENT
  611.  
  612.        The next type of statement should be easy, since we  already have
  613.        the process  down  pat.    The  syntax  I've chosen for the WHILE
  614.        statement is
  615.  
  616.  
  617.                  WHILE <condition> <block> ENDWHILE
  618.  
  619.  
  620.        I know,  I  know,  we  don't  REALLY  need separate kinds of ter-
  621.        minators for each construct ... you can see that by the fact that
  622.        in our one-character version, 'e' is used for all of them.  But I
  623.        also remember  MANY debugging sessions in Pascal, trying to track
  624.        down a wayward END that the compiler obviously thought I meant to
  625.        put  somewhere  else.   It's been my experience that specific and
  626.        unique  keywords,  although  they add to the  vocabulary  of  the
  627.        language,  give  a  bit of error-checking that is worth the extra
  628.        work for the compiler writer.
  629.  
  630.        Now,  consider  what  the  WHILE  should be translated into.   It
  631.        should be:
  632.  
  633.  
  634.             L1:  <condition>
  635.                  BEQ L2
  636.                  <block>
  637.                  BRA L1
  638.             L2:
  639.  
  640.  
  641.        As before, comparing the two representations gives us the actions
  642.        needed at each point.
  643.  
  644.  
  645.             WHILE          { L1 = NewLabel;
  646.                              PostLabel(L1) }
  647.             <condition>    { Emit(BEQ L2) }
  648.             <block>
  649.             ENDWHILE       { Emit(BRA L1);
  650.                              PostLabel(L2) }
  651.  
  652.  
  653.        The code follows immediately from the syntax:A3A3
  654.  
  655.                                     - 11 -A*A*
  656.  
  657. PA A
  658.  
  659.  
  660.  
  661.  
  662.  
  663.        {--------------------------------------------------------------}
  664.        { Parse and Translate a WHILE Statement }
  665.  
  666.        procedure DoWhile;
  667.        var L1, L2: string;
  668.        begin
  669.           Match('w');
  670.           L1 := NewLabel;
  671.           L2 := NewLabel;
  672.           PostLabel(L1);
  673.           Condition;
  674.           EmitLn('BEQ ' + L2);
  675.           Block;
  676.           Match('e');
  677.           EmitLn('BRA ' + L1);
  678.           PostLabel(L2);
  679.        end;
  680.        {--------------------------------------------------------------}
  681.  
  682.  
  683.        Since  we've  got a new statement, we have to add a  call  to  it
  684.        within procedure Block:
  685.  
  686.  
  687.        {--------------------------------------------------------------}
  688.        { Recognize and Translate a Statement Block }
  689.  
  690.        procedure Block;
  691.        begin
  692.           while not(Look in ['e', 'l']) do begin
  693.              case Look of
  694.               'i': DoIf;
  695.               'w': DoWhile;
  696.               else Other;
  697.              end;
  698.           end;
  699.        end;
  700.        {--------------------------------------------------------------}
  701.  
  702.  
  703.        No other changes are necessary.
  704.  
  705.        OK, try the new program.  Note that this  time,  the  <condition>
  706.        code is INSIDE the upper label, which is just where we wanted it.
  707.        Try some nested loops.  Try some loops within IF's, and some IF's
  708.        within loops.  If you get  a  bit  confused as to what you should
  709.        type, don't be discouraged:  you  write  bugs in other languages,
  710.        too, don't you?  It'll look a lot  more  meaningful  when  we get
  711.        full keywords.
  712.  
  713.        I hope by now that you're beginning to  get  the  idea  that this
  714.        really  IS easy.  All we have to do to accomodate a new construct
  715.        is to work out  the  syntax-directed translation of it.  The code
  716.        almost falls out  from  there,  and  it doesn't affect any of theA*A*
  717.                                     - 12 -
  718.  
  719. PA A
  720.  
  721.  
  722.  
  723.  
  724.  
  725.        other routines.  Once you've gotten the feel of the thing, you'll
  726.        see that you  can  add  new  constructs  about as fast as you can
  727.        dream them up.
  728.  
  729.  
  730.        THE LOOP STATEMENT
  731.  
  732.        We could stop right here, and  have  a language that works.  It's
  733.        been  shown  many  times that a high-order language with only two
  734.        constructs, the IF and the WHILE, is sufficient  to  write struc-
  735.        tured  code.   But we're on a roll now, so let's  richen  up  the
  736.        repertoire a bit.
  737.  
  738.        This construct is even easier, since it has no condition  test at
  739.        all  ... it's an infinite loop.  What's the point of such a loop?
  740.        Not much, by  itself,  but  later  on  we're going to add a BREAK
  741.        command,  that  will  give us a way out.  This makes the language
  742.        considerably richer than Pascal, which  has  no  break,  and also
  743.        avoids the funny  WHILE(1) or WHILE TRUE of C and Pascal.
  744.  
  745.        The syntax is simply
  746.  
  747.             LOOP <block> ENDLOOP
  748.  
  749.        and the syntax-directed translation is:
  750.  
  751.  
  752.             LOOP           { L = NewLabel;
  753.                              PostLabel(L) }
  754.             <block>
  755.             ENDLOOP        { Emit(BRA L }
  756.  
  757.  
  758.        The corresponding code is shown below.  Since  I've  already used
  759.        'l'  for  the  ELSE, I've used  the  last  letter,  'p',  as  the
  760.        "keyword" this time.
  761.  
  762.  
  763.        {--------------------------------------------------------------}
  764.        { Parse and Translate a LOOP Statement }
  765.  
  766.        procedure DoLoop;
  767.        var L: string;
  768.        begin
  769.           Match('p');
  770.           L := NewLabel;
  771.           PostLabel(L);
  772.           Block;
  773.           Match('e');
  774.           EmitLn('BRA ' + L);
  775.        end;
  776.        {--------------------------------------------------------------}ABAB
  777.                                     - 13 -A*A*
  778.  
  779. PA A
  780.  
  781.  
  782.  
  783.  
  784.  
  785.        When you insert this routine, don't forget to add a line in Block
  786.        to call it.
  787.  
  788.  
  789.        REPEAT-UNTIL
  790.  
  791.        Here's one construct that I lifted right from Pascal.  The syntax
  792.        is
  793.  
  794.  
  795.             REPEAT <block> UNTIL <condition>  ,
  796.  
  797.  
  798.        and the syntax-directed translation is:
  799.  
  800.  
  801.             REPEAT         { L = NewLabel;
  802.                              PostLabel(L) }
  803.             <block>
  804.             UNTIL
  805.             <condition>    { Emit(BEQ L) }
  806.  
  807.  
  808.        As usual, the code falls out pretty easily:
  809.  
  810.  
  811.        {--------------------------------------------------------------}
  812.        { Parse and Translate a REPEAT Statement }
  813.  
  814.        procedure DoRepeat;
  815.        var L: string;
  816.        begin
  817.           Match('r');
  818.           L := NewLabel;
  819.           PostLabel(L);
  820.           Block;
  821.           Match('u');
  822.           Condition;
  823.           EmitLn('BEQ ' + L);
  824.        end;
  825.        {--------------------------------------------------------------}
  826.  
  827.  
  828.        As  before, we have to add the call  to  DoRepeat  within  Block.
  829.        This time, there's a difference, though.  I decided  to  use  'r'
  830.        for REPEAT (naturally), but I also decided to use 'u'  for UNTIL.
  831.        This means that the 'u' must be added to the set of characters in
  832.        the while-test.  These  are  the  characters  that signal an exit
  833.        from the current  block  ... the "follow" characters, in compiler
  834.        jargon.A-A-
  835.  
  836.                                     - 14 -A*A*
  837.  
  838. PA A
  839.  
  840.  
  841.  
  842.  
  843.  
  844.        {--------------------------------------------------------------}
  845.        { Recognize and Translate a Statement Block }
  846.  
  847.        procedure Block;
  848.        begin
  849.           while not(Look in ['e', 'l', 'u']) do begin
  850.              case Look of
  851.               'i': DoIf;
  852.               'w': DoWhile;
  853.               'p': DoLoop;
  854.               'r': DoRepeat;
  855.               else Other;
  856.              end;
  857.           end;
  858.        end;
  859.        {--------------------------------------------------------------}
  860.  
  861.  
  862.        THE FOR LOOP
  863.  
  864.        The FOR loop  is a very handy one to have around, but it's a bear
  865.        to translate.  That's not so much because the construct itself is
  866.        hard ... it's only a loop  after  all ... but simply because it's
  867.        hard to implement  in  assembler  language.    Once  the  code is
  868.        figured out, the translation is straightforward enough.
  869.  
  870.        C fans love  the  FOR-loop  of  that language (and, in fact, it's
  871.        easier to code), but I've chosen instead a syntax very  much like
  872.        the one from good ol' BASIC:
  873.  
  874.  
  875.             FOR <ident> = <expr1> TO <expr2> <block> ENDFOR
  876.  
  877.  
  878.        The translation of a FOR loop  can  be just about as difficult as
  879.        you choose  to  make  it,  depending  upon  the way you decide to
  880.        define  the rules as to how to handle the limits.  Does expr2 get
  881.        evaluated  every time through the loop, for  example,  or  is  it
  882.        treated as a constant limit?   Do  you always go through the loop
  883.        at least once,  as  in  FORTRAN,  or  not? It gets simpler if you
  884.        adopt the point of view that the construct is equivalent to:
  885.  
  886.  
  887.             <ident> = <expr1>
  888.             TEMP = <expr2>
  889.             WHILE <ident> <= TEMP
  890.             <block>
  891.             ENDWHILE
  892.  
  893.  
  894.        Notice that with this definition of the loop, <block> will not be
  895.        executed at all if <expr1> is initially larger than <expr2>.ABAB
  896.                                     - 15 -A*A*
  897.  
  898. PA A
  899.  
  900.  
  901.  
  902.  
  903.  
  904.        The 68000 code needed to do this is trickier than  anything we've
  905.        done so far.  I had a couple  of  tries  at  it, putting both the
  906.        counter  and  the    upper limit on the stack, both in registers,
  907.        etc.  I  finally  arrived  at  a hybrid arrangement, in which the
  908.        loop counter is in memory (so that it can be accessed  within the
  909.        loop), and the upper limit is on the stack.  The  translated code
  910.        came out like this:
  911.  
  912.  
  913.                  <ident>             get name of loop counter
  914.                  <expr1>             get initial value
  915.                  LEA <ident>(PC),A0  address the loop counter
  916.                  SUBQ #1,D0          predecrement it
  917.                  MOVE D0,(A0)        save it
  918.                  <expr1>             get upper limit
  919.                  MOVE D0,-(SP)       save it on stack
  920.  
  921.             L1:  LEA <ident>(PC),A0  address loop counter
  922.                  MOVE (A0),D0        fetch it to D0
  923.                  ADDQ #1,D0          bump the counter
  924.                  MOVE D0,(A0)        save new value
  925.                  CMP (SP),D0         check for range
  926.                  BLE L2              skip out if D0 > (SP)
  927.                  <block>
  928.                  BRA L1              loop for next pass
  929.             L2:  ADDQ #2,SP          clean up the stack
  930.  
  931.  
  932.        Wow!    That  seems like a lot of code ...  the  line  containing
  933.        <block> seems to almost get lost.  But that's the best I could do
  934.        with it.   I guess it helps to keep in mind that it's really only
  935.        sixteen  words,  after  all.  If  anyone else can  optimize  this
  936.        better, please let me know.
  937.  
  938.        Still, the parser  routine  is  pretty  easy now that we have the
  939.        code:
  940.  
  941.  
  942.        {--------------------------------------------------------------}
  943.        { Parse and Translate a FOR Statement }
  944.  
  945.        procedure DoFor;
  946.        var L1, L2: string;
  947.            Name: char;
  948.        begin
  949.           Match('f');
  950.           L1 := NewLabel;
  951.           L2 := NewLabel;
  952.           Name := GetName;
  953.           Match('=');
  954.           Expression;
  955.           EmitLn('SUBQ #1,D0');
  956.           EmitLn('LEA ' + Name + '(PC),A0');
  957.           EmitLn('MOVE D0,(A0)');A*A*
  958.                                     - 16 -
  959.  
  960. PA A
  961.  
  962.  
  963.  
  964.  
  965.  
  966.           Expression;
  967.           EmitLn('MOVE D0,-(SP)');
  968.           PostLabel(L1);
  969.           EmitLn('LEA ' + Name + '(PC),A0');
  970.           EmitLn('MOVE (A0),D0');
  971.           EmitLn('ADDQ #1,D0');
  972.           EmitLn('MOVE D0,(A0)');
  973.           EmitLn('CMP (SP),D0');
  974.           EmitLn('BGT ' + L2);
  975.           Block;
  976.           Match('e');
  977.           EmitLn('BRA ' + L1);
  978.           PostLabel(L2);
  979.           EmitLn('ADDQ #2,SP');
  980.        end;
  981.        {--------------------------------------------------------------}
  982.  
  983.  
  984.        Since we don't have  expressions  in this parser, I used the same
  985.        trick as for Condition, and wrote the routine
  986.  
  987.  
  988.        {--------------------------------------------------------------}
  989.        { Parse and Translate an Expression }
  990.        { This version is a dummy }
  991.  
  992.        Procedure Expression;
  993.        begin
  994.           EmitLn('<expr>');
  995.        end;
  996.        {--------------------------------------------------------------}
  997.  
  998.  
  999.        Give it a try.  Once again,  don't  forget  to  add  the  call in
  1000.        Block.    Since  we don't have any input for the dummy version of
  1001.        Expression, a typical input line would look something like
  1002.  
  1003.             afi=bece
  1004.  
  1005.        Well, it DOES generate a lot of code, doesn't it?    But at least
  1006.        it's the RIGHT code.
  1007.  
  1008.  
  1009.        THE DO STATEMENT
  1010.  
  1011.        All this made me wish for a simpler version of the FOR loop.  The
  1012.        reason for all the code  above  is  the  need  to  have  the loop
  1013.        counter accessible as a variable within the loop.  If all we need
  1014.        is a counting loop to make us go through  something  a  specified
  1015.        number of times, but  don't  need  access  to the counter itself,
  1016.        there is a much easier solution.  The 68000 has a  "decrement and
  1017.        branch nonzero" instruction built in which is ideal for counting.
  1018.        For good measure, let's add this construct, too.   This  will  be
  1019.        the last of our loop structures.A*A*
  1020.                                     - 17 -
  1021.  
  1022. PA A
  1023.  
  1024.  
  1025.  
  1026.  
  1027.  
  1028.        The syntax and its translation is:
  1029.  
  1030.  
  1031.             DO
  1032.             <expr>         { Emit(SUBQ #1,D0);
  1033.                              L = NewLabel;
  1034.                              PostLabel(L);
  1035.                              Emit(MOVE D0,-(SP) }
  1036.             <block>
  1037.             ENDDO          { Emit(MOVE (SP)+,D0;
  1038.                              Emit(DBRA D0,L) }
  1039.  
  1040.  
  1041.        That's quite a bit simpler!  The loop will execute  <expr> times.
  1042.        Here's the code:
  1043.  
  1044.  
  1045.        {--------------------------------------------------------------}
  1046.        { Parse and Translate a DO Statement }
  1047.  
  1048.        procedure Dodo;
  1049.        var L: string;
  1050.        begin
  1051.           Match('d');
  1052.           L := NewLabel;
  1053.           Expression;
  1054.           EmitLn('SUBQ #1,D0');
  1055.           PostLabel(L);
  1056.           EmitLn('MOVE D0,-(SP)');
  1057.           Block;
  1058.           EmitLn('MOVE (SP)+,D0');
  1059.           EmitLn('DBRA D0,' + L);
  1060.        end;
  1061.        {--------------------------------------------------------------}
  1062.  
  1063.  
  1064.        I think you'll have to agree, that's a whole lot simpler than the
  1065.        classical FOR.  Still, each construct has its place.
  1066.  
  1067.  
  1068.        THE BREAK STATEMENT
  1069.  
  1070.        Earlier I promised you a BREAK statement to accompany LOOP.  This
  1071.        is  one  I'm sort of proud of.  On the face of it a  BREAK  seems
  1072.        really  tricky.  My first approach was to just use it as an extra
  1073.        terminator to Block, and split all the loops into two parts, just
  1074.        as  I did with the ELSE half of an IF.  That  turns  out  not  to
  1075.        work, though, because the BREAK statement is almost certainly not
  1076.        going to show  up at the same level as the loop itself.  The most
  1077.        likely place for a BREAK is right after an IF, which  would cause
  1078.        it to exit to the IF  construct,  not the enclosing loop.  WRONG.
  1079.        The  BREAK  has  to exit the inner LOOP, even if it's nested down
  1080.        into several levels of IFs.A6A6
  1081.                                     - 18 -A*A*
  1082.  
  1083. PA A
  1084.  
  1085.  
  1086.  
  1087.  
  1088.  
  1089.        My next thought was that I would just store away, in  some global
  1090.        variable, the ending label of the innermost loop.    That doesn't
  1091.        work  either, because there may be a break  from  an  inner  loop
  1092.        followed by a break from an outer one.  Storing the label for the
  1093.        inner loop would clobber the label for the  outer  one.    So the
  1094.        global variable turned into a stack.  Things were starting to get
  1095.        messy.
  1096.  
  1097.        Then  I  decided  to take my own advice.  Remember  in  the  last
  1098.        session when  I  pointed  out  how  well  the implicit stack of a
  1099.        recursive descent parser was  serving  our needs?  I said that if
  1100.        you begin to  see  the  need  for  an external stack you might be
  1101.        doing  something  wrong.   Well, I was.  It is indeed possible to
  1102.        let the recursion built into  our parser take care of everything,
  1103.        and the solution is so simple that it's surprising.
  1104.  
  1105.        The secret is  to  note  that  every BREAK statement has to occur
  1106.        within a block ... there's no place else for it to be.  So all we
  1107.        have  to  do  is to pass into  Block  the  exit  address  of  the
  1108.        innermost loop.  Then it can pass the address to the routine that
  1109.        translates the  break instruction.  Since an IF statement doesn't
  1110.        change the loop level, procedure DoIf doesn't need to do anything
  1111.        except  pass the label into ITS blocks (both  of  them).    Since
  1112.        loops DO change the level,  each  loop  construct  simply ignores
  1113.        whatever label is above it and passes its own exit label along.
  1114.  
  1115.        All  this  is easier to show you than it is to  describe.    I'll
  1116.        demonstrate with the easiest loop, which is LOOP:
  1117.  
  1118.  
  1119.        {--------------------------------------------------------------}
  1120.        { Parse and Translate a LOOP Statement }
  1121.  
  1122.        procedure DoLoop;
  1123.        var L1, L2: string;
  1124.        begin
  1125.           Match('p');
  1126.           L1 := NewLabel;
  1127.           L2 := NewLabel;
  1128.           PostLabel(L1);
  1129.           Block(L2);
  1130.           Match('e');
  1131.           EmitLn('BRA ' + L1);
  1132.           PostLabel(L2);
  1133.        end;
  1134.        {--------------------------------------------------------------}
  1135.  
  1136.  
  1137.        Notice that DoLoop now has TWO labels, not just one.   The second
  1138.        is to give the BREAK instruction a target to jump  to.   If there
  1139.        is no BREAK within  the  loop, we've wasted a label and cluttered
  1140.        up things a bit, but there's no harm done.ABAB
  1141.                                     - 19 -A*A*
  1142.  
  1143. PA A
  1144.  
  1145.  
  1146.  
  1147.  
  1148.  
  1149.        Note also that Block now has a parameter, which  for  loops  will
  1150.        always be the exit address.  The new version of Block is:
  1151.  
  1152.  
  1153.        {--------------------------------------------------------------}
  1154.        { Recognize and Translate a Statement Block }
  1155.  
  1156.        procedure Block(L: string);
  1157.        begin
  1158.           while not(Look in ['e', 'l', 'u']) do begin
  1159.              case Look of
  1160.               'i': DoIf(L);
  1161.               'w': DoWhile;
  1162.               'p': DoLoop;
  1163.               'r': DoRepeat;
  1164.               'f': DoFor;
  1165.               'd': DoDo;
  1166.               'b': DoBreak(L);
  1167.               else Other;
  1168.              end;
  1169.           end;
  1170.        end;
  1171.        {--------------------------------------------------------------}
  1172.  
  1173.  
  1174.        Again,  notice  that  all Block does with the label is to pass it
  1175.        into DoIf and  DoBreak.    The  loop  constructs  don't  need it,
  1176.        because they are going to pass their own label anyway.
  1177.  
  1178.        The new version of DoIf is:AUAU
  1179.  
  1180. APAP
  1181.  
  1182.                                     - 20 -A*A*
  1183.  
  1184. PA A
  1185.  
  1186.  
  1187.  
  1188.  
  1189.  
  1190.        {--------------------------------------------------------------}
  1191.        { Recognize and Translate an IF Construct }
  1192.  
  1193.        procedure Block(L: string); Forward;
  1194.  
  1195.  
  1196.        procedure DoIf(L: string);
  1197.        var L1, L2: string;
  1198.        begin
  1199.           Match('i');
  1200.           Condition;
  1201.           L1 := NewLabel;
  1202.           L2 := L1;
  1203.           EmitLn('BEQ ' + L1);
  1204.           Block(L);
  1205.           if Look = 'l' then begin
  1206.              Match('l');
  1207.              L2 := NewLabel;
  1208.              EmitLn('BRA ' + L2);
  1209.              PostLabel(L1);
  1210.              Block(L);
  1211.           end;
  1212.           Match('e');
  1213.           PostLabel(L2);
  1214.        end;
  1215.        {--------------------------------------------------------------}
  1216.  
  1217.  
  1218.        Here,  the  only  thing  that  changes  is  the addition  of  the
  1219.        parameter to procedure Block.  An IF statement doesn't change the
  1220.        loop  nesting level, so DoIf just passes the  label  along.    No
  1221.        matter how many levels of IF nesting we have, the same label will
  1222.        be used.
  1223.  
  1224.        Now, remember that DoProgram also calls Block, so it now needs to
  1225.        pass it a label.  An  attempt  to  exit the outermost block is an
  1226.        error, so DoProgram  passes  a  null  label  which  is  caught by
  1227.        DoBreak:
  1228.  
  1229.  
  1230.        {--------------------------------------------------------------}
  1231.        { Recognize and Translate a BREAK }
  1232.  
  1233.        procedure DoBreak(L: string);
  1234.        begin
  1235.           Match('b');
  1236.           if L <> '' then
  1237.              EmitLn('BRA ' + L)
  1238.           else Abort('No loop to break from');
  1239.        end;A-A-
  1240.  
  1241.                                     - 21 -A*A*
  1242.  
  1243. PA A
  1244.  
  1245.  
  1246.  
  1247.  
  1248.  
  1249.        {--------------------------------------------------------------}
  1250.  
  1251.        { Parse and Translate a Program }
  1252.  
  1253.        procedure DoProgram;
  1254.        begin
  1255.           Block('');
  1256.           if Look <> 'e' then Expected('End');
  1257.           EmitLn('END')
  1258.        end;
  1259.        {--------------------------------------------------------------}
  1260.  
  1261.  
  1262.        That  ALMOST takes care of everything.  Give it a try, see if you
  1263.        can "break" it <pun>.  Careful, though.  By this time  we've used
  1264.        so many letters, it's hard to think of characters that aren't now
  1265.        representing  reserved  words.    Remember:  before  you  try the
  1266.        program, you're going to have to edit every occurence of Block in
  1267.        the other loop constructs to include the new parameter.    Do  it
  1268.        just like I did for LOOP.
  1269.  
  1270.        I  said ALMOST above.  There is one slight problem: if you take a
  1271.        hard  look  at  the code generated for DO, you'll see that if you
  1272.        break  out  of  this loop, the value of the loop counter is still
  1273.        left on the stack.  We're going to have to fix that!  A shame ...
  1274.        that was one  of  our  smaller  routines, but it can't be helped.
  1275.        Here's a version that doesn't have the problem:
  1276.  
  1277.  
  1278.        {--------------------------------------------------------------}
  1279.        { Parse and Translate a DO Statement }
  1280.  
  1281.        procedure Dodo;
  1282.        var L1, L2: string;
  1283.        begin
  1284.           Match('d');
  1285.           L1 := NewLabel;
  1286.           L2 := NewLabel;
  1287.           Expression;
  1288.           EmitLn('SUBQ #1,D0');
  1289.           PostLabel(L1);
  1290.           EmitLn('MOVE D0,-(SP)');
  1291.           Block(L2);
  1292.           EmitLn('MOVE (SP)+,D0');
  1293.           EmitLn('DBRA D0,' + L1);
  1294.           EmitLn('SUBQ #2,SP');
  1295.           PostLabel(L2);
  1296.           EmitLn('ADDQ #2,SP');
  1297.        end;
  1298.        {--------------------------------------------------------------}
  1299.  
  1300.  
  1301.        The  two  extra  instructions,  the  SUBQ and ADDQ, take care  of
  1302.        leaving the stack in the right shape.A*A*
  1303.                                     - 22 -
  1304.  
  1305. PA A
  1306.  
  1307.  
  1308.  
  1309.  
  1310.  
  1311.        CONCLUSION
  1312.  
  1313.        At this point we have created a number of control  constructs ...
  1314.        a richer set, really, than that provided by almost any other pro-
  1315.        gramming language.  And,  except  for the FOR loop, it was pretty
  1316.        easy to do.  Even that one was tricky only because it's tricky in
  1317.        assembler language.
  1318.  
  1319.        I'll conclude this session here.  To wrap the thing up with a red
  1320.        ribbon, we really  should  have  a  go  at  having  real keywords
  1321.        instead of these mickey-mouse  single-character  things.   You've
  1322.        already seen that  the  extension to multi-character words is not
  1323.        difficult, but in this case it will make a big difference  in the
  1324.        appearance of our input code.  I'll save that little bit  for the
  1325.        next installment.  In that installment we'll also address Boolean
  1326.        expressions, so we can get rid of the dummy version  of Condition
  1327.        that we've used here.  See you then.
  1328.  
  1329.        For reference purposes, here is  the  completed  parser  for this
  1330.        session:
  1331.  
  1332.  
  1333.        {--------------------------------------------------------------}
  1334.        program Branch;
  1335.  
  1336.        {--------------------------------------------------------------}
  1337.        { Constant Declarations }
  1338.  
  1339.        const TAB = ^I;
  1340.              CR  = ^M;
  1341.  
  1342.  
  1343.        {--------------------------------------------------------------}
  1344.        { Variable Declarations }
  1345.  
  1346.        var Look  : char;              { Lookahead Character }
  1347.            Lcount: integer;           { Label Counter }
  1348.  
  1349.  
  1350.        {--------------------------------------------------------------}
  1351.        { Read New Character From Input Stream }
  1352.  
  1353.        procedure GetChar;
  1354.        begin
  1355.           Read(Look);
  1356.        end;AEAE
  1357.  
  1358.                                     - 23 -A*A*
  1359.  
  1360. PA A
  1361.  
  1362.  
  1363.  
  1364.  
  1365.  
  1366.        {--------------------------------------------------------------}
  1367.        { Report an Error }
  1368.  
  1369.        procedure Error(s: string);
  1370.        begin
  1371.           WriteLn;
  1372.           WriteLn(^G, 'Error: ', s, '.');
  1373.        end;
  1374.  
  1375.  
  1376.        {--------------------------------------------------------------}
  1377.        { Report Error and Halt }
  1378.  
  1379.        procedure Abort(s: string);
  1380.        begin
  1381.           Error(s);
  1382.           Halt;
  1383.        end;
  1384.  
  1385.  
  1386.        {--------------------------------------------------------------}
  1387.        { Report What Was Expected }
  1388.  
  1389.        procedure Expected(s: string);
  1390.        begin
  1391.           Abort(s + ' Expected');
  1392.        end;
  1393.  
  1394.        {--------------------------------------------------------------}
  1395.        { Match a Specific Input Character }
  1396.  
  1397.        procedure Match(x: char);
  1398.        begin
  1399.           if Look = x then GetChar
  1400.           else Expected('''' + x + '''');
  1401.        end;
  1402.  
  1403.  
  1404.        {--------------------------------------------------------------}
  1405.        { Recognize an Alpha Character }
  1406.  
  1407.        function IsAlpha(c: char): boolean;
  1408.        begin
  1409.           IsAlpha := UpCase(c) in ['A'..'Z'];
  1410.        end;
  1411.  
  1412.  
  1413.        {--------------------------------------------------------------}
  1414.        { Recognize a Decimal Digit }
  1415.  
  1416.        function IsDigit(c: char): boolean;
  1417.        begin
  1418.           IsDigit := c in ['0'..'9'];
  1419.        end;A*A*
  1420.                                     - 24 -
  1421.  
  1422. PA A
  1423.  
  1424.  
  1425.  
  1426.  
  1427.  
  1428.        {--------------------------------------------------------------}
  1429.        { Recognize an Addop }
  1430.  
  1431.        function IsAddop(c: char): boolean;
  1432.        begin
  1433.           IsAddop := c in ['+', '-'];
  1434.        end;
  1435.  
  1436.  
  1437.        {--------------------------------------------------------------}
  1438.        { Recognize White Space }
  1439.  
  1440.        function IsWhite(c: char): boolean;
  1441.        begin
  1442.           IsWhite := c in [' ', TAB];
  1443.        end;
  1444.  
  1445.  
  1446.        {--------------------------------------------------------------}
  1447.        { Skip Over Leading White Space }
  1448.  
  1449.        procedure SkipWhite;
  1450.        begin
  1451.           while IsWhite(Look) do
  1452.              GetChar;
  1453.        end;
  1454.  
  1455.  
  1456.        {--------------------------------------------------------------}
  1457.        { Get an Identifier }
  1458.  
  1459.        function GetName: char;
  1460.        begin
  1461.           if not IsAlpha(Look) then Expected('Name');
  1462.           GetName := UpCase(Look);
  1463.           GetChar;
  1464.        end;
  1465.  
  1466.  
  1467.        {--------------------------------------------------------------}
  1468.        { Get a Number }
  1469.  
  1470.        function GetNum: char;
  1471.        begin
  1472.           if not IsDigit(Look) then Expected('Integer');
  1473.           GetNum := Look;
  1474.           GetChar;
  1475.        end;A9A9
  1476.  
  1477.                                     - 25 -A*A*
  1478.  
  1479. PA A
  1480.  
  1481.  
  1482.  
  1483.  
  1484.  
  1485.        {--------------------------------------------------------------}
  1486.        { Generate a Unique Label }
  1487.  
  1488.        function NewLabel: string;
  1489.        var S: string;
  1490.        begin
  1491.           Str(LCount, S);
  1492.           NewLabel := 'L' + S;
  1493.           Inc(LCount);
  1494.        end;
  1495.  
  1496.  
  1497.        {--------------------------------------------------------------}
  1498.        { Post a Label To Output }
  1499.  
  1500.        procedure PostLabel(L: string);
  1501.        begin
  1502.           WriteLn(L, ':');
  1503.        end;
  1504.  
  1505.  
  1506.        {--------------------------------------------------------------}
  1507.        { Output a String with Tab }
  1508.  
  1509.        procedure Emit(s: string);
  1510.        begin
  1511.           Write(TAB, s);
  1512.        end;
  1513.  
  1514.  
  1515.        {--------------------------------------------------------------}
  1516.  
  1517.        { Output a String with Tab and CRLF }
  1518.  
  1519.        procedure EmitLn(s: string);
  1520.        begin
  1521.           Emit(s);
  1522.           WriteLn;
  1523.        end;
  1524.  
  1525.  
  1526.        {--------------------------------------------------------------}
  1527.        { Parse and Translate a Boolean Condition }
  1528.  
  1529.        procedure Condition;
  1530.        begin
  1531.           EmitLn('<condition>');
  1532.        end;A9A9
  1533.  
  1534.                                     - 26 -A*A*
  1535.  
  1536. PA A
  1537.  
  1538.  
  1539.  
  1540.  
  1541.  
  1542.        {--------------------------------------------------------------}
  1543.        { Parse and Translate a Math Expression }
  1544.  
  1545.        procedure Expression;
  1546.        begin
  1547.           EmitLn('<expr>');
  1548.        end;
  1549.  
  1550.  
  1551.        {--------------------------------------------------------------}
  1552.        { Recognize and Translate an IF Construct }
  1553.  
  1554.        procedure Block(L: string); Forward;
  1555.  
  1556.  
  1557.        procedure DoIf(L: string);
  1558.        var L1, L2: string;
  1559.        begin
  1560.           Match('i');
  1561.           Condition;
  1562.           L1 := NewLabel;
  1563.           L2 := L1;
  1564.           EmitLn('BEQ ' + L1);
  1565.           Block(L);
  1566.           if Look = 'l' then begin
  1567.              Match('l');
  1568.              L2 := NewLabel;
  1569.              EmitLn('BRA ' + L2);
  1570.              PostLabel(L1);
  1571.              Block(L);
  1572.           end;
  1573.           Match('e');
  1574.           PostLabel(L2);
  1575.        end;
  1576.  
  1577.  
  1578.        {--------------------------------------------------------------}
  1579.        { Parse and Translate a WHILE Statement }
  1580.  
  1581.        procedure DoWhile;
  1582.        var L1, L2: string;
  1583.        begin
  1584.           Match('w');
  1585.           L1 := NewLabel;
  1586.           L2 := NewLabel;
  1587.           PostLabel(L1);
  1588.           Condition;
  1589.           EmitLn('BEQ ' + L2);
  1590.           Block(L2);
  1591.           Match('e');
  1592.           EmitLn('BRA ' + L1);
  1593.           PostLabel(L2);
  1594.        end;A6A6
  1595.                                     - 27 -A*A*
  1596.  
  1597. PA A
  1598.  
  1599.  
  1600.  
  1601.  
  1602.  
  1603.        {--------------------------------------------------------------}
  1604.        { Parse and Translate a LOOP Statement }
  1605.  
  1606.        procedure DoLoop;
  1607.        var L1, L2: string;
  1608.        begin
  1609.           Match('p');
  1610.           L1 := NewLabel;
  1611.           L2 := NewLabel;
  1612.           PostLabel(L1);
  1613.           Block(L2);
  1614.           Match('e');
  1615.           EmitLn('BRA ' + L1);
  1616.           PostLabel(L2);
  1617.        end;
  1618.  
  1619.  
  1620.        {--------------------------------------------------------------}
  1621.        { Parse and Translate a REPEAT Statement }
  1622.  
  1623.        procedure DoRepeat;
  1624.        var L1, L2: string;
  1625.        begin
  1626.           Match('r');
  1627.           L1 := NewLabel;
  1628.           L2 := NewLabel;
  1629.           PostLabel(L1);
  1630.           Block(L2);
  1631.           Match('u');
  1632.           Condition;
  1633.           EmitLn('BEQ ' + L1);
  1634.           PostLabel(L2);
  1635.        end;
  1636.  
  1637.  
  1638.        {--------------------------------------------------------------}
  1639.        { Parse and Translate a FOR Statement }
  1640.  
  1641.        procedure DoFor;
  1642.        var L1, L2: string;
  1643.            Name: char;
  1644.        begin
  1645.           Match('f');
  1646.           L1 := NewLabel;
  1647.           L2 := NewLabel;
  1648.           Name := GetName;
  1649.           Match('=');
  1650.           Expression;
  1651.           EmitLn('SUBQ #1,D0');
  1652.           EmitLn('LEA ' + Name + '(PC),A0');
  1653.           EmitLn('MOVE D0,(A0)');
  1654.           Expression;
  1655.           EmitLn('MOVE D0,-(SP)');
  1656.           PostLabel(L1);A*A*
  1657.                                     - 28 -
  1658.  
  1659. PA A
  1660.  
  1661.  
  1662.  
  1663.  
  1664.  
  1665.           EmitLn('LEA ' + Name + '(PC),A0');
  1666.           EmitLn('MOVE (A0),D0');
  1667.           EmitLn('ADDQ #1,D0');
  1668.           EmitLn('MOVE D0,(A0)');
  1669.           EmitLn('CMP (SP),D0');
  1670.           EmitLn('BGT ' + L2);
  1671.           Block(L2);
  1672.           Match('e');
  1673.           EmitLn('BRA ' + L1);
  1674.           PostLabel(L2);
  1675.           EmitLn('ADDQ #2,SP');
  1676.        end;
  1677.  
  1678.  
  1679.        {--------------------------------------------------------------}
  1680.        { Parse and Translate a DO Statement }
  1681.  
  1682.        procedure Dodo;
  1683.        var L1, L2: string;
  1684.        begin
  1685.           Match('d');
  1686.           L1 := NewLabel;
  1687.           L2 := NewLabel;
  1688.           Expression;
  1689.           EmitLn('SUBQ #1,D0');
  1690.           PostLabel(L1);
  1691.           EmitLn('MOVE D0,-(SP)');
  1692.           Block(L2);
  1693.           EmitLn('MOVE (SP)+,D0');
  1694.           EmitLn('DBRA D0,' + L1);
  1695.           EmitLn('SUBQ #2,SP');
  1696.           PostLabel(L2);
  1697.           EmitLn('ADDQ #2,SP');
  1698.        end;
  1699.  
  1700.  
  1701.        {--------------------------------------------------------------}
  1702.        { Recognize and Translate a BREAK }
  1703.  
  1704.        procedure DoBreak(L: string);
  1705.        begin
  1706.           Match('b');
  1707.           EmitLn('BRA ' + L);
  1708.        end;
  1709.  
  1710.  
  1711.        {--------------------------------------------------------------}
  1712.        { Recognize and Translate an "Other" }
  1713.  
  1714.        procedure Other;
  1715.        begin
  1716.           EmitLn(GetName);
  1717.        end;A6A6
  1718.                                     - 29 -A*A*
  1719.  
  1720. PA A
  1721.  
  1722.  
  1723.  
  1724.  
  1725.  
  1726.        {--------------------------------------------------------------}
  1727.        { Recognize and Translate a Statement Block }
  1728.  
  1729.        procedure Block(L: string);
  1730.        begin
  1731.           while not(Look in ['e', 'l', 'u']) do begin
  1732.              case Look of
  1733.               'i': DoIf(L);
  1734.               'w': DoWhile;
  1735.               'p': DoLoop;
  1736.               'r': DoRepeat;
  1737.               'f': DoFor;
  1738.               'd': DoDo;
  1739.               'b': DoBreak(L);
  1740.               else Other;
  1741.              end;
  1742.           end;
  1743.        end;
  1744.  
  1745.  
  1746.        {--------------------------------------------------------------}
  1747.  
  1748.        { Parse and Translate a Program }
  1749.  
  1750.        procedure DoProgram;
  1751.        begin
  1752.           Block('');
  1753.           if Look <> 'e' then Expected('End');
  1754.           EmitLn('END')
  1755.        end;
  1756.  
  1757.  
  1758.        {--------------------------------------------------------------}
  1759.  
  1760.        { Initialize }
  1761.  
  1762.        procedure Init;
  1763.        begin
  1764.           LCount := 0;
  1765.           GetChar;
  1766.        end;
  1767.  
  1768.  
  1769.        {--------------------------------------------------------------}
  1770.        { Main Program }
  1771.  
  1772.        begin
  1773.           Init;
  1774.           DoProgram;
  1775.        end.
  1776.        {--------------------------------------------------------------}ANAN
  1777.                                     - 30 -A*A*
  1778.  
  1779. PA A
  1780.  
  1781.  
  1782.  
  1783.  
  1784.  
  1785.        *****************************************************************
  1786.        *                                                               *
  1787.        *                        COPYRIGHT NOTICE                       *
  1788.        *                                                               *
  1789.        *   Copyright (C) 1988 Jack W. Crenshaw. All rights reserved.   *
  1790.        *                                                               *
  1791.        *****************************************************************AUAU
  1792.  
  1793.  
  1794.  
  1795.  
  1796.  
  1797. A0A0
  1798.  
  1799.                                     - 31 -A*A*
  1800. @