
(*
 * TPTC - Turbo Pascal to C translator
 *
 * (C) 1988 Samuel H. Smith (rev. 13-Feb-88)
 *
 *)


(*
 * expression parser
 *
 *)
function pterm: string; forward;

function iscall(var lv: string): boolean;
   {see if the given lvalue is a function call or not}
begin
   iscall := lv[length(lv)] = ')';
end;


procedure make_pointer(var expr: string);
   {convert the expression into a pointer constant, if possible}
var
   sym:  symptr;
begin
   
   case(expr[1]) of
      '*':
      begin
         delete(expr,1,1);
         exit;
      end;
      
      'a'..'z','A'..'Z','_':
      begin         {pass pointer to strings/arrays}
         sym := locatesym(expr);
         if (sym <> nil) and ((sym^.symtype = s_string) or
                              (sym^.suptype = ss_array)) then
         begin
            {null}
         end
         else
         
         if expr[length(expr)-1] = '(' then    {remove () from function calls}
            dec(expr[0],2)
            
         else
            expr := '&' + expr;
      end;
      
   end;

end;


function isnumber(var lv: string): boolean;
  {see if the given value is a literal number}
var
   i: integer;
begin
   for i := 1 to length(lv) do
      case lv[i] of
         '0'..'9','.': ;
         else
            isnumber := false;
            exit;
      end;
   isnumber := true;
end;


procedure subtract_base(var expr: string; base: integer);
   {subtract the specified base from the given expression;
    use constant folding if possible}
begin
   if base <> 0 then
      if isnumber(expr) then
         expr := itoa(atoi(expr) - base)
      else
      if base > 0 then
         expr := expr + '-' + itoa(base)
      else
         expr := expr + '+' + itoa(-base);
end;


function exprtype: char;
   {determine expression type and return the printf code for the type}
var
   xt:      char;

begin
   case cexprtype of
      s_char:    xt := 'c';
      s_file:    xt := '@';
      s_double:  xt := 'f';
      s_string:  xt := 's';
      s_bool:    xt := 'b';
      s_int:     xt := 'd';
      s_long:    xt := 'D'; { calling routine should convert to "ld" }
      else       xt := '?';
   end;

   exprtype := xt;
end;


function strtype(ty: char): boolean;
   {see if the expression is a string data type or not}
begin
   case ty of
      's','c':  strtype := true;
      else      strtype := false;
   end;
end;



function psetof:  string;
   {parse a literal set; returns the set literal translated into
    the form: setof(.....)}
var
   ex: string;

begin
   ex := 'setof(';
   if tok[1] <> ']' then
      ex := ex + pterm;

   while (tok = '..') or (tok[1] = ',') do
   begin
      if tok = '..' then       
         ex := ex + ',__,'
      else
         ex := ex + ',';

      gettok;
      ex := ex + pterm;
   end;

   if ex[length(ex)] <> '(' then
      ex := ex + ',';
   ex := ex + '_E)';
   psetof := ex;
end;


function pterm:   string;
   {parse an expression term;  returns the translated expression term;
    detects subexpressions, set literals and lvalues(variable names)}
var
   ex: string;
   builtin: boolean;

begin
   if debug_parse then write(' <term>');

   if (toktype = identifier) and (cursym <> nil) then
      builtin := cursym^.suptype = ss_builtin
   else
      builtin := false;

   (* process pos(c,str) and pos(str,str) *)
   if builtin and (tok = 'POS') then
   begin
      if debug_parse then write(' <pos>');
      gettok;   {consume the keyword}
      if tok[1] <> '(' then
         syntax('"(" expected (pterm.pos)');
      
      gettok;   {consume the (}
      ex := pexpr;
      if exprtype{(ex)} = 'c' then
         ex := 'cpos(' + ex
      else
         ex := 'spos(' + ex;

      gettok;   {consume the ,}
      ex := ex + ',' + pexpr;
      gettok;   {consume the )}
      pterm := ex + ')';
      cexprtype := s_int;
   end
   else

   (* process chr(n) *)
   if builtin and (tok = 'CHR') then
   begin
      if debug_parse then write(' <chr>');
      gettok;   {consume the keyword}
      if tok[1] <> '(' then
         syntax('"(" expected (pterm.chr)');
      
      gettok;   {consume the (}
      ex := pexpr;
      gettok;   {consume the )}

      if isnumber(ex) then
         ex := numlit(atoi(ex))
      else
         ex := 'chr('+ex+')';

      pterm := ex;
      cexprtype := s_char;
   end
   else

   (* translate NOT term into !term *)
   if builtin and (tok = 'NOT') then
   begin
      if debug_parse then write(' <not>');
      gettok;
      pterm := '!' + pterm;
      cexprtype := s_bool;
   end
   else

   (* process port/memory array references *)
   if builtin and ((tok = 'PORT') or (tok = 'PORTW') or
                   (tok = 'MEM')  or (tok = 'MEMW')) then
   begin
      if debug_parse then write(' <port>');
      if tok = 'PORT'  then ex := 'inportb('    else
      if tok = 'PORTW' then ex := 'inport('     else
      if tok = 'MEM'   then ex := 'peekb('      else
                            ex := 'peek(';

      gettok;     {consume the keyword}
      gettok;     {consume the [ }

      repeat
         ex := ex + pexpr;
         if tok[1] = ':' then
         begin
            gettok;
            ex := ex + ',';
         end;
      until (tok[1] = ']') or recovery;

      gettok;     {consume the ] }
      pterm := ex + ')';
      cexprtype := s_int;
   end
   else

   (* translate bitwise not (mt+) *)
   if (tok[1] = '?') or (tok[1] = '~') or (tok[1] = '\') then
   begin
      if debug_parse then write(' <bitnot>');
      gettok;
      pterm := '!' + pterm;         {what is a bitwise NOT in c?}
   end
   else

   (* process unary minus *)
   if tok = '-' then
   begin
      if debug_parse then write(' <unary>');
      gettok;
      pterm := '-' + pterm;
   end
   else

   (* translate address-of operator *)
   if tok[1] = '@' then
   begin
      if debug_parse then write(' <ref>');
      gettok;  {consume the '@'}
      ex := plvalue;
      make_pointer(ex);
      pterm := ex;
   end
   else

   (* pass numbers *)
   if toktype = number then
   begin
      if debug_parse then write(' <number>');
      pterm := tok;
      gettok;
      cexprtype := s_int;
   end
   else

   (* pass strings *)
   if toktype = strng then
   begin
      if debug_parse then write(' <string>');
      pterm := tok;
      gettok;
      cexprtype := s_string;
   end
   else

   (* pass characters *)
   if toktype = chars then
   begin
      if debug_parse then write(' <char>');
      pterm := tok;
      gettok;
      cexprtype := s_char;
   end
   else

   (* pass sub expressions *)
   if tok[1] = '(' then
   begin
      if debug_parse then write(' <subexp>');
      gettok;
      pterm := '(' + pexpr + ')';
      gettok;
   end
   else

   (* translate literal sets *)
   if tok[1] = '[' then
   begin
      if debug_parse then write(' <setlit>');
      gettok;
      pterm := psetof;
      gettok;
      cexprtype := s_struct;
   end

   (* otherwise the term will be treated as an lvalue *)
   else
      pterm := plvalue;
end;


function pexpr: string;
   {top level expression parser; parse and translate an expression and
    return the translated expr}
var
   ex:       string;
   ty:       char;
   ex2:      string;
   ty2:      char;

   procedure relop(newop: string40);
   begin
      if debug_parse then write(' <relop>');
      gettok;        {consume the operator token}

      ex2 := pterm;  {get the second term}
      ty2 := exprtype;

      {use strcmp if either param is a string}
      if ty = 's' then
      begin
         if ty2 = 's' then
            ex := 'strcmp(' + ex + ',' + ex2 + ') ' + newop + ' 0'
         else
         if ex2[1] = '''' then
            ex := 'strcmp(' + ex + ',"' +
                     copy(ex2,2,length(ex2)-2) + '") ' + newop + ' 0'
         else
            ex := 'strcmp(' + ex + ',ctos(' + ex2 + ')) ' + newop + ' 0'
      end
      else

      if ty = 'c' then
      begin
         if ty2 = 's' then
            ex := 'strcmp(ctos(' + ex + '),' + ex2 + ') ' + newop + ' 0'
         else
            ex := ex + ' ' + newop + ' ' + ex2
      end

      else
         ex := ex + ' ' + newop + ' ' + ex2;

      cexprtype := s_bool;
   end;


   procedure addop;

      procedure add_scat;
      var
         p: integer;

      begin
         {find end of control string}
         p := 7;  {position of 'scat("%'}
         while (ex[p] <> '"') or
               ((ex[p] = '"') and (ex[p-1] = '\') and (ex[p-2] <> '\')) do
            p := succ(p);
         p := succ(p);

         {add literals to the control string if possible}
         if (ex2[1] = '''') or (ex2[1] = '"') then
            ex := copy(ex,1,p-2) + 
                  copy(ex2,2,length(ex2)-2) +
                  copy(ex,p-1,length(ex)-p+2)

         else {add a parameter to the control string}
            ex := copy(ex,1,p-2) + '%' + ty2 +
                  copy(ex,p-1,length(ex)-p+1) + ',' + ex2 + ')';
      end;

   begin
      if debug_parse then write(' <addop>');
      gettok;        {consume the operator token}

      ex2 := pterm;  {get the second term}
      ty2 := exprtype;

(* writeln('ex{',ex,'}',ty,' ex2{',ex2,'}',ty2); *)

      {continue adding string params to scat control string}
      if (ex[5] = '(') and (copy(ex,1,4) = 'scat') then
         add_scat
      else

      {start new scat call if any par is a string}
      if strtype(ty) or strtype(ty2) then
      begin
         if (ex[1] = '''') or (ex[1] = '"') then
            ex := 'scat("' + copy(ex,2,length(ex)-2) + '")'
         else
            ex := 'scat("%' + ty + '",' + ex + ')';
         add_scat;
      end
      else
         ex := ex + ' + ' + ex2;

(* writeln('ex=',ex); *)
   end;

   procedure mulop(newop: string40);
   begin
      if debug_parse then write(' <mulop>');
      gettok;        {consume the operator token}

      ex2 := pterm;  {get the second term}
      ex := ex + ' ' + newop + ' ' + ex2;
   end;

   procedure andop(newop: char);
   begin
      if debug_parse then write(' <andop>');
      gettok;        {consume the operator token}

      ex2 := pterm;  {get the second term}
      ty2 := exprtype;

      {boolean and/or?}
      if (ty = 'b') or (ty2 = 'b') then
      begin
         ex := ex + ' ' + newop + newop + ' ' + ex2;
         cexprtype := s_bool;
      end
      else  {otherwise bitwise}
         ex := ex + ' ' + newop + ' ' + ex2;
   end;


begin
   if debug_parse then write(' <expr>');
   ex := pterm;
   ty := exprtype;

   while true do
   begin
      (* process operators *)
           if tok    = '>='  then relop(tok)
      else if tok    = '<='  then relop(tok)
      else if tok    = '<>'  then relop('!=')
      else if tok[1] = '>'   then relop(tok)
      else if tok[1] = '<'   then relop(tok)
      else if tok[1] = '='   then relop('==')
      else if tok[1] = '+'   then addop
      else if tok[1] = '-'   then mulop(tok)
      else if tok[1] = '*'   then mulop(tok)
      else if tok[1] = '/'   then mulop(tok)
      else if tok[1] = '&'   then mulop(tok)  {mt+}
      else if tok[1] = '!'   then mulop('|')  {mt+}
      else if tok[1] = '|'   then mulop('|')  {mt+}
      else if tok    = 'DIV' then mulop('/')
      else if tok    = 'MOD' then mulop('%')
      else if tok    = 'SHR' then mulop('>>')
      else if tok    = 'SHL' then mulop('<<')
      else if tok    = 'XOR' then mulop('^')
      else if tok    = 'AND' then andop('&')
      else if tok    = 'OR'  then andop('|')
      else

      (* translate the expr IN set operator *)
      if tok = 'IN'  then
      begin
         gettok;
         ex := 'inset('+ex+',' + pterm + ')';
      end
      else

      (* ran out of legal expression operators; return what we found *)
      begin
         pexpr := ex;
         exit;
      end;
   end;

end;


function plvalue: string;
   {parse and translate an lvalue specification and return the translated
    lvalue as a string}

var
   lv:       string;
   expr:     string;
   funcid:   string40;
   pref:     string40;
   idok:     boolean;
   sym:      symptr;
   func:     symptr;
   btype:    symtypes;
   cstype:   supertypes;
   bstype:   supertypes;
   pvars:    integer;
   cbase:    integer;
   bbase:    integer;
      
begin
   if debug_parse then write(' <lvalue>');
   plvalue := 'lvalue';

(* lvalues must begin with an identifier in pascal *)
   if toktype <> identifier then
   begin
      syntax('Identifier expected (plvalue)');
      exit;
   end;

(* assign initial part of the lvalue *)
   idok := false;
   pref := '';
   lv := ltok;
   funcid := tok;
   bstype := ss_scalar;
   bbase := 0;
   cbase := 0;

   sym := cursym;
   if sym <> nil then
   begin
      cstype := sym^.suptype;
      cbase := sym^.base;
      cexprtype := sym^.symtype;
      lv := sym^.repid;          {use replacement identifier}

      {dereference VAR paremter pointers}
      if sym^.parcount = -2 then
      begin
         if debug_parse then write(' <var.deref>');
         pref := '*';
      end;
      
      {prefix with pointer if this is a member identifier and a with
       is in effect}
      if (sym^.parcount < 0) and (sym^.pvar > 0) and (withlevel > 0) then
      begin
         if debug_parse then write(' <with.deref>');
         pref := 'with'+itoa(withlevel)+'->';
      end;
      
   end;
   

(* process a list of qualifiers and modifiers *)
   gettok;

   repeat
      if toktype = identifier then
      begin

         if cursym <> nil then               {find record member types}
         begin
            sym := cursym;
            cstype := sym^.suptype;
            cbase := sym^.base;
            cexprtype := sym^.symtype;
            ltok := sym^.repid;          {use replacement identifier}
         end;
         
      end;
      
      (* process identifiers (variable or field names) *)
      if idok and (toktype = identifier) then
      begin
         if debug_parse then write(' <ident>');
         lv := lv + ltok;
         gettok;
         idok := false;
      end
      else

      (* pointers *)
      if tok = '^' then
      begin
         if debug_parse then write(' <deref>');
         pref := '*' + pref;
         gettok;
      end
      else

      (* pointer subscripts *)
      if tok = '^[' then
      begin
         if debug_parse then write(' <ptr.subs>');
         lv := lv + '[';
         gettok;

         while tok <> ']' do
         begin
            lv := lv + pexpr;
            if tok = ',' then
            begin
               lv := lv + '][';
               gettok;
            end;
         end;

         lv := lv + ']';
         gettok;
      end
      else

      (* pointer members *)
      if tok = '^.' then
      begin
         if debug_parse then write(' <ptr.deref>');
         lv := lv + '->';
         gettok;
         idok := true;
      end
      else

      (* record members *)
      if tok = '.' then
      begin
         if debug_parse then write(' <member>');
         if pref = '*' then     {translate *id. into id->}
         begin
            pref := '';
            lv := lv + '->';
         end
         else
            lv := lv + '.';
         idok := true;
         gettok;
      end
      else

      (* subscripts *)
      if tok[1] = '[' then
      begin
         if debug_parse then write(' <subs>');
         btype := cexprtype;
         bstype := cstype;
         bbase := cbase;

         if copy(pref,1,1) = '*' then
            pref := '';       {replace '*id[' with 'id['}

         lv := lv + '[';
         gettok;

         repeat
            expr := pexpr;

            if tok[1] = ',' then
            begin
               lv := lv + expr + '][';
               gettok;
               bstype := ss_scalar;
            end;
         until tok[1] = ']';

         subtract_base(expr,bbase);
         lv := lv + expr + ']';

         if (btype = s_string) and (bstype <> ss_array) then
         begin
            btype := s_char;
            ltok := lv;
            if expr = '-1' then
               warning('Dynamic length reference');
         end;

         cexprtype := btype;
         cstype := ss_scalar;
         cbase := 0;
         gettok;
      end
      else

      (* function calls *)
      if tok[1] = '(' then
      begin
         if debug_parse then write(' <func>');
         func := locatesym(funcid);
         pvars := 0;
         if func <> nil then
         begin
            pvars := func^.pvar;          {determine return type}
            cexprtype := func^.symtype;
         end;
            
         btype := cexprtype;
         lv := lv + '(';
         gettok;

         while tok[1] <> ')' do
         begin
            expr := pexpr;
            if (pvars and 1) = 1 then     {prefix VAR paremeters}
               make_pointer(expr);

            lv := lv + expr;
            pvars := pvars shr 1;

            if (tok[1] = ',') or (tok = ':') then
            begin
               lv := lv + ',';
               gettok;
            end;
         end;

         lv := lv + ')';
         gettok;
         cexprtype := btype;
      end
      else

(* otherwise just return what was found so far *)
      begin

         (* add dummy param list to function calls where the proc
            expects no parameters *)
         if sym <> nil then
         begin
            if (not iscall(lv)) and (sym^.parcount >= 0) then
               lv := lv + '()';
         end;

         plvalue := pref + lv;
         exit;
      end;

   until recovery;

   plvalue := pref + lv;
end;

