home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / PP002.ZIP / PP.PAS < prev    next >
Pascal/Delphi Source File  |  1993-04-23  |  29KB  |  1,197 lines

  1. Program PowerPascal;
  2. {$X+}
  3. {
  4.   Who:  Michael Warot
  5.   When: November 12,1989 (my 26'th Birthday!)
  6.   What: The beginnings of a language compiler, takes source from
  7.         STDIN, and generates Assembler Source for STDOUT
  8.  
  9.   Based on the article "The Nuts & Bolts of Compiler Construction"
  10.                         By Jack W. Crenshaw
  11.                         Computer Language
  12.                         Volume 6, Number 3  (March 1989)
  13.  
  14.   KISS.PAS is pretty much the same as that given in the article, with
  15.   the appropriate modifications for the MS-DOS environment.
  16.  
  17.   All further versions are based on my assumptions of what should be
  18.   happening inside a compiler. You learn many interesting things when
  19.   you write your own compiler.
  20.  
  21.   Triva: _ is a valid variable name!
  22.  
  23.   Output to assembler file is to eliminate the need for handling
  24.   variable allocation, symbol table handling, and linking.
  25.   This does, however make optimization more difficult.
  26.  
  27. /-Version Number
  28. |
  29. |  Additions & Modifications from previous version....
  30. V ----------------------------------------------------------------------------
  31.  
  32. 2 GetName returns a multi-character name
  33. 2 GetNum  returns a multi-digit number
  34.  
  35. 3 SkipSpace procedure added, handles tabs, cr, lf, and spaces
  36. 3 Match handles a string (for ':=')
  37. 3 Match, GetName, and GetNum all call SkipSpace after doing thier work
  38.  
  39. 4 Statement procedure added to allow for other than assignments
  40. 4 NewLabel  returns a label for jumps, etc
  41. 4 PutLabel  anchors a jump to a particular address
  42. 4 JumpTo    generates code for a jump
  43. 4 IfJumpTo  generates "Jump if <> 0"
  44. 4 IfNotJumpTo generates "Jump if = 0"
  45. 4 While_Loop generates proper code for: while..expression..do..statement
  46. 4 BlockStatement handles begin..statement..[;..statement]..end
  47. 4 Repeat_Loop handles repeat..[statement..;]..until
  48. 4 _Program handles whole program generation
  49. 4 ProgramExit generates DOS exit code
  50.  
  51. 6 Procedure GetToken added
  52.   Handles brace comments
  53.   Handles $12FaC hex constants
  54.   Handles 1243 decimal constants
  55.   Handles 'string constants' and checks for un-terminated string constant
  56. 6 Procedure Match modified to expect a token.
  57.   All routines now use GetToken properly
  58.  
  59. 7 Added simple symbol table
  60.   Added routines to put variables after the end of the code
  61. 7 Added routines to generate prefix and suffix code
  62. 7 Added EMIT to generate some output, for testing, etc.
  63.  
  64. 8 Add FOR x := y TO z DO
  65.   Add WriteLn
  66.   Fix gettoken, so that writeln matches writeln, not write!
  67.   Add Inc_Const, for better code output...
  68.  
  69. 9 Eliminate spurious comments in generated listing
  70.   Put all code generation in GenCode
  71.  
  72. 10 Add variable support, instead of adding variables on the fly.
  73.    Add type support, instead of default to integer
  74.  
  75. 12 Fix bug in FOR, didn't allocate variable for limit correctly...
  76.  
  77. 13 Move input and output from stdin and stdout to file I/O.
  78.  
  79. 14 Add line number to error message, to make life a little easier.
  80.    Fix missing DUP in storage declarations to MASM causeing misallocation
  81.  
  82.    Add support for string expressions....   (NIY)
  83.    Add support for variable types           (NIY)
  84.    Add automatic casting...                 (NIY)
  85.    Fix bug in gettoken that handled '' as a string constant improperly
  86.    Add code to handle (* *) comments
  87.  
  88. 04/22/93 - Power Pascal Version 0.001 (Or do you say Pascal/2?)
  89. 15 Modify to generate OS/2 Full Screen 32 Bit code!
  90.  
  91. 04/23/93 - Version 0.002
  92.    Revise code, add VOID type, IsVar flag in symbol table
  93.    Use code from \GENERIC\CONFIG for '' handling, fixes problems with ''''
  94.    and such.
  95.    Start adding support code for procedures, etc.
  96. }
  97. Uses
  98.   Swap;
  99. Const
  100.   CR  = ^M;
  101.   LF  = ^J;
  102.   Tab = ^I;
  103.   HexCode   = '0123456789ABCDEF';
  104.  
  105.   MASM      = 'C:\MASM\MASM.EXE';
  106.   LINK      = 'C:\OS2\LINK386.EXE';
  107.  
  108. Type
  109.   Str32     = String[32];
  110.   Token     = (_Unknown,_string_constant,_numeric_Constant,_name,
  111.                _program,_Var,_Begin,_While,_do,_repeat,_Until,
  112.                _Emit,_Write,_WriteLn,
  113.                _period,_comma,
  114.                _plus,_minus,_mul,_div,_lparen,_rparen,_separator,
  115.                _assign,_equal,_greater,_less,_less_eq,_greater_eq,_not_eq,
  116.                _colon,
  117.                _if,_then,_else,_for,_to,
  118.                _procedure,_function,
  119.                _end);
  120.  
  121.   ObjCode   = (_Call,_Return,_Clear,_LoadConst,_LoadVar,_Push,_PopAdd,_PopSub,
  122.                _PopMul,_PopDiv,_Store,_Inc_Const,_PutLabel,
  123.                _JumpTo,_IfJumpTo,_IfNotJumpTo,
  124.                _ProgramInit,_ProgramExit,
  125.                _Logical,_Logical_Not,
  126.                Greater,Less,_PutC,_PutWord,_PutCrLf,_PutString);
  127. Const
  128.   MaxToken  = Ord(_end);
  129.   TokenName : Array[0..MaxToken] of Str32 =
  130.               ('','','','',
  131.                'PROGRAM','VAR','BEGIN','WHILE','DO','REPEAT','UNTIL',
  132.                'EMIT','WRITE','WRITELN',
  133.                '.',',',
  134.                '+','-','*','/','(',')',';',
  135.                ':=','=','>','<','<=','>=','<>',':',
  136.                'IF','THEN','ELSE','FOR','TO',
  137.                'PROCEDURE','FUNCTION',
  138.                'END');
  139.  
  140. Type
  141.   NameStr   = String;
  142.   LabelStr  = String;
  143. Var
  144.   Look           : Char;
  145.   Current_String : String;
  146.   Current_Token  : Token;
  147.   Current_Number : Longint;
  148.  
  149.   Source,Dest    : Text;
  150.   Name           : String;
  151.   LineCount      : Longint;
  152.  
  153. function  numb(i : integer):string;
  154. var
  155.   s : string;
  156. begin
  157.   str(i,s);
  158.   numb := s;
  159. end;
  160.  
  161. Procedure Abort(S : String); Forward;
  162.  
  163. Procedure GetChar;
  164. begin
  165.   if Not Eof(Source) then Read(Source,Look)
  166.                      else Look := '.';
  167.   {                      Abort('Unexpected end of file'); }
  168.   If Look = #13 then Inc(LineCount);
  169. end;
  170.  
  171. procedure SkipSpace;
  172. begin
  173.   While (look in [Cr,Lf,Tab,' ']) AND (Not Eof(Source)) do
  174.     GetChar;
  175. end;
  176.  
  177. Procedure GetToken;
  178. label
  179.   restart,
  180.   done;
  181. var
  182.   i,j : word;
  183.   x   : boolean;
  184.   last: char;
  185. begin
  186. RESTART:
  187.   Current_String := '';
  188.   Current_Token  := _Unknown;
  189.   Current_Number := 0;
  190.   SkipSpace;
  191.   Case Look of
  192.     '{'  : begin
  193.              repeat
  194.                GetChar;
  195.              until Look = '}';
  196.              GetChar;
  197.              Goto Restart;
  198.            end;
  199.  
  200.     '('  : begin
  201.              getchar;
  202.              if look = '*' then
  203.              begin
  204.                getchar;
  205.                repeat
  206.                  last := look;
  207.                  getchar;
  208.                until (last = '*') and (look = ')');
  209.                getchar;
  210.  
  211.                Goto Restart;
  212.              end
  213.              else
  214.                current_token := _lparen;
  215.            end;
  216.  
  217.     '''' : begin
  218.              getchar;
  219.              current_string := '';
  220.              x := false;
  221.              repeat
  222.                case look of
  223.                  cr    : abort('String exceeds line');
  224.                  ''''  : begin
  225.                            getchar;
  226.                            if look <> '''' then
  227.                              x := true
  228.                            else
  229.                            begin
  230.                              current_string := current_string + look;
  231.                              getchar;
  232.                            end;
  233.                          end;
  234.                else
  235.                  current_string := current_string + look;
  236.                  getchar;
  237.                end;
  238.              until x;
  239.              current_token := _string_constant;
  240.            end;
  241.  
  242.     '$'  : begin
  243.              GetChar;
  244.              While (UpCase(Look) in ['0'..'9','A'..'F']) do
  245.              begin
  246.                Current_Number := Current_Number SHL 4 +
  247.                                  Pos(UpCase(Look),HexCode)-1;
  248.                GetChar;
  249.              end;
  250.              Current_Token := _numeric_constant;
  251.            end;
  252.     '0'..'9' : begin
  253.                  while look in ['0'..'9'] do
  254.                  begin
  255.                    Current_Number := Current_Number * 10 +
  256.                                      Pos(Look,HexCode)-1;
  257.                    GetChar;
  258.                  end;
  259.                  current_token := _numeric_constant;
  260.                end;
  261.     '_','A'..'Z',
  262.         'a'..'z'   : begin
  263.                        While UpCase(Look) in ['_','0'..'9',
  264.                                                   'A'..'Z',
  265.                                                   'a'..'z' ] do
  266.                        begin
  267.                          Current_String := Current_String + UpCase(Look);
  268.                          GetChar;
  269.                          for i := 0 to MaxToken do
  270.                            if Current_String = TokenName[i] then
  271.                            begin
  272.                              Current_Token := Token(i);
  273.                           {   goto done; }
  274.                            end;
  275.                        end;
  276.                        If Current_Token = _Unknown then
  277.                          Current_Token := _name;
  278.                      end;
  279.   else
  280.     Current_String := UpCase(Look); GetChar;
  281.     Repeat
  282.       J := 0;
  283.       For i := 0 to MaxToken do
  284.         if (Current_string+UpCase(Look)) = TokenName[i] then
  285.           J := i;
  286.       If J <> 0 then
  287.       begin
  288.         Current_String := Current_String + UpCase(Look);
  289.         GetChar;
  290.       end;
  291.     Until J = 0;
  292.  
  293.     For i := 0 to MaxToken do
  294.       if Current_String = TokenName[i] then
  295.         J := i;
  296.     Current_Token := Token(j);
  297.   end; { Case Look }
  298.  
  299. { If we get here, we have a string that makes no sense! }
  300.  
  301. DONE:
  302. end;
  303.  
  304. (*********************
  305.     Error Reporting
  306.  *********************)
  307.  
  308. procedure Error(s : string);
  309. begin
  310.   WriteLn;
  311.   WriteLn(^G,'(',LineCount+1,') Error: ',s,'.');
  312. end;
  313.  
  314. procedure Abort(S : String);
  315. begin
  316.   Error(S);
  317.   Halt;
  318. end;
  319.  
  320. procedure Expected(s : string);
  321. begin
  322.   Abort(s + ' Expected');
  323. end;
  324.  
  325. (*************************
  326.      Symbol Table Stuff
  327.  *************************)
  328. Const
  329.   _Integer = 0;
  330.   _Byte    = 1;
  331.   _Long    = 2;
  332.   _Void    = 3;
  333. Type
  334.   TType    = Record
  335.                Name  : String[32];
  336.                Size  : Word;
  337.              End;
  338.  
  339.   Symbol   = Record
  340.                Name  : String[32];
  341.                Kind  : Integer;
  342.                IsVar : Boolean;
  343.              End;
  344.  
  345. Const
  346.   TypeInteger  : TType = (Name : '_INTEGER'; Size :2);
  347.   TypeByte     : TType = (Name : '_BYTE';    Size :1);
  348.   TypeLong     : TType = (Name : '_LONG';    Size :4);
  349.   TypeVoid     : TType = (Name : '_VOID';    Size :0);
  350. Var
  351.   SymbolTable  : Array[0..512] of Symbol;
  352.   SymbolCount  : Integer;
  353.  
  354.   TypeTable    : Array[0..512] of TType;
  355.   TypeCount    : Integer;
  356.  
  357.   StringConst  : Array[0..63]  of String;
  358.   StringCount  : Integer;
  359.  
  360. function ToUpper(S : String):String;
  361. begin
  362.   asm
  363.     cld
  364.     lea    si,S
  365.     les    di,@Result
  366.     SEGSS  lodsb
  367.     stosb
  368.     xor    ah,ah
  369.     xchg   ax,cx
  370.     jcxz   @3
  371.   @1:
  372.     SEGSS  lodsb
  373.     cmp    al,'a'
  374.     ja     @2
  375.     cmp    al,'z'
  376.     jb     @2
  377.     sub    al,20H
  378.   @2:
  379.     stosb
  380.     loop   @1
  381.   @3:
  382.   end;
  383. end;
  384.  
  385. function GetName:String;
  386. begin
  387.   If Current_Token = _Name then
  388.     GetName := '_' + ToUpper(Current_String)
  389.   else
  390.     Expected('Name');
  391.   GetToken;
  392. end;
  393.  
  394. function GetNumber:Integer;
  395. begin
  396.   GetNumber := Current_Number;
  397.   GetToken;
  398. end;
  399.  
  400. Procedure AddSymbol(_Name : String; _Kind : Integer; _IsVar : Boolean);
  401. Begin
  402.   SymbolTable[SymbolCount].Name  := _Name;
  403.   SymbolTable[SymbolCount].Kind  := _Kind;
  404.   SymbolTable[SymbolCount].IsVar := _IsVar;
  405.   Inc(SymbolCount);
  406. End; { AddSymbol }
  407.  
  408. Function LookSymbol(_Name : String):Integer;
  409. { True if _NAME is in table }
  410. Var
  411.   q,r : Integer;
  412. Begin
  413.   r := -1;
  414.   For q := 0 to SymbolCount-1 do
  415.     If SymbolTable[q].Name = _Name then
  416.       r := q;
  417.   If r <> -1 then
  418.     LookSymbol := SymbolTable[r].Kind
  419.   else
  420.     LookSymbol := -1;
  421. End;
  422.  
  423. Function CheckSymbol(_Name : String): Integer;
  424. Var
  425.   tmp : integer;
  426. Begin
  427.   tmp := LookSymbol(_Name);
  428.   if tmp = -1 then
  429.     Expected('identifier');
  430.   CheckSymbol := tmp;
  431. End;
  432.  
  433. Procedure DumpSymbols;
  434. var
  435.   i : integer;
  436. Begin
  437.   WriteLn(Dest,'; Variable Area');
  438.   for i := 0 to SymbolCount - 1 do
  439.     If SymbolTable[i].IsVar then
  440.       WriteLn(Dest,SymbolTable[i].Name,TAB,
  441.                    'DB',TAB,
  442.                    TypeTable[SymbolTable[i].Kind].Size,TAB,
  443.                    'DUP (?)');
  444. End;
  445.  
  446. Function LookType(    _Name : String):Integer;
  447. { True if _NAME is in table }
  448. Var
  449.   q,r : Integer;
  450. Begin
  451.   r := -1;
  452.   For q := 0 to TypeCount-1 do
  453.     If TypeTable[q].Name = _Name then
  454.       r := q;
  455.   LookType := r;
  456. End;
  457.  
  458. Procedure CheckType(_Name : String);
  459. Begin
  460.   If (LookType(_Name) = -1) then
  461.     Expected('type');
  462. End;
  463.  
  464. Function DoStringConst(S : String):String;
  465. Begin
  466.   StringConst[StringCount] := S;
  467.   DoStringConst := '_STR'+Numb(StringCount);
  468.   Inc(StringCount);
  469. End;
  470.  
  471.  
  472. Procedure DumpStrings;
  473. Var
  474.   i : integer;
  475.   j : byte;
  476.   s : string;
  477. Begin
  478.   WriteLn(Dest,'; String constants');
  479.   for i := 0 to StringCount-1 do
  480.   begin
  481.     s := StringConst[i];
  482.     WriteLn(Dest,'_STR'+Numb(i),TAB,
  483.                  'DD',TAB,
  484.                  Numb(Length(S)));
  485.     Write(Dest,TAB,'DB',TAB,'''');
  486.     For j := 1 to length(s) do
  487.       If S[j] <> '''' then
  488.         Write(Dest,S[j])
  489.       else
  490.         Write(Dest,'''''');
  491.     WriteLn(Dest,'''');
  492.   end;
  493. End;
  494.  
  495.  
  496. (*************************
  497.       Code Generator
  498.  *************************)
  499. Var
  500.   LabelCount : Word;
  501.  
  502. procedure Emit(s : string);
  503. begin
  504.   Write(Dest,'      ', s);
  505. end;
  506.  
  507. procedure EmitLn(s : string);
  508. begin
  509.   Emit(s);
  510.   WriteLn(Dest);
  511. end;
  512.  
  513. function  NewLabel:LabelStr;
  514. var
  515.   tmp : string;
  516. begin
  517.   Str(LabelCount,tmp); Inc(LabelCount);
  518.   tmp := 'L'+tmp;
  519.   NewLabel := tmp;
  520. end;
  521.  
  522. Function GenCode(c : ObjCode;
  523.                    n : integer;
  524.                    s : string) : integer;
  525. Var
  526.  Tmp : String;
  527.  x,y : integer;
  528. Begin
  529.   Case c of
  530.     _Call       : EmitLn('CALL  '+S);
  531.     _Return     : EmitLn('RET');
  532.     _Clear      : EmitLn('XOR   EAX,EAX');
  533.     _LoadConst  : EmitLn('MOV   EAX,'+Numb(N));
  534.     _LoadVar    : begin
  535.                     x := CheckSymbol(s);
  536.                     y := TypeTable[x].Size;
  537.                     Case Y of
  538.                       1 : begin
  539.                             EmitLn('XOR   EAX,EAX');
  540.                             EmitLn('MOV   AL,BYTE PTR['+S+']');
  541.                           end;
  542.                       2 : begin
  543.                             EmitLn('XOR   EAX,EAX');
  544.                             EmitLn('MOV   AX,WORD PTR['+S+']')
  545.                           end;
  546.                       4 : EmitLn('MOV   EAX,DWORD PTR['+S+']');
  547.                     else
  548.                         Abort('Illegal variable size');
  549.                     end;
  550.                   end;
  551.  
  552.     _Push       : EmitLn('PUSH  EAX');
  553.     _PopAdd     : begin
  554.                     EmitLn('POP   EBX');
  555.                     EmitLn('ADD   EAX,EBX');
  556.                   end;
  557.     _PopSub     : begin
  558.                     EmitLn('POP   EBX');
  559.                     EmitLn('SUB   EBX,EAX');
  560.                     EmitLn('MOV   EAX,EBX');
  561.                   end;
  562.     _PopMul     : begin
  563.                     EmitLn('POP   EBX');
  564.                     EmitLn('MUL   EBX');
  565.                   end;
  566.     _PopDiv     : begin
  567.                     EmitLn('MOV   EBX,EAX');
  568.                     EmitLn('XOR   EDX,EDX');
  569.                     EmitLn('POP   EAX');
  570.                     EmitLn('DIV   EBX');
  571.                   end;
  572.     _Store      : begin
  573.                     x := CheckSymbol(s);
  574.                     y := TypeTable[x].Size;
  575.                     Case Y of
  576.                       1 : EmitLn('MOV   BYTE  PTR['+S+'],AL ');
  577.                       2 : EmitLn('MOV   WORD  PTR['+S+'],AX ');
  578.                       4 : EmitLn('MOV   DWORD PTR['+S+'],EAX');
  579.                     else
  580.                       Abort('Illegal variable size');
  581.                     end;
  582.                   end;
  583.     _Inc_Const  : begin
  584.                     if N = 1 then
  585.                       EmitLn('INC   ['+S+']')
  586.                     else
  587.                       EmitLn('ADD   ['+S+'],'+numb(N) );
  588.                   end;
  589.     _PutLabel   : WriteLn(Dest,S+':');
  590.     _JumpTo     : EmitLn('JMP   '+S);
  591.     _IfJumpTo   : Begin
  592.                     Tmp := NewLabel;
  593.                     EmitLn('OR    EAX,EAX');
  594.                     EmitLn('JNZ   '+S);
  595.                   End;
  596.  
  597.     _IfNotJumpTo: Begin
  598.                     Tmp := NewLabel;
  599.                     EmitLn('OR    EAX,EAX');   { Avoid 128 byte jump bounds }
  600.                     EmitLn('JZ    '+S);
  601.                   End;
  602.  
  603.     _ProgramInit: Begin
  604.                     WriteLn(Dest,'      .386                        ');
  605.                     WriteLn(Dest,'      .model   flat,syscall,os_os2');
  606.                     WriteLn(Dest,'      .code                       ');
  607.                   End;
  608.  
  609.     _ProgramExit: Begin
  610.                     EmitLn('CALL  _EXIT');
  611.                   End;
  612.  
  613.     _Logical    : Begin
  614.                     EmitLn('NEG   EAX');         { AX <> 0 ---> Carry  }
  615.                     EmitLn('MOV   EAX,0');       {       0 ---> AX     }
  616.                     EmitLn('SBC   EAX,EAX');     { Carry  ----> ALL AX }
  617.                   End;
  618.  
  619.     _Logical_Not: Begin
  620.                     EmitLn('NEG   EAX');         { AX <> 0 ---> Carry  }
  621.                     EmitLn('MOV   EAX,-1');      {      -1 ---> AX     }
  622.                     EmitLn('ADC   EAX,0');       { Not Carry -> ALL AX }
  623.                   End;
  624.  
  625.     Greater     : Begin
  626.                     EmitLn('POP   EBX');
  627.                     EmitLn('SUB   EAX,EBX');
  628.                     EmitLn('MOV   EAX,0' );
  629.                     EmitLn('SBB   EAX,0' );
  630.                   end;
  631.  
  632.     Less        : Begin
  633.                     EmitLn('POP   EBX');
  634.                     EmitLn('SUB   EBX,EAX');
  635.                     EmitLn('MOV   EAX,0' );
  636.                     EmitLn('SBB   EAX,0' );
  637.                   end;
  638.  
  639.     _PutC       : EmitLn('CALL  PUTC');
  640.  
  641.     _PutWord    : EmitLn('CALL  WriteEAX');
  642.  
  643.     _PutCrLf    : EmitLn('CALL  DoCR');
  644.  
  645.     _PutString  : Begin
  646.                     EmitLn('LEA   EDX,'+S);
  647.                     EmitLn('CALL  WriteStr');
  648.                   End;
  649.  
  650.   else
  651.     Abort('Unknown ObjCode');
  652.   end;
  653. End;
  654.  
  655. (**********************
  656.     Parsing Routines
  657.  **********************)
  658.  
  659. function IsCompareOp(x : token): boolean;
  660. begin
  661.   IsCompareOp := x in [_equal.._not_eq];
  662. end;
  663.  
  664. function IsAddOp(x : token): boolean;
  665. begin
  666.   IsAddOp := x in [_plus,_minus];
  667. end;
  668.  
  669. function IsMulOp(x : token): boolean;
  670. begin
  671.   IsMulOp := x in [_mul,_div];
  672. end;
  673.  
  674. procedure Match(x : Token);
  675. begin
  676.   If Current_Token <> X then
  677.   begin
  678.     If Ord(X) <= MaxToken then
  679.       Expected(TokenName[ord(x)])
  680.     else
  681.       Abort('Unknown Token expected, compiler error!');
  682.   end
  683.   else
  684.     GetToken;
  685. end;
  686.  
  687. (*************************
  688.     Expression Parser
  689.  *************************)
  690.  
  691. function  Expression:integer; Forward;
  692. function  Value:integer;
  693. var
  694.   kind : integer;
  695. begin
  696.   kind := -1;
  697.   If Current_Token = _lparen then
  698.   begin
  699.     Match(_lparen);
  700.     kind := Expression;
  701.     Match(_rparen);
  702.   end
  703.   else
  704.   begin
  705.     If Current_Token = _name then
  706.       Kind := GenCode(_LoadVar,0,GetName)
  707.     else
  708.       If Current_Token = _numeric_constant then
  709.         Kind := GenCode(_LoadConst,GetNumber,'')
  710.       else
  711.         Error('Error in expression');
  712.   end;
  713. end;
  714.  
  715. procedure Factor;
  716. var
  717.   tmp : token;
  718.   kind : integer;
  719. begin
  720.   kind := Value;
  721.   while IsCompareOp(Current_Token) do
  722.   begin
  723.     GenCode(_Push,kind,'');
  724.     tmp := Current_Token;
  725.     Match(tmp);
  726.     Value;
  727.  
  728.     case tmp of
  729.       _equal       : begin
  730.                        GenCode(_PopSub,     kind,'');
  731.                        GenCode(_Logical,    kind,'');
  732.                      end;
  733.       _not_eq      : begin
  734.                        GenCode(_PopSub,     kind,'');
  735.                        GenCode(_Logical_Not,kind,'');
  736.                      end;
  737.       _greater     : GenCode(Greater,     kind,'');
  738.       _less        : GenCode(Less,        kind,'');
  739.       _greater_eq  : begin
  740.                        GenCode(Less,        kind,'');
  741.                        GenCode(_Logical_Not,kind,'');
  742.                      end;
  743.       _less_eq     : begin
  744.                        GenCode(Greater,     kind,'');
  745.                        GenCode(_Logical_Not,kind,'');
  746.                      end;
  747.     end;
  748.   end;
  749. end;
  750.  
  751. procedure Multiply;
  752. begin
  753.   Match(_mul);
  754.   Factor;
  755.   GenCode(_PopMul,0,'');
  756. end;
  757.  
  758. procedure Divide;
  759. begin
  760.   Match(_div);
  761.   Factor;
  762.   GenCode(_PopDiv,0,'');
  763. end;
  764.  
  765. procedure Term;
  766. begin
  767.   Factor;
  768.   while IsMulOp(Current_Token) do
  769.   begin
  770.     GenCode(_Push,0,'');
  771.     case Current_Token of
  772.       _mul : Multiply;
  773.       _div : Divide;
  774.     end;
  775.   end;
  776. end;
  777.  
  778. procedure Add;
  779. begin
  780.   Match(_plus);
  781.   Term;
  782.   GenCode(_PopAdd,0,'');
  783. end;
  784.  
  785. procedure Subtract;
  786. begin
  787.   Match(_minus);
  788.   Term;
  789.   GenCode(_PopSub,0,'');
  790. end;
  791.  
  792. function Expression : integer;     { returns expression type }
  793. var
  794.   kind : integer;
  795. begin
  796.   kind := -1;
  797.   If IsAddOp(Current_Token) then GenCode(_Clear,0,'')
  798.                             else Term;
  799.   while IsAddOp(Current_Token) do
  800.   begin
  801.     GenCode(_Push,0,'');
  802.     case Current_Token of
  803.       _plus   : Add;
  804.       _minus  : Subtract;
  805.     end;
  806.   end;
  807.   Expression := kind;
  808. end;
  809.  
  810. (*************************
  811.      Statement Parser
  812.  *************************)
  813.  
  814. procedure Statement; Forward;
  815.  
  816. procedure Assignment;
  817. var
  818.   tmp : string;
  819. begin
  820.   Tmp := GetName;
  821.  
  822.   If LookSymbol(Tmp) = _Void then
  823.   begin
  824.     GenCode(_Call,0,Tmp);
  825.   end
  826.   else
  827.   begin
  828.     Match(_assign);
  829.     Expression;
  830.     GenCode(_Store,0,Tmp);
  831.   end;
  832. end;
  833.  
  834. procedure While_Loop;
  835. var
  836.   TestLabel,
  837.   DoneLabel : LabelStr;
  838. begin
  839.   Match(_While);
  840.  
  841.   TestLabel := NewLabel;
  842.   DoneLabel := NewLabel;
  843.  
  844.   GenCode(_PutLabel,0,TestLabel);
  845.   Expression;
  846.   GenCode(_IfNotJumpTo,0,DoneLabel);
  847.   Match(_do);
  848.  
  849.   Statement;
  850.   GenCode(_JumpTo,0,TestLabel);
  851.  
  852.   GenCode(_PutLabel,0,DoneLabel);
  853. end;
  854.  
  855. procedure For_Loop;
  856. var
  857.   DoneLabel,
  858.   TestLabel   : LabelStr;
  859.   Index,Limit : String;
  860. begin
  861.   Match(_For);
  862.   TestLabel  := NewLabel;
  863.   DoneLabel  := NewLabel;
  864.  
  865.   Index := GetName;
  866.   Limit := 'Lim'+Index;
  867.   AddSymbol(Limit,_Long,True);
  868.   Match(_assign);
  869.   Expression;  GenCode(_Store,0,Index);
  870.   Match(_to);
  871.   Expression;  GenCode(_Store,0,Limit);
  872.  
  873.   GenCode(_PutLabel,0,TestLabel);
  874.   Match(_do);
  875.   GenCode(_LoadVar,0,Index);
  876.   GenCode(_Push,0,'');
  877.   GenCode(_LoadVar,0,Limit);
  878.   GenCode(Greater,0,'');
  879.   GenCode(_IfJumpTo,0,DoneLabel);
  880.  
  881.   Statement;
  882.   GenCode(_Inc_Const,1,Index);
  883.   GenCode(_JumpTo,0,TestLabel);
  884.  
  885.   GenCode(_PutLabel,0,DoneLabel);
  886. end;
  887.  
  888. procedure If_Then_Else;
  889. var
  890.   ElseLabel,
  891.   DoneLabel  : LabelStr;
  892. begin
  893.   Match(_If);
  894.  
  895.   ElseLabel := NewLabel;
  896.   DoneLabel := NewLabel;
  897.  
  898.   Expression;
  899.   Match(_then);
  900.   GenCode(_IfNotJumpTo,0,ElseLabel);
  901.  
  902.   Statement;
  903.  
  904.   If Current_Token = _Separator then
  905.     GenCode(_PutLabel,0,ElseLabel)
  906.   else
  907.   begin
  908.     Match(_else);
  909.     GenCode(_JumpTo,0,DoneLabel);
  910.     GenCode(_PutLabel,0,ElseLabel);
  911.     Statement;
  912.   end;
  913.  
  914.   GenCode(_PutLabel,0,DoneLabel);
  915. end;
  916.  
  917. procedure BlockStatement;
  918. var
  919.   tmp : NameStr;
  920. begin
  921.   Match(_Begin);
  922.  
  923.   while Current_Token <> _End do
  924.   begin
  925.     If Current_Token = _Separator then
  926.       GetToken
  927.     else
  928.       Statement;
  929.   end;
  930.   Match(_End);
  931. end;
  932.  
  933. procedure VarStatement(var kind : integer);
  934. var
  935.   Name : NameStr;
  936. begin
  937.   Name := GetName;
  938.   If (Current_Token = _Comma) then
  939.   begin
  940.     Match(_Comma);
  941.     VarStatement(kind);
  942.   end
  943.   else
  944.   begin
  945.     Match(_Colon);
  946.     kind := LookType(GetName);
  947.     If Kind = -1 then Expected('TYPE');
  948.   end;
  949.   AddSymbol(Name,kind,True);
  950. end;
  951.  
  952. procedure VarBlock;
  953. var
  954.   tmp  : NameStr;
  955.   kind : integer;
  956. begin
  957.   Match(_Var);
  958.   while (Current_Token = _Name) do
  959.   begin
  960.     VarStatement(kind);
  961.     Match(_separator);
  962.   end;
  963. end;
  964.  
  965. procedure Repeat_Loop;
  966. var
  967.   tmp   : NameStr;
  968.   Start : LabelStr;
  969. begin
  970.   Match(_Repeat);
  971.  
  972.   Start := NewLabel;
  973.   GenCode(_PutLabel,0,Start);
  974.  
  975.   repeat
  976.     If Current_Token <> _Until then
  977.     begin
  978.       Statement;
  979.       Match(_separator);
  980.     end;
  981.   until Current_Token = _Until;
  982.  
  983.   Match(_Until);
  984.  
  985.   Expression;
  986.   GenCode(_IfNotJumpTo,0,Start);
  987. end;
  988.  
  989. Procedure Write_Work;
  990. Var
  991.   sx : string;
  992. Begin
  993.   If Current_Token = _Lparen then      { Fix for WriteLn; (No Operands) }
  994.   begin
  995.     Match(_lparen);
  996.     Repeat
  997.       if Current_Token = _String_Constant then
  998.       begin
  999.         sx := DoStringConst(Current_String);
  1000.         Match(_String_Constant);
  1001.         GenCode(_PutString,0,sx);
  1002.       end
  1003.       else
  1004.       begin
  1005.         Expression;
  1006.         GenCode(_PutWord,0,'');
  1007.       end;
  1008.       If Current_Token <> _Rparen then
  1009.         Match(_comma);
  1010.     Until Current_Token = _Rparen;
  1011.     Match(_Rparen);
  1012.   end;
  1013. End;
  1014.  
  1015. Procedure DoProcedure;
  1016. Var
  1017.   ProcName : NameStr;
  1018. Begin
  1019.   Match(_Procedure);
  1020.   ProcName := GetName;
  1021.   Match(_Separator);
  1022.   GenCode(_PutLabel,0,ProcName);
  1023.   AddSymbol(ProcName,_Void,False);
  1024.   BlockStatement;
  1025.   GenCode(_Return,0,'');
  1026. End;
  1027.  
  1028. procedure Statement;
  1029. begin
  1030.   Case Current_Token of
  1031.     _while  : while_Loop;
  1032.     _repeat : repeat_loop;
  1033.     _for    : for_loop;
  1034.     _if     : if_then_else;
  1035.     _begin  : BlockStatement;
  1036.     _emit   : begin
  1037.                 Match(_emit);
  1038.                 Match(_lparen);
  1039.                 Repeat
  1040.                   Expression;
  1041.                   GenCode(_PutC,0,'');
  1042.                   If Current_Token <> _Rparen then
  1043.                     Match(_comma);
  1044.                 Until Current_Token = _Rparen;
  1045.                 Match(_Rparen);
  1046.               end;
  1047.     _Write  : begin
  1048.                 Match(_Write);
  1049.                 Write_Work;
  1050.               end;
  1051.     _WriteLn: begin
  1052.                 Match(_WriteLn);
  1053.                 Write_Work;
  1054.                 GenCode(_PutCrLf,0,'');
  1055.               end;
  1056.   else
  1057.     Assignment;
  1058.   end;
  1059. end;
  1060.  
  1061. (****************************
  1062.          Program Parser
  1063.  ****************************)
  1064. var
  1065.   ProgramName : NameStr;
  1066.  
  1067. procedure _Program_;
  1068. var
  1069.   tmp : NameStr;
  1070.   lib : text;
  1071.   buf : string;
  1072.   done : boolean;
  1073. begin
  1074.   If Current_Token = _Program then
  1075.   begin
  1076.     Match(_Program);
  1077.     ProgramName := GetName;
  1078.     Match(_separator);
  1079.   end;
  1080.  
  1081.   GenCode(_ProgramInit,0,ProgramName);
  1082.  
  1083.   Done := False;
  1084.   Repeat
  1085.     Case Current_Token of
  1086.       _Var       : VarBlock;
  1087.       _Procedure : DoProcedure;
  1088.       _Separator : Match(_Separator);
  1089.     else
  1090.       Done := True;
  1091.     End;
  1092.   Until Done;
  1093.  
  1094.   GenCode(_PutLabel,0,'MAIN');
  1095.   AddSymbol('Main',_Void,False);
  1096.   BlockStatement;
  1097.   GenCode(_ProgramExit,0,'');
  1098.  
  1099.   WriteLn(Dest,'; ***** Library Code ***** ');
  1100.  
  1101.   Assign(Lib,'LIB.ASM');
  1102. {$I-}  Reset(Lib); {$I+}
  1103.   If IOresult = 0 then
  1104.   begin
  1105.     while not eof(lib) do
  1106.     begin
  1107.       readln(lib,buf);
  1108.       writeln(Dest,buf);
  1109.     end;
  1110.     close(lib);
  1111.   end;
  1112.  
  1113.   WriteLn(Dest,'; ***** Library Ends *****');
  1114.   DumpSymbols;
  1115.   DumpStrings;
  1116.   EmitLn('db      100 dup(0)');
  1117.   EmitLn('end     main   ');
  1118. end;
  1119.  
  1120. (**************************
  1121.         Main Program
  1122.  **************************)
  1123.  
  1124. procedure Init;
  1125. begin
  1126.   LineCount   := 0;
  1127.   LabelCount  := 0;
  1128.   SymbolCount := 0;
  1129.   StringCount := 0;
  1130.  
  1131.   TypeTable[0] := TypeInteger;
  1132.   TypeTable[1] := TypeByte;
  1133.   TypeTable[2] := TypeLong;
  1134.   TypeTable[3] := TypeVoid;
  1135.   TypeCount    := 4;
  1136.  
  1137.   ProgramName := 'NONAME';
  1138.   GetChar;
  1139.   GetToken;
  1140. end;
  1141.  
  1142. procedure usage;
  1143. begin
  1144.   WriteLn('Power Pascal -- Copyright(C) 1993, Blue Star Systems, all rights reserved');
  1145.   WriteLn;
  1146.   WriteLn('Usage : PP filename  (.PRG assumed) ');
  1147.   Halt(0);
  1148. end;
  1149.  
  1150. Var
  1151.   Err : Byte;
  1152.   F   : file;
  1153. Begin
  1154.   If ParamCount = 0 then usage;
  1155.   Name := ParamStr(1);
  1156.   If Pos('?',name) <> 0 then Usage;
  1157.  
  1158.   Assign(Source,Name+'.PRG');
  1159. {$I-} Reset(Source); {$I+}
  1160.   If IOresult <> 0 then
  1161.   begin
  1162.     WriteLn('Error opening input file ',Name,'.prg');
  1163.     Halt(1);
  1164.   end;
  1165.  
  1166.   Assign(Dest,Name+'.ASM');
  1167. {$I-} ReWrite(Dest); {$I+}
  1168.   If IOresult <> 0 then
  1169.   begin
  1170.     WriteLn('Error opening output file, ',Name,'.asm');
  1171.     Halt(2);
  1172.   end;
  1173.  
  1174.   Init;
  1175.   _Program_;
  1176.  
  1177.   Close(Source);
  1178.   Close(Dest);
  1179.   WriteLn('Total of ',LineCount,' Lines processed');
  1180.  
  1181.   Swap.SetMemTop(HeapPtr);
  1182.                   Err := ExecPrg(MASM+' '+Name+';');
  1183.   If Err = 0 then Err := ExecPrg(LINK+' '+Name+','+Name+',NUL,C:\OS2\DOSCALLS,PP');
  1184.  
  1185.   Swap.SetMemTop(HeapEnd);
  1186.  
  1187.   if err = 0 then
  1188.   begin
  1189.     assign(f,Name+'.OBJ');
  1190.     {$I-} reset(f,1); {$I+}
  1191.     if ioresult = 0 then
  1192.     begin
  1193.       close(f);
  1194.       erase(f);
  1195.     end;
  1196.   end;
  1197. End.