home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / turbopas / tptc17sc.arc / TPCSCAN.INC < prev    next >
Text File  |  1988-03-26  |  20KB  |  834 lines

  1.  
  2. (*
  3.  * TPTC - Turbo Pascal to C translator
  4.  *
  5.  * (C) 1988 Samuel H. Smith (rev. 13-Feb-88)
  6.  *
  7.  *)
  8.  
  9.  
  10. (********************************************************************)
  11. (*
  12.  * lexical scanner
  13.  *
  14.  *)
  15.  
  16. function numlit(n: integer): anystring;
  17. var
  18.    lit: string[6];
  19.    
  20.    {convert an integer into a c style numeric character literal}
  21.    function digit(n: integer): char;
  22.       (* convert an integer into a hex digit *)
  23.    begin
  24.       n := n and 15;
  25.       if n > 9 then n := n + 7;
  26.       digit := chr( n + ord('0') );
  27.    end;
  28.  
  29. begin
  30.    lit := '''\?''';
  31.  
  32.    case n of
  33.      $07:   lit[3] := 'a';
  34.      $08:   lit[3] := 'b';
  35.      $09:   lit[3] := 't';
  36.      $0a:   lit[3] := 'n';
  37.      $0b:   lit[3] := 'v';
  38.      $0c:   lit[3] := 'f';
  39.      $0d:   lit[3] := 'r';
  40.  
  41.      32..126,128..254:
  42.             lit := ''''+chr(n)+'''';
  43.  
  44.      else   begin
  45.                lit := '''\x??''';
  46.                lit[4] := digit(n shr 4);
  47.                lit[5] := digit(n);
  48.             end;
  49.    end;
  50.  
  51.    numlit := lit;
  52.    toktype := chars;
  53. end;
  54.  
  55.  
  56. (********************************************************************)
  57. procedure getchar;
  58.    {consume the current char and get the next one}
  59. var
  60.    stack: char;
  61. begin
  62.    if ofs(stack) < minstack then
  63.       fatal('Out of stack space');
  64.  
  65.    while (srclevel > 0) and eof(srcfd[srclevel]) do
  66.    begin
  67.       if not linestart then putline;
  68.       putln('/* TPTC: end of '+srcfiles[srclevel]+' */');
  69.       
  70.       if debug then writeln;
  71.       writeln(^M,srcfiles[srclevel],'(',srclines[srclevel],')');
  72.       
  73.       close(srcfd[srclevel]);
  74.       freemem(inbuf[srclevel],inbufsiz);
  75.  
  76.       dec(srclevel);
  77.       statustime := 0;
  78.    end;
  79.     
  80.    if eof(srcfd[srclevel]) then
  81.       nextc := '.'
  82.    else
  83.       read(srcfd[srclevel], nextc);
  84.  
  85.    if nextc = ^J then
  86.    begin
  87.       inc(srclines[srclevel]);
  88.       inc(srctotal);
  89.       
  90.       mark_time(curtime);
  91.       if (curtime >= statustime) or debug then
  92.       begin
  93.          if debug then writeln;
  94.          write(^M,srcfiles[srclevel],'(',srclines[srclevel],')');
  95.          statustime := curtime+statrate;
  96.          abortcheck;
  97.       end;
  98.    end;
  99. end;
  100.  
  101.  
  102. (********************************************************************)
  103. function usec: char;
  104.    {use up the current character(return it) and get
  105.     the next one from the input stream}
  106. var
  107.    c: char;
  108. begin
  109.    c := nextc;
  110.    getchar;
  111.    usec := c;
  112. end;
  113.  
  114.  
  115. (********************************************************************)
  116. function newc(n: string40): string40;
  117.    {replace the current character with a different one and get the next
  118.     character from the input stream}
  119. var
  120.    c: char;
  121. begin
  122.    c := nextc;
  123.    getchar;
  124.    newc := n;
  125. end;
  126.  
  127.  
  128. (********************************************************************)
  129. procedure concat_tokens;
  130.    {concatenate the next token and the current token}
  131. var
  132.    cur: string;
  133. begin
  134.    cur := ltok;
  135.    ltok := nextc;
  136.    toktype := unknown;
  137.    scan_tok;
  138.  
  139.    ltok := copy(cur,1,length(cur)-1) + copy(ltok,2,255);
  140.    ltok[1] := '"';
  141.    ltok[length(ltok)] := '"';
  142.    toktype := strng;
  143. end;
  144.  
  145.  
  146. (********************************************************************)
  147. procedure scan_ident;
  148.    {scan an identifier; output is ltok; nextc is first character following
  149.     the identifier; toktype = identifier;  this is the protocol for all of
  150.     the scan_xxxx procedures in the lexical analyzer}
  151. begin
  152.  
  153.    toktype := unknown;
  154.    ltok := '';
  155.  
  156.    repeat
  157.       case nextc of
  158.          'A'..'Z':
  159.             begin
  160.                if map_lower then
  161.                   nextc := chr( ord(nextc)+32 );
  162.                ltok := ltok + nextc;
  163.                getchar;
  164.             end;
  165.  
  166.          'a'..'z', '0'..'9', '_','@':
  167.             ltok := ltok + usec;
  168.  
  169.          else
  170.             toktype := identifier;
  171.       end;
  172.  
  173.    until toktype = identifier;
  174. end;
  175.  
  176.  
  177.  
  178. (********************************************************************)
  179. procedure scan_preproc;
  180.    {scan a tshell preprocessor directive;  same syntax as C already}
  181. begin
  182.    puts('#');
  183.  
  184.    repeat
  185.       puts(nextc);
  186.       getchar;
  187.    until nextc = ^M;
  188.  
  189.    getchar;
  190.    putline;
  191.    toktype := unknown;
  192. end;
  193.  
  194.  
  195. (********************************************************************)
  196. procedure scan_number;
  197.    {scan a number;  this also processes #nnn character literals, which are
  198.     converted into octal character literals.  imbedded periods are processed,
  199.     and a special condition is noted for trailing periods.  this is needed
  200.     for scanning the ".." keyword when used after numbers.  an ungetchar
  201.     facility would be more general, but isn't needed anywhere else.
  202.     in pascal/mt+, #nnn is translated into nnnL }
  203. var
  204.    hasdot:  boolean;
  205.    charlit:   boolean;
  206.    islong:  boolean;
  207.  
  208. begin
  209.    hasdot := false;
  210.    islong := false;
  211.    charlit := false;
  212.    toktype := number;
  213.  
  214. (* check for preprocessor directives, character literals or long literals *)
  215.    if nextc = '#' then
  216.    begin
  217.       ltok := '';
  218.       if mt_plus then
  219.          islong := true
  220.       else
  221.          charlit := true;
  222.    end;
  223.  
  224.    getchar;
  225.  
  226. (* check for preprocessor directives *)
  227.    if tshell and charlit and (nextc >= 'a') and (nextc <= 'z') then
  228.       scan_preproc
  229.    else
  230.  
  231.    repeat
  232.       case nextc of
  233.          '$','0'..'9','a'..'f','A'..'F':
  234.             ltok := ltok + usec;
  235.  
  236.          '.':
  237.             if hasdot then
  238.             begin
  239.                if ltok[length(ltok)] = '.' then
  240.                begin
  241.                   ltok[0] := pred(ltok[0]);  {remove trailing ., part of ..}
  242.                   if charlit then
  243.                      ltok := numlit(atoi(ltok));
  244.                   extradot := true;
  245.                end;
  246.                exit;
  247.             end
  248.             else
  249.  
  250.             begin
  251.                hasdot := true;
  252.                ltok := ltok + usec;
  253.             end;
  254.  
  255.          else
  256.             begin
  257.                if charlit then
  258.                begin
  259.                   ltok := numlit(atoi(ltok));
  260.                   if (nextc = '''') or (nextc = '^') or (nextc = '#') then
  261.                      concat_tokens;
  262.                   exit;
  263.                end;
  264.                
  265.                if ltok[1] = '$' then
  266.                   ltok := '0x' + copy(ltok,2,99);
  267.                if islong then
  268.                   ltok := ltok + 'L';
  269.                exit;
  270.             end;
  271.       end;
  272.  
  273.    until true=false;
  274. end;
  275.  
  276.  
  277. (********************************************************************)
  278. procedure scan_hat;
  279.    {scan tokens starting with ^ - returns ^X as a character literal 
  280.     corresponding to the specified control character.  returns ^ident as
  281.     an identifier with the leading ^ intact.  also scans ^. and ^[.}
  282. var
  283.    c: char;
  284.  
  285. begin
  286.    getchar;
  287.  
  288.    if ((nextc = '.') or (nextc = '[')) and 
  289.       ((ptoktype = identifier) or (ptok = ']')) then
  290.    begin
  291.       ltok := '^' + usec;     {^. or ^[}
  292.       exit;
  293.    end;
  294.  
  295.    case nextc of
  296.       '@','['..'`':
  297.          ltok := usec;
  298.          
  299.       'A'..'Z','a'..'z':
  300.          begin
  301.             ltok := nextc;
  302.             scan_ident;
  303.          end;
  304.        else
  305.          exit;
  306.    end;
  307.  
  308.    if length(ltok) = 1 then      {^c = control char}
  309.    begin
  310.       ltok := numlit( ord(upcase(ltok[1])) - ord('@') );
  311.       if (nextc = '''') or (nextc = '^') or (nextc = '#') then
  312.          concat_tokens;
  313.    end
  314.    else
  315.       ltok := '^' + ltok;        {^ident = pointer to ident}
  316.  
  317. end;
  318.  
  319.  
  320. (********************************************************************)
  321. procedure scan_dot;
  322.    {scans tokens starting with "."; knows about the 'extra dot' condition
  323.     that comes up in number scanning.  returns a token of either '.' or '..'}
  324. begin
  325.    getchar;
  326.  
  327.    if (nextc = '.') or extradot then
  328.    begin
  329.       ltok := '..';
  330.       extradot := false;
  331.    end;
  332.  
  333.    if nextc = '.' then
  334.       getchar;
  335. end;
  336.  
  337.  
  338. (********************************************************************)
  339. procedure scan_string;
  340.    {scans a literal string.  processes imbedded quotes ala pascal.  translates
  341.     the string into a C string with the proper escapes on imbedded quotes.
  342.     converts single character strings into character constants.  these are
  343.     sometimes converted back to strings when the parser needs to}
  344. begin
  345.    toktype := unknown;
  346.    ltok := '"';
  347.    getchar;
  348.  
  349.    repeat
  350.       case nextc of
  351.          ^J,^M:
  352.             begin
  353.                error_message('Closing quote expected (scan_string)');
  354.                toktype := strng;
  355.             end;
  356.       
  357.          '''':
  358.             begin
  359.                getchar;     {consume the quote}
  360.       
  361.                if nextc = '''' then
  362.                   ltok := ltok + usec
  363.                   {double quotes are coded as a single quote}
  364.                else
  365.       
  366.                begin        {end of string}
  367.                   ltok := ltok + '"';
  368.                   toktype := strng;
  369.                end;
  370.             end;
  371.       
  372.          '"':  ltok := ltok + newc('\"');
  373.          '\':  ltok := ltok + newc('\\');
  374.  
  375.          else  ltok := ltok + usec;
  376.       end;
  377.  
  378.    until toktype = strng;
  379.  
  380.    if length(ltok) = 3 then
  381.    begin
  382.       ltok[1] := '''';
  383.       ltok[3] := '''';
  384.       toktype := chars;
  385.    end;
  386.  
  387.    if ltok = '"\""' then
  388.    begin
  389.       ltok := '''"''';
  390.       toktype := chars;
  391.    end
  392.    else
  393.  
  394.    if (ltok = '"''"') or (ltok = '''''''') then
  395.       ltok := '''\'''''
  396.    else
  397.  
  398.    if (ltok = '"\\"') then
  399.    begin
  400.       ltok := '''\\''';
  401.       toktype := chars;
  402.    end;
  403.  
  404.    if (nextc = '^') or (nextc = '#') then
  405.       concat_tokens;
  406. end;
  407.  
  408.  
  409. (********************************************************************)
  410. procedure scan_pragma(var isinclude: anystring);
  411.    {scans a turbo pascal compiler option and translates it into a comment.
  412.     include directive is translated into the #include.
  413.     returns with the first non-blank after the pragma}
  414. var
  415.    code: anystring;
  416.    prag: anystring;
  417.    arg:  anystring;
  418.  
  419.    procedure scanword(var dest: anystring);
  420.    begin
  421.       dest := '       ';  {insure dest[2] is initialized}
  422.       dest := '';
  423.       while true do
  424.       case nextc of
  425.          ' ', '*', '}', ',':
  426.             exit;
  427.          else
  428.          begin
  429.             dest := dest + upcase(nextc);
  430.             getchar;
  431.          end;
  432.       end;
  433.    end;
  434.    
  435. begin
  436.    isinclude := '';
  437.    
  438.    repeat
  439.       if nextc = ',' then
  440.          newline;
  441.  
  442.       getchar;   {consume the $ or ,}
  443.    
  444.       {get the progma code}
  445.       scanword(code);
  446.       
  447.       if nextc = ' ' then
  448.       begin
  449.          getchar;
  450.          scanword(arg);
  451.       end
  452.       else
  453.          arg := '';
  454.       
  455.       if code[2] = '+' then
  456.          arg := 'ON'
  457.       else
  458.       if code[2] = '-' then
  459.          arg := 'OFF';
  460.  
  461.       prag := '/* '+code[1]+'(' + arg + ')' + ' */';
  462.       
  463.       case code[1] of
  464.  
  465.          'D':  if code[2] = 'E' then
  466.                   prag := '#define '+arg;
  467.                
  468.          'E':  if code[2] = 'N' then
  469.                   prag := '#endif'
  470.                else
  471.                if code[2] = 'L' then
  472.                   prag := '#else';
  473.                
  474.          'I':  if code[2] = ' ' then
  475.                begin
  476.                   if pos('.',arg) = 0 then
  477.                      arg := arg + '.PAS';
  478.                   prag := '#include "' + arg + '"   ';
  479.                            
  480.                   if includeinclude then
  481.                   begin
  482.                      prag := '';
  483.                      isinclude := arg;
  484.                   end;
  485.                end
  486.                else
  487.                
  488.                if code[2] = 'F' then
  489.                begin
  490.                   if code[3] = 'N' then
  491.                      prag := '#ifndef '+arg
  492.                   else
  493.                      prag := '#ifdef '+arg;
  494.                end;
  495.  
  496.          'U':  if code[2] = 'N' then
  497.                   prag := '#undef '+arg;
  498.                
  499.       end;
  500.  
  501.       puts(prag);
  502.       puts('   ');
  503.  
  504.       while nextc = ' ' do
  505.          getchar;
  506.  
  507.    until nextc <> ',';
  508.  
  509. end;
  510.  
  511.  
  512. (********************************************************************)
  513. procedure open_include(name: anystring);
  514. begin
  515.    if length(name) = 0 then exit;
  516.    
  517.    inc(srctotal);
  518.    inc(objtotal);
  519.  
  520.    inc(srclevel);
  521.    if srclevel > maxincl then
  522.       fatal('Includes nested too deeply');
  523.       
  524.    srcfiles[srclevel] := name;
  525.    srclines[srclevel] := 1;
  526.    
  527.    assign(srcfd[srclevel],name);
  528.    {$I-} reset(srcfd[srclevel]); {$I+}
  529.    if ioresult <> 0 then
  530.    begin
  531.       dec(srclevel);
  532.       ltok := name;
  533.       warning('Missing include file');
  534.    end
  535.    else
  536.  
  537.    begin
  538.       if not linestart then putline;
  539.       putln('/* TPTC: include '+name+' */');
  540.  
  541.       if maxavail-300 <= inbufsiz then
  542.       begin
  543.          ltok := name;
  544.          fatal('Out of memory');
  545.       end;
  546.          
  547.       getmem(inbuf[srclevel],inbufsiz);
  548.       SetTextBuf(srcfd[srclevel],inbuf[srclevel]^,inbufsiz);
  549.    end;
  550.    
  551.    if {quietmode and} not debug then
  552.       write(^M,'':40,^M)
  553.    else
  554.       writeln;
  555.    statustime := 0;
  556. end;
  557.  
  558.  
  559. (********************************************************************)
  560. procedure scan_curlycomment;
  561.    {processes a curly-brace enclosed comment}
  562. var
  563.    isinclude: anystring;
  564.    
  565. begin
  566.    toktype := comment;
  567.    getchar;   {consume the open comment}
  568.  
  569.    isinclude := '';
  570.    if nextc = '$' then
  571.       scan_pragma(isinclude);
  572.  
  573.    if nextc = '}' then
  574.    begin
  575.       getchar;
  576.       open_include(isinclude);
  577.       exit;
  578.    end;
  579.  
  580.    if pass_comments then
  581.       puts('  /* ');
  582.  
  583.    while nextc <> '}' do
  584.    begin
  585.       if pass_comments then
  586.          puts(nextc);
  587.       getchar;
  588.    end;
  589.  
  590.    if pass_comments then
  591.    begin
  592.       puts(' */ ');
  593.       if nospace then newline;
  594.    end;
  595.  
  596.    getchar;   {consume the close comment}
  597.    open_include(isinclude);
  598. end;
  599.  
  600.  
  601. (********************************************************************)
  602. procedure scan_parencomment;
  603.    {process a (* enclosed comment}
  604. var
  605.    isinclude: anystring;
  606.    
  607. begin
  608.    toktype := comment;
  609.    getchar;   {consume the *}
  610.  
  611.    isinclude := '';
  612.    if nextc = '$' then
  613.       scan_pragma(isinclude);
  614.  
  615.    if pass_comments then
  616.       puts('/*');
  617.  
  618.    repeat
  619.       if pass_comments then
  620.          puts(nextc);
  621.  
  622.       if nextc = '*' then
  623.       begin
  624.          getchar;
  625.  
  626.          if nextc = ')' then
  627.          begin
  628.             getchar;
  629.             if pass_comments then
  630.             begin
  631.                puts('/ ');
  632.                if nospace then putline;
  633.             end;
  634.             open_include(isinclude);
  635.             exit;
  636.          end;
  637.       end
  638.       else
  639.          getchar;
  640.  
  641.    until true=false;
  642. end;
  643.  
  644.  
  645. (********************************************************************)
  646. procedure scan_blanks;
  647.    {scan white space.  this procedure sometimes passes whitespace to the
  648.     output.  it keeps track of the indentation of the current line so it
  649.     can be used by newline}
  650. var
  651.    indent:        anystring;
  652.    valid:         boolean;
  653.  
  654. begin
  655.    linestart := false;
  656.    indent := '';
  657.    valid := false;
  658.  
  659.    repeat
  660.  
  661.       case nextc of
  662.          ^J,^M:  begin
  663.                     if (nospace = false) and (nextc = ^J) then 
  664.                        putline;
  665.                     
  666.                     indent := '';
  667.                     linestart := true;
  668.                     getchar;
  669.                  end;
  670.  
  671.          ' ',^I,^@,^L:
  672.                  indent := indent + usec;
  673.  
  674.          '#':    if linestart and tshell then
  675.                  begin
  676.                     puts(indent);     {pass preprocessor directives}
  677.                     indent := '';     {without change (single-line only)}
  678.  
  679.                     repeat
  680.                        puts(nextc);
  681.                        getchar;
  682.                     until nextc = ^M;
  683.  
  684.                     getchar;
  685.                     putline;
  686.                  end
  687.                  else
  688.                     valid := true;
  689.  
  690.          else
  691.                  valid := true;
  692.       end;
  693.  
  694.    until valid;
  695.  
  696.    if linestart then
  697.    begin
  698.       spaces := indent;
  699.       if nospace=false then
  700.          puts(spaces);
  701.  
  702.       linestart := true;
  703.    end;
  704. end;
  705.  
  706.  
  707. (********************************************************************)
  708. procedure scan_tok;
  709.    {scans the next lexical token; returns the token in ltok and toktype}
  710. begin
  711.    scan_blanks;
  712.  
  713.    toktype := unknown;
  714.    ltok := nextc;
  715.  
  716.    case nextc of
  717.       'a'..'z', 
  718.       '_', 'A'..'Z': scan_ident;
  719.  
  720.       '$':           scan_number;
  721.       '0'..'9':      scan_number;
  722.  
  723.       '''':          scan_string;
  724.  
  725.       '^':           scan_hat;
  726.  
  727.       '#':           begin
  728.                         scan_number;
  729.                         if toktype = unknown then
  730.                            scan_tok;         {in case of #directive}
  731.                      end;
  732.  
  733.  
  734.       '<':           begin
  735.                         getchar;
  736.                         if (nextc = '>') or (nextc = '=') then
  737.                            ltok := '<' + usec;
  738.                      end;
  739.  
  740.       '>':           begin
  741.                         getchar;
  742.                         if nextc = '=' then
  743.                            ltok := '>' + usec;
  744.                      end;
  745.  
  746.       ':':           begin
  747.                         getchar;
  748.                         if nextc = '=' then
  749.                            ltok := ':' + usec;
  750.                      end;
  751.  
  752.       '.':           scan_dot;
  753.  
  754.       '{':           scan_curlycomment;
  755.  
  756.       '(':           begin
  757.                         getchar;
  758.                         if nextc = '*' then
  759.                            scan_parencomment;
  760.                      end;
  761.  
  762.       else           getchar;   {consume the unknown char}
  763.    end;
  764. end;
  765.  
  766.  
  767. (********************************************************************)
  768. procedure gettok;
  769.    {get the next input token;  this is the top level of the lexical analyzer.
  770.     it returns ltok, tok(ltok in upper case), toktype.  it translates BEGIN
  771.     and END into braces; it checks for statement and section keywords}
  772. var
  773.    i:             integer;
  774.  
  775. begin
  776.    ptoktype := toktype;
  777.    ptok := tok;
  778.    cursym := nil;
  779.    
  780.    repeat
  781.       scan_tok;
  782.    until toktype <> comment;
  783.    tok := ltok;
  784.  
  785.    if debug then write(' {',ltok,'}');
  786.  
  787.    if toktype = identifier then
  788.    begin
  789.       stoupper(tok);
  790.  
  791.       if tok = 'BEGIN' then
  792.       begin
  793.          tok := '{';
  794.          ltok := tok;
  795.          toktype := keyword;
  796.       end
  797.       else
  798.  
  799.       if tok = 'END' then
  800.       begin
  801.          tok := '}';
  802.          ltok := tok;
  803.          toktype := keyword;
  804.       end;
  805.  
  806.       (* check for statement keywords *)
  807.       i := 0;
  808.       repeat
  809.          inc(i);
  810.          if tok[1] = keywords[i][1] then              {hack for speed}
  811.          if length(tok) = length(keywords[i]) then
  812.          if tok = keywords[i] then
  813.             toktype := keyword;
  814.       until (i = nkeywords) or (toktype = keyword);
  815.  
  816.       (* get symbol table information for this item *)
  817.       cursym := locatesym(tok);
  818.    end;
  819. end;
  820.  
  821.  
  822. (********************************************************************)
  823. function usetok: string80;
  824.    {return (use) and consume current token}
  825. var
  826.    tv: string80;
  827. begin
  828.    tv := ltok;
  829.    gettok;
  830.    usetok := tv;
  831. end;
  832.  
  833.  
  834.