wxWidgets/wxPython/wxSWIG/SWIG/comment.cxx
Robin Dunn c90f71dd8c Since I have made several changes to SWIG over the years to accomodate
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
2002-04-29 19:56:57 +00:00

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);
}
}
}
}