001: /*
002: * CppTagger.java
003: *
004: * Copyright (C) 1998-2003 Peter Graves
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: */
020:
021: package org.armedbear.j;
022:
023: import java.util.ArrayList;
024: import java.util.Stack;
025:
026: public final class CppTagger extends JavaTagger implements Constants {
027: // States.
028: private static final int NEUTRAL = 0;
029: private static final int CLASS_NAME = 1;
030: private static final int CLASS_PROLOG = 2;
031: private static final int METHOD_NAME = 3;
032: private static final int METHOD_PROLOG = 4;
033: private static final int INITIALIZATION_LIST = 5;
034:
035: private static final String classSeparator = "::";
036:
037: public CppTagger(SystemBuffer buffer) {
038: super (buffer);
039: }
040:
041: public void run() {
042: ArrayList tags = new ArrayList();
043: String className = null;
044: Stack classNames = new Stack();
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 (className != null)
074: token = className + classSeparator + token;
075: tags.add(new CppTag(token, tokenStart, TAG_METHOD));
076: skipBrace();
077: state = NEUTRAL;
078: continue;
079: }
080: if (c == ':') {
081: if (className != null)
082: token = className + classSeparator + token;
083: tags.add(new CppTag(token, tokenStart, TAG_METHOD));
084: state = INITIALIZATION_LIST;
085: pos.skip(1);
086: continue;
087: }
088: if (pos.lookingAt("throw")) {
089: if (className != null)
090: token = className + classSeparator + token;
091: state = METHOD_PROLOG;
092: pos.skip(5); // Skip over "throw".
093: continue;
094: }
095: if (pos.lookingAt("const")) {
096: if (className != null)
097: token = className + classSeparator + token;
098: state = METHOD_PROLOG;
099: pos.skip(5); // Skip over "const".
100: continue;
101: }
102: state = NEUTRAL; // Fall through...
103: }
104: if (state == INITIALIZATION_LIST) {
105: if (c == '{') {
106: skipBrace();
107: state = NEUTRAL;
108: continue;
109: }
110: pos.next();
111: continue;
112: }
113: if (state == CLASS_PROLOG) {
114: if (c == '{') {
115: if (className != null)
116: classNames.push(className);
117: className = token;
118: // Add a tag for the class itself.
119: tags.add(new CppTag("class " + token, tokenStart,
120: TAG_CLASS));
121: state = NEUTRAL;
122: pos.next();
123: continue;
124: }
125: if (c == ';') {
126: // It was just a declaration.
127: pos.next();
128: state = NEUTRAL;
129: continue;
130: }
131: pos.next();
132: continue;
133: }
134: if (state == METHOD_PROLOG) {
135: if (c == '{') {
136: // Opening brace of method body.
137: tags.add(new CppTag(token, tokenStart, TAG_METHOD));
138: skipBrace();
139: state = NEUTRAL;
140: continue;
141: }
142: if (c == ';') {
143: // It was just a declaration.
144: pos.next();
145: state = NEUTRAL;
146: continue;
147: }
148: pos.next();
149: continue;
150: }
151: if (c == '}') {
152: if (classNames.empty())
153: className = null;
154: else
155: className = (String) classNames.pop();
156: pos.next();
157: continue;
158: }
159: if (isIdentifierStart(c)) {
160: gatherToken();
161: if (state == CLASS_NAME)
162: state = CLASS_PROLOG;
163: else if (token.equals("class"))
164: state = CLASS_NAME;
165: else if (token.equals("operator")
166: || token.endsWith("::operator")) {
167: gatherOperatorName();
168: token = "operator " + token;
169: state = METHOD_NAME;
170: }
171: continue;
172: }
173: if (c == '(') {
174: skipParen();
175: state = METHOD_NAME;
176: continue;
177: }
178: pos.next();
179: }
180: buffer.setTags(tags);
181: }
182:
183: private void gatherToken() {
184: tokenStart = new Position(pos);
185: FastStringBuffer sb = new FastStringBuffer();
186: char c;
187: while (isIdentifierPart(c = pos.getChar())) {
188: sb.append(c);
189: if (!pos.next())
190: break;
191: }
192: // Token can't end with ':'.
193: while (sb.length() > 0 && sb.charAt(sb.length() - 1) == ':')
194: sb.setLength(sb.length() - 1);
195: token = sb.toString();
196: }
197:
198: private void gatherOperatorName() {
199: pos.skipWhitespace();
200: tokenStart = new Position(pos);
201: FastStringBuffer sb = new FastStringBuffer();
202: char c;
203: while ((c = pos.getChar()) != '(') {
204: sb.append(c);
205: if (!pos.next())
206: break;
207: }
208: token = sb.toString();
209: }
210:
211: private static final boolean isIdentifierStart(char c) {
212: if (c >= 'a' && c <= 'z')
213: return true;
214: if (c >= 'A' && c <= 'Z')
215: return true;
216: if (c == '_' || c == ':' || c == '~')
217: return true;
218: return false;
219: }
220:
221: private static final boolean isIdentifierPart(char c) {
222: if (c >= 'a' && c <= 'z')
223: return true;
224: if (c >= 'A' && c <= 'Z')
225: return true;
226: if (c >= '0' && c <= '9')
227: return true;
228: if (c == '_' || c == ':' || c == '~')
229: return true;
230: return false;
231: }
232: }
|