c90f71dd8c
special cases and other things in wxPython, and since I plan on making several more, I've decided to put the SWIG sources in wxPython's CVS instead of relying on maintaining patches. This effectivly becomes a fork of an obsolete version of SWIG, :-( but since SWIG 1.3 still doesn't have some things I rely on in 1.1, not to mention that my custom patches would all have to be redone, I felt that this is the easier road to take. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15307 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
698 lines
20 KiB
C++
698 lines
20 KiB
C++
/*******************************************************************************
|
|
* Simplified Wrapper and Interface Generator (SWIG)
|
|
*
|
|
* Author : David Beazley
|
|
*
|
|
* Department of Computer Science
|
|
* University of Chicago
|
|
* 1100 E 58th Street
|
|
* Chicago, IL 60637
|
|
* beazley@cs.uchicago.edu
|
|
*
|
|
* Please read the file LICENSE for the copyright and terms by which SWIG
|
|
* can be used and distributed.
|
|
*******************************************************************************/
|
|
|
|
#include "internal.h"
|
|
|
|
/*******************************************************************************
|
|
* $Header$
|
|
*
|
|
* File : comment.cxx
|
|
*
|
|
* This is a semi-magical module for associating C/C++ comments with
|
|
* documentation entries. While this sounds like it might be easy,
|
|
* there are a number of subtle problems getting things to associate
|
|
* correctly.
|
|
*
|
|
* Here's the general idea :
|
|
*
|
|
* 1. The parser and scanner feed both C comments and documentation
|
|
* entries to this class. These may show up in really bizarre
|
|
* orders (not necessarily the order seen in an interface file).
|
|
*
|
|
* 2. We maintain separate lists of comments and documentation
|
|
* entries.
|
|
*
|
|
* 3. Periodically, we go through the list of documentation entries
|
|
* and see if we can associate any comments.
|
|
*
|
|
* 4. Upon completion of parsing, it's critical that we cleanup
|
|
* the lists using the cleanup() method.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// struct Comment
|
|
//
|
|
// Structure used to maintain a linked list of comments for later use.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
class Comment {
|
|
public:
|
|
String *text; // Text of the comment
|
|
int first_line; // First line of the comment
|
|
int last_line; // Last line of the comment
|
|
int column; // First column of comment
|
|
char *file; // Name of the file that it was in
|
|
Comment *next; // Next comment (when in a linked list)
|
|
Comment *prev; // Previous comment
|
|
static Comment *comment_list; // List of all comments
|
|
|
|
Comment(char *t, int line, int col, char *f);
|
|
~Comment();
|
|
static Comment *find(DocEntry *de, CommentHandler *ch);
|
|
void attach(DocEntry *de, CommentHandler *ch);
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Create a new comment. Automatically puts it on the linked list
|
|
// -----------------------------------------------------------------------
|
|
Comment::Comment(char *t, int line, int col, char *f) {
|
|
int nlines = 0;
|
|
char *c;
|
|
|
|
text = new String(t);
|
|
c = t;
|
|
while (*c) {
|
|
if (*c == '\n') nlines++;
|
|
c++;
|
|
}
|
|
first_line = line;
|
|
column = col;
|
|
last_line = line + nlines - 1;
|
|
file = copy_string(f);
|
|
if (comment_list) {
|
|
comment_list->prev = this;
|
|
}
|
|
next = comment_list;
|
|
comment_list = this;
|
|
prev = 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Destroy a comment
|
|
// -----------------------------------------------------------------------
|
|
Comment::~Comment() {
|
|
delete text;
|
|
delete file;
|
|
// Remove from linked list (if applicable)
|
|
if (prev) {
|
|
prev->next = next;
|
|
}
|
|
if (next) {
|
|
next->prev = prev;
|
|
}
|
|
if (this == comment_list) comment_list = next;
|
|
}
|
|
// -----------------------------------------------------------------------
|
|
// find(DocEntry *de, CommentHandler *ch)
|
|
//
|
|
// This function tries to a find a comment matching the search criteria
|
|
// of a given comment handler and documentation entry.
|
|
// -----------------------------------------------------------------------
|
|
|
|
Comment *Comment::find(DocEntry *de, CommentHandler *ch) {
|
|
Comment *c;
|
|
|
|
c = comment_list;
|
|
|
|
// Start walking down our list of stored comments
|
|
|
|
while (c) {
|
|
// printf("Searching %x : %s\n", c, c->text->get());
|
|
if (strcmp(de->file,c->file) == 0) {
|
|
|
|
// At least comment is in the right file. Now check line numbers
|
|
|
|
if (ch->location == BEFORE) {
|
|
|
|
// Check to see if the last line of the comment is close
|
|
// enough to our declaration.
|
|
|
|
if ((c->last_line <= de->line_number) &&
|
|
((de->line_number - c->last_line) <= ch->skip_lines)) {
|
|
return c;
|
|
}
|
|
} else { // AFTER mode
|
|
// Check to see if the first line of the comment is close
|
|
// enough to our declaration.
|
|
|
|
if ((c->first_line >= de->end_line) &&
|
|
((c->first_line - de->end_line) <= ch->skip_lines)) {
|
|
return c;
|
|
}
|
|
}
|
|
// Check to see if the line numbers are too small. Comments
|
|
// are processed in order so there's no sense in checking
|
|
// all entries.
|
|
|
|
if (c->last_line < de->line_number)
|
|
return 0;
|
|
|
|
}
|
|
c = c->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// void attach(DocEntry *de, CommentHandler *ch)
|
|
//
|
|
// This function attachs a comment to a documentation entry and applies
|
|
// all of the style information in the comment handler.
|
|
// -----------------------------------------------------------------------
|
|
void Comment::attach(DocEntry *de, CommentHandler *ch) {
|
|
int nlines = 0;
|
|
char **split = 0;
|
|
char *c;
|
|
int i,lnum,el;
|
|
if (!de) return;
|
|
|
|
// If we're ignoring comments, forget it
|
|
if (ch->ignore) {
|
|
return;
|
|
}
|
|
|
|
// If the comment is formatted, no style processing is applied
|
|
|
|
if (de->format) {
|
|
de->text << *text;
|
|
return;
|
|
}
|
|
|
|
// Untabify the comment
|
|
|
|
if (ch->untabify) text->untabify();
|
|
|
|
// Count how many lines we have
|
|
|
|
c = text->get();
|
|
while (*c) {
|
|
if (*c == '\n') nlines++;
|
|
c++;
|
|
}
|
|
|
|
if (nlines == 0) return;
|
|
|
|
// Tokenize the documentation string into lines
|
|
|
|
split = new char*[nlines+1];
|
|
c = text->get();
|
|
i = 0;
|
|
split[i] = c;
|
|
while (*c) {
|
|
if (*c == '\n') {
|
|
*(c++) = 0;
|
|
split[++i] = c;
|
|
} else c++;
|
|
}
|
|
lnum = 0;
|
|
|
|
// Now process the chop_top and chop_bottom values
|
|
// if nlines < (chop_top + chop_bottom), then we do nothing
|
|
|
|
if (nlines > (ch->chop_top + ch->chop_bottom)) {
|
|
lnum += ch->chop_top;
|
|
el = nlines-ch->chop_bottom;
|
|
} else {
|
|
el = nlines;
|
|
}
|
|
|
|
// Now process in-between lines
|
|
|
|
while (lnum < el) {
|
|
/* Chop line */
|
|
if (split[lnum]) {
|
|
if (strlen(split[lnum]) > (unsigned) (ch->chop_left+ch->chop_right)) {
|
|
if (ch->chop_right > 0)
|
|
split[lnum][strlen(split[lnum]) - ch->chop_right] = 0;
|
|
de->text << &split[lnum][ch->chop_left];
|
|
}
|
|
}
|
|
lnum++;
|
|
de->text << "\n";
|
|
}
|
|
|
|
// printf("*** ATTACHING %s : %s\n", de->usage.get(), de->text.get());
|
|
delete split;
|
|
}
|
|
|
|
|
|
CommentHandler *comment_handler = 0;
|
|
Comment *Comment::comment_list = 0;
|
|
|
|
// ------------------------------------------------------------------------
|
|
// struct DocEntryList
|
|
//
|
|
// This structure manages a linked list of documentation entries that
|
|
// haven't had comments attached to them yet.
|
|
//
|
|
// As a general rule, this list tends to remain rather short.
|
|
// ------------------------------------------------------------------------
|
|
|
|
struct DocEntryList {
|
|
DocEntry *de;
|
|
CommentHandler *ch;
|
|
DocEntryList *next;
|
|
DocEntryList *prev;
|
|
static DocEntryList *doc_list;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Create a new list entry
|
|
// -----------------------------------------------------------------------
|
|
DocEntryList(DocEntry *d, CommentHandler *c) {
|
|
|
|
de = d;
|
|
ch = c;
|
|
next = doc_list;
|
|
prev = 0;
|
|
if (doc_list)
|
|
doc_list->prev = this;
|
|
doc_list = this;
|
|
|
|
// Only allow a few doc entries to survive
|
|
|
|
if (this->next) {
|
|
if (this->next->next) {
|
|
delete this->next->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Destroy a list entry
|
|
// -----------------------------------------------------------------------
|
|
~DocEntryList() {
|
|
if (prev) {
|
|
prev->next = next;
|
|
}
|
|
if (next) {
|
|
next->prev = prev;
|
|
}
|
|
if (this == doc_list) doc_list = next;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// static check()
|
|
//
|
|
// Checks the list of documentation entries to see if any can be associated.
|
|
// -----------------------------------------------------------------------
|
|
|
|
static void check() {
|
|
|
|
DocEntryList *dl, *dl_temp;
|
|
Comment *cmt;
|
|
|
|
// printf ("Checking\n");
|
|
dl = doc_list;
|
|
while (dl) {
|
|
cmt = Comment::find(dl->de,dl->ch);
|
|
if (cmt) {
|
|
// Okay, we found a matching comment. Attach it to this
|
|
// documentation entry.
|
|
cmt->attach(dl->de,dl->ch);
|
|
|
|
// Destroy the comment and doc list entry
|
|
delete cmt;
|
|
|
|
// Declarations are always coming in order so we're going
|
|
// to blow away all of them past this point
|
|
|
|
dl_temp = dl->next;
|
|
delete dl;
|
|
dl = dl_temp;
|
|
} else {
|
|
dl = dl->next;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
DocEntryList *DocEntryList::doc_list = 0;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// CommentHandler::CommentHandler()
|
|
//
|
|
// Constructor. Creates a new comment handler. Sets up some default values
|
|
// for comment handling.
|
|
//
|
|
// Inputs : None
|
|
//
|
|
// Output : New CommentHandler object.
|
|
//
|
|
// Side Effects : Sets default comment handling parameters.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
CommentHandler::CommentHandler() {
|
|
skip_lines = 1;
|
|
location = AFTER;
|
|
chop_top = 0;
|
|
chop_bottom = 0;
|
|
chop_left = 3;
|
|
chop_right = 0;
|
|
untabify = 1;
|
|
ignore = 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// CommentHandler::CommentHandler(CommentHandler *c)
|
|
//
|
|
// Constructor. Creates a new comment handler, but copies attributes from
|
|
// another handler.
|
|
//
|
|
// Inputs :
|
|
// c = A different comment handler.
|
|
//
|
|
// Output : A new CommentHandler object.
|
|
//
|
|
// Side Effects : None
|
|
// -----------------------------------------------------------------------------
|
|
|
|
CommentHandler::CommentHandler(CommentHandler *c) {
|
|
skip_lines = c->skip_lines;
|
|
location = c->location;
|
|
chop_top = c->chop_top;
|
|
chop_bottom = c->chop_bottom;
|
|
chop_left = c->chop_left;
|
|
chop_right = c->chop_right;
|
|
untabify = c->untabify;
|
|
ignore = c->ignore;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// CommentHandler::~CommentHandler()
|
|
//
|
|
// Destructor. Destroys a comment handler. Does nothing interesting at the
|
|
// moment.
|
|
//
|
|
// Inputs : None
|
|
//
|
|
// Output : None
|
|
//
|
|
// Side Effects : None
|
|
// -----------------------------------------------------------------------------
|
|
|
|
CommentHandler::~CommentHandler() {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// void CommentHandler::add_comment(char *text, int line_num, int col, char *file)
|
|
//
|
|
// This function takes a character string as comment text and appends
|
|
// it to the current comment string (which is held in Comment::comment_list)
|
|
//
|
|
// 1. If two comments appear in successive lines, they are
|
|
// concatenated. This is to handle C++ style comments like the
|
|
// one surrounding this text.
|
|
//
|
|
// 2. If a new comment appears, we simply create a new one
|
|
//
|
|
// Inputs :
|
|
// text = Text of the comment
|
|
// line_num = Starting line number of the comment
|
|
// col = Starting column of the comment
|
|
// file = File in which the comment was located.
|
|
//
|
|
// Output : None
|
|
//
|
|
// Side Effects :
|
|
// Saves the comment in an internal linked list.
|
|
// If multiple comments appear in succession, some may end up
|
|
// in our comment list permanently (ie. never attached to any
|
|
// particular declaration).
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CommentHandler::add_comment(char *text, int line_num, int col, char *file) {
|
|
|
|
char *c;
|
|
int nlines = 0;
|
|
Comment *cmt;
|
|
|
|
// printf("line_num = %d, %s\n", line_num,text);
|
|
|
|
// Count up how many lines are in this comment
|
|
|
|
c = text;
|
|
while (*c) {
|
|
if (*c == '\n') nlines++;
|
|
c++;
|
|
}
|
|
|
|
// Check to see if this comment is in a successive line to the last one
|
|
|
|
cmt = Comment::comment_list;
|
|
|
|
if (cmt) {
|
|
|
|
// Check for column alignment
|
|
if ((cmt->column == col) && (line_num == (cmt->last_line + 1)) &&
|
|
(nlines <= 1)) {
|
|
*(cmt->text) << text;
|
|
cmt->last_line = line_num + nlines - 1;
|
|
} else {
|
|
// This is a new comment, add it to our list
|
|
cmt = new Comment(text,line_num,col,file);
|
|
}
|
|
} else {
|
|
cmt = new Comment(text,line_num,col,file);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// void CommentHanlder::set_entry(DocEntry *d)
|
|
//
|
|
// This grabs a DocEntry and hangs onto it.
|
|
//
|
|
// We will place the doc entry into our documentation list and then
|
|
// check it to see if any comments are sitting around.
|
|
//
|
|
// Inputs : d = Documentation Entry
|
|
//
|
|
// Output : None
|
|
//
|
|
// Side Effects :
|
|
// May attach comments to the documentation entry. In this case,
|
|
// comments and DocEntries may be removed from internal lists.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CommentHandler::set_entry(DocEntry *d) {
|
|
|
|
// printf("Set entry : file: %s, line %d, %s\n", d->file, d->line_number, d->usage.get());
|
|
|
|
// Create a new list entry and save it
|
|
|
|
new DocEntryList(d,this);
|
|
|
|
// Check all of the documentation entries to see if they can be placed
|
|
|
|
DocEntryList::check();
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// static void CommentHandler::cleanup()
|
|
//
|
|
// Checks all documentation entries and sees if there are any comments available.
|
|
// If so, they are attached. This function is usually only called upon completion
|
|
// of parsing.
|
|
//
|
|
// Inputs : None
|
|
//
|
|
// Output : None
|
|
//
|
|
// Side Effects :
|
|
// Removes documentation entries and comments from internal lists.
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CommentHandler::cleanup() {
|
|
int nc, nd;
|
|
Comment *c;
|
|
DocEntryList *d;
|
|
|
|
DocEntryList::check();
|
|
|
|
// Figure out how bad we're doing on memory
|
|
|
|
nc = 0;
|
|
nd = 0;
|
|
c = Comment::comment_list;
|
|
while (c) {
|
|
nc++;
|
|
c = c->next;
|
|
}
|
|
|
|
d = DocEntryList::doc_list;
|
|
while(d) {
|
|
nd++;
|
|
d = d->next;
|
|
}
|
|
|
|
if (Verbose) {
|
|
printf("%d unprocessed comments, %d unprocessed doc entries.\n",nc,nd);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// void CommentHandler::style(char *name, char *value)
|
|
//
|
|
// Processes comment handling style parameters. The following parameters
|
|
// are available :
|
|
//
|
|
// after - Comments appear after a declaration
|
|
// before - Comments appear before a declaration
|
|
// skip - Number of blank lines between comment and decl.
|
|
// chop_top - Number of lines to chop from top of a comment
|
|
// chop_bottom - Number of lines to chop from bottom of a comment
|
|
// chop_left - Number of characters to chop from left
|
|
// chop_right - Number of characters to chop from right
|
|
// tabify - Leave tabs in comment text
|
|
// untabify - Strip tabs and convert them into spaces.
|
|
// ignore - Ignore comments
|
|
// enable - Enable comments
|
|
//
|
|
// Inputs :
|
|
// name - Name of style parameter
|
|
// value - Optional parameter value
|
|
//
|
|
// Output : None
|
|
//
|
|
// Side Effects : Changes style of comment handler object.
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void CommentHandler::style(char *name, char *value) {
|
|
|
|
if (strcmp(name,"before") == 0) {
|
|
location = BEFORE;
|
|
} else if (strcmp(name,"after") == 0) {
|
|
location = AFTER;
|
|
} else if (strcmp(name,"skip") == 0) {
|
|
if (value)
|
|
skip_lines = atoi(value);
|
|
} else if (strcmp(name,"chop_top") == 0) {
|
|
if (value)
|
|
chop_top = atoi(value);
|
|
} else if (strcmp(name,"chop_bottom") == 0) {
|
|
if (value)
|
|
chop_bottom = atoi(value);
|
|
} else if (strcmp(name,"chop_left") == 0) {
|
|
if (value)
|
|
chop_left = atoi(value);
|
|
} else if (strcmp(name,"chop_right") == 0) {
|
|
if (value)
|
|
chop_right = atoi(value);
|
|
} else if (strcmp(name,"tabify") == 0) {
|
|
untabify = 0;
|
|
} else if (strcmp(name,"untabify") == 0) {
|
|
untabify = 1;
|
|
} else if (strcmp(name,"ignore") == 0) {
|
|
ignore = 1;
|
|
} else if (strcmp(name,"enable") == 0) {
|
|
ignore = 0;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// void CommentHandler::parse_args(int argc, char **argv)
|
|
//
|
|
// Function for processing command line options given on the SWIG command line.
|
|
// See the help string below for available options.
|
|
//
|
|
// Inputs :
|
|
// argc = Argument count
|
|
// argv = Argument strings
|
|
//
|
|
// Output : None
|
|
//
|
|
// Side Effects :
|
|
// Changes various style parameters for the top-level CommentHandler.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static char *comment_usage = "\
|
|
Comment Style Options : \n\
|
|
-Safter - Use comments after a declaration.\n\
|
|
-Sbefore - Use comments before a declaration.\n\
|
|
-Schop_bottom n - Chop n lines from bottom of comments.\n\
|
|
-Schop_left n - Chop n characters from left of a comment.\n\
|
|
-Schop_right n - Chop n characters from right of a comment.\n\
|
|
-Schop_top n - Chop n lines from top of comments.\n\
|
|
-Signore - Ignore comments.\n\
|
|
-Sskip n - Max lines between comment and declaration.\n\
|
|
-Stabify - Do not convert tabs.\n\
|
|
-Suntabify - Convert tabs into spaces (the default).\n\n";
|
|
|
|
void CommentHandler::parse_args(int argc, char **argv) {
|
|
int i;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (argv[i]) {
|
|
if (strcmp(argv[i],"-Sbefore") == 0) {
|
|
this->style("before",0);
|
|
mark_arg(i);
|
|
} else if (strcmp(argv[i],"-Safter") == 0) {
|
|
this->style("after",0);
|
|
mark_arg(i);
|
|
} else if (strcmp(argv[i],"-Schop_top") == 0) {
|
|
if (argv[i+1]) {
|
|
this->style("chop_top",argv[i+1]);
|
|
mark_arg(i);
|
|
mark_arg(i+1);
|
|
i++;
|
|
} else {
|
|
arg_error();
|
|
}
|
|
} else if (strcmp(argv[i],"-Schop_bottom") == 0) {
|
|
if (argv[i+1]) {
|
|
this->style("chop_bottom",argv[i+1]);
|
|
mark_arg(i);
|
|
mark_arg(i+1);
|
|
i++;
|
|
} else {
|
|
arg_error();
|
|
}
|
|
} else if (strcmp(argv[i],"-Schop_left") == 0) {
|
|
if (argv[i+1]) {
|
|
this->style("chop_left",argv[i+1]);
|
|
mark_arg(i);
|
|
mark_arg(i+1);
|
|
i++;
|
|
} else {
|
|
arg_error();
|
|
}
|
|
} else if (strcmp(argv[i],"-Schop_right") == 0) {
|
|
if (argv[i+1]) {
|
|
this->style("chop_right",argv[i+1]);
|
|
mark_arg(i);
|
|
mark_arg(i+1);
|
|
i++;
|
|
} else {
|
|
arg_error();
|
|
}
|
|
} else if (strcmp(argv[i],"-Sskip") == 0) {
|
|
if (argv[i+1]) {
|
|
this->style("skip",argv[i+1]);
|
|
mark_arg(i);
|
|
mark_arg(i+1);
|
|
i++;
|
|
} else {
|
|
arg_error();
|
|
}
|
|
} else if (strcmp(argv[i],"-Suntabify") == 0) {
|
|
this->style("untabify",0);
|
|
mark_arg(i);
|
|
} else if (strcmp(argv[i],"-Stabify") == 0) {
|
|
this->style("tabify",0);
|
|
mark_arg(i);
|
|
} else if (strcmp(argv[i],"-Signore") == 0) {
|
|
this->style("ignore",0);
|
|
} else if (strcmp(argv[i],"-help") == 0) {
|
|
fputs(comment_usage,stderr);
|
|
}
|
|
}
|
|
}
|
|
}
|