001: /*
002: * CTagger.java
003: *
004: * Copyright (C) 1998-2002 Peter Graves
005: * $Id: CTagger.java,v 1.8 2003/12/30 19:27:59 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import gnu.regexp.RE;
025: import gnu.regexp.UncheckedRE;
026: import java.util.ArrayList;
027:
028: public final class CTagger extends JavaTagger {
029: // States.
030: private static final int NEUTRAL = 0;
031: private static final int METHOD_NAME = 1;
032: private static final int PARAMETER_LIST = 2;
033:
034: private static RE lynxArgsMacroRE = new UncheckedRE(
035: "ARGS[0-9][0-9]?");
036:
037: private CMode mode = (CMode) CMode.getMode();
038:
039: public CTagger(SystemBuffer buffer) {
040: super (buffer);
041: }
042:
043: public void run() {
044: ArrayList tags = new ArrayList();
045: pos = new Position(buffer.getFirstLine(), 0);
046: token = null;
047: tokenStart = null;
048: int state = NEUTRAL;
049: while (!pos.atEnd()) {
050: char c = pos.getChar();
051: if (Character.isWhitespace(c)) {
052: pos.skipWhitespace();
053: continue;
054: }
055: if (c == '\'' || c == '"') {
056: pos.skipQuote();
057: continue;
058: }
059: if (pos.lookingAt("/*")) {
060: skipComment(pos);
061: continue;
062: }
063: if (pos.lookingAt("//")) {
064: skipSingleLineComment(pos);
065: continue;
066: }
067: if (c == '#' && pos.getOffset() == 0) {
068: skipPreprocessor(pos);
069: continue;
070: }
071: if (state == METHOD_NAME) {
072: if (c == '{') {
073: if (token != null && !mode.isKeyword(token))
074: tags.add(new CTag(token, tokenStart));
075: skipBrace();
076: state = NEUTRAL;
077: continue;
078: } else if (mode.isIdentifierStart(c)) {
079: state = PARAMETER_LIST;
080: pos.next();
081: continue;
082: } else {
083: state = NEUTRAL;
084: pos.next();
085: continue;
086: }
087: }
088: if (state == PARAMETER_LIST) {
089: if (c == '{') {
090: if (token != null && !mode.isKeyword(token))
091: tags.add(new CTag(token, tokenStart));
092: skipBrace();
093: state = NEUTRAL;
094: continue;
095: } else if (c == '(') {
096: state = NEUTRAL;
097: skipParen();
098: continue;
099: } else {
100: pos.next();
101: continue;
102: }
103: }
104: if (c == '}') {
105: pos.next();
106: continue;
107: }
108: if (mode.isIdentifierStart(c)) {
109: tokenStart = pos.copy();
110: String s = gatherToken(pos);
111: if (s.startsWith("ARGS") && lynxArgsMacroRE.isMatch(s)) {
112: // Lynx "ARGSnn" macro.
113: ;
114: } else if (s.equals("NOARGS")) {
115: // Lynx macro.
116: state = METHOD_NAME;
117: } else if (isDefunStart(s)) {
118: // Emacs macro.
119: while (true) {
120: c = pos.getChar();
121: if (c == '"') {
122: pos.next();
123: break;
124: }
125: if (!pos.next())
126: break;
127: }
128: tokenStart = pos.copy();
129: token = gatherDefunName(pos);
130: tags.add(new CTag(token, tokenStart));
131: while ((c = pos.getChar()) != '{') {
132: if (c == '"' || c == '\'') {
133: pos.skipQuote();
134: continue;
135: }
136: if (c == '/' && pos.lookingAt("/*")) {
137: skipComment(pos);
138: continue;
139: }
140: if (!pos.next())
141: break;
142: }
143: if (c == '{')
144: skipBrace();
145: } else
146: token = s;
147: continue;
148: }
149: if (c == '(') {
150: skipParen();
151: state = METHOD_NAME;
152: continue;
153: }
154: pos.next();
155: }
156: buffer.setTags(tags);
157: }
158:
159: private String gatherToken(Position pos) {
160: FastStringBuffer sb = new FastStringBuffer();
161: char c;
162: while (mode.isIdentifierPart(c = pos.getChar())) {
163: sb.append(c);
164: if (!pos.next())
165: break;
166: }
167: return sb.toString();
168: }
169:
170: private static boolean isDefunStart(String s) {
171: if (s.length() < 5)
172: return false;
173: char c = s.charAt(0);
174: if (c == 'D') {
175: if (s.equals("DEFUN")) // Emacs, rep
176: return true;
177: if (s.equals("DEFUN_INT")) // rep
178: return true;
179: } else if (c == 'S') {
180: if (s.equals("SCM_DEFINE")) // guile
181: return true;
182: }
183: return false;
184: }
185:
186: private static String gatherDefunName(Position pos) {
187: FastStringBuffer sb = new FastStringBuffer();
188: while (true) {
189: char c = pos.getChar();
190: if (c == '"') {
191: pos.next(); // Skip past final quote char.
192: break;
193: }
194: if (c == EOL)
195: break;
196: sb.append(c);
197: if (!pos.next())
198: break;
199: }
200: return sb.toString();
201: }
202: }
|