home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / Misc / CLISP-1.LHA / CLISP960530-sr.lha / utils / ansidecl.d < prev    next >
Encoding:
Text File  |  1996-04-15  |  19.9 KB  |  541 lines

  1. # Programm zum Umwandeln von Funktionsdeklarationen vom traditionellen Stil
  2. # in die ANSI-Syntax bei nicht allzu übel geformten C-Programmen
  3. # Bruno Haible 16.2.1993
  4.  
  5. # Ziel:
  6. # 1. Token &! streichen.
  7. # 2. Deklarationen
  8. #           funname(vname1,...,vnamek)
  9. #             sspec1 tspec1 vname1 ;
  10. #             ...;
  11. #             sspeck tspeck vnamek ;
  12. #             {
  13. #    umwandeln in
  14. #           funname ( sspec1 tspec1 vname1 , ..., sspeck tspeck vnamek ) {
  15. #    unter Beibehaltung aller Kommentare, Präprozessor-Kommandos usw.
  16.  
  17. # Methode:
  18. # Mit Kenntnis der Begriffe "Präprozessor-Kommando", "Kommentar", "Token"
  19. # wird nach den öffnenden geschweiften Klammern '{' des äußersten
  20. # Schachtelungslevels gesucht, denen ein Strichpunkt ';' oder
  21. # eine Klammer zu ')' unmittelbar vorangeht.
  22. # Von dort aus wird (unter Mitzählen der Strichpunkte im äußersten
  23. # Schachtelungslevel) die vorige Klammer zu ')' des äußersten Schachtelungs-
  24. # levels gesucht. Die Strichpunkte werden in Kommata bzw. eine Klammer zu
  25. # umgewandelt. Bis zur vorigen Klammer auf '(' des äußersten Schachtelungs-
  26. # levels wird alles durch Leerstellen ersetzt.
  27.  
  28.  
  29. #define MAXARGCOUNT  50  # maximale Anzahl Argumente einer Funktionsdefinition
  30. #define MAXHEADERLEN  5000  # maximale Länge eines Funktionsdefinitions-Kopfes
  31.  
  32. #define local static
  33. #define global
  34. #define var
  35. #define loop  while (1)
  36. #define until(exp)  while (!(exp))
  37. typedef unsigned char  uintB;
  38. typedef unsigned short  uintW;
  39. typedef unsigned long  uintL;
  40. typedef int  boolean;
  41. #define FALSE 0
  42. #define TRUE 1
  43. #define NULL ((void*)0)
  44.  
  45. #if defined(__TURBOC__) || defined(__GO32__) || defined(__WATCOMC__)
  46. #define STDC_HEADERS 1
  47. #else
  48. #if defined(unix) || defined(__unix)
  49. #include "config.h"
  50. #endif
  51. #endif
  52. #ifdef STDC_HEADERS
  53. #include <stdlib.h>
  54. #endif
  55.  
  56. #include <stdio.h>
  57.  
  58. local FILE* infile;
  59. local FILE* outfile;
  60.  
  61. # Input
  62. # =====
  63.  
  64. local uintL input_line;
  65.  
  66. local int in_char (void)
  67.   { var int c = getc(infile);
  68.     if (c=='\n') { input_line++; }
  69.     return c;
  70.   }
  71.  
  72. local int peek_char (void)
  73.   { var int c = getc(infile);
  74.     if (!(c==EOF)) { ungetc(c,infile); }
  75.     return c;
  76.   }
  77.  
  78. local uintL last_good_input_line;
  79.  
  80. # Output
  81. # ======
  82.  
  83. # Output kann immer ein wenig gepuffert werden:
  84. enum out_mode { direct, buffered };
  85. local struct { enum out_mode mode; # Output-Modus
  86.                uintB buffer[MAXHEADERLEN]; # Buffer
  87.                uintL buffindex; # Index in den Buffer
  88.              }
  89.       out;
  90.  
  91. #define char_out(char)  putc(char,outfile)
  92.  
  93. # Output-Bufferung ausschalten:
  94. local void outbuffer_off (void)
  95.   { if (out.mode==buffered)
  96.       { var uintL index = 0;
  97.         while (index < out.buffindex)
  98.           { char_out(out.buffer[index]); index++; }
  99.         out.mode = direct;
  100.   }   }
  101.  
  102. # Output-Bufferung ausschalten und dabei an einer Stelle einen String einfügen:
  103. local void outbuffer_off_insert (uintL insertpoint, char* insert)
  104.   { if (out.mode==buffered)
  105.       { var uintL index = 0;
  106.         loop
  107.           { if (index==insertpoint)
  108.               { while (!(*insert==0)) { char_out(*insert++); } }
  109.             if (index == out.buffindex) break;
  110.             char_out(out.buffer[index]); index++;
  111.           }
  112.         out.mode = direct;
  113.   }   }
  114.  
  115. # Output-Bufferung einschalten:
  116. local void outbuffer_on (void)
  117.   { if (out.mode==direct)
  118.       { out.buffindex = 0;
  119.         out.mode = buffered;
  120.   }   }
  121.  
  122. # Character ausgeben:
  123. local void out_char (int c)
  124.   { if (out.mode==buffered)
  125.       { if (out.buffindex < MAXHEADERLEN)
  126.           { out.buffer[out.buffindex++] = c; }
  127.           else
  128.           # Buffer voll -> Buffer abschalten
  129.           { outbuffer_off(); char_out(c); }
  130.       }
  131.       else
  132.       { char_out(c); }
  133.   }
  134.  
  135. # lexikalische Analyse
  136. # ====================
  137.  
  138. # Holt das nächste Character:
  139. local int next_char (void)
  140.   { var int c = in_char();
  141.     if (!(c==EOF))
  142.       { out_char(c); } # c auch ausgeben
  143.     return c;
  144.   }
  145.  
  146. # Für unsere Zwecke brauchen ++ -> != usw. nicht als eigene Token betrachtet
  147. # zu werden, wir kennen also nur:
  148. #   EOF
  149. #   Identifier
  150. #   Zahl-Konstanten
  151. #   Character-Konstanten
  152. #   String-Konstanten
  153. #   Operator/Separator
  154. # Verallgemeinerte Tokens: Expressions (mit balancierten Klammern)
  155. enum token_type { eof, eol, ident, number, charconst, stringconst, sep, expr };
  156. typedef struct { enum token_type type;
  157.                  # falls Bufferung aktiv:
  158.                  uintL startindex; # Startindex im Buffer
  159.                  uintL endindex; # Endindex im Buffer
  160.                  # bei sep (Operator/Separator):
  161.                  uintB ch;
  162.                }
  163.         token_;
  164. typedef token_* Token;
  165.  
  166. # globale Token-Tabelle (Inhalt nur während einer Bufferungsperiode gültig):
  167. #define MAXTOKENS  20000
  168. local struct { uintL index;
  169.                token_ data[MAXTOKENS];
  170.              }
  171.       tokens;
  172.  
  173. # Holt das nächste Token:
  174. # (Innerhalb von Präprozessor-Direktiven zählt Zeilenende als eigenes Token,
  175. # und '#' leitet keine verschachtelte Präprozessor-Direktive ein.)
  176. local Token nexttoken (boolean within_prep_directive)
  177.   { if (tokens.index == MAXTOKENS)
  178.       # kein Platz mehr in der Token-Tabelle -> nicht mehr buffern
  179.       { outbuffer_off(); tokens.index = 0;
  180.         fprintf(stderr,"Token-Tabelle übergelaufen in Zeile %lu.\nFehler irgendwo nach Zeile %lu.\n",input_line,last_good_input_line);
  181.         exit(1);
  182.       }
  183.     # Nun ist tokens.index < MAXTOKENS .
  184.    {var Token token = &tokens.data[tokens.index]; # Platz fürs nächste Token
  185.     tokens.index++;
  186.     restart:
  187.     token->startindex = out.buffindex;
  188.     if (peek_char() == '&')
  189.       { in_char(); # '&' überlesen
  190.         if (peek_char() == '!')
  191.           # '&!'
  192.           { in_char(); goto restart; } # '!' überlesen
  193.           else
  194.           { out_char('&'); token->type = sep; token->ch = '&'; goto fertig; }
  195.       }
  196.     { var int c = next_char();
  197.       switch (c)
  198.         { case EOF:
  199.             # EOF
  200.             token->type = eof; goto fertig;
  201.           case ' ': case '\v': case '\t':
  202.             # Whitespace. überlesen
  203.             goto restart;
  204.           case '\n':
  205.             # Zeilenende
  206.             if (within_prep_directive)
  207.               { token->type = eol; goto fertig; } # als Token zurück
  208.               else
  209.               { goto restart; } # überlesen
  210.           case '\\':
  211.             if (peek_char()=='\n')
  212.               # Zeilenende nach '\'. überlesen
  213.               { next_char(); goto restart; }
  214.               else
  215.               goto separator;
  216.           case '/':
  217.             if (peek_char() == '*')
  218.               # Kommentar
  219.               { next_char();
  220.                 loop { c = next_char();
  221.                        if (c==EOF) { fprintf(stderr,"Unbeendeter Kommentar\n"); break; }
  222.                        if ((c=='*') && (peek_char()=='/')) { next_char(); break; }
  223.                      }
  224.                 goto restart;
  225.               }
  226.               else
  227.               goto separator;
  228.           case '*':
  229.             if (peek_char() == '/')
  230.               # illegales Kommentar-Ende
  231.               { fprintf(stderr,"Kommentar-Ende außerhalb Kommentar in Zeile %lu\n",input_line); }
  232.             goto separator;
  233.           case '#':
  234.             if (within_prep_directive)
  235.               { goto separator; }
  236.               else
  237.               { # Präprozessor-Anweisung.
  238.                 # Bis Zeilenende oder EOF lesen.
  239.                 loop
  240.                   { var Token subtoken = nexttoken(TRUE);
  241.                     if ((subtoken->type == eof) || (subtoken->type == eol))
  242.                       break;
  243.                   }
  244.                 goto restart; # und überlesen
  245.               }
  246.           case '.':
  247.             c = peek_char();
  248.             if (!(((c>='0') && (c<='9')) || (c=='.'))) goto separator;
  249.           case '0': case '1': case '2': case '3': case '4':
  250.           case '5': case '6': case '7': case '8': case '9':
  251.             # Zahl. Weiterlesen, solange alphanumerisches Zeichen oder '.':
  252.             loop
  253.               { c = peek_char();
  254.                 if (((c>='0') && (c<='9'))
  255.                     || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))
  256.                     || (c=='.')
  257.                    )
  258.                   { next_char(); }
  259.                   else
  260.                   break;
  261.               }
  262.             token->type = number; goto fertig;
  263.           case '\'':
  264.             # Character-Konstante
  265.             loop
  266.               { c = next_char();
  267.                 if (c==EOF) { fprintf(stderr,"Unbeendete Character-Konstante\n"); break; }
  268.                 if (c=='\'') break;
  269.                 if (c=='\\') { c = next_char(); }
  270.               }
  271.             token->type = charconst; goto fertig;
  272.           case '\"':
  273.             # String-Konstante
  274.             loop
  275.               {
  276. #ifndef QUOTE_QUOTES
  277.                 c = next_char();
  278.                 if (c==EOF) { fprintf(stderr,"Unbeendete String-Konstante\n"); break; }
  279. #else # muß Single-Quotes in Strings quotieren:
  280.                 c = in_char();
  281.                 if (c==EOF) { fprintf(stderr,"Unbeendete String-Konstante\n"); break; }
  282.                 if (c=='\'')
  283.                   { # statt "'" ein "\047" ausgeben:
  284.                     out_char('\\');
  285.                     out_char('0'+((((unsigned char)'\'')/64)%8));
  286.                     out_char('0'+((((unsigned char)'\'')/8)%8));
  287.                     out_char('0'+(((unsigned char)'\'')%8));
  288.                     continue;
  289.                   }
  290.                 out_char(c);
  291. #endif
  292.                 if (c=='\"') break;
  293.                 if (c=='\\') { c = next_char(); }
  294.               }
  295.             token->type = stringconst; goto fertig;
  296.           case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  297.           case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
  298.           case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
  299.           case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
  300.           case 'Y': case 'Z':
  301.           case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  302.           case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
  303.           case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
  304.           case 's': case 't': case 'u': case 'v': case 'w': case 'x':
  305.           case 'y': case 'z':
  306.           case '_':
  307.             # Identifier. alles alphanumerische überlesen.
  308.             loop
  309.               { c = peek_char();
  310.                 if (   ((c>='0') && (c<='9'))
  311.                     || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))
  312.                     || (c=='_')
  313.                    )
  314.                   { next_char(); }
  315.                   else
  316.                   break;
  317.               }
  318.             token->type = ident; goto fertig;
  319.           default:
  320.           separator:
  321.             token->type = sep; token->ch = c; goto fertig;
  322.     }   }
  323.     fertig:
  324.     token->endindex = out.buffindex;
  325.     return token;
  326.   }}
  327. #define next_token() nexttoken(FALSE)
  328.  
  329. # Klammern mitzählen:
  330. #define MAXBRACES 1000 # maximale Verschachtelungstiefe von Klammern
  331. local struct { uintL count;
  332.                struct { uintB brace_type; uintL input_line; } opening[MAXBRACES];
  333.              }
  334.       open_braces;
  335.  
  336. # Mitzählen einer öffnenden Klammer:
  337. local void handle_opening_token (Token token)
  338.   { if (open_braces.count < MAXBRACES)
  339.       { open_braces.opening[open_braces.count].brace_type = token->ch;
  340.         open_braces.opening[open_braces.count].input_line = input_line;
  341.       }
  342.     open_braces.count++;
  343.   }
  344.  
  345. # Mitzählen einer schließenden Klammer (ohne Überprüfung der Verschachtelung):
  346. # local void handle_closing_token (Token token)
  347. #   { open_braces.count--; }
  348. # oder als Macro
  349. #define handle_closing_token(token)  { open_braces.count--; }
  350.  
  351. # nächste Expression mit balancierten Klammern '()', '{}', '[]' lesen:
  352. # (Dabei ist auf das Niveau open_braces.count=0 zu kommen,
  353. #  evtl. ist jetzt schon open_braces.count>0.)
  354. local Token next_balanced_token (Token start_token)
  355.   { var uintL open_braces_start = 0; # Ziel-Niveau: 0
  356.     var Token token = (start_token==NULL ? next_token() : start_token);
  357.     var uintL startindex = token->startindex;
  358.     loop
  359.       { # Hier stets  open_braces.count >= open_braces_start .
  360.         switch (token->type)
  361.           { case eof:
  362.               if (open_braces.count > open_braces_start)
  363.                 { if (open_braces.count <= MAXBRACES)
  364.                     fprintf(stderr,"Nicht geschlossene '%c' in Zeile %lu\n",
  365.                                    open_braces.opening[open_braces.count-1].brace_type,
  366.                                    open_braces.opening[open_braces.count-1].input_line
  367.                            );
  368.                     else
  369.                     fprintf(stderr,"Nicht geschlossene '(' oder '{' oder '['\n");
  370.                 }
  371.               return token; # EOF-Token als Ergebnis
  372.             case sep:
  373.               switch (token->ch)
  374.                 { case '(': case '{': case '[':
  375.                     handle_opening_token(token);
  376.                     break;
  377.                   case ')': case '}': case ']':
  378.                     if (open_braces.count > open_braces_start)
  379.                       { open_braces.count--;
  380.                         if (open_braces.count < MAXBRACES)
  381.                           { var uintB opening_ch = open_braces.opening[open_braces.count].brace_type;
  382.                             var uintB closing_ch = token->ch;
  383.                             if (!(   ((opening_ch == '(') && (closing_ch == ')'))
  384.                                   || ((opening_ch == '{') && (closing_ch == '}'))
  385.                                   || ((opening_ch == '[') && (closing_ch == ']'))
  386.                                ) )
  387.                               { fprintf(stderr,"Öffnende Klammer '%c' in Zeile %lu\n und schließende Klammer '%c'\n in Zeile %lu passen nicht zusammen.\n",
  388.                                         opening_ch,open_braces.opening[open_braces.count].input_line,
  389.                                         closing_ch,input_line
  390.                                        );
  391.                           }   }
  392.                       }
  393.                       else
  394.                       { fprintf(stderr,"Nicht geöffnete '%c' in Zeile %lu\n",
  395.                                 token->ch,input_line
  396.                                );
  397.                         goto fertig;
  398.                       }
  399.                     break;
  400.                   default:
  401.                     break;
  402.                 }
  403.             default: ;
  404.               # alles andere ist ausbalanciert
  405.           }
  406.         if (open_braces.count == open_braces_start) break; # fertig ausbalanciert?
  407.         token = next_token(); # nein -> nächstes Token lesen
  408.       }
  409.     fertig:
  410.     token->startindex = startindex;
  411.     return token;
  412.   }
  413.  
  414. # Umwandlung der Funktionsdefinitions-Deklarationen auf dem ganzen File
  415. # vornehmen:
  416. local void convert (void)
  417.   { input_line = 1; last_good_input_line = 1;
  418.     out.mode = direct;
  419.     open_braces.count = 0;
  420.     restart1: # Hier out.mode=direct, neues Token lesen:
  421.     tokens.index = 0; # Token-Tabelle leeren
  422.    {var Token token = next_token(); # nächstes Token lesen
  423.     restart2: # Hier out.mode=direct, neues Token gelesen.
  424.     if ((token->type == sep) && (token->ch == '(')) # Klammer auf?
  425.       # ja -> aufpassen:
  426.       { handle_opening_token(token);
  427.         outbuffer_on(); # Buffer einschalten
  428.         # Es sollten nun k Identifier, getrennt durch k-1 Kommata, kommen:
  429.         token = next_token();
  430.         if ((token->type == sep) && (token->ch == ')')) # Klammer zu?
  431.           # ja -> in eine leere Parameterliste evtl. 'void' einfügen:
  432.           { var uintL insertpoint = token->startindex;
  433.             handle_closing_token(token);
  434.             token = next_token();
  435.             if (!((token->type == sep) && (token->ch == '{'))) # geschweifte Klammer auf?
  436.               { outbuffer_off(); goto restart2; }
  437.             # token = '{'
  438.             # Umwandeln: "void" einfügen.
  439.             outbuffer_off_insert(insertpoint,"void");
  440.             token = next_balanced_token(token); # Funktionsdefinition überlesen
  441.           }
  442.           else
  443.           # nein -> nichtleere Parameterliste verarbeiten:
  444.           { var Token param_names[MAXARGCOUNT];
  445.             var Token param_comma[MAXARGCOUNT];
  446.             var uintL param_count = 0;
  447.             loop
  448.               { if (param_count==MAXARGCOUNT) goto nix; # kein Platz mehr?
  449.                 if (!(token->type == ident)) # Identifier?
  450.                   goto nix;
  451.                 param_names[param_count] = token; # ja -> abspeichern
  452.                 token = next_token();
  453.                 if (!(   ((token->type == sep) && (token->ch == ')')) # Klammer zu?
  454.                       || ((token->type == sep) && (token->ch == ',')) # oder Komma?
  455.                    ) )
  456.                   goto nix;
  457.                 param_comma[param_count] = token; # ja -> abspeichern
  458.                 param_count++;
  459.                 if ((token->type == sep) && (token->ch == ')')) # Klammer zu?
  460.                   break;
  461.                 token = next_token();
  462.               }
  463.             # token = ')'
  464.             handle_closing_token(token);
  465.             # Parameter-Deklarationen verarbeiten:
  466.            {var Token paramdecl_semicolons[MAXARGCOUNT];
  467.             var uintL paramdecl_count = 0;
  468.             loop
  469.               { token = next_token();
  470.                 if ((token->type == sep) && (token->ch == '(')) # Klammer auf?
  471.                   # Entscheidung der Zweideutigkeit (Beispiel:
  472.                   # " macro(macroarg) (x) int x; {los();} "
  473.                   # -> zugunsten der Erkennung von "macro(macroarg)" und nicht
  474.                   # "macro":
  475.                   { outbuffer_off(); goto restart2; }
  476.                 if ((token->type == sep) && (token->ch == '{')) # geschweifte Klammer auf?
  477.                   break;
  478.                 loop
  479.                   { token = next_balanced_token(token);
  480.                     if (token->type==eof) goto nix;
  481.                     if ((token->type == sep) && (token->ch == ';')) # Semikolon?
  482.                       break;
  483.                     # Entscheidung der Zweideutigkeit (Beispiel:
  484.                     # " foo(a,b) int a; bar(x,y) int x; int y; {los();} "
  485.                     # -> zugunsten der Erkennung von "bar" und nicht "foo"):
  486.                     token = next_token();
  487.                     if ((token->type == sep) && (token->ch == '(')) # Klammer auf?
  488.                       { outbuffer_off(); goto restart2; }
  489.                   }
  490.                 if (paramdecl_count==MAXARGCOUNT) goto nix; # kein Platz mehr?
  491.                 paramdecl_semicolons[paramdecl_count] = token;
  492.                 paramdecl_count++;
  493.               }
  494.             # token = '{'
  495.             if ((param_count == paramdecl_count) # gleichviele Variablen wie Deklarationen?
  496.                 && (out.mode==buffered) # und Buffer noch da?
  497.                )
  498.               # ja -> Modifikation durchführen:
  499.               # Alle param_names und param_comma durch Leerstellen und
  500.               # alle paramdecl_semicolons durch Komma bzw. Klammer zu
  501.               # überschreiben:
  502.               { do { param_count--;
  503.                     {var Token name = param_names[param_count];
  504.                      var uintL index = name->startindex;
  505.                      var uintL endindex = name->endindex;
  506.                      until (index==endindex) { out.buffer[index++] = ' '; }
  507.                     }
  508.                     {var Token comma = param_comma[param_count];
  509.                      var uintL index = comma->startindex;
  510.                      var uintL endindex = comma->endindex;
  511.                      until (index==endindex) { out.buffer[index++] = ' '; }
  512.                    }}
  513.                    until (param_count==0);
  514.                 out.buffer[paramdecl_semicolons[--paramdecl_count]->startindex] = ')';
  515.                 until (paramdecl_count==0)
  516.                   { out.buffer[paramdecl_semicolons[--paramdecl_count]->startindex] = ','; }
  517.               }
  518.             outbuffer_off();
  519.             last_good_input_line = input_line;
  520.             token = next_balanced_token(token); # Funktionsdefinition überlesen
  521.           }}
  522.       }
  523.       else
  524.       { nix: # keine umwandelbare Funktionsdefinition
  525.         outbuffer_off();
  526.         token = next_balanced_token(token);
  527.       }
  528.     # Hier ist wieder out.mode=direct.
  529.     if (!(token->type==eof)) goto restart1;
  530.     # Bei EOF am Input: fertig (da outfile ungebuffert).
  531.   }}
  532.  
  533. int main ()
  534.   { infile = stdin;
  535.     outfile = stdout;
  536.     convert();
  537.     if (ferror(stdin) || ferror(stdout)) { exit(1); }
  538.     exit(0);
  539.   }
  540.  
  541.