001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2004 Robert Grimm
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package xtc.parser;
020:
021: import java.io.IOException;
022: import java.io.Reader;
023:
024: import xtc.tree.Location;
025:
026: /**
027: * The superclass of all packrat parsers. Note that packrat parsers
028: * also are {@link Result results}. However, this is purely a
029: * performance optimization and a packrat parser may only be treated
030: * as a result if it is returned from the {@link #character()} method.
031: *
032: * @author Robert Grimm
033: * @version $Revision: 1.1 $
034: */
035: public abstract class PackratParser extends Result {
036:
037: /**
038: * The dummy packrat parser. The dummy packrat parser must
039: * <i>not</i> be used for parsing anything. Its sole purpose is to
040: * enable the creation of a {@link ParseError#DUMMY dummy parse
041: * error}.
042: */
043: public static final PackratParser DUMMY = new PackratParser() {
044: public PackratParser next() {
045: throw new IllegalStateException("Dummy packrat parser");
046: }
047: };
048:
049: /** The reader for the character stream to be parsed. */
050: protected final Reader yyReader;
051:
052: /** The current index into the character stream. */
053: protected final int yyCount;
054:
055: /**
056: * The file name. This field is not final as to allow parsers, such
057: * as the C preprocessor, to adjust the file name while parsing.
058: */
059: protected String yyFile;
060:
061: /**
062: * The flag for whether the previous character was a carriage
063: * return. This flag is needed to correctly track line and column
064: * numbers.
065: */
066: protected boolean yySeenCR;
067:
068: /**
069: * The current line. This field is not final as to allow parsers,
070: * such as the C preprocessor, to adjust the line number while
071: * parsing.
072: */
073: protected int yyLine;
074:
075: /**
076: * The current column. This field is not final as to allow parsers,
077: * such as the C preprocessor, to adjust the column number while
078: * parsing.
079: */
080: protected int yyColumn;
081:
082: /**
083: * The field for this parser's character. Note that -2 indicates
084: * that the character has not yet been parsed and -1 that the
085: * end-of-file has been reached.
086: */
087: private int yyChar;
088:
089: /**
090: * Create a new, empty packrat parser. This constructor is used only
091: * for creating the {@link #DUMMY dummy packrat parser}.
092: */
093: private PackratParser() {
094: yyReader = null;
095: yyCount = -1;
096: yyFile = "*** Not a file! ***";
097: yySeenCR = false;
098: yyLine = -1;
099: yyColumn = -1;
100: yyChar = -2;
101: }
102:
103: /**
104: * Create a new packrat parser.
105: *
106: * @param reader The reader for the character stream to be parsed.
107: * @param file The name of the file backing the character stream.
108: */
109: public PackratParser(Reader reader, String file) {
110: yyReader = reader;
111: yyCount = 0;
112: yyFile = file;
113: yySeenCR = false;
114: yyLine = 1;
115: yyColumn = 0;
116: yyChar = -2;
117: }
118:
119: /**
120: * Create a new packrat parser, moving ahead one character. The
121: * specified packrat parser must represent a valid character; i.e.,
122: * its {@link #hasValue()} method must return <code>true</code>.
123: *
124: * @param previous The previous packrat parser.
125: */
126: protected PackratParser(PackratParser previous) {
127: yyReader = previous.yyReader;
128: yyCount = previous.yyCount + 1;
129: yyChar = -2;
130: }
131:
132: /**
133: * Get the parser for the next character. A concrete implementation
134: * of this method should simply return the parser object created by
135: * using the {@link #PackratParser(PackratParser)} constructor with
136: * <code>this</code> as the argument.
137: *
138: * @return The parser for the next character.
139: */
140: protected abstract PackratParser next();
141:
142: /**
143: * Update the location information for this parser. This method
144: * correctly sets this parser's {@link #yyFile}, {@link #yySeenCR},
145: * {@link #yyLine}, and {@link #yyColumn} fields. It must be
146: * invoked right after setting the previous parser's {@link #yyChar}
147: * field to its character value.
148: *
149: * @param previous The previous packrat parser.
150: */
151: private void updateLocation(PackratParser previous) {
152: yyFile = previous.yyFile;
153:
154: switch (previous.yyChar) {
155: case '\t':
156: yySeenCR = false;
157: yyLine = previous.yyLine;
158: yyColumn = ((previous.yyColumn >> 3) + 1) << 3;
159: return;
160: case '\n':
161: yySeenCR = false;
162: if (previous.yySeenCR) {
163: yyLine = previous.yyLine;
164: yyColumn = previous.yyColumn;
165: } else {
166: yyLine = previous.yyLine + 1;
167: yyColumn = 0;
168: }
169: return;
170: case '\r':
171: yySeenCR = true;
172: yyLine = previous.yyLine + 1;
173: yyColumn = 0;
174: return;
175: default:
176: yySeenCR = false;
177: yyLine = previous.yyLine;
178: yyColumn = previous.yyColumn + 1;
179: return;
180: }
181: }
182:
183: /**
184: * Parse a character. This method returns the result of parsing the
185: * next character offered by this parser's character stream. If
186: * there is another character, the result is this parser; otherwise,
187: * it is a {@link ParseError}.
188: *
189: * @return The corresponding result.
190: * @throws IOException Signals an exceptional condition while
191: * accessing the character stream.
192: */
193: protected final Result character() throws IOException {
194: switch (yyChar) {
195: case -2:
196: yyChar = yyReader.read();
197: if (0 <= yyChar) {
198: parser = next();
199: parser.updateLocation(this );
200: return this ;
201: }
202: /* Fall through. */
203:
204: case -1:
205: return new ParseError("End-of-file", this );
206:
207: default:
208: return this ;
209: }
210: }
211:
212: public boolean hasValue() {
213: return (0 <= yyChar);
214: }
215:
216: public char charValue() {
217: if (0 <= yyChar) {
218: return (char) yyChar;
219: } else {
220: throw new IllegalStateException(
221: "No character value available");
222: }
223: }
224:
225: public Object semanticValue() {
226: throw new IllegalStateException("No semantic value available");
227: }
228:
229: public ParseError parseError() {
230: return ParseError.DUMMY;
231: }
232:
233: public SemanticValue createValue(Object value, ParseError error) {
234: return new SemanticValue(value, parser, error);
235: }
236:
237: /**
238: * Get the difference between this parser and the specified parser.
239: * Both parsers must parse the same character stream.
240: *
241: * @param o The other parser.
242: * @return The difference as a string.
243: */
244: protected final String getDifference(PackratParser o) {
245: PackratParser p;
246: int n;
247:
248: if (yyCount < o.yyCount) {
249: p = this ;
250: n = o.yyCount - yyCount;
251: } else if (yyCount > o.yyCount) {
252: p = o;
253: n = yyCount - o.yyCount;
254: } else {
255: return "";
256: }
257:
258: StringBuffer buf = new StringBuffer(n);
259:
260: int i = 0;
261: do {
262: buf.append((char) p.yyChar);
263: p = p.parser;
264: i++;
265: } while (i < n);
266:
267: return buf.toString();
268: }
269:
270: /**
271: * Get the current file name.
272: *
273: * @return The file name.
274: */
275: public final String file() {
276: return yyFile;
277: }
278:
279: /**
280: * Get the current line number.
281: *
282: * @return The line number.
283: */
284: public final int line() {
285: return yyLine;
286: }
287:
288: /**
289: * Get the current column number.
290: *
291: * @return The column number.
292: */
293: public final int column() {
294: return yyColumn;
295: }
296:
297: /**
298: * Get the current location.
299: *
300: * @return The location.
301: */
302: public final Location location() {
303: return new Location(yyFile, yyLine, yyColumn);
304: }
305:
306: public String toString() {
307: StringBuffer buf = new StringBuffer();
308:
309: buf.append(yyFile);
310: buf.append(':');
311: buf.append(Integer.toString(yyLine));
312: buf.append(',');
313: buf.append(Integer.toString(yyColumn));
314: buf.append(": ");
315:
316: PackratParser p = this ;
317: int i = 0;
318:
319: do {
320: if (0 <= p.yyChar) {
321: buf.append((char) p.yyChar);
322: p = p.parser;
323: i++;
324:
325: if ((20 <= i) && (0 <= p.yyChar)) {
326: buf.append(" ...");
327: }
328: } else {
329: break;
330: }
331: } while (i < 20);
332:
333: return buf.toString();
334: }
335:
336: }
|