001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.lexer;
043:
044: import org.netbeans.cnd.api.lexer.CppStringTokenId;
045: import org.netbeans.api.lexer.Token;
046: import org.netbeans.spi.lexer.Lexer;
047: import org.netbeans.spi.lexer.LexerInput;
048: import org.netbeans.spi.lexer.LexerRestartInfo;
049: import org.netbeans.spi.lexer.TokenFactory;
050:
051: /**
052: * Lexical analyzer for C/C++ string language.
053: * based on JavaStringLexer
054: *
055: * @author Vladimir Voskeresensky
056: * @version 1.00
057: */
058:
059: public class CppStringLexer implements Lexer<CppStringTokenId> {
060:
061: private static final int EOF = LexerInput.EOF;
062:
063: private LexerInput input;
064:
065: private TokenFactory<CppStringTokenId> tokenFactory;
066: private boolean escapedLF = false;
067: private final boolean dblQuoted;
068:
069: public CppStringLexer(LexerRestartInfo<CppStringTokenId> info,
070: boolean doubleQuotedString) {
071: this .input = info.input();
072: this .tokenFactory = info.tokenFactory();
073: this .dblQuoted = doubleQuotedString;
074: assert (info.state() == null); // passed argument always null
075: }
076:
077: public Object state() {
078: return null;
079: }
080:
081: public Token<CppStringTokenId> nextToken() {
082: while (true) {
083: int ch = read();
084: switch (ch) {
085: case EOF:
086: if (input.readLength() > 0) {
087: return token(CppStringTokenId.TEXT);
088: } else {
089: return null;
090: }
091: case '\\': //NOI18N
092: if (input.readLength() > 1) {// already read some text
093: input.backup(1);
094: return tokenFactory.createToken(
095: CppStringTokenId.TEXT, input.readLength());
096: }
097: switch (ch = read()) {
098: case 'b': //NOI18N
099: return token(CppStringTokenId.BACKSPACE);
100: case 'e': //NOI18N
101: return token(CppStringTokenId.ANSI_COLOR);
102: case 'f': //NOI18N
103: return token(CppStringTokenId.FORM_FEED);
104: case 'n': //NOI18N
105: return token(CppStringTokenId.NEWLINE);
106: case 'r': //NOI18N
107: return token(CppStringTokenId.CR);
108: case 't': //NOI18N
109: return token(CppStringTokenId.TAB);
110: case '\'': //NOI18N
111: return token(CppStringTokenId.SINGLE_QUOTE);
112: case '"': //NOI18N
113: return token(CppStringTokenId.DOUBLE_QUOTE);
114: case '\\': //NOI18N
115: return token(CppStringTokenId.BACKSLASH);
116: case 'u': //NOI18N
117: while ('u' == (ch = read())) {
118: }
119: ; //NOI18N
120:
121: for (int i = 0;; i++) {
122: ch = Character.toLowerCase(ch);
123:
124: if ((ch < '0' || ch > '9')
125: && (ch < 'a' || ch > 'f')) { //NOI18N
126: input.backup(1);
127: return token(CppStringTokenId.UNICODE_ESCAPE_INVALID);
128: }
129:
130: if (i == 3) { // four digits checked, valid sequence
131: return token(CppStringTokenId.UNICODE_ESCAPE);
132: }
133:
134: ch = read();
135: }
136:
137: case '0':
138: case '1':
139: case '2':
140: case '3': //NOI18N
141: switch (read()) {
142: case '0':
143: case '1':
144: case '2':
145: case '3': //NOI18N
146: case '4':
147: case '5':
148: case '6':
149: case '7': //NOI18N
150: switch (read()) {
151: case '0':
152: case '1':
153: case '2':
154: case '3': //NOI18N
155: case '4':
156: case '5':
157: case '6':
158: case '7': //NOI18N
159: return token(CppStringTokenId.OCTAL_ESCAPE);
160: }
161: input.backup(1);
162: // return token(CppStringTokenId.OCTAL_ESCAPE_INVALID);
163: return token(CppStringTokenId.OCTAL_ESCAPE);
164: }
165: input.backup(1);
166: // return token(CppStringTokenId.OCTAL_ESCAPE_INVALID);
167: return token(CppStringTokenId.OCTAL_ESCAPE);
168: }
169: input.backup(1);
170: return token(CppStringTokenId.ESCAPE_SEQUENCE_INVALID);
171: } // end of switch (ch)
172: } // end of while(true)
173: }
174:
175: private Token<CppStringTokenId> token(CppStringTokenId id) {
176: escapedLF = false;
177: return tokenFactory.createToken(id);
178: }
179:
180: @SuppressWarnings("fallthrough")
181: protected final int read() {
182: boolean skipEscapedLF = true;
183: int c = input.read();
184: if (skipEscapedLF) { // skip escaped LF
185: int next;
186: while (c == '\\') {
187: switch (next = input.read()) {
188: case '\r':
189: input.consumeNewline();
190: // nobreak
191: case '\n':
192: escapedLF = true;
193: next = input.read();
194: break;
195: default:
196: input.backup(1);
197: assert c == '\\' : "must be backslash " + (char) c;
198: return c; // normal backslash, not escaped LF
199: }
200: c = next;
201: }
202: }
203: return c;
204: }
205:
206: public void release() {
207: }
208:
209: }
|