001: /*
002: ***** BEGIN LICENSE BLOCK *****
003: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
004: *
005: * The contents of this file are subject to the Common Public
006: * License Version 1.0 (the "License"); you may not use this file
007: * except in compliance with the License. You may obtain a copy of
008: * the License at http://www.eclipse.org/legal/cpl-v10.html
009: *
010: * Software distributed under the License is distributed on an "AS
011: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
012: * implied. See the License for the specific language governing
013: * rights and limitations under the License.
014: *
015: * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
016: * Copyright (C) 2004-2007 Thomas E Enebo <enebo@acm.org>
017: *
018: * Alternatively, the contents of this file may be used under the terms of
019: * either of the GNU General Public License Version 2 or later (the "GPL"),
020: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
021: * in which case the provisions of the GPL or the LGPL are applicable instead
022: * of those above. If you wish to allow use of your version of this file only
023: * under the terms of either the GPL or the LGPL, and not to allow others to
024: * use your version of this file under the terms of the CPL, indicate your
025: * decision by deleting the provisions above and replace them with the notice
026: * and other provisions required by the GPL or the LGPL. If you do not delete
027: * the provisions above, a recipient may use your version of this file under
028: * the terms of any one of the CPL, the GPL or the LGPL.
029: ***** END LICENSE BLOCK *****/
030: package org.jruby.lexer.yacc;
031:
032: import org.jruby.ast.StrNode;
033: import org.jruby.parser.Tokens;
034: import org.jruby.util.ByteList;
035:
036: public class HeredocTerm extends StrTerm {
037: private final String eos;
038: private final int func;
039: private final String lastLine;
040:
041: public HeredocTerm(String eos, int func, String lastLine) {
042: this .eos = eos;
043: this .func = func;
044: this .lastLine = lastLine;
045: }
046:
047: public int parseString(RubyYaccLexer lexer, LexerSource src)
048: throws java.io.IOException {
049: boolean indent = (func & RubyYaccLexer.STR_FUNC_INDENT) != 0;
050: ByteList str = new ByteList();
051: // BEGIN NETBEANS MODIFICATIONS
052: if (lexer.getPreserveSpaces()) {
053: boolean done = src.matchString(eos, indent);
054: if (done) {
055: lexer.yaccValue = new StrNode(lexer.getPosition(), str);
056: lexer.setStrTerm(new StringTerm(-1, '\0', '\0'));
057: src.setIsANewLine(true);
058: return Tokens.tSTRING_END;
059: }
060: }
061: // END NETBEANS MODIFICATIONS
062: if (src.peek((char) RubyYaccLexer.EOF)) {
063: throw new SyntaxException(src.getPosition(),
064: "can't find string \"" + eos
065: + "\" anywhere before EOF");
066: }
067: if (src.wasBeginOfLine() && src.matchString(eos, indent)) {
068: // BEGIN NETBEANS MODIFICATIONS
069: if (lastLine != null)
070: // END NETBEANS MODIFICATIONS
071: src.unreadMany(lastLine);
072: lexer.yaccValue = new Token(eos, lexer.getPosition());
073: return Tokens.tSTRING_END;
074: }
075:
076: if ((func & RubyYaccLexer.STR_FUNC_EXPAND) == 0) {
077: /*
078: * if (c == '\n') { support.unread(c); }
079: */
080:
081: // Something missing here...
082: /*
083: * int lastLineLength = here.getLastLineLength();
084: *
085: * if (lastLineLength > 0) { // It looks like I needed to append
086: * last line as well...
087: * support.unreadMany(here.getLastLineLength());
088: * str.append(support.readLine()); str.append("\n"); }
089: */
090:
091: do {
092: str.append(src.readLineBytes());
093: str.append('\n');
094:
095: if (src.peek('\0')) {
096: throw new SyntaxException(src.getPosition(),
097: "can't find string \"" + eos
098: + "\" anywhere before EOF");
099: }
100: } while (!src.matchString(eos, indent));
101: } else {
102: char c = src.read();
103: ByteList buffer = new ByteList();
104: if (c == '#') {
105: switch (c = src.read()) {
106: case '$':
107: case '@':
108: // BEGIN NETBEANS MODIFICATIONS
109: if (processingEmbedded == LOOKING_FOR_EMBEDDED) {
110: processingEmbedded = EMBEDDED_DVAR;
111: }
112: // END NETBEANS MODIFICATIONS
113: src.unread(c);
114: lexer.setValue(new Token("#" + c, lexer
115: .getPosition()));
116: return Tokens.tSTRING_DVAR;
117: case '{':
118: // BEGIN NETBEANS MODIFICATIONS
119: if (processingEmbedded == LOOKING_FOR_EMBEDDED) {
120: processingEmbedded = EMBEDDED_DEXPR;
121: }
122: // END NETBEANS MODIFICATIONS
123: lexer.setValue(new Token("#" + c, lexer
124: .getPosition()));
125: return Tokens.tSTRING_DBEG;
126: }
127: buffer.append('#');
128: }
129:
130: src.unread(c);
131:
132: // MRI has extra pointer which makes our code look a little bit more strange in
133: // comparison
134: do {
135: // BEGIN NETBEANS MODIFICATIONS
136: //if ((c = new StringTerm(func, '\n', '\0').parseStringIntoBuffer(src, buffer)) == RubyYaccLexer.EOF) {
137: StringTerm stringTerm = new StringTerm(func, '\n', '\0');
138: stringTerm.processingEmbedded = processingEmbedded;
139: if ((c = stringTerm.parseStringIntoBuffer(src, buffer)) == RubyYaccLexer.EOF) {
140: // END NETBEANS MODIFICATIONS
141: throw new SyntaxException(src.getPosition(),
142: "can't find string \"" + eos
143: + "\" anywhere before EOF");
144: }
145: // BEGIN NETBEANS MODIFICATIONS
146: // Completed expansion token
147: if (processingEmbedded == EMBEDDED_DVAR
148: || processingEmbedded == EMBEDDED_DEXPR) {
149: processingEmbedded = LOOKING_FOR_EMBEDDED;
150: }
151: // END NETBEANS MODIFICATIONS
152: if (c != '\n') {
153: lexer.yaccValue = new StrNode(lexer.getPosition(),
154: buffer);
155: return Tokens.tSTRING_CONTENT;
156: }
157: buffer.append(src.read());
158: if ((c = src.read()) == RubyYaccLexer.EOF) {
159: throw new SyntaxException(src.getPosition(),
160: "can't find string \"" + eos
161: + "\" anywhere before EOF");
162: }
163: // We need to pushback so when whole match looks it did not
164: // lose a char during last EOF
165: src.unread(c);
166: } while (!src.matchString(eos, indent));
167: str = buffer;
168: }
169:
170: // BEGIN NETBEANS MODIFICATIONS
171: // DVARs last only for a single string token so shut if off here.
172: if (processingEmbedded == EMBEDDED_DVAR) {
173: processingEmbedded = LOOKING_FOR_EMBEDDED;
174: // } else if ((processingEmbedded == EMBEDDED_DEXPR) && (str.length() == 0)) {
175: // // Unbalanced expression - see #96485
176: // processingEmbedded = LOOKING_FOR_EMBEDDED;
177: }
178: // END NETBEANS MODIFICATIONS
179:
180: // BEGIN NETBEANS MODIFICATIONS
181: if (lastLine != null)
182: // END NETBEANS MODIFICATIONS
183: src.unreadMany(lastLine);
184: // BEGIN NETBEANS MODIFICATIONS
185: // When handling heredocs in syntax highlighting mode, process the end marker
186: // separately
187: if (lastLine == null) {
188: src.unreadMany(eos + "\n"); // \r?
189: //done = true;
190: } else {
191: // END NETBEANS MODIFICATIONS
192: lexer.setStrTerm(new StringTerm(-1, '\0', '\0'));
193: // BEGIN NETBEANS MODIFICATIONS
194: }
195: // END NETBEANS MODIFICATIONS
196: lexer.yaccValue = new StrNode(lexer.getPosition(), str);
197: return Tokens.tSTRING_CONTENT;
198: }
199:
200: // BEGIN NETBEANS MODIFICATIONS
201: /**
202: * Report whether this string should be substituting things like \n into newlines.
203: * E.g. are we dealing with a "" string or a '' string (or their alternate representations)
204: */
205: public boolean isSubstituting() {
206: return (func & RubyYaccLexer.STR_FUNC_EXPAND) != 0;
207: }
208:
209: /**
210: * Record any mutable state from this StrTerm such that it can
211: * be set back to this exact state through a call to {@link setMutableState}
212: * later on. Necessary for incremental lexing where we may restart
213: * lexing parts of a string (since they can be split up due to
214: * Ruby embedding like "Evaluated by Ruby: #{foo}".
215: */
216: public Object getMutableState() {
217: return new MutableTermState(processingEmbedded);
218: }
219:
220: /**
221: * Apply the given state object (earlier returned by {@link getMutableState})
222: * to this StringTerm to revert state to the earlier snapshot.
223: */
224: public void setMutableState(Object o) {
225: MutableTermState state = (MutableTermState) o;
226: if (state != null) {
227: this .processingEmbedded = state.processingEmbedded;
228: }
229: }
230:
231: public void splitEmbeddedTokens() {
232: if (processingEmbedded == IGNORE_EMBEDDED) {
233: processingEmbedded = LOOKING_FOR_EMBEDDED;
234: }
235: }
236:
237: private class MutableTermState {
238: private MutableTermState(int embeddedCode) {
239: this .processingEmbedded = embeddedCode;
240: }
241:
242: public boolean equals(Object obj) {
243: if (obj == null)
244: return false;
245: if (getClass() != obj.getClass())
246: return false;
247: final MutableTermState other = (MutableTermState) obj;
248:
249: if (this .processingEmbedded != other.processingEmbedded)
250: return false;
251: return true;
252: }
253:
254: public int hashCode() {
255: int hash = 7;
256:
257: hash = 83 * hash + this .processingEmbedded;
258: return hash;
259: }
260:
261: public String toString() {
262: return "HeredocTermState[" + processingEmbedded + "]";
263: }
264:
265: private int processingEmbedded;
266: }
267:
268: // Equals - primarily for unit testing (incremental lexing tests
269: // where we do full-file-lexing and compare state to incremental lexing)
270: public boolean equals(Object obj) {
271: if (obj == null)
272: return false;
273: if (getClass() != obj.getClass())
274: return false;
275: final HeredocTerm other = (HeredocTerm) obj;
276:
277: if (this .eos != other.eos
278: && (this .eos == null || !this .eos.equals(other.eos)))
279: return false;
280: if (this .func != other.func)
281: return false;
282: if (this .lastLine != other.lastLine
283: && (this .lastLine == null || !this .lastLine
284: .equals(other.lastLine)))
285: return false;
286: return true;
287: }
288:
289: public int hashCode() {
290: int hash = 7;
291:
292: hash = 83 * hash + (this .eos != null ? this .eos.hashCode() : 0);
293: hash = 83 * hash + this .func;
294: hash = 83
295: * hash
296: + (this .lastLine != null ? this .lastLine.hashCode() : 0);
297: return hash;
298: }
299:
300: public String toString() {
301: return "HeredocTerm[" + func + "," + eos + "," + lastLine + ","
302: + processingEmbedded + "]";
303: }
304:
305: // END NETBEANS MODIFICATIONS
306: }
|