Article View: gnu.emacs.bug
Article #99883Diffs to make etags work with C++
From: Ray Lischner
Date: Mon, 19 Mar 1990 22:57
Date: Mon, 19 Mar 1990 22:57
428 lines
12457 bytes
12457 bytes
We do a lot of C++ development, so we needed etags to recognize C++
instead of C. I made some changes that seem to work. The tricky
parts were recognizing the C++ keyword "operator", with the operator
token that follows it, e.g., "operator++", "operator->*", and handling
C++ class definitions. Inline member functions declared in the class
definition are not recognized; use separate inline declarations
instead.
Following are the diffs for etags.c that came with GNU Emacs 18.55. I
successfully compiled and ran it on an Apollo DN4000, running
Domain/OS 10.2; since I didn't alter any headers, I see no reason why
it should not work on other systems.
*** etags.c-dist Fri May 12 19:08:05 1989
--- etags.c Mon Mar 19 11:13:12 1990
***************
*** 130,135 ****
--- 130,136 ----
#define intoken(arg) (_itk[arg]) /* T if char can be in token */
#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
#define isgood(arg) (_gd[arg]) /* T if char can be after ')' */
+ #define iscpp(arg) (_cpptk[arg]) /* T if char can be C++ operator */
#define max(I1,I2) (I1 > I2 ? I1 : I2)
***************
*** 154,160 ****
int number; /* tokens found so far on line starting with # (including #) */
logical gotone, /* found a func already on line */
/* boolean "func" (see init) */
! _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
/* typedefs are recognized using a simple finite automata,
* tydef is its state variable.
--- 155,161 ----
int number; /* tokens found so far on line starting with # (including #) */
logical gotone, /* found a func already on line */
/* boolean "func" (see init) */
! _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177],_cpptk[0177];
/* typedefs are recognized using a simple finite automata,
* tydef is its state variable.
***************
*** 178,186 ****
--- 179,190 ----
/* token starting chars */
*intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789",
/* valid in-token chars */
+ *cpptk = "()[]+-=<>,/*&^|~!%",
+ /* additional C++ operator chars */
*notgd = ",;"; /* non-valid after-function chars */
int file_num = 0; /* current file number */
+ int cppflag = 0; /* -+: .c means C++ */
int aflag = 0; /* -a: append to tags */
int tflag = 0; /* -t: create tags for typedefs */
int uflag = 0; /* -u: update tags */
***************
*** 285,290 ****
--- 289,297 ----
eflag = 0;
break;
#endif
+ case '+':
+ cppflag++;
+ break;
case 'a':
aflag++;
break;
***************
*** 460,466 ****
for (i = 0; i < 0177; i++)
{
! _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
_gd[i] = TRUE;
}
for (sp = white; *sp; sp++)
--- 467,473 ----
for (i = 0; i < 0177; i++)
{
! _cpptk[i] = _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
_gd[i] = TRUE;
}
for (sp = white; *sp; sp++)
***************
*** 473,483 ****
--- 480,493 ----
_btk[*sp] = TRUE;
for (sp = notgd; *sp; sp++)
_gd[*sp] = FALSE;
+ for (sp = cpptk; *sp; sp++)
+ _cpptk[*sp] = TRUE;
_wht[0] = _wht['\n'];
_etk[0] = _etk['\n'];
_btk[0] = _btk['\n'];
_itk[0] = _itk['\n'];
_gd[0] = _gd['\n'];
+ _cpptk[0] = _cpptk['\n'];
}
/*
***************
*** 517,524 ****
fclose(inf);
return;
}
! /* .scm or .sm or .scheme implies scheme source code */
if (cp && (!strcmp (cp + 1, "sm")
|| !strcmp (cp + 1, "scm")
|| !strcmp (cp + 1, "scheme")
|| !strcmp (cp + 1, "t")
--- 527,535 ----
fclose(inf);
return;
}
! /* .scm or .sc or .sm or .scheme implies scheme source code */
if (cp && (!strcmp (cp + 1, "sm")
+ || !strcmp (cp + 1, "sc")
|| !strcmp (cp + 1, "scm")
|| !strcmp (cp + 1, "scheme")
|| !strcmp (cp + 1, "t")
***************
*** 533,538 ****
--- 544,562 ----
fclose(inf);
return;
}
+ /* .C, .cc, .cxx, .hxx mean C++; or the user gave us -+ with .c or .h */
+ if (cp && (!strcmp (cp + 1, "C")
+ || !strcmp (cp + 1, "cc")
+ || !strcmp (cp + 1, "cxx")
+ || !strcmp (cp + 1, "hxx")
+ || (cppflag && (!strcmp (cp + 1, "c")
+ || !strcmp (cp + 1, "h")))))
+ {
+ C_entries(1);
+ fclose(inf);
+ return;
+ }
+
/* if not a .c or .h or .y file, try fortran */
if (cp && (cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
&& cp[2] == '\0')
***************
*** 544,550 ****
}
rewind(inf); /* no fortran tags found, try C */
}
! C_entries();
fclose(inf);
}
--- 568,574 ----
}
rewind(inf); /* no fortran tags found, try C */
}
! C_entries(0);
fclose(inf);
}
***************
*** 595,608 ****
np->left = np->right = 0;
if (eflag)
{
! linestart[linelen] = 0;
}
else if (xflag == 0)
{
sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart);
linestart = tem;
}
- np->pat = savestr (linestart);
if (head == NULL)
head = np;
else
--- 619,632 ----
np->left = np->right = 0;
if (eflag)
{
! np->pat = savenstr (linestart, linelen);
}
else if (xflag == 0)
{
sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart);
linestart = tem;
+ np->pat = savestr (linestart);
}
if (head == NULL)
head = np;
else
***************
*** 782,788 ****
number = 0; \
}
! C_entries ()
{
register int c;
register char *token, *tp, *lp;
--- 806,877 ----
number = 0; \
}
! /*
! With C++, the keyword "operator" can be followed by an operator
! token, e.g., operator++, which must be treated as an identifier.
! This is irritating because we need to look for the magic keyword,
! operator, then grab the following token, making sure we don't
! grab too much, e.g., operator()() is valid: we want "operator()".
! There can be white space between the "operator" and the token,
! which we delete by shifting the rest of the line over. This
! makes it easier for the user to find the operator, even if it
! makes type cast operators look funny: "operatorint".
!
! The value of "tokend" is a little odd. It points two past the last
! character of the token, e.g., the line contains "operator+=(int x)",
! then tokstart points to the letter 'o', and tokend points to the
! character '='. On exit, tokend points to the letter 'i' of "int".
! The "tokptr" operator is there just so we can update it. On entry,
! in our example, it pointer to the 'r' in "operator"; on exit, it
! points to the '=' character.
! */
! #define OPERATOR "operator"
! #define OPERLEN (sizeof(OPERATOR)-1)
!
! check_operator(tokptr, tokend, tokstart)
! char** tokptr; /* pointer to last character of the token */
! char** tokend; /* two past the last character of the token */
! char* tokstart; /* first character of the token */
! {
! char* ws;
! char* p;
!
! if (*tokend-tokstart-1 != OPERLEN)
! return;
! else if (strncmp(OPERATOR, tokstart, OPERLEN) != 0)
! return;
!
! /* skip over the white space */
! for (ws = *tokend - 1; iswhite(*ws); ++ws)
! ;
! /* shift the rest of the line to delete the white space */
! for (p = *tokend - 1; *ws; )
! *p++ = *ws++;
!
! p = *tokend - 1;
!
! /* identifiers are different */
! if (begtoken(*p))
! {
! for (p = *tokend - 1; intoken(*p); ++p)
! ;
! }
! else
! {
! for (p = *tokend - 1; iscpp(*p); ++p)
! if (p[1] == '(')
! {
! ++p;
! break;
! }
! }
!
! *tokend = p + 1;
! *tokptr = p - 1;
! }
!
! C_entries (is_cpp)
! int is_cpp; /* T means C++; F means C */
{
register int c;
register char *token, *tp, *lp;
***************
*** 856,861 ****
--- 945,954 ----
lp++;
incomm = TRUE;
}
+ /* C++ comments can start with "//", and go to the end of the line */
+ else if (is_cpp && *lp == '/')
+ /* this is one way to discard the rest of the line */
+ lp += strlen(lp);
continue;
case '#':
if (lp == lb.buffer + 1)
***************
*** 887,893 ****
{
int f;
char *buf = lb.buffer;
! int endpos = lp - lb.buffer;
char *lp1 = lp;
int line = lineno;
long linestart = linecharno;
--- 980,986 ----
{
int f;
char *buf = lb.buffer;
! int endpos;
char *lp1 = lp;
int line = lineno;
long linestart = linecharno;
***************
*** 894,900 ****
#ifdef VMS
long vmslinestart = vmslinecharno;
#endif
! int tem = consider_token (&lp1, token, &f, level);
lp = lp1;
if (tem)
{
--- 987,999 ----
#ifdef VMS
long vmslinestart = vmslinecharno;
#endif
! int tem;
!
! if (is_cpp)
! check_operator(&tp, &lp1, token);
! endpos = lp1 - lb.buffer;
!
! tem = consider_token (&lp1, token, &f, level, is_cpp);
lp = lp1;
if (tem)
{
***************
*** 923,928 ****
--- 1022,1032 ----
}
else if (intoken(c))
tp++;
+ else if (is_cpp && c == ':' && *lp == ':')
+ {
+ ++lp;
+ tp += 2;
+ }
}
else if (begtoken(c))
{
***************
*** 941,949 ****
* It updates the input line * so that the '(' will be
* in it when it returns.
*/
! consider_token (lpp, token, f, level)
char **lpp, *token;
int *f, level;
{
reg char *lp = *lpp;
reg char c;
--- 1045,1054 ----
* It updates the input line * so that the '(' will be
* in it when it returns.
*/
! consider_token (lpp, token, f, level, is_cpp)
char **lpp, *token;
int *f, level;
+ int is_cpp;
{
reg char *lp = *lpp;
reg char c;
***************
*** 986,993 ****
tydef=begin;
goto badone;
}
if (tydef==begin && (istoken(token, "struct", 6) ||
! istoken(token, "union", 5) || istoken(token, "enum", 4)))
{
tydef=tag_ok;
goto badone;
--- 1091,1111 ----
tydef=begin;
goto badone;
}
+ /* in C++, class foo {}; behaves like a typedef in C by defining "foo"
+ as a type name. The same goes for struct, union, and enum.
+ */
+ if (tflag && is_cpp && (istoken(token, "class", 5) ||
+ istoken(token, "struct", 6) ||
+ istoken(token, "union", 5) ||
+ istoken(token, "enum", 4)))
+ {
+ tydef=tag_ok;
+ goto badone;
+ }
+
if (tydef==begin && (istoken(token, "struct", 6) ||
! istoken(token, "union", 5) || istoken(token, "enum", 4)||
! (is_cpp && istoken(token, "class", 5))))
{
tydef=tag_ok;
goto badone;
***************
*** 995,1001 ****
if (tydef==tag_ok)
{
tydef=middle;
! goto badone;
}
if (tydef==begin) /* e.g. typedef ->int<- */
{
--- 1113,1133 ----
if (tydef==tag_ok)
{
tydef=middle;
! if (is_cpp && c == ';')
! {
! /* things like "struct foo;" need to be ignored; this is not a
! definition of struct foo.
! */
! tydef=none;
! }
! else if (is_cpp)
! {
! *f = 0;
! win = 1;
! goto ret;
! }
! else
! goto badone;
}
if (tydef==begin) /* e.g. typedef ->int<- */
{
Technical Details
Message-ID:
<9003192257.AA00312@pdx.MENTOR.COM>Path:
arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!bu.edu!snorkelwacker!tut.cis.ohio-state.edu!GARP.MIT.EDU!ogicse!mntgfx!lischShow Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: fb2f1,db48ac8a61a9b13a,start X-Google-Attributes: gidfb2f1,public X-Google-ArrivalTime: 1990-03-20 02:33:21 PST Path: arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!bu.edu!snorkelwacker!tut.cis.ohio-state.edu!GARP.MIT.EDU!ogicse!mntgfx!lisch From: ogicse!mntgfx!lisch@GARP.MIT.EDU (Ray Lischner) Newsgroups: gnu.emacs.bug Subject: Diffs to make etags work with C++ Message-ID: <9003192257.AA00312@pdx.MENTOR.COM> Date: 19 Mar 90 22:57:10 GMT Sender: daemon@tut.cis.ohio-state.edu Distribution: gnu Organization: GNUs Not Usenet Lines: 428 Posted: Mon Mar 19 23:57:10 1990