home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / D / CLISP / CLISPSRC.TAR / clisp-1995-01-01 / utils / mergestrings.d < prev    next >
Encoding:
Text File  |  1994-07-23  |  12.6 KB  |  367 lines

  1. # Programm zum Zusammenfassen von adjazenten Strings in C-Programmen
  2. # Bruno Haible 6.9.1991, 5.4.1993
  3.  
  4. # Ziel:
  5. # Adjazente Strings wie "abc" "def" in "abcdef" umwandeln.
  6. # Evtl. lange Zeilen in kurze Zeilen zerlegen, ohne Tokens zu zerhacken.
  7.  
  8. # Methode:
  9. # Mit Kenntnis der Begriffe "PrΣprozessor-Kommando", "Kommentar", "Token".
  10. # Aufeinanderfolgende String-Tokens werden zusammengemerged.
  11. # Kommentare k÷nnen getrost weggeworfen werden, und auf Strings innerhalb von
  12. # PrΣprozessor-Anweisungen braucht nicht geachtet zu werden, da dieser Pass
  13. # nach dem C-PrΣprozessor stattfindet.
  14.  
  15.  
  16. #define MAXSTRINGLEN  5000 /* maximale LΣnge eines Strings incl. Zwischenraum */
  17.  
  18. #define local static
  19. #define global
  20. #define var
  21. #define loop  while (1)
  22. #define until(exp)  while (!(exp))
  23. #define elif  else if
  24. typedef unsigned char  uintB;
  25. typedef unsigned short  uintW;
  26. typedef unsigned long  uintL;
  27. typedef int  boolean;
  28. #define FALSE 0
  29. #define TRUE 1
  30. #define NULL ((void*)0)
  31.  
  32. #include <stdio.h>
  33.  
  34. local FILE* infile;
  35. local FILE* outfile;
  36.  
  37. # Input
  38. # =====
  39.  
  40. local uintL input_line;
  41.  
  42. local int in_char ()
  43.   { var int c = getc(infile);
  44.     if (c=='\n') { input_line++; }
  45.     return c;
  46.   }
  47.  
  48. local int peek_char ()
  49.   { var int c = getc(infile);
  50.     if (!(c==EOF)) { ungetc(c,infile); }
  51.     return c;
  52.   }
  53.  
  54. # Output
  55. # ======
  56.  
  57. # Output kann immer ein wenig gepuffert werden:
  58. local struct { enum { direct, buffered } mode; # Output-Modus
  59.                uintB buffer[MAXSTRINGLEN]; # Buffer
  60.                uintL buffindex; # Index in den Buffer
  61.              }
  62.       out;
  63.  
  64. #ifdef BREAK_LINES
  65.   local uintL outpos;
  66.   local void char_out (c)
  67.     char c;
  68.     { putc(c,outfile); if (c=='\n') outpos = 0; else outpos++; }
  69. #else
  70.   #define char_out(char)  putc(char,outfile)
  71. #endif
  72.  
  73. # Output-Bufferung ausschalten:
  74. local void outbuffer_off ()
  75.   { if (out.mode==buffered)
  76.       { var uintL index = 0;
  77.         while (index < out.buffindex)
  78.           { char_out(out.buffer[index]); index++; }
  79.         out.mode = direct;
  80.   }   }
  81.  
  82. # Output-Bufferung einschalten:
  83. local void outbuffer_on ()
  84.   { if (out.mode==direct)
  85.       { out.buffindex = 0;
  86.         out.mode = buffered;
  87.   }   }
  88.  
  89. # Character ausgeben:
  90. local void out_char (c)
  91.   var char c;
  92.   { if (out.mode==buffered)
  93.       { if (out.buffindex < MAXSTRINGLEN)
  94.           { out.buffer[out.buffindex++] = c; }
  95.           else
  96.           # Buffer voll -> Buffer abschalten
  97.           { outbuffer_off(); char_out(c); }
  98.       }
  99.       else
  100.       { char_out(c); }
  101.   }
  102.  
  103. # lexikalische Analyse
  104. # ====================
  105.  
  106. # Holt das nΣchste Character:
  107. local int next_char ()
  108.   { var int c = in_char();
  109.     if (!(c==EOF))
  110.       { out_char(c); } # c auch ausgeben
  111.     return c;
  112.   }
  113.  
  114. #ifndef BREAK_LINES
  115. # Fⁿr unsere Zwecke brauchen ++ -> != usw. nicht als eigene Token betrachtet
  116. # zu werden, wir kennen also nur: EOF, String-Konstanten, andere.
  117. #endif
  118. typedef enum { eof, eol, ident, number, charconst, stringconst, sep } Token;
  119. #define MAXTOKENLEN 1000
  120. local char token_buffer[MAXTOKENLEN+1]; # Inhalt des Tokens, falls ident oder number
  121. local int token_length; # dazu die LΣnge
  122. local uintL out_buffindex_stringstart; # out.buffindex beim Stringanfang
  123.  
  124. # Holt das nΣchste Token:
  125. # (Innerhalb von PrΣprozessor-Direktiven zΣhlt Zeilenende als eigenes Token,
  126. # und '#' leitet keine verschachtelte PrΣprozessor-Direktive ein.)
  127. local Token nexttoken (within_prep_directive)
  128.   var boolean within_prep_directive;
  129.   { restart:
  130.     { var int c = next_char();
  131.       switch (c)
  132.         { case EOF:
  133.             # EOF
  134.             return eof;
  135.           case ' ': case '\v': case '\t':
  136.             # Whitespace. ⁿberlesen
  137.             goto restart;
  138.           case '\n':
  139.             # Zeilenende
  140.             if (within_prep_directive)
  141.               { return eol; } # als Token zurⁿck
  142.               else
  143.               { goto restart; } # ⁿberlesen
  144.           case '\\':
  145.             if (peek_char()=='\n')
  146.               # Zeilenende nach '\'. ⁿberlesen
  147.               { next_char(); goto restart; }
  148.               else
  149.               goto separator;
  150.           case '/':
  151.             if (peek_char() == '*')
  152.               # Kommentar
  153.               { next_char();
  154.                 loop { c = next_char();
  155.                        if (c==EOF) { fprintf(stderr,"Unbeendeter Kommentar\n"); break; }
  156.                        if ((c=='*') && (peek_char()=='/')) { next_char(); break; }
  157.                      }
  158.                 goto restart;
  159.               }
  160.               else
  161.               goto separator;
  162.           case '*':
  163.             if (peek_char() == '/')
  164.               # illegales Kommentar-Ende
  165.               { fprintf(stderr,"Kommentar-Ende au▀erhalb Kommentar in Zeile %lu\n",input_line); }
  166.             goto separator;
  167.           case '#':
  168.             if (within_prep_directive)
  169.               { goto separator; }
  170.               else
  171.               { # PrΣprozessor-Anweisung.
  172.                 # Bis Zeilenende oder EOF lesen.
  173.                 # Dabei aber '#line' - Anweisungen verarbeiten.
  174.                 var uintL new_input_line = 0; # vorerst
  175.                 var Token subtoken;
  176.                 subtoken = nexttoken(TRUE);
  177.                 if ((subtoken == eof) || (subtoken == eol)) goto end_directive;
  178.                 if (subtoken == number) goto line;
  179.                 if ((subtoken == ident)
  180.                     && (token_length == 4)
  181.                     && (token_buffer[0] == 'l')
  182.                     && (token_buffer[1] == 'i')
  183.                     && (token_buffer[2] == 'n')
  184.                     && (token_buffer[3] == 'e')
  185.                    )
  186.                   { # '#line'-Anweisung -> erst noch weiterlesen:
  187.                     subtoken = nexttoken(TRUE);
  188.                     if ((subtoken == eof) || (subtoken == eol)) goto end_directive;
  189.                     if (subtoken == number)
  190.                       { line:
  191.                         if (token_length>0)
  192.                           { token_buffer[token_length] = '\0';
  193.                            {var long token_value = atol(token_buffer);
  194.                             if (token_value>0)
  195.                               { new_input_line = token_value; } # Zeilennummer neu setzen
  196.                           }}
  197.                   }   }
  198.                 loop
  199.                   { subtoken = nexttoken(TRUE);
  200.                     if ((subtoken == eof) || (subtoken == eol)) goto end_directive;
  201.                   }
  202.                 end_directive:
  203.                 if (new_input_line>0) { input_line = new_input_line; }
  204.                 goto restart; # und ⁿberlesen
  205.               }
  206.           case '.':
  207.             c = peek_char();
  208.             if (!((c>='0') && (c<='9'))) goto separator;
  209.           case '0': case '1': case '2': case '3': case '4':
  210.           case '5': case '6': case '7': case '8': case '9':
  211.             # Zahl. Weiterlesen, solange alphanumerisches Zeichen oder '.':
  212.             { var char* ptr = &token_buffer[0];
  213.               var uintL len = 0;
  214.               loop
  215.                 { *ptr++ = c; len++;
  216.                   c = peek_char();
  217.                   if (((c>='0') && (c<='9'))
  218.                       || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))
  219.                       || (c=='.')
  220.                      )
  221.                     { next_char(); }
  222.                     else
  223.                     break;
  224.                 }
  225.               token_length = len;
  226.             }
  227.             return number;
  228.           case '\'':
  229.             # Character-Konstante
  230.             loop
  231.               { c = next_char();
  232.                 if (c==EOF) { fprintf(stderr,"Unbeendete Character-Konstante\n"); break; }
  233.                 if (c=='\'') break;
  234.                 if (c=='\\') { c = next_char(); }
  235.               }
  236.             return  charconst;
  237.           case '\"':
  238.             # String-Konstante
  239.             if (within_prep_directive)
  240.               # String-Konstanten in PrΣprozessor-Direktiven unverΣndert lassen
  241.               { loop
  242.                   { c = next_char();
  243.                     if (c==EOF) { fprintf(stderr,"Unbeendete String-Konstante\n"); break; }
  244.                     if (c=='\"') break;
  245.                     if (c=='\\') { c = next_char(); }
  246.               }   }
  247.               else
  248.               # fⁿrs AnhΣngen weiterer Strings vorbereiten
  249.               { out_buffindex_stringstart = out.buffindex;
  250.                 loop
  251.                   { c = peek_char();
  252.                     if (c==EOF) { fprintf(stderr,"Unbeendete String-Konstante\n"); break; }
  253.                     if (c=='\"') break;
  254.                     next_char();
  255.                     if (c=='\\') { c = next_char(); }
  256.                   }
  257.                 outbuffer_on(); # Ab jetzt alles in den Buffer
  258.                 next_char(); # Anfⁿhrungszeichen zu loswerden
  259.               }
  260.             return stringconst;
  261.           case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  262.           case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
  263.           case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
  264.           case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
  265.           case 'Y': case 'Z':
  266.           case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  267.           case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
  268.           case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
  269.           case 's': case 't': case 'u': case 'v': case 'w': case 'x':
  270.           case 'y': case 'z':
  271.           case '_':
  272.             # Identifier. alles alphanumerische ⁿberlesen.
  273.             { var char* ptr = &token_buffer[0];
  274.               var uintL len = 0;
  275.               loop
  276.                 { *ptr++ = c; len++;
  277.                   c = peek_char();
  278.                   if (   ((c>='0') && (c<='9'))
  279.                       || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))
  280.                       || (c=='_')
  281.                      )
  282.                     { next_char(); }
  283.                     else
  284.                     break;
  285.                 }
  286.               token_length = len;
  287.             }
  288.             return ident;
  289.           default:
  290.           separator:
  291.             #ifdef BREAK_LINES
  292.             # Multicharacter-Tokens ->, <<, >>, ++, --, +=, &&, || etc. erkennen:
  293.             loop
  294.               { c = peek_char();
  295.                 if ((c=='<') || (c=='>') || (c=='+') || (c=='-') || (c=='=') || (c=='&') || (c=='|'))
  296.                   { next_char(); }
  297.                   else
  298.                   break;
  299.               }
  300.             #endif
  301.             return sep;
  302.   } }   }
  303. #define next_token() nexttoken(FALSE)
  304.  
  305.  
  306. int main ()
  307.   { infile = stdin;
  308.     outfile = stdout;
  309.     input_line = 1; out.mode = direct;
  310.     #ifdef BREAK_LINES
  311.     outpos = 0;
  312.     #endif
  313.     loop
  314.       { # Hier ist out.mode = direct.
  315.         var Token token = next_token();
  316.         if (token==stringconst)
  317.           # weitere Strings zu lesen versuchen:
  318.           { var boolean something_merged = FALSE;
  319.             loop
  320.               { # Hier ist out.mode = buffered und out.buffindex = 1.
  321.                 token = next_token();
  322.                 if (!(token==stringconst)) break;
  323.                 if (out.mode==direct) break; # Buffer ⁿbergelaufen?
  324.                 # out.buffer enthΣlt alles vom Anfⁿhrungszeichen zu des 1. Strings
  325.                 # bis zum Anfⁿhrungszeichen zu des 2. Strings.
  326.                 # Von 0 (= Index des Anfⁿhrungszeichen zu des 1. Strings) bis
  327.                 # out_buffindex_stringstart-1 (= Index des Anfⁿhrungszeichen auf
  328.                 # des 2. Strings) (incl.) wird der Bufferinhalt vergessen, von
  329.                 # out_buffindex_stringstart bis out.buffindex-2 (incl.) wird der
  330.                 # Inhalt sofort ausgegeben, und das Anfⁿhrungszeichen zu des 2.
  331.                 # Strings wandert von Position buffindex-1 nach Position 0.
  332.                 { var uintL i;
  333.                   for (i=out_buffindex_stringstart; i<out.buffindex-1; i++)
  334.                     { char_out(out.buffer[i]); }
  335.                 }
  336.                 out.buffindex = 1; something_merged = TRUE;
  337.               }
  338.             outbuffer_off();
  339.             if (something_merged)
  340.               # neue Zeilennummer ausgeben:
  341.               #if 0 # so ist ANSI-Standard:
  342.               fprintf(outfile,"\n%s %lu\n","#line",input_line);
  343.               #else # so gefΣllt es 'cc' besser:
  344.               fprintf(outfile,"\n%s %lu\n","#",input_line);
  345.               #endif
  346.           }
  347.           else
  348.           { outbuffer_off();
  349.             if (token==eof) break;
  350.           }
  351.         #ifdef BREAK_LINES
  352.         if (outpos >= BREAK_LINES)
  353.           { # in die neue Zeile gehen und dabei dieselbe Zeilennummer ausgeben:
  354.             #if 0 # so ist ANSI-Standard:
  355.             fprintf(outfile,"\n%s %lu\n ","#line",input_line);
  356.             #else # so gefΣllt es 'cc' besser:
  357.             fprintf(outfile,"\n%s %lu\n ","#",input_line);
  358.             #endif
  359.             outpos = 1; # die neue Zeile fΣngt mit einer Leerstelle an
  360.           }
  361.         #endif
  362.       }
  363.     if (ferror(stdin) || ferror(stdout)) { exit(1); }
  364.     exit(0);
  365.   }
  366.  
  367.