home *** CD-ROM | disk | FTP | other *** search
/ The Education Master 1994 (4th Edition) / EDUCATIONS_MASTER_4TH_EDITION.bin / files / progscal / tinypasc / tusems.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-02-24  |  22.8 KB  |  792 lines

  1.   {TUSEMS -- semantics stuff for simple Turbo compiler }
  2.  
  3.   {**********************}
  4.   procedure TAB;
  5.   begin
  6.     if llen<opcd_position then begin
  7.       write(rfile, ' ':opcd_position-llen);
  8.       llen:=opcd_position;
  9.       end
  10.     else 
  11.     if llen<opnd_position then begin
  12.       write(rfile, ' ':opnd_position-llen);
  13.       llen:=opnd_position;
  14.       end
  15.     else begin
  16.       write(rfile, ' ');
  17.       llen:=llen+1;
  18.       end
  19.     end;
  20.  
  21.   {***********************}
  22.   procedure WRCODE(STR: string31);
  23.     var SX: int;
  24.   begin
  25.     sx:=1;
  26.     while sx<=length(str) do begin
  27.       if str[sx]=' ' then begin
  28.         tab;
  29.         while str[sx]=' ' do sx:=sx+1;
  30.         end
  31.       else begin
  32.         write(rfile, str[sx]);
  33.         llen:=llen+1;
  34.         sx:=sx+1;
  35.         end
  36.       end;
  37.     if length(comment)>0 then begin
  38.       writeln(rfile, ' ; ', comment);
  39.       comment:='';
  40.       end
  41.     else writeln(rfile);
  42.     llen:=0;
  43.     end;
  44.       
  45.   {*******************}
  46.   procedure CODE(STR: string31);
  47.   begin
  48.     tab;
  49.     wrcode(str);
  50.     end;
  51.     
  52.   {*******************}
  53.   procedure CODE2(S1, S2: string31);
  54.   begin
  55.     code(concat(s1, s2));
  56.     end;
  57.   
  58.   {*******************}
  59.   procedure CODE3(S1, S2, S3: string31);
  60.   begin
  61.     code(concat(s1, concat(s2, s3)));
  62.     end;
  63.     
  64.   {*******************}
  65.   procedure CODE4(S1, S2, S3, S4: string31);
  66.   begin
  67.     code(concat(s1, concat(s2, concat(s3, s4))));
  68.     end;
  69.     
  70.   {*******************}
  71.   procedure LCODE(THE_LABEL, STR: string31);
  72.   begin
  73.     write(rfile, the_label  {, ':'} );  
  74.              {NOTE: the Microsoft assembler expects
  75.                a colon or other separator between a label and
  76.                the operation code.  CHASM doesn't}
  77.     llen:=llen+length(the_label) {+1} ;
  78.     tab;
  79.     wrcode(str);
  80.     end;
  81.     
  82.   {*******************}
  83.   procedure CODESTRINGS;
  84.     var STR: string80;
  85.         SX: integer;
  86.   begin
  87.     while gslist<>nil do
  88.     with gslist^.left^ do begin  {points to a STRNG}
  89.       str:='';
  90.       sx:=stx;
  91.       while strtab[sx]<>chr(0) do begin
  92.         str:=concat(str, ' ');
  93.         str[length(str)]:=strtab[sx];
  94.         if strtab[sx]='''' then
  95.           str:=concat(str, '''');
  96.         sx:=sx+1;
  97.         end;
  98.       lcode(concat('SS', num2string(strngnum)),
  99.             concat('DB ''',
  100.               concat(str, ''',0')));
  101.       gslist:=gslist^.right;  {next list member}
  102.       end;
  103.     strx:=0;
  104.     gslist:=nil;
  105.     end;
  106.  
  107.   {******************}
  108.   procedure INCL_FILE(FNAME: string31);
  109.     var IFILE: text;
  110.         LINE: longstring;
  111.   begin
  112.     writeln(rfile, ' ; <', fname, '> included');
  113.     {$I-}
  114.     assign(ifile, fname);
  115.     reset(ifile);  {$I+}
  116.     if ioresult<>0 then error(concat('can''t open ', fname))
  117.     else begin
  118.       while not(eof(ifile)) do begin
  119.         readln(ifile, line);
  120.         writeln(rfile, line);
  121.         end;
  122.       close(ifile);
  123.       end;
  124.     writeln(rfile, ' ; ... end of include ', fname);
  125.     end;
  126.  
  127.   {******************}
  128.   procedure INIT_SEM;
  129.     { Semantics initialization -- called before any productions
  130.       are applied. }
  131.  
  132.     {..................}
  133.     procedure DEF_FUNCTION(NAME: string15; PARMS: int);
  134.       var TSYMP: symtabp;
  135.     begin   {some predefined functions}
  136.       tsymp:=makesym(symtab, name, func_type, plevel);
  137.       with tsymp^ do begin
  138.         faddr:=0;
  139.         is_system:=true;
  140.         pbytes:=parms*varsize;
  141.         is_actual:=true;
  142.         end
  143.       end;
  144.  
  145.   begin
  146.     llen:=0;
  147.     labcount:=0;        {for made-up labels}
  148.     trcount:=0;
  149.     main_symp:=nil;
  150.     plevel:=0;
  151.     gslist:=nil;
  152.     comment:='';
  153.     next_snum:=0;   {string code numbers}
  154.     def_function('WRITE', 0);  {takes integers and strings}
  155.     def_function('READ', 0);   {reads and returns an integer}
  156.     def_function('WRITELN', 0);  {takes integers and strings}
  157.     def_function('EOF', 0);    {returns 1 if end of file, 0 otherwise}
  158.     def_function('HALT', 0);   {stops process}
  159.     writeln(rfile, ' ; Tiny Pascal assembler code');
  160.     code('MOV SP,OFFSET(STACKORG)');   {set stack pointer}
  161.     code('MOV BP,SP');         {and marker pointer}
  162.     code('CALL MAIN');       {must be name of some procedure}
  163.     code('INT 020H');   {return to system}
  164.     incl_file('STDIO.HDR');  {system procedures}
  165.     end;
  166.  
  167.   {******************}
  168.   procedure END_SEM;
  169.     var HX: integer;
  170.         TSYMP: symtabp;
  171.   begin
  172.     {dump global variables}
  173.     writeln(rfile, ' ; GLOBAL VARIABLES');
  174.     for hx:=0 to hlimit do begin
  175.       tsymp:=symtab[hx];
  176.       while tsymp<>nil do
  177.       with tsymp^ do begin
  178.         if (level=0) and
  179.            (symt=var_type) then
  180.           lcode(sym, 'DW 0');
  181.         tsymp:=next;
  182.         end
  183.       end;
  184.     writeln(rfile, ' ; RUNTIME STACK');
  185.     code('DS 2000');
  186.     lcode('STACKORG', 'DW 0');  {bottom of stack}
  187.     if main_symp=nil then
  188.     error('no MAIN procedure found')
  189.     else begin
  190.       writeln(rfile, ' ; MAIN stack space');
  191.       for hx:=1 to (main_symp^.pbytes div 2)+1 do code('DW 0');
  192.           {these allow space for the MAIN program's formal parameters}
  193.       end;
  194.     if errors>0 then begin
  195.       writeln(rfile, '; **** ', errors:1, ' error(s) seen');
  196.       writeln('**** ', errors:1, ' error(s) seen');
  197.       end
  198.     else begin
  199.       writeln(rfile, '; NO errors');
  200.       writeln('NO errors');
  201.       end
  202.     end;
  203.  
  204.   {******************}
  205.   function NEW_SEM {(SEMTYP: semtype): semrecp} ;
  206.     var TSEMP, TSEMP1: semrecp;
  207.   begin
  208.     new(tsemp);
  209.     new_sem:=tsemp;
  210.     with tsemp^ do begin
  211.       semt:=semtyp;
  212.       case semtyp of
  213.         ident: symp:=nil;
  214.         fixed: numval:=0;
  215.         strng: begin
  216.           stx:=strx;
  217.           strngnum:=next_snum;
  218.           next_snum:=next_snum+1;
  219.           tsemp1:=new_sem(stmtlist);
  220.           with tsemp1^ do begin  {link into a global list of strings}
  221.             left:=tsemp;
  222.             right:=gslist;
  223.             end;
  224.           gslist:=tsemp1;
  225.           end;
  226.         addop..funcall: begin
  227.           left:=nil;
  228.           right:=nil;
  229.           end;
  230.         if_then_else: begin
  231.           b1:=nil;
  232.           s1:=nil;
  233.           s2:=nil;
  234.           end;
  235.         ELSE ;
  236.         end
  237.       end
  238.     end;
  239.  
  240.   {************************}
  241.   procedure TRACEIT(TRACE: boolean; MSG: string80; SEMP: semrecp);
  242.     var STR: longstring;
  243.   begin
  244.     if trace then begin
  245.       write(rfile, ' ':trcount, msg, ': ');
  246.       if semp<>nil then begin
  247.         write(rfile, sem_names[semp^.semt]);
  248.         if semp^.semt=ident then begin
  249.           write(rfile, ': ', semp^.symp^.sym);
  250.           end
  251.         end
  252.       else write(rfile, sem_names[other]);
  253.       writeln(rfile);
  254.       trcount:=trcount+2;
  255.       end
  256.     end;
  257.  
  258.   {************************}
  259.   procedure ENDIT(TRACE: boolean);
  260.   begin
  261.     if trace then begin
  262.       trcount:=trcount-2;
  263.       writeln(rfile, ' ':trcount, '[exit]');
  264.       end
  265.     end;
  266.  
  267.   {************************}
  268.   procedure DUMPTREE(MSG: string80; ROOT: semrecp);
  269.     var CH: char;
  270.   begin
  271.     if trace then begin
  272.       write(rfile, '***DUMP ', msg, ' ');
  273.       dump_sem(2, root, '');
  274.       writeln(rfile);
  275.       writeln(rfile, '***END');
  276.       end
  277.     end;
  278.  
  279.   {***********************}
  280.   procedure DISP_SEM(TSEMP: semrecp);
  281.   begin  {recursively dispose a SEMREC tree}
  282.     if tsemp<>nil then
  283.     with tsemp^ do begin
  284.       case semt of
  285.         addop..funcall: begin
  286.           disp_sem(left);
  287.           disp_sem(right);
  288.           end;
  289.         if_then_else: begin
  290.           disp_sem(b1);
  291.           disp_sem(s1);
  292.           disp_sem(s2);
  293.           end;
  294.         ELSE  ;
  295.         end;
  296.       dispose(tsemp);
  297.       end
  298.     end;
  299.   
  300.   {************************}
  301.   function OPCODE(SEMT: semtype): string8;
  302.   begin
  303.     case semt of
  304.       addop: opcode:='ADD';
  305.       subop: opcode:='SUB';
  306.       mpyop: opcode:='IMULW';
  307.       divop: opcode:='IDIVW';
  308.       end
  309.     end;
  310.  
  311.   {***********************}
  312.   function IS_SIMPLE(ROOT: semrecp): boolean;
  313.   begin
  314.     if root^.semt=fixed then is_simple:=true
  315.     else
  316.     if root^.semt=ident then
  317.     is_simple:=(root^.symp^.symt=var_type)
  318.     else is_simple:=false;
  319.     end;
  320.  
  321.   {***********************}
  322.   function NAMEOF(ROOT: semrecp): string15;
  323.   begin   {ROOT has to be an IDENT or a FIXED;
  324.              this returns a string that will go into an
  325.              appropriate instruction location}
  326.     with root^ do begin
  327.       nameof:='';  {default}
  328.       if semt=fixed then nameof:=num2string(numval)
  329.       else
  330.       if semt=ident then begin
  331.         with symp^ do begin
  332.           if length(comment)>0 then
  333.             comment:=concat(comment, ', ');
  334.           comment:=concat(comment, symp^.sym);
  335.           case symt of
  336.             var_type: if level=0 then nameof:=sym
  337.               else nameof:=concat(num2string(vaddr), '[BP]');
  338.             func_type: nameof:=
  339.                    concat(num2string(pbytes+2*varsize), '[BP]');
  340.             user: symerror(sym, 'undeclared variable');
  341.             ELSE  ;
  342.             end
  343.           end
  344.         end
  345.       else error('BUG2: nameof');
  346.       end
  347.     end;
  348.  
  349.   {***********************}
  350.   function NEW_LABEL: string8;
  351.   begin
  352.     new_label:=concat('XXX', num2string(labcount));
  353.     labcount:=labcount+1;
  354.     end;
  355.  
  356.   procedure EVAL (ROOT: semrecp); forward;
  357.  
  358.   {************************}
  359.   procedure CODE_USER(ROOT: semrecp);
  360.     var FPARM: semrecp;
  361.         POSITION: integer;
  362.   begin  {an ordinary user procedure}
  363.     code('PUSH AX');  {place for return value}
  364.     fparm:=root^.right;
  365.     position:=0;
  366.     while fparm<>nil do
  367.     with fparm^ do begin
  368.       eval(left);  {one parameter to AX}
  369.       code('PUSH AX');    {push it on the stack}
  370.       if left^.semt=strng then
  371.         error('string parameter is invalid');
  372.       position:=position+varsize;
  373.       fparm:=fparm^.right;
  374.       end;
  375.     with root^.left^.symp^ do begin
  376.       if (position>pbytes) then
  377.       error('too many actual parameters')
  378.       else
  379.       if (position<pbytes) then begin
  380.         code('MOV AX,0');
  381.         while (position<pbytes) do begin
  382.             {add more parameter places for local variables}
  383.           code('PUSH AX');
  384.           position:=position+varsize;
  385.           end
  386.         end;
  387.       code2('CALL ', sym);
  388.       end
  389.     end;
  390.  
  391.   {*************************}
  392.   procedure CODE_SYSTEM(ROOT: semrecp);
  393.     var FPARM: semrecp;
  394.   begin  {system procedure -- broken into
  395.             unit calls, but at high level takes
  396.             an arbitrary number of mixed integers
  397.             and string addresses}
  398.     {The only one we have so far is WRITE/WRITELN}
  399.     with root^.left^.symp^ do
  400.     if (sym='WRITE') or
  401.        (sym='WRITELN') then begin
  402.       fparm:=root^.right;   {formal parameter list}
  403.       while fparm<>nil do
  404.       with fparm^.left^ do begin
  405.         if semt=strng then begin
  406.           code3('MOV BX,OFFSET(SS', num2string(strngnum), ')');
  407.                     {address to BX}
  408.           code('CALL SYS_SWRT');
  409.           end
  410.         else begin
  411.           eval(fparm^.left);  {integer left over in AX}
  412.           code('CALL SYS_IWRT');
  413.           end;
  414.         fparm:=fparm^.right;
  415.         end;
  416.       if sym='WRITELN' then
  417.         code('CALL SYS_WRTLN');
  418.       end
  419.     else if sym='HALT' then code('INT 020H')
  420.     else if sym='READ' then code('CALL READ')
  421.     else symerror(sym, 'missing system procedure');
  422.     end;
  423.  
  424.   {***********************}
  425.   procedure CODE_FUNCALL(ROOT: semrecp);
  426.     var FPARM: semrecp;
  427.         POS: integer;
  428.         FROOT: semrecp;
  429.   begin
  430.     if root^.semt=ident then begin
  431.       froot:=new_sem(funcall);
  432.       froot^.left:=root;
  433.       code_funcall(froot);
  434.       end
  435.     else
  436.     if root^.left^.symp^.is_system then code_system(root)
  437.     else code_user(root);
  438.     end;
  439.  
  440.   {************************}
  441.   procedure EVAL {(ROOT: semrecp)} ;
  442.     var LABEL1, LABEL2: string8;
  443.   begin
  444.     if root<>nil then
  445.     with root^ do
  446.     case semt of
  447.       ident: if symp^.symt=var_type then code2('MOV AX,', nameof(root))
  448.              else if symp^.symt=func_type then code_funcall(root)
  449.              else symerror(symp^.sym, 'invalid as an expression');
  450.       fixed: code2('MOV AX,', num2string(numval));
  451.       strng: code2('MOV AX,SS', num2string(stx));
  452.       addop, subop:
  453.         if is_simple(right) then begin
  454.           eval(left);  {goes to AX}
  455.           code3(opcode(semt), ' AX,', nameof(right));
  456.           end
  457.         else begin
  458.           eval(right);
  459.           code('PUSH AX');  {put in stack temporarily}
  460.           eval(left);  {left side to AX}
  461.           code('POP DX');  {get the right value back from stack}
  462.           code2(opcode(semt), ' AX,DX');
  463.           end;
  464.       mpyop, divop: begin
  465.         eval(right);     {divisor to AX}
  466.         code('PUSH AX');
  467.         eval(left);      {dividend to AX}
  468.         if semt=divop then code('CWD');  {sign extend into DX}
  469.         code('POP CX');
  470.         code2(opcode(semt), ' CX');
  471.         end;
  472.       assignop: begin
  473.         if right^.semt=fixed then  {an immediate on the right is OK}
  474.           code4('MOVW ', nameof(left), ',', nameof(right))
  475.         else begin
  476.           eval(right);  {goes to AX}
  477.           code3('MOV ', nameof(left), ',AX');
  478.           end
  479.         end;
  480.       while_do: begin
  481.         label1:=new_label;
  482.         lcode(label1, 'EQU $');
  483.         eval(left);   {boolean condition}
  484.         code('CMP AX,0');
  485.         label2:=new_label;
  486.         code2('JLE ', label2);
  487.         eval(right);  {statement or statement list}
  488.         code2('JMP ', label1);
  489.         lcode(label2, 'EQU $');
  490.         end;
  491.       stmtlist:
  492.         while root<>nil do begin
  493.           eval(root^.left);
  494.           root:=root^.right;
  495.           end;
  496.       funcall: code_funcall(root);
  497.       if_then_else: begin
  498.         label1:=new_label;
  499.         eval(b1);   {boolean condition}
  500.         code('CMP AX,0');
  501.         code2('JLE ', label1);
  502.         eval(s1);   {THEN statement}
  503.         label2:=new_label;
  504.         code2('JMP ', label2);
  505.         lcode(label1, 'EQU $');
  506.         eval(s2);   {ELSE statement}
  507.         lcode(label2, 'EQU $');
  508.         end
  509.       end
  510.     end;
  511.  
  512.   {***********************}
  513.   procedure FUNC_OPEN(ID: semrecp);
  514.   begin
  515.    { Picture of stack just after a call:
  516.                function return value  (2 bytes)
  517.                parm1                  (2 bytes)
  518.                parm2                  (2 bytes)
  519.                 ...
  520.                parmN                  (2 bytes)  <-- SP+2
  521.                return address         (2 bytes)  <-- SP
  522.  
  523.    We then push a `previous' BP, and set BP to the new SP, adding
  524.      one more word to the stack.
  525.    This convention is nearly like that used in Turbo, but with
  526.      the following two exceptions:
  527.      1) in Turbo, parameters are pushed in reverse order, i.e.
  528.         last parameter is pushed first and vice versa.
  529.      2) no stack space for a return value is provided in Turbo, as
  530.         in our scheme.
  531.    In both, the function's return value is returned in AX}
  532.  
  533.     lcode(id^.symp^.sym, 'PROC NEAR');
  534.        {marks the procedure's entry location}
  535.     code('PUSH BP');  {marker location}
  536.     code('MOV BP,SP');  {set BP to current SP}
  537.     end;
  538.  
  539.   {***********************}
  540.   procedure FUNC_CLOSE(ID: semrecp);
  541.   begin    {code an EXIT operation}
  542.     comment:=id^.symp^.sym;
  543.     code3('MOV AX,', num2string(id^.symp^.pbytes+2*varsize), '[BP]');
  544.     code('POP BP');     {restore BP}
  545.     code2('RET ', num2string(id^.symp^.pbytes+varsize));
  546.     codestrings;
  547.     code('ENDP');
  548.     end;
  549.  
  550.   {**************************}
  551.   function LIST_SYM(SYM: symbol; POSITION: int):int;
  552.   begin
  553.     writeln(rfile, ' ; ', sym, ' ':(maxtoklen+2-length(sym)),
  554.                    position:1, '[BP]');
  555.     list_sym:=position-varsize;
  556.     end;
  557.                    
  558.   {***********************}
  559.   procedure LIST_SYMS(FID, FPLIST: semrecp);
  560.     var POSITION: int;
  561.   begin
  562.     writeln(rfile, ' ;    SYMBOL TABLE');
  563.     position:=fid^.symp^.pbytes+4;   {return value}
  564.     position:=list_sym(fid^.symp^.sym, position);
  565.     while fplist<>nil do begin
  566.       position:=list_sym(fplist^.left^.symp^.sym, position);
  567.       fplist:=fplist^.right;
  568.       end;
  569.     writeln(rfile);
  570.     end;
  571.     
  572.   {*************************}
  573.   procedure DECL_VARS(IDLIST: semrecp; NEXT_ADDR: int; FPS: boolean);
  574.   begin  {declare local (fps) or global ~(fps) variables}
  575.     while idlist<>nil do begin
  576.       if idlist^.left^.semt<>ident then
  577.         error('need an identifier')
  578.       else 
  579.       with idlist^.left^ do begin
  580.         if fps then begin   {locals}
  581.           if symp^.symt=user then begin  {hasn't been declared yet}
  582.             symp^.symt:=var_type;
  583.             symp^.level:=plevel;
  584.             end
  585.           else
  586.           if symp^.level<plevel then {this shadows a global}
  587.           symp:=forcesym(symtab, symp^.sym, var_type, plevel)
  588.           else
  589.           symerror(symp^.sym, 'multiply declared');
  590.           with symp^ do begin
  591.             vaddr:=next_addr;
  592.             next_addr:=next_addr-varsize;
  593.             end
  594.           end
  595.         else   {global variables}
  596.         with symp^ do begin
  597.           if symt<>user then
  598.             symerror(sym, 'multiply declared');
  599.           symt:=var_type;  {vaddr isn't needed}
  600.           end
  601.         end;
  602.       idlist:=idlist^.right;
  603.       end
  604.     end;
  605.       
  606.   {************************}
  607.   procedure DECL_FUNC(ID, PARMS, BODY: semrecp);
  608.     var NPARMS: int;
  609.         TP: semrecp;
  610.   begin
  611.     nparms:=0;
  612.     tp:=parms;
  613.     while tp<>nil do begin  {count the parameters}
  614.       nparms:=nparms+1;
  615.       tp:=tp^.right;
  616.       end;
  617.     with id^, symp^ do begin
  618.       if (symt=var_type) or  {previously declared a variable}
  619.          ((symt=func_type) and is_actual)
  620.              {previously declared as a full procedure}
  621.         then symerror(sym, 'multiply declared');
  622.       if symt=user then begin   {hasn't been seen before}
  623.         faddr:=0;
  624.         is_system:=false;
  625.         end;
  626.       symt:=func_type;
  627.       is_actual:=(body<>nil);
  628.       plevel:=plevel+1;   {at local level for parameters}
  629.       decl_vars(parms, varsize*(nparms+1), true);
  630.       pbytes:=varsize*(nparms);
  631.       if sym='MAIN' then main_symp:=symp;
  632.       end
  633.     end;
  634.     
  635.   {*********************}
  636.   procedure APPLY(PFLAG: int; var TSEMP: semrecp);
  637.   
  638.     {.....................}
  639.     function IS_ARITH(TSEMP: semrecp): boolean;
  640.     begin
  641.       is_arith:=tsemp^.semt in [ident, fixed, addop, subop,
  642.                                 mpyop, divop, funcall];
  643.       end;
  644.                                 
  645.     {....................}
  646.     function IS_STRING(TSEMP: semrecp): boolean;
  647.     begin
  648.       is_string:=tsemp^.semt=strng;
  649.       end;
  650.     
  651.     {....................}
  652.     procedure BIN_TREE(STYPE: semtype);
  653.     begin
  654.       tsemp:=new_sem(stype);
  655.       tsemp^.left:=sem[tos-2];
  656.       tsemp^.right:=sem[tos];
  657.       if not(is_arith(sem[tos-2]) and
  658.              is_arith(sem[tos])) then
  659.         error('nonarithmetic operand');
  660.       end;
  661.   
  662.     {....................}
  663.     function NCONC(STL, ST: semrecp): semrecp;
  664.     begin   {STL is a list based on the RIGHT pointer.
  665.              It may be NIL}
  666.       if stl=nil then nconc:=st
  667.       else begin
  668.         nconc:=stl;
  669.         while stl^.right<>nil do stl:=stl^.right;
  670.         stl^.right:=st;
  671.         end
  672.       end;
  673.   
  674.     {....................}
  675.     function IS_VOID(TSEMP: semrecp): boolean;
  676.     begin   {look for the special identifier VOID}
  677.       if tsemp=nil then is_void:=true
  678.       else begin
  679.         is_void:=false;
  680.         if tsemp^.semt=ident then
  681.         if tsemp^.symp^.sym='VOID' then is_void:=true;
  682.         end
  683.       end;
  684.   
  685.   begin
  686.     case pflag of
  687.       ADDOPR:  { Expr -> Expr + Term }
  688.         begin
  689.           bin_tree(addop);
  690.           end;
  691.       ASSIGN:  { Stmt -> <identifier> := Expr }
  692.         begin
  693.           bin_tree(assignop);
  694.           end;
  695.       BLOCK:  { Stmt -> BEGIN StmtList END }
  696.         begin
  697.           tsemp:=sem[tos-1];
  698.           end;
  699.       DIVOPR:  { Term -> Term / Primary }
  700.         begin
  701.           bin_tree(divop);
  702.           end;
  703.       EXPRLIST1:  { ExprList -> Expr }
  704.         if not(is_void(sem[tos])) then begin
  705.           tsemp:=new_sem(expr_list);
  706.           tsemp^.left:=sem[tos];
  707.           end;
  708.       EXPRLIST2:  { ExprList -> ExprList , Expr }
  709.         if not(is_void(sem[tos])) then begin
  710.           tsemp:=new_sem(expr_list);
  711.           tsemp^.left:=sem[tos];
  712.           tsemp:=nconc(sem[tos-2], tsemp);
  713.           end
  714.         else tsemp:=sem[tos-2];
  715.       FDECL:  { FuncDecl -> FUNCTION <identifier> ( ExprList ) ; Stmt }
  716.         begin  {should be at global level}
  717.           plevel:=0;   {just in case}
  718.           decl_func(sem[tos-5], sem[tos-3], sem[tos]);
  719.             {... also increments PLEVEL by one}
  720.           if sem[tos]<>nil then begin
  721.             func_open(sem[tos-5]);
  722.             eval(sem[tos]);   {evaluate the Stmt}
  723.             func_close(sem[tos-5]);
  724.             list_syms(sem[tos-5], sem[tos-3]);
  725.             end;
  726.           disp_sem(sem[tos-5]);
  727.           disp_sem(sem[tos-3]);
  728.           disp_sem(sem[tos]);
  729.           clearsym(symtab, plevel);
  730.           plevel:=0;
  731.           end;
  732.       FUNCP:  { Primary -> <identifier> ( ExprList ) }
  733.         begin   {function call}
  734.           tsemp:=new_sem(funcall);
  735.           tsemp^.left:=sem[tos-3];
  736.           tsemp^.right:=sem[tos-1];
  737.           end;
  738.       IFTHEN:  { Stmt -> IF Expr THEN Stmt ELSE Stmt }
  739.         begin
  740.           tsemp:=new_sem(if_then_else);
  741.           with tsemp^ do begin
  742.             b1:=sem[tos-4];
  743.             s1:=sem[tos-2];
  744.             s2:=sem[tos];
  745.             if is_string(b1) then
  746.               error('if-expr is a string');
  747.             end
  748.           end;
  749.       MPYOPR:  { Term -> Term * Primary }
  750.         begin
  751.           bin_tree(mpyop);
  752.           end;
  753.       PAREN:  { Primary -> ( Expr ) }
  754.         begin
  755.           tsemp:=sem[tos-1];
  756.           end;
  757.       SEXPR:  { Stmt -> Expr }
  758.         begin
  759.           if not(sem[tos]^.semt in [ident, funcall]) then
  760.             error('invalid statement');
  761.             {Can't check for function call since function names
  762.                haven't been defined yet}
  763.           end;
  764.       STLIST2:  { StmtList -> StmtList Stmt ; }
  765.         begin
  766.           tsemp:=new_sem(stmtlist);
  767.           tsemp^.left:=sem[tos-1];
  768.           tsemp:=nconc(sem[tos-2], tsemp);
  769.           end;
  770.       SUBOPR:  { Expr -> Expr - Term }
  771.         begin
  772.           bin_tree(subop);
  773.           end;
  774.       VDECL:  { FuncDecl -> VAR ExprList }
  775.         begin
  776.           decl_vars(sem[tos], 0, false);
  777.           disp_sem(sem[tos]);
  778.           end;
  779.       WHILEDO:  { Stmt -> WHILE Expr DO Stmt }
  780.         begin
  781.           tsemp:=new_sem(while_do);
  782.           tsemp^.left:=sem[tos-2];
  783.           tsemp^.right:=sem[tos];
  784.           if is_string(tsemp^.left) then
  785.             error('while-expr is a string');
  786.           end
  787.       ELSE  writeln(rfile, pflag);
  788.         error('unknown production flag');
  789.       end  { apply case };
  790.     end;
  791.  
  792.