diff options
author | Andrew Sapperstein <asapperstein@dhcp-172-18-103-217.mtv.corp.google.com> | 2011-05-20 12:12:34 -0700 |
---|---|---|
committer | Andrew Sapperstein <asapperstein@google.com> | 2011-05-20 13:05:21 -0700 |
commit | 3447a5916aa62f44de24cc441fc9987116ddff52 (patch) | |
tree | 93e76178d68ad8b4df9ac5b834767e69ff2568a4 | |
parent | b87cccb1bb91d78fa2479e6e4b9de45fb320b4a5 (diff) | |
download | antlr-3447a5916aa62f44de24cc441fc9987116ddff52.tar.gz |
Uploading antlr runtime for doclava use.
Change-Id: Iea8a6dd3a2c46dd3c13a81da6aa94e01bf98b854
87 files changed, 14235 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..efe3a09 --- /dev/null +++ b/Android.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := antlr +#LOCAL_SDK_VERSION := 8 +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_JAVA_LIBRARY) + +# Also build a host-side library +# include $(CLEAR_VARS) +# +# LOCAL_SRC_FILES := $(call all-java-files-under, src) +# LOCAL_MODULE := antlrlib +# +# include $(BUILD_HOST_JAVA_LIBRARY) + diff --git a/src/org/antlr/runtime/ANTLRFileStream.java b/src/org/antlr/runtime/ANTLRFileStream.java new file mode 100755 index 0000000..27ef58b --- /dev/null +++ b/src/org/antlr/runtime/ANTLRFileStream.java @@ -0,0 +1,78 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.io.*; + +/** This is a char buffer stream that is loaded from a file + * all at once when you construct the object. This looks very + * much like an ANTLReader or ANTLRInputStream, but it's a special case + * since we know the exact size of the object to load. We can avoid lots + * of data copying. + */ +public class ANTLRFileStream extends ANTLRStringStream { + protected String fileName; + + public ANTLRFileStream(String fileName) throws IOException { + this(fileName, null); + } + + public ANTLRFileStream(String fileName, String encoding) throws IOException { + this.fileName = fileName; + load(fileName, encoding); + } + + public void load(String fileName, String encoding) + throws IOException + { + if ( fileName==null ) { + return; + } + File f = new File(fileName); + int size = (int)f.length(); + InputStreamReader isr; + FileInputStream fis = new FileInputStream(fileName); + if ( encoding!=null ) { + isr = new InputStreamReader(fis, encoding); + } + else { + isr = new InputStreamReader(fis); + } + try { + data = new char[size]; + super.n = isr.read(data); + } + finally { + isr.close(); + } + } + + public String getSourceName() { + return fileName; + } +} diff --git a/src/org/antlr/runtime/ANTLRInputStream.java b/src/org/antlr/runtime/ANTLRInputStream.java new file mode 100755 index 0000000..e544aae --- /dev/null +++ b/src/org/antlr/runtime/ANTLRInputStream.java @@ -0,0 +1,70 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.io.*; + +/** A kind of ReaderStream that pulls from an InputStream. + * Useful for reading from stdin and specifying file encodings etc... + */ +public class ANTLRInputStream extends ANTLRReaderStream { + public ANTLRInputStream() { + } + + public ANTLRInputStream(InputStream input) throws IOException { + this(input, null); + } + + public ANTLRInputStream(InputStream input, int size) throws IOException { + this(input, size, null); + } + + public ANTLRInputStream(InputStream input, String encoding) throws IOException { + this(input, INITIAL_BUFFER_SIZE, encoding); + } + + public ANTLRInputStream(InputStream input, int size, String encoding) throws IOException { + this(input, size, READ_BUFFER_SIZE, encoding); + } + + public ANTLRInputStream(InputStream input, + int size, + int readBufferSize, + String encoding) + throws IOException + { + InputStreamReader isr; + if ( encoding!=null ) { + isr = new InputStreamReader(input, encoding); + } + else { + isr = new InputStreamReader(input); + } + load(isr, size, readBufferSize); + } +} diff --git a/src/org/antlr/runtime/ANTLRReaderStream.java b/src/org/antlr/runtime/ANTLRReaderStream.java new file mode 100755 index 0000000..24d51ad --- /dev/null +++ b/src/org/antlr/runtime/ANTLRReaderStream.java @@ -0,0 +1,95 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.io.*; + +/** Vacuum all input from a Reader and then treat it like a StringStream. + * Manage the buffer manually to avoid unnecessary data copying. + * + * If you need encoding, use ANTLRInputStream. + */ +public class ANTLRReaderStream extends ANTLRStringStream { + public static final int READ_BUFFER_SIZE = 1024; + public static final int INITIAL_BUFFER_SIZE = 1024; + + public ANTLRReaderStream() { + } + + public ANTLRReaderStream(Reader r) throws IOException { + this(r, INITIAL_BUFFER_SIZE, READ_BUFFER_SIZE); + } + + public ANTLRReaderStream(Reader r, int size) throws IOException { + this(r, size, READ_BUFFER_SIZE); + } + + public ANTLRReaderStream(Reader r, int size, int readChunkSize) throws IOException { + load(r, size, readChunkSize); + } + + public void load(Reader r, int size, int readChunkSize) + throws IOException + { + if ( r==null ) { + return; + } + if ( size<=0 ) { + size = INITIAL_BUFFER_SIZE; + } + if ( readChunkSize<=0 ) { + readChunkSize = READ_BUFFER_SIZE; + } + // System.out.println("load "+size+" in chunks of "+readChunkSize); + try { + // alloc initial buffer size. + data = new char[size]; + // read all the data in chunks of readChunkSize + int numRead=0; + int p = 0; + do { + if ( p+readChunkSize > data.length ) { // overflow? + // System.out.println("### overflow p="+p+", data.length="+data.length); + char[] newdata = new char[data.length*2]; // resize + System.arraycopy(data, 0, newdata, 0, data.length); + data = newdata; + } + numRead = r.read(data, p, readChunkSize); + // System.out.println("read "+numRead+" chars; p was "+p+" is now "+(p+numRead)); + p += numRead; + } while (numRead!=-1); // while not EOF + // set the actual size of the data available; + // EOF subtracted one above in p+=numRead; add one back + super.n = p+1; + //System.out.println("n="+n); + } + finally { + r.close(); + } + } +} diff --git a/src/org/antlr/runtime/ANTLRStringStream.java b/src/org/antlr/runtime/ANTLRStringStream.java new file mode 100755 index 0000000..17af23c --- /dev/null +++ b/src/org/antlr/runtime/ANTLRStringStream.java @@ -0,0 +1,230 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.util.ArrayList; +import java.util.List; + +/** A pretty quick CharStream that pulls all data from an array + * directly. Every method call counts in the lexer. Java's + * strings aren't very good so I'm avoiding. + */ +public class ANTLRStringStream implements CharStream { + /** The data being scanned */ + protected char[] data; + + /** How many characters are actually in the buffer */ + protected int n; + + /** 0..n-1 index into string of next char */ + protected int p=0; + + /** line number 1..n within the input */ + protected int line = 1; + + /** The index of the character relative to the beginning of the line 0..n-1 */ + protected int charPositionInLine = 0; + + /** tracks how deep mark() calls are nested */ + protected int markDepth = 0; + + /** A list of CharStreamState objects that tracks the stream state + * values line, charPositionInLine, and p that can change as you + * move through the input stream. Indexed from 1..markDepth. + * A null is kept @ index 0. Create upon first call to mark(). + */ + protected List markers; + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + /** What is name or source of this char stream? */ + public String name; + + public ANTLRStringStream() { + } + + /** Copy data in string to a local char array */ + public ANTLRStringStream(String input) { + this(); + this.data = input.toCharArray(); + this.n = input.length(); + } + + /** This is the preferred constructor as no data is copied */ + public ANTLRStringStream(char[] data, int numberOfActualCharsInArray) { + this(); + this.data = data; + this.n = numberOfActualCharsInArray; + } + + /** Reset the stream so that it's in the same state it was + * when the object was created *except* the data array is not + * touched. + */ + public void reset() { + p = 0; + line = 1; + charPositionInLine = 0; + markDepth = 0; + } + + public void consume() { + //System.out.println("prev p="+p+", c="+(char)data[p]); + if ( p < n ) { + charPositionInLine++; + if ( data[p]=='\n' ) { + /* + System.out.println("newline char found on line: "+line+ + "@ pos="+charPositionInLine); + */ + line++; + charPositionInLine=0; + } + p++; + //System.out.println("p moves to "+p+" (c='"+(char)data[p]+"')"); + } + } + + public int LA(int i) { + if ( i==0 ) { + return 0; // undefined + } + if ( i<0 ) { + i++; // e.g., translate LA(-1) to use offset i=0; then data[p+0-1] + if ( (p+i-1) < 0 ) { + return CharStream.EOF; // invalid; no char before first char + } + } + + if ( (p+i-1) >= n ) { + //System.out.println("char LA("+i+")=EOF; p="+p); + return CharStream.EOF; + } + //System.out.println("char LA("+i+")="+(char)data[p+i-1]+"; p="+p); + //System.out.println("LA("+i+"); p="+p+" n="+n+" data.length="+data.length); + return data[p+i-1]; + } + + public int LT(int i) { + return LA(i); + } + + /** Return the current input symbol index 0..n where n indicates the + * last symbol has been read. The index is the index of char to + * be returned from LA(1). + */ + public int index() { + return p; + } + + public int size() { + return n; + } + + public int mark() { + if ( markers==null ) { + markers = new ArrayList(); + markers.add(null); // depth 0 means no backtracking, leave blank + } + markDepth++; + CharStreamState state = null; + if ( markDepth>=markers.size() ) { + state = new CharStreamState(); + markers.add(state); + } + else { + state = (CharStreamState)markers.get(markDepth); + } + state.p = p; + state.line = line; + state.charPositionInLine = charPositionInLine; + lastMarker = markDepth; + return markDepth; + } + + public void rewind(int m) { + CharStreamState state = (CharStreamState)markers.get(m); + // restore stream state + seek(state.p); + line = state.line; + charPositionInLine = state.charPositionInLine; + release(m); + } + + public void rewind() { + rewind(lastMarker); + } + + public void release(int marker) { + // unwind any other markers made after m and release m + markDepth = marker; + // release this marker + markDepth--; + } + + /** consume() ahead until p==index; can't just set p=index as we must + * update line and charPositionInLine. + */ + public void seek(int index) { + if ( index<=p ) { + p = index; // just jump; don't update stream state (line, ...) + return; + } + // seek forward, consume until p hits index + while ( p<index ) { + consume(); + } + } + + public String substring(int start, int stop) { + return new String(data,start,stop-start+1); + } + + public int getLine() { + return line; + } + + public int getCharPositionInLine() { + return charPositionInLine; + } + + public void setLine(int line) { + this.line = line; + } + + public void setCharPositionInLine(int pos) { + this.charPositionInLine = pos; + } + + public String getSourceName() { + return name; + } + + public String toString() { return new String(data); } +} diff --git a/src/org/antlr/runtime/BaseRecognizer.java b/src/org/antlr/runtime/BaseRecognizer.java new file mode 100755 index 0000000..667664d --- /dev/null +++ b/src/org/antlr/runtime/BaseRecognizer.java @@ -0,0 +1,886 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** A generic recognizer that can handle recognizers generated from + * lexer, parser, and tree grammars. This is all the parsing + * support code essentially; most of it is error recovery stuff and + * backtracking. + */ +public abstract class BaseRecognizer { + public static final int MEMO_RULE_FAILED = -2; + public static final int MEMO_RULE_UNKNOWN = -1; + public static final int INITIAL_FOLLOW_STACK_SIZE = 100; + + // copies from Token object for convenience in actions + public static final int DEFAULT_TOKEN_CHANNEL = Token.DEFAULT_CHANNEL; + public static final int HIDDEN = Token.HIDDEN_CHANNEL; + + public static final String NEXT_TOKEN_RULE_NAME = "nextToken"; + + /** State of a lexer, parser, or tree parser are collected into a state + * object so the state can be shared. This sharing is needed to + * have one grammar import others and share same error variables + * and other state variables. It's a kind of explicit multiple + * inheritance via delegation of methods and shared state. + */ + protected RecognizerSharedState state; + + public BaseRecognizer() { + state = new RecognizerSharedState(); + } + + public BaseRecognizer(RecognizerSharedState state) { + if ( state==null ) { + state = new RecognizerSharedState(); + } + this.state = state; + } + + /** reset the parser's state; subclasses must rewinds the input stream */ + public void reset() { + // wack everything related to error recovery + if ( state==null ) { + return; // no shared state work to do + } + state._fsp = -1; + state.errorRecovery = false; + state.lastErrorIndex = -1; + state.failed = false; + state.syntaxErrors = 0; + // wack everything related to backtracking and memoization + state.backtracking = 0; + for (int i = 0; state.ruleMemo!=null && i < state.ruleMemo.length; i++) { // wipe cache + state.ruleMemo[i] = null; + } + } + + + /** Match current input symbol against ttype. Attempt + * single token insertion or deletion error recovery. If + * that fails, throw MismatchedTokenException. + * + * To turn off single token insertion or deletion error + * recovery, override recoverFromMismatchedToken() and have it + * throw an exception. See TreeParser.recoverFromMismatchedToken(). + * This way any error in a rule will cause an exception and + * immediate exit from rule. Rule would recover by resynchronizing + * to the set of symbols that can follow rule ref. + */ + public Object match(IntStream input, int ttype, BitSet follow) + throws RecognitionException + { + //System.out.println("match "+((TokenStream)input).LT(1)); + Object matchedSymbol = getCurrentInputSymbol(input); + if ( input.LA(1)==ttype ) { + input.consume(); + state.errorRecovery = false; + state.failed = false; + return matchedSymbol; + } + if ( state.backtracking>0 ) { + state.failed = true; + return matchedSymbol; + } + matchedSymbol = recoverFromMismatchedToken(input, ttype, follow); + return matchedSymbol; + } + + /** Match the wildcard: in a symbol */ + public void matchAny(IntStream input) { + state.errorRecovery = false; + state.failed = false; + input.consume(); + } + + public boolean mismatchIsUnwantedToken(IntStream input, int ttype) { + return input.LA(2)==ttype; + } + + public boolean mismatchIsMissingToken(IntStream input, BitSet follow) { + if ( follow==null ) { + // we have no information about the follow; we can only consume + // a single token and hope for the best + return false; + } + // compute what can follow this grammar element reference + if ( follow.member(Token.EOR_TOKEN_TYPE) ) { + BitSet viableTokensFollowingThisRule = computeContextSensitiveRuleFOLLOW(); + follow = follow.or(viableTokensFollowingThisRule); + if ( state._fsp>=0 ) { // remove EOR if we're not the start symbol + follow.remove(Token.EOR_TOKEN_TYPE); + } + } + // if current token is consistent with what could come after set + // then we know we're missing a token; error recovery is free to + // "insert" the missing token + + //System.out.println("viable tokens="+follow.toString(getTokenNames())); + //System.out.println("LT(1)="+((TokenStream)input).LT(1)); + + // BitSet cannot handle negative numbers like -1 (EOF) so I leave EOR + // in follow set to indicate that the fall of the start symbol is + // in the set (EOF can follow). + if ( follow.member(input.LA(1)) || follow.member(Token.EOR_TOKEN_TYPE) ) { + //System.out.println("LT(1)=="+((TokenStream)input).LT(1)+" is consistent with what follows; inserting..."); + return true; + } + return false; + } + + /** Report a recognition problem. + * + * This method sets errorRecovery to indicate the parser is recovering + * not parsing. Once in recovery mode, no errors are generated. + * To get out of recovery mode, the parser must successfully match + * a token (after a resync). So it will go: + * + * 1. error occurs + * 2. enter recovery mode, report error + * 3. consume until token found in resynch set + * 4. try to resume parsing + * 5. next match() will reset errorRecovery mode + * + * If you override, make sure to update syntaxErrors if you care about that. + */ + public void reportError(RecognitionException e) { + // if we've already reported an error and have not matched a token + // yet successfully, don't report any errors. + if ( state.errorRecovery ) { + //System.err.print("[SPURIOUS] "); + return; + } + state.syntaxErrors++; // don't count spurious + state.errorRecovery = true; + + displayRecognitionError(this.getTokenNames(), e); + } + + public void displayRecognitionError(String[] tokenNames, + RecognitionException e) + { + String hdr = getErrorHeader(e); + String msg = getErrorMessage(e, tokenNames); + emitErrorMessage(hdr+" "+msg); + } + + /** What error message should be generated for the various + * exception types? + * + * Not very object-oriented code, but I like having all error message + * generation within one method rather than spread among all of the + * exception classes. This also makes it much easier for the exception + * handling because the exception classes do not have to have pointers back + * to this object to access utility routines and so on. Also, changing + * the message for an exception type would be difficult because you + * would have to subclassing exception, but then somehow get ANTLR + * to make those kinds of exception objects instead of the default. + * This looks weird, but trust me--it makes the most sense in terms + * of flexibility. + * + * For grammar debugging, you will want to override this to add + * more information such as the stack frame with + * getRuleInvocationStack(e, this.getClass().getName()) and, + * for no viable alts, the decision description and state etc... + * + * Override this to change the message generated for one or more + * exception types. + */ + public String getErrorMessage(RecognitionException e, String[] tokenNames) { + String msg = e.getMessage(); + if ( e instanceof UnwantedTokenException ) { + UnwantedTokenException ute = (UnwantedTokenException)e; + String tokenName="<unknown>"; + if ( ute.expecting== Token.EOF ) { + tokenName = "EOF"; + } + else { + tokenName = tokenNames[ute.expecting]; + } + msg = "extraneous input "+getTokenErrorDisplay(ute.getUnexpectedToken())+ + " expecting "+tokenName; + } + else if ( e instanceof MissingTokenException ) { + MissingTokenException mte = (MissingTokenException)e; + String tokenName="<unknown>"; + if ( mte.expecting== Token.EOF ) { + tokenName = "EOF"; + } + else { + tokenName = tokenNames[mte.expecting]; + } + msg = "missing "+tokenName+" at "+getTokenErrorDisplay(e.token); + } + else if ( e instanceof MismatchedTokenException ) { + MismatchedTokenException mte = (MismatchedTokenException)e; + String tokenName="<unknown>"; + if ( mte.expecting== Token.EOF ) { + tokenName = "EOF"; + } + else { + tokenName = tokenNames[mte.expecting]; + } + msg = "mismatched input "+getTokenErrorDisplay(e.token)+ + " expecting "+tokenName; + } + else if ( e instanceof MismatchedTreeNodeException ) { + MismatchedTreeNodeException mtne = (MismatchedTreeNodeException)e; + String tokenName="<unknown>"; + if ( mtne.expecting==Token.EOF ) { + tokenName = "EOF"; + } + else { + tokenName = tokenNames[mtne.expecting]; + } + msg = "mismatched tree node: "+mtne.node+ + " expecting "+tokenName; + } + else if ( e instanceof NoViableAltException ) { + //NoViableAltException nvae = (NoViableAltException)e; + // for development, can add "decision=<<"+nvae.grammarDecisionDescription+">>" + // and "(decision="+nvae.decisionNumber+") and + // "state "+nvae.stateNumber + msg = "no viable alternative at input "+getTokenErrorDisplay(e.token); + } + else if ( e instanceof EarlyExitException ) { + //EarlyExitException eee = (EarlyExitException)e; + // for development, can add "(decision="+eee.decisionNumber+")" + msg = "required (...)+ loop did not match anything at input "+ + getTokenErrorDisplay(e.token); + } + else if ( e instanceof MismatchedSetException ) { + MismatchedSetException mse = (MismatchedSetException)e; + msg = "mismatched input "+getTokenErrorDisplay(e.token)+ + " expecting set "+mse.expecting; + } + else if ( e instanceof MismatchedNotSetException ) { + MismatchedNotSetException mse = (MismatchedNotSetException)e; + msg = "mismatched input "+getTokenErrorDisplay(e.token)+ + " expecting set "+mse.expecting; + } + else if ( e instanceof FailedPredicateException ) { + FailedPredicateException fpe = (FailedPredicateException)e; + msg = "rule "+fpe.ruleName+" failed predicate: {"+ + fpe.predicateText+"}?"; + } + return msg; + } + + /** Get number of recognition errors (lexer, parser, tree parser). Each + * recognizer tracks its own number. So parser and lexer each have + * separate count. Does not count the spurious errors found between + * an error and next valid token match + * + * See also reportError() + */ + public int getNumberOfSyntaxErrors() { + return state.syntaxErrors; + } + + /** What is the error header, normally line/character position information? */ + public String getErrorHeader(RecognitionException e) { + if ( getSourceName()!=null ) + return getSourceName()+" line "+e.line+":"+e.charPositionInLine; + + return "line "+e.line+":"+e.charPositionInLine; + } + + /** How should a token be displayed in an error message? The default + * is to display just the text, but during development you might + * want to have a lot of information spit out. Override in that case + * to use t.toString() (which, for CommonToken, dumps everything about + * the token). This is better than forcing you to override a method in + * your token objects because you don't have to go modify your lexer + * so that it creates a new Java type. + */ + public String getTokenErrorDisplay(Token t) { + String s = t.getText(); + if ( s==null ) { + if ( t.getType()==Token.EOF ) { + s = "<EOF>"; + } + else { + s = "<"+t.getType()+">"; + } + } + s = s.replaceAll("\n","\\\\n"); + s = s.replaceAll("\r","\\\\r"); + s = s.replaceAll("\t","\\\\t"); + return "'"+s+"'"; + } + + /** Override this method to change where error messages go */ + public void emitErrorMessage(String msg) { + System.err.println(msg); + } + + /** Recover from an error found on the input stream. This is + * for NoViableAlt and mismatched symbol exceptions. If you enable + * single token insertion and deletion, this will usually not + * handle mismatched symbol exceptions but there could be a mismatched + * token that the match() routine could not recover from. + */ + public void recover(IntStream input, RecognitionException re) { + if ( state.lastErrorIndex==input.index() ) { + // uh oh, another error at same token index; must be a case + // where LT(1) is in the recovery token set so nothing is + // consumed; consume a single token so at least to prevent + // an infinite loop; this is a failsafe. + input.consume(); + } + state.lastErrorIndex = input.index(); + BitSet followSet = computeErrorRecoverySet(); + beginResync(); + consumeUntil(input, followSet); + endResync(); + } + + /** A hook to listen in on the token consumption during error recovery. + * The DebugParser subclasses this to fire events to the listenter. + */ + public void beginResync() { + } + + public void endResync() { + } + + /* Compute the error recovery set for the current rule. During + * rule invocation, the parser pushes the set of tokens that can + * follow that rule reference on the stack; this amounts to + * computing FIRST of what follows the rule reference in the + * enclosing rule. This local follow set only includes tokens + * from within the rule; i.e., the FIRST computation done by + * ANTLR stops at the end of a rule. + * + * EXAMPLE + * + * When you find a "no viable alt exception", the input is not + * consistent with any of the alternatives for rule r. The best + * thing to do is to consume tokens until you see something that + * can legally follow a call to r *or* any rule that called r. + * You don't want the exact set of viable next tokens because the + * input might just be missing a token--you might consume the + * rest of the input looking for one of the missing tokens. + * + * Consider grammar: + * + * a : '[' b ']' + * | '(' b ')' + * ; + * b : c '^' INT ; + * c : ID + * | INT + * ; + * + * At each rule invocation, the set of tokens that could follow + * that rule is pushed on a stack. Here are the various "local" + * follow sets: + * + * FOLLOW(b1_in_a) = FIRST(']') = ']' + * FOLLOW(b2_in_a) = FIRST(')') = ')' + * FOLLOW(c_in_b) = FIRST('^') = '^' + * + * Upon erroneous input "[]", the call chain is + * + * a -> b -> c + * + * and, hence, the follow context stack is: + * + * depth local follow set after call to rule + * 0 <EOF> a (from main()) + * 1 ']' b + * 3 '^' c + * + * Notice that ')' is not included, because b would have to have + * been called from a different context in rule a for ')' to be + * included. + * + * For error recovery, we cannot consider FOLLOW(c) + * (context-sensitive or otherwise). We need the combined set of + * all context-sensitive FOLLOW sets--the set of all tokens that + * could follow any reference in the call chain. We need to + * resync to one of those tokens. Note that FOLLOW(c)='^' and if + * we resync'd to that token, we'd consume until EOF. We need to + * sync to context-sensitive FOLLOWs for a, b, and c: {']','^'}. + * In this case, for input "[]", LA(1) is in this set so we would + * not consume anything and after printing an error rule c would + * return normally. It would not find the required '^' though. + * At this point, it gets a mismatched token error and throws an + * exception (since LA(1) is not in the viable following token + * set). The rule exception handler tries to recover, but finds + * the same recovery set and doesn't consume anything. Rule b + * exits normally returning to rule a. Now it finds the ']' (and + * with the successful match exits errorRecovery mode). + * + * So, you cna see that the parser walks up call chain looking + * for the token that was a member of the recovery set. + * + * Errors are not generated in errorRecovery mode. + * + * ANTLR's error recovery mechanism is based upon original ideas: + * + * "Algorithms + Data Structures = Programs" by Niklaus Wirth + * + * and + * + * "A note on error recovery in recursive descent parsers": + * http://portal.acm.org/citation.cfm?id=947902.947905 + * + * Later, Josef Grosch had some good ideas: + * + * "Efficient and Comfortable Error Recovery in Recursive Descent + * Parsers": + * ftp://www.cocolab.com/products/cocktail/doca4.ps/ell.ps.zip + * + * Like Grosch I implemented local FOLLOW sets that are combined + * at run-time upon error to avoid overhead during parsing. + */ + protected BitSet computeErrorRecoverySet() { + return combineFollows(false); + } + + /** Compute the context-sensitive FOLLOW set for current rule. + * This is set of token types that can follow a specific rule + * reference given a specific call chain. You get the set of + * viable tokens that can possibly come next (lookahead depth 1) + * given the current call chain. Contrast this with the + * definition of plain FOLLOW for rule r: + * + * FOLLOW(r)={x | S=>*alpha r beta in G and x in FIRST(beta)} + * + * where x in T* and alpha, beta in V*; T is set of terminals and + * V is the set of terminals and nonterminals. In other words, + * FOLLOW(r) is the set of all tokens that can possibly follow + * references to r in *any* sentential form (context). At + * runtime, however, we know precisely which context applies as + * we have the call chain. We may compute the exact (rather + * than covering superset) set of following tokens. + * + * For example, consider grammar: + * + * stat : ID '=' expr ';' // FOLLOW(stat)=={EOF} + * | "return" expr '.' + * ; + * expr : atom ('+' atom)* ; // FOLLOW(expr)=={';','.',')'} + * atom : INT // FOLLOW(atom)=={'+',')',';','.'} + * | '(' expr ')' + * ; + * + * The FOLLOW sets are all inclusive whereas context-sensitive + * FOLLOW sets are precisely what could follow a rule reference. + * For input input "i=(3);", here is the derivation: + * + * stat => ID '=' expr ';' + * => ID '=' atom ('+' atom)* ';' + * => ID '=' '(' expr ')' ('+' atom)* ';' + * => ID '=' '(' atom ')' ('+' atom)* ';' + * => ID '=' '(' INT ')' ('+' atom)* ';' + * => ID '=' '(' INT ')' ';' + * + * At the "3" token, you'd have a call chain of + * + * stat -> expr -> atom -> expr -> atom + * + * What can follow that specific nested ref to atom? Exactly ')' + * as you can see by looking at the derivation of this specific + * input. Contrast this with the FOLLOW(atom)={'+',')',';','.'}. + * + * You want the exact viable token set when recovering from a + * token mismatch. Upon token mismatch, if LA(1) is member of + * the viable next token set, then you know there is most likely + * a missing token in the input stream. "Insert" one by just not + * throwing an exception. + */ + protected BitSet computeContextSensitiveRuleFOLLOW() { + return combineFollows(true); + } + + // what is exact? it seems to only add sets from above on stack + // if EOR is in set i. When it sees a set w/o EOR, it stops adding. + // Why would we ever want them all? Maybe no viable alt instead of + // mismatched token? + protected BitSet combineFollows(boolean exact) { + int top = state._fsp; + BitSet followSet = new BitSet(); + for (int i=top; i>=0; i--) { + BitSet localFollowSet = (BitSet)state.following[i]; + /* + System.out.println("local follow depth "+i+"="+ + localFollowSet.toString(getTokenNames())+")"); + */ + followSet.orInPlace(localFollowSet); + if ( exact ) { + // can we see end of rule? + if ( localFollowSet.member(Token.EOR_TOKEN_TYPE) ) { + // Only leave EOR in set if at top (start rule); this lets + // us know if have to include follow(start rule); i.e., EOF + if ( i>0 ) { + followSet.remove(Token.EOR_TOKEN_TYPE); + } + } + else { // can't see end of rule, quit + break; + } + } + } + return followSet; + } + + /** Attempt to recover from a single missing or extra token. + * + * EXTRA TOKEN + * + * LA(1) is not what we are looking for. If LA(2) has the right token, + * however, then assume LA(1) is some extra spurious token. Delete it + * and LA(2) as if we were doing a normal match(), which advances the + * input. + * + * MISSING TOKEN + * + * If current token is consistent with what could come after + * ttype then it is ok to "insert" the missing token, else throw + * exception For example, Input "i=(3;" is clearly missing the + * ')'. When the parser returns from the nested call to expr, it + * will have call chain: + * + * stat -> expr -> atom + * + * and it will be trying to match the ')' at this point in the + * derivation: + * + * => ID '=' '(' INT ')' ('+' atom)* ';' + * ^ + * match() will see that ';' doesn't match ')' and report a + * mismatched token error. To recover, it sees that LA(1)==';' + * is in the set of tokens that can follow the ')' token + * reference in rule atom. It can assume that you forgot the ')'. + */ + protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow) + throws RecognitionException + { + RecognitionException e = null; + // if next token is what we are looking for then "delete" this token + if ( mismatchIsUnwantedToken(input, ttype) ) { + e = new UnwantedTokenException(ttype, input); + /* + System.err.println("recoverFromMismatchedToken deleting "+ + ((TokenStream)input).LT(1)+ + " since "+((TokenStream)input).LT(2)+" is what we want"); + */ + beginResync(); + input.consume(); // simply delete extra token + endResync(); + reportError(e); // report after consuming so AW sees the token in the exception + // we want to return the token we're actually matching + Object matchedSymbol = getCurrentInputSymbol(input); + input.consume(); // move past ttype token as if all were ok + return matchedSymbol; + } + // can't recover with single token deletion, try insertion + if ( mismatchIsMissingToken(input, follow) ) { + Object inserted = getMissingSymbol(input, e, ttype, follow); + e = new MissingTokenException(ttype, input, inserted); + reportError(e); // report after inserting so AW sees the token in the exception + return inserted; + } + // even that didn't work; must throw the exception + e = new MismatchedTokenException(ttype, input); + throw e; + } + + /** Not currently used */ + public Object recoverFromMismatchedSet(IntStream input, + RecognitionException e, + BitSet follow) + throws RecognitionException + { + if ( mismatchIsMissingToken(input, follow) ) { + // System.out.println("missing token"); + reportError(e); + // we don't know how to conjure up a token for sets yet + return getMissingSymbol(input, e, Token.INVALID_TOKEN_TYPE, follow); + } + // TODO do single token deletion like above for Token mismatch + throw e; + } + + /** Match needs to return the current input symbol, which gets put + * into the label for the associated token ref; e.g., x=ID. Token + * and tree parsers need to return different objects. Rather than test + * for input stream type or change the IntStream interface, I use + * a simple method to ask the recognizer to tell me what the current + * input symbol is. + * + * This is ignored for lexers. + */ + protected Object getCurrentInputSymbol(IntStream input) { return null; } + + /** Conjure up a missing token during error recovery. + * + * The recognizer attempts to recover from single missing + * symbols. But, actions might refer to that missing symbol. + * For example, x=ID {f($x);}. The action clearly assumes + * that there has been an identifier matched previously and that + * $x points at that token. If that token is missing, but + * the next token in the stream is what we want we assume that + * this token is missing and we keep going. Because we + * have to return some token to replace the missing token, + * we have to conjure one up. This method gives the user control + * over the tokens returned for missing tokens. Mostly, + * you will want to create something special for identifier + * tokens. For literals such as '{' and ',', the default + * action in the parser or tree parser works. It simply creates + * a CommonToken of the appropriate type. The text will be the token. + * If you change what tokens must be created by the lexer, + * override this method to create the appropriate tokens. + */ + protected Object getMissingSymbol(IntStream input, + RecognitionException e, + int expectedTokenType, + BitSet follow) + { + return null; + } + + public void consumeUntil(IntStream input, int tokenType) { + //System.out.println("consumeUntil "+tokenType); + int ttype = input.LA(1); + while (ttype != Token.EOF && ttype != tokenType) { + input.consume(); + ttype = input.LA(1); + } + } + + /** Consume tokens until one matches the given token set */ + public void consumeUntil(IntStream input, BitSet set) { + //System.out.println("consumeUntil("+set.toString(getTokenNames())+")"); + int ttype = input.LA(1); + while (ttype != Token.EOF && !set.member(ttype) ) { + //System.out.println("consume during recover LA(1)="+getTokenNames()[input.LA(1)]); + input.consume(); + ttype = input.LA(1); + } + } + + /** Push a rule's follow set using our own hardcoded stack */ + protected void pushFollow(BitSet fset) { + if ( (state._fsp +1)>=state.following.length ) { + BitSet[] f = new BitSet[state.following.length*2]; + System.arraycopy(state.following, 0, f, 0, state.following.length); + state.following = f; + } + state.following[++state._fsp] = fset; + } + + /** Return List<String> of the rules in your parser instance + * leading up to a call to this method. You could override if + * you want more details such as the file/line info of where + * in the parser java code a rule is invoked. + * + * This is very useful for error messages and for context-sensitive + * error recovery. + */ + public List getRuleInvocationStack() { + String parserClassName = getClass().getName(); + return getRuleInvocationStack(new Throwable(), parserClassName); + } + + /** A more general version of getRuleInvocationStack where you can + * pass in, for example, a RecognitionException to get it's rule + * stack trace. This routine is shared with all recognizers, hence, + * static. + * + * TODO: move to a utility class or something; weird having lexer call this + */ + public static List getRuleInvocationStack(Throwable e, + String recognizerClassName) + { + List rules = new ArrayList(); + StackTraceElement[] stack = e.getStackTrace(); + int i = 0; + for (i=stack.length-1; i>=0; i--) { + StackTraceElement t = stack[i]; + if ( t.getClassName().startsWith("org.antlr.runtime.") ) { + continue; // skip support code such as this method + } + if ( t.getMethodName().equals(NEXT_TOKEN_RULE_NAME) ) { + continue; + } + if ( !t.getClassName().equals(recognizerClassName) ) { + continue; // must not be part of this parser + } + rules.add(t.getMethodName()); + } + return rules; + } + + public int getBacktrackingLevel() { return state.backtracking; } + + public void setBacktrackingLevel(int n) { state.backtracking = n; } + + /** Return whether or not a backtracking attempt failed. */ + public boolean failed() { return state.failed; } + + /** Used to print out token names like ID during debugging and + * error reporting. The generated parsers implement a method + * that overrides this to point to their String[] tokenNames. + */ + public String[] getTokenNames() { + return null; + } + + /** For debugging and other purposes, might want the grammar name. + * Have ANTLR generate an implementation for this method. + */ + public String getGrammarFileName() { + return null; + } + + public abstract String getSourceName(); + + /** A convenience method for use most often with template rewrites. + * Convert a List<Token> to List<String> + */ + public List toStrings(List tokens) { + if ( tokens==null ) return null; + List strings = new ArrayList(tokens.size()); + for (int i=0; i<tokens.size(); i++) { + strings.add(((Token)tokens.get(i)).getText()); + } + return strings; + } + + /** Given a rule number and a start token index number, return + * MEMO_RULE_UNKNOWN if the rule has not parsed input starting from + * start index. If this rule has parsed input starting from the + * start index before, then return where the rule stopped parsing. + * It returns the index of the last token matched by the rule. + * + * For now we use a hashtable and just the slow Object-based one. + * Later, we can make a special one for ints and also one that + * tosses out data after we commit past input position i. + */ + public int getRuleMemoization(int ruleIndex, int ruleStartIndex) { + if ( state.ruleMemo[ruleIndex]==null ) { + state.ruleMemo[ruleIndex] = new HashMap(); + } + Integer stopIndexI = + (Integer)state.ruleMemo[ruleIndex].get(new Integer(ruleStartIndex)); + if ( stopIndexI==null ) { + return MEMO_RULE_UNKNOWN; + } + return stopIndexI.intValue(); + } + + /** Has this rule already parsed input at the current index in the + * input stream? Return the stop token index or MEMO_RULE_UNKNOWN. + * If we attempted but failed to parse properly before, return + * MEMO_RULE_FAILED. + * + * This method has a side-effect: if we have seen this input for + * this rule and successfully parsed before, then seek ahead to + * 1 past the stop token matched for this rule last time. + */ + public boolean alreadyParsedRule(IntStream input, int ruleIndex) { + int stopIndex = getRuleMemoization(ruleIndex, input.index()); + if ( stopIndex==MEMO_RULE_UNKNOWN ) { + return false; + } + if ( stopIndex==MEMO_RULE_FAILED ) { + //System.out.println("rule "+ruleIndex+" will never succeed"); + state.failed=true; + } + else { + //System.out.println("seen rule "+ruleIndex+" before; skipping ahead to @"+(stopIndex+1)+" failed="+state.failed); + input.seek(stopIndex+1); // jump to one past stop token + } + return true; + } + + /** Record whether or not this rule parsed the input at this position + * successfully. Use a standard java hashtable for now. + */ + public void memoize(IntStream input, + int ruleIndex, + int ruleStartIndex) + { + int stopTokenIndex = state.failed?MEMO_RULE_FAILED:input.index()-1; + if ( state.ruleMemo==null ) { + System.err.println("!!!!!!!!! memo array is null for "+ getGrammarFileName()); + } + if ( ruleIndex >= state.ruleMemo.length ) { + System.err.println("!!!!!!!!! memo size is "+state.ruleMemo.length+", but rule index is "+ruleIndex); + } + if ( state.ruleMemo[ruleIndex]!=null ) { + state.ruleMemo[ruleIndex].put( + new Integer(ruleStartIndex), new Integer(stopTokenIndex) + ); + } + } + + /** return how many rule/input-index pairs there are in total. + * TODO: this includes synpreds. :( + */ + public int getRuleMemoizationCacheSize() { + int n = 0; + for (int i = 0; state.ruleMemo!=null && i < state.ruleMemo.length; i++) { + Map ruleMap = state.ruleMemo[i]; + if ( ruleMap!=null ) { + n += ruleMap.size(); // how many input indexes are recorded? + } + } + return n; + } + + public void traceIn(String ruleName, int ruleIndex, Object inputSymbol) { + System.out.print("enter "+ruleName+" "+inputSymbol); + if ( state.backtracking>0 ) { + System.out.print(" backtracking="+state.backtracking); + } + System.out.println(); + } + + public void traceOut(String ruleName, + int ruleIndex, + Object inputSymbol) + { + System.out.print("exit "+ruleName+" "+inputSymbol); + if ( state.backtracking>0 ) { + System.out.print(" backtracking="+state.backtracking); + if ( state.failed ) System.out.print(" failed"); + else System.out.print(" succeeded"); + } + System.out.println(); + } + +} diff --git a/src/org/antlr/runtime/BitSet.java b/src/org/antlr/runtime/BitSet.java new file mode 100755 index 0000000..f013a26 --- /dev/null +++ b/src/org/antlr/runtime/BitSet.java @@ -0,0 +1,325 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.util.List; + +/**A stripped-down version of org.antlr.misc.BitSet that is just + * good enough to handle runtime requirements such as FOLLOW sets + * for automatic error recovery. + */ +public class BitSet implements Cloneable { + protected final static int BITS = 64; // number of bits / long + protected final static int LOG_BITS = 6; // 2^6 == 64 + + /* We will often need to do a mod operator (i mod nbits). Its + * turns out that, for powers of two, this mod operation is + * same as (i & (nbits-1)). Since mod is slow, we use a + * precomputed mod mask to do the mod instead. + */ + protected final static int MOD_MASK = BITS - 1; + + /** The actual data bits */ + protected long bits[]; + + /** Construct a bitset of size one word (64 bits) */ + public BitSet() { + this(BITS); + } + + /** Construction from a static array of longs */ + public BitSet(long[] bits_) { + bits = bits_; + } + + /** Construction from a list of integers */ + public BitSet(List items) { + this(); + for (int i = 0; i < items.size(); i++) { + Integer v = (Integer) items.get(i); + add(v.intValue()); + } + } + + /** Construct a bitset given the size + * @param nbits The size of the bitset in bits + */ + public BitSet(int nbits) { + bits = new long[((nbits - 1) >> LOG_BITS) + 1]; + } + + public static BitSet of(int el) { + BitSet s = new BitSet(el + 1); + s.add(el); + return s; + } + + public static BitSet of(int a, int b) { + BitSet s = new BitSet(Math.max(a,b)+1); + s.add(a); + s.add(b); + return s; + } + + public static BitSet of(int a, int b, int c) { + BitSet s = new BitSet(); + s.add(a); + s.add(b); + s.add(c); + return s; + } + + public static BitSet of(int a, int b, int c, int d) { + BitSet s = new BitSet(); + s.add(a); + s.add(b); + s.add(c); + s.add(d); + return s; + } + + /** return this | a in a new set */ + public BitSet or(BitSet a) { + if ( a==null ) { + return this; + } + BitSet s = (BitSet)this.clone(); + s.orInPlace(a); + return s; + } + + /** or this element into this set (grow as necessary to accommodate) */ + public void add(int el) { + int n = wordNumber(el); + if (n >= bits.length) { + growToInclude(el); + } + bits[n] |= bitMask(el); + } + + /** + * Grows the set to a larger number of bits. + * @param bit element that must fit in set + */ + public void growToInclude(int bit) { + int newSize = Math.max(bits.length << 1, numWordsToHold(bit)); + long newbits[] = new long[newSize]; + System.arraycopy(bits, 0, newbits, 0, bits.length); + bits = newbits; + } + + public void orInPlace(BitSet a) { + if ( a==null ) { + return; + } + // If this is smaller than a, grow this first + if (a.bits.length > bits.length) { + setSize(a.bits.length); + } + int min = Math.min(bits.length, a.bits.length); + for (int i = min - 1; i >= 0; i--) { + bits[i] |= a.bits[i]; + } + } + + /** + * Sets the size of a set. + * @param nwords how many words the new set should be + */ + private void setSize(int nwords) { + long newbits[] = new long[nwords]; + int n = Math.min(nwords, bits.length); + System.arraycopy(bits, 0, newbits, 0, n); + bits = newbits; + } + + private final static long bitMask(int bitNumber) { + int bitPosition = bitNumber & MOD_MASK; // bitNumber mod BITS + return 1L << bitPosition; + } + + public Object clone() { + BitSet s; + try { + s = (BitSet)super.clone(); + s.bits = new long[bits.length]; + System.arraycopy(bits, 0, s.bits, 0, bits.length); + } + catch (CloneNotSupportedException e) { + throw new InternalError(); + } + return s; + } + + public int size() { + int deg = 0; + for (int i = bits.length - 1; i >= 0; i--) { + long word = bits[i]; + if (word != 0L) { + for (int bit = BITS - 1; bit >= 0; bit--) { + if ((word & (1L << bit)) != 0) { + deg++; + } + } + } + } + return deg; + } + + public boolean equals(Object other) { + if ( other == null || !(other instanceof BitSet) ) { + return false; + } + + BitSet otherSet = (BitSet)other; + + int n = Math.min(this.bits.length, otherSet.bits.length); + + // for any bits in common, compare + for (int i=0; i<n; i++) { + if (this.bits[i] != otherSet.bits[i]) { + return false; + } + } + + // make sure any extra bits are off + + if (this.bits.length > n) { + for (int i = n+1; i<this.bits.length; i++) { + if (this.bits[i] != 0) { + return false; + } + } + } + else if (otherSet.bits.length > n) { + for (int i = n+1; i<otherSet.bits.length; i++) { + if (otherSet.bits[i] != 0) { + return false; + } + } + } + + return true; + } + + public boolean member(int el) { + if ( el<0 ) { + return false; + } + int n = wordNumber(el); + if (n >= bits.length) return false; + return (bits[n] & bitMask(el)) != 0; + } + + // remove this element from this set + public void remove(int el) { + int n = wordNumber(el); + if (n < bits.length) { + bits[n] &= ~bitMask(el); + } + } + + public boolean isNil() { + for (int i = bits.length - 1; i >= 0; i--) { + if (bits[i] != 0) return false; + } + return true; + } + + private final int numWordsToHold(int el) { + return (el >> LOG_BITS) + 1; + } + + public int numBits() { + return bits.length << LOG_BITS; // num words * bits per word + } + + /** return how much space is being used by the bits array not + * how many actually have member bits on. + */ + public int lengthInLongWords() { + return bits.length; + } + + /**Is this contained within a? */ + /* + public boolean subset(BitSet a) { + if (a == null || !(a instanceof BitSet)) return false; + return this.and(a).equals(this); + } + */ + + public int[] toArray() { + int[] elems = new int[size()]; + int en = 0; + for (int i = 0; i < (bits.length << LOG_BITS); i++) { + if (member(i)) { + elems[en++] = i; + } + } + return elems; + } + + public long[] toPackedArray() { + return bits; + } + + private final static int wordNumber(int bit) { + return bit >> LOG_BITS; // bit / BITS + } + + public String toString() { + return toString(null); + } + + public String toString(String[] tokenNames) { + StringBuffer buf = new StringBuffer(); + String separator = ","; + boolean havePrintedAnElement = false; + buf.append('{'); + + for (int i = 0; i < (bits.length << LOG_BITS); i++) { + if (member(i)) { + if (i > 0 && havePrintedAnElement ) { + buf.append(separator); + } + if ( tokenNames!=null ) { + buf.append(tokenNames[i]); + } + else { + buf.append(i); + } + havePrintedAnElement = true; + } + } + buf.append('}'); + return buf.toString(); + } + + +} diff --git a/src/org/antlr/runtime/BufferedTokenStream.java b/src/org/antlr/runtime/BufferedTokenStream.java new file mode 100755 index 0000000..7742d4f --- /dev/null +++ b/src/org/antlr/runtime/BufferedTokenStream.java @@ -0,0 +1,272 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.runtime; + +import java.util.List; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +/** Buffer all input tokens but do on-demand fetching of new tokens from + * lexer. Useful when the parser or lexer has to set context/mode info before + * proper lexing of future tokens. The ST template parser needs this, + * for example, because it has to constantly flip back and forth between + * inside/output templates. E.g., <names:{hi, <it>}> has to parse names + * as part of an expression but "hi, <it>" as a nested template. + * + * You can't use this stream if you pass whitespace or other off-channel + * tokens to the parser. The stream can't ignore off-channel tokens. + * (UnbufferedTokenStream is the same way.) + * + * This is not a subclass of UnbufferedTokenStream because I don't want + * to confuse small moving window of tokens it uses for the full buffer. + */ +public class BufferedTokenStream implements TokenStream { + protected TokenSource tokenSource; + + /** Record every single token pulled from the source so we can reproduce + * chunks of it later. The buffer in LookaheadStream overlaps sometimes + * as its moving window moves through the input. This list captures + * everything so we can access complete input text. + */ + protected List<Token> tokens = new ArrayList<Token>(100); + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + /** The index into the tokens list of the current token (next token + * to consume). tokens[p] should be LT(1). p=-1 indicates need + * to initialize with first token. The ctor doesn't get a token. + * First call to LT(1) or whatever gets the first token and sets p=0; + */ + protected int p = -1; + + protected int range = -1; // how deep have we gone? + + public BufferedTokenStream() {;} + + public BufferedTokenStream(TokenSource tokenSource) { + this.tokenSource = tokenSource; + } + + public TokenSource getTokenSource() { return tokenSource; } + + public int index() { return p; } + + public int range() { return range; } + + public int mark() { + if ( p == -1 ) setup(); + lastMarker = index(); + return lastMarker; + } + + public void release(int marker) { + // no resources to release + } + + public void rewind(int marker) { + seek(marker); + } + + public void rewind() { + seek(lastMarker); + } + + public void reset() { + p = 0; + lastMarker = 0; + } + + public void seek(int index) { p = index; } + + public int size() { return tokens.size(); } + + /** Move the input pointer to the next incoming token. The stream + * must become active with LT(1) available. consume() simply + * moves the input pointer so that LT(1) points at the next + * input symbol. Consume at least one token. + * + * Walk past any token not on the channel the parser is listening to. + */ + public void consume() { + if ( p == -1 ) setup(); + p++; + sync(p); + } + + /** Make sure index i in tokens has a token. */ + protected void sync(int i) { + int n = i - tokens.size() + 1; // how many more elements we need? + //System.out.println("sync("+i+") needs "+n); + if ( n > 0 ) fetch(n); + } + + /** add n elements to buffer */ + protected void fetch(int n) { + for (int i=1; i<=n; i++) { + Token t = tokenSource.nextToken(); + t.setTokenIndex(tokens.size()); + //System.out.println("adding "+t+" at index "+tokens.size()); + tokens.add(t); + if ( t.getType()==Token.EOF ) break; + } + } + + public Token get(int i) { + if ( i < 0 || i >= tokens.size() ) { + throw new NoSuchElementException("token index "+i+" out of range 0.."+(tokens.size()-1)); + } + return tokens.get(i); + } + + /** Get all tokens from start..stop inclusively */ + public List get(int start, int stop) { + if ( start<0 || stop<0 ) return null; + if ( p == -1 ) setup(); + List subset = new ArrayList(); + if ( stop>=tokens.size() ) stop = tokens.size()-1; + for (int i = start; i <= stop; i++) { + Token t = tokens.get(i); + if ( t.getType()==Token.EOF ) break; + subset.add(t); + } + return subset; + } + + public int LA(int i) { return LT(i).getType(); } + + protected Token LB(int k) { + if ( (p-k)<0 ) return null; + return tokens.get(p-k); + } + + public Token LT(int k) { + if ( p == -1 ) setup(); + if ( k==0 ) return null; + if ( k < 0 ) return LB(-k); + + int i = p + k - 1; + sync(i); + if ( i >= tokens.size() ) { // return EOF token + // EOF must be last token + return tokens.get(tokens.size()-1); + } + if ( i>range ) range = i; + return tokens.get(i); + } + + protected void setup() { sync(0); p = 0; } + + /** Reset this token stream by setting its token source. */ + public void setTokenSource(TokenSource tokenSource) { + this.tokenSource = tokenSource; + tokens.clear(); + p = -1; + } + + public List getTokens() { return tokens; } + + public List getTokens(int start, int stop) { + return getTokens(start, stop, (BitSet)null); + } + + /** Given a start and stop index, return a List of all tokens in + * the token type BitSet. Return null if no tokens were found. This + * method looks at both on and off channel tokens. + */ + public List getTokens(int start, int stop, BitSet types) { + if ( p == -1 ) setup(); + if ( stop>=tokens.size() ) stop=tokens.size()-1; + if ( start<0 ) start=0; + if ( start>stop ) return null; + + // list = tokens[start:stop]:{Token t, t.getType() in types} + List<Token> filteredTokens = new ArrayList<Token>(); + for (int i=start; i<=stop; i++) { + Token t = tokens.get(i); + if ( types==null || types.member(t.getType()) ) { + filteredTokens.add(t); + } + } + if ( filteredTokens.size()==0 ) { + filteredTokens = null; + } + return filteredTokens; + } + + public List getTokens(int start, int stop, List types) { + return getTokens(start,stop,new BitSet(types)); + } + + public List getTokens(int start, int stop, int ttype) { + return getTokens(start,stop,BitSet.of(ttype)); + } + + public String getSourceName() { return tokenSource.getSourceName(); } + + /** Grab *all* tokens from stream and return string */ + public String toString() { + if ( p == -1 ) setup(); + fill(); + return toString(0, tokens.size()-1); + } + + public String toString(int start, int stop) { + if ( start<0 || stop<0 ) return null; + if ( p == -1 ) setup(); + if ( stop>=tokens.size() ) stop = tokens.size()-1; + StringBuffer buf = new StringBuffer(); + for (int i = start; i <= stop; i++) { + Token t = tokens.get(i); + if ( t.getType()==Token.EOF ) break; + buf.append(t.getText()); + } + return buf.toString(); + } + + public String toString(Token start, Token stop) { + if ( start!=null && stop!=null ) { + return toString(start.getTokenIndex(), stop.getTokenIndex()); + } + return null; + } + + /** Get all tokens from lexer until EOF */ + public void fill() { + if ( p == -1 ) setup(); + if ( tokens.get(p).getType()==Token.EOF ) return; + + int i = p+1; + sync(i); + while ( tokens.get(i).getType()!=Token.EOF ) { + i++; + sync(i); + } + } +} diff --git a/src/org/antlr/runtime/CharStream.java b/src/org/antlr/runtime/CharStream.java new file mode 100755 index 0000000..db55755 --- /dev/null +++ b/src/org/antlr/runtime/CharStream.java @@ -0,0 +1,57 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A source of characters for an ANTLR lexer */ +public interface CharStream extends IntStream { + public static final int EOF = -1; + + /** For infinite streams, you don't need this; primarily I'm providing + * a useful interface for action code. Just make sure actions don't + * use this on streams that don't support it. + */ + public String substring(int start, int stop); + + /** Get the ith character of lookahead. This is the same usually as + * LA(i). This will be used for labels in the generated + * lexer code. I'd prefer to return a char here type-wise, but it's + * probably better to be 32-bit clean and be consistent with LA. + */ + public int LT(int i); + + /** ANTLR tracks the line information automatically */ + int getLine(); + + /** Because this stream can rewind, we need to be able to reset the line */ + void setLine(int line); + + void setCharPositionInLine(int pos); + + /** The index of the character relative to the beginning of the line 0..n-1 */ + int getCharPositionInLine(); +} diff --git a/src/org/antlr/runtime/CharStreamState.java b/src/org/antlr/runtime/CharStreamState.java new file mode 100755 index 0000000..04d969a --- /dev/null +++ b/src/org/antlr/runtime/CharStreamState.java @@ -0,0 +1,45 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** When walking ahead with cyclic DFA or for syntactic predicates, + * we need to record the state of the input stream (char index, + * line, etc...) so that we can rewind the state after scanning ahead. + * + * This is the complete state of a stream. + */ +public class CharStreamState { + /** Index into the char stream of next lookahead char */ + int p; + + /** What line number is the scanner at before processing buffer[p]? */ + int line; + + /** What char position 0..n-1 in line is scanner before processing buffer[p]? */ + int charPositionInLine; +} diff --git a/src/org/antlr/runtime/ClassicToken.java b/src/org/antlr/runtime/ClassicToken.java new file mode 100755 index 0000000..72c2bd9 --- /dev/null +++ b/src/org/antlr/runtime/ClassicToken.java @@ -0,0 +1,141 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A Token object like we'd use in ANTLR 2.x; has an actual string created + * and associated with this object. These objects are needed for imaginary + * tree nodes that have payload objects. We need to create a Token object + * that has a string; the tree node will point at this token. CommonToken + * has indexes into a char stream and hence cannot be used to introduce + * new strings. + */ +public class ClassicToken implements Token { + protected String text; + protected int type; + protected int line; + protected int charPositionInLine; + protected int channel=DEFAULT_CHANNEL; + + /** What token number is this from 0..n-1 tokens */ + protected int index; + + public ClassicToken(int type) { + this.type = type; + } + + public ClassicToken(Token oldToken) { + text = oldToken.getText(); + type = oldToken.getType(); + line = oldToken.getLine(); + charPositionInLine = oldToken.getCharPositionInLine(); + channel = oldToken.getChannel(); + } + + public ClassicToken(int type, String text) { + this.type = type; + this.text = text; + } + + public ClassicToken(int type, String text, int channel) { + this.type = type; + this.text = text; + this.channel = channel; + } + + public int getType() { + return type; + } + + public void setLine(int line) { + this.line = line; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public int getLine() { + return line; + } + + public int getCharPositionInLine() { + return charPositionInLine; + } + + public void setCharPositionInLine(int charPositionInLine) { + this.charPositionInLine = charPositionInLine; + } + + public int getChannel() { + return channel; + } + + public void setChannel(int channel) { + this.channel = channel; + } + + public void setType(int type) { + this.type = type; + } + + public int getTokenIndex() { + return index; + } + + public void setTokenIndex(int index) { + this.index = index; + } + + public CharStream getInputStream() { + return null; + } + + public void setInputStream(CharStream input) { + } + + public String toString() { + String channelStr = ""; + if ( channel>0 ) { + channelStr=",channel="+channel; + } + String txt = getText(); + if ( txt!=null ) { + txt = txt.replaceAll("\n","\\\\n"); + txt = txt.replaceAll("\r","\\\\r"); + txt = txt.replaceAll("\t","\\\\t"); + } + else { + txt = "<no text>"; + } + return "[@"+getTokenIndex()+",'"+txt+"',<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+"]"; + } +} diff --git a/src/org/antlr/runtime/CommonToken.java b/src/org/antlr/runtime/CommonToken.java new file mode 100755 index 0000000..f01e1f4 --- /dev/null +++ b/src/org/antlr/runtime/CommonToken.java @@ -0,0 +1,191 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.io.Serializable; + +public class CommonToken implements Token, Serializable { + protected int type; + protected int line; + protected int charPositionInLine = -1; // set to invalid position + protected int channel=DEFAULT_CHANNEL; + protected transient CharStream input; + + /** We need to be able to change the text once in a while. If + * this is non-null, then getText should return this. Note that + * start/stop are not affected by changing this. + */ + protected String text; + + /** What token number is this from 0..n-1 tokens; < 0 implies invalid index */ + protected int index = -1; + + /** The char position into the input buffer where this token starts */ + protected int start; + + /** The char position into the input buffer where this token stops */ + protected int stop; + + public CommonToken(int type) { + this.type = type; + } + + public CommonToken(CharStream input, int type, int channel, int start, int stop) { + this.input = input; + this.type = type; + this.channel = channel; + this.start = start; + this.stop = stop; + } + + public CommonToken(int type, String text) { + this.type = type; + this.channel = DEFAULT_CHANNEL; + this.text = text; + } + + public CommonToken(Token oldToken) { + text = oldToken.getText(); + type = oldToken.getType(); + line = oldToken.getLine(); + index = oldToken.getTokenIndex(); + charPositionInLine = oldToken.getCharPositionInLine(); + channel = oldToken.getChannel(); + input = oldToken.getInputStream(); + if ( oldToken instanceof CommonToken ) { + start = ((CommonToken)oldToken).start; + stop = ((CommonToken)oldToken).stop; + } + } + + public int getType() { + return type; + } + + public void setLine(int line) { + this.line = line; + } + + public String getText() { + if ( text!=null ) { + return text; + } + if ( input==null ) { + return null; + } + if ( start<input.size() && stop<input.size() ) { + text = input.substring(start,stop); + } + else { + text = "<EOF>"; + } + return text; + } + + /** Override the text for this token. getText() will return this text + * rather than pulling from the buffer. Note that this does not mean + * that start/stop indexes are not valid. It means that that input + * was converted to a new string in the token object. + */ + public void setText(String text) { + this.text = text; + } + + public int getLine() { + return line; + } + + public int getCharPositionInLine() { + return charPositionInLine; + } + + public void setCharPositionInLine(int charPositionInLine) { + this.charPositionInLine = charPositionInLine; + } + + public int getChannel() { + return channel; + } + + public void setChannel(int channel) { + this.channel = channel; + } + + public void setType(int type) { + this.type = type; + } + + public int getStartIndex() { + return start; + } + + public void setStartIndex(int start) { + this.start = start; + } + + public int getStopIndex() { + return stop; + } + + public void setStopIndex(int stop) { + this.stop = stop; + } + + public int getTokenIndex() { + return index; + } + + public void setTokenIndex(int index) { + this.index = index; + } + + public CharStream getInputStream() { + return input; + } + + public void setInputStream(CharStream input) { + this.input = input; + } + + public String toString() { + String channelStr = ""; + if ( channel>0 ) { + channelStr=",channel="+channel; + } + String txt = getText(); + if ( txt!=null ) { + txt = txt.replaceAll("\n","\\\\n"); + txt = txt.replaceAll("\r","\\\\r"); + txt = txt.replaceAll("\t","\\\\t"); + } + else { + txt = "<no text>"; + } + return "[@"+getTokenIndex()+","+start+":"+stop+"='"+txt+"',<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+"]"; + } +} diff --git a/src/org/antlr/runtime/CommonTokenStream.java b/src/org/antlr/runtime/CommonTokenStream.java new file mode 100755 index 0000000..28135a6 --- /dev/null +++ b/src/org/antlr/runtime/CommonTokenStream.java @@ -0,0 +1,153 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.runtime; + +/** The most common stream of tokens where every token is buffered up + * and tokens are filtered for a certain channel (the parser will only + * see these tokens). + * + * Even though it buffers all of the tokens, this token stream pulls tokens + * from the tokens source on demand. In other words, until you ask for a + * token using consume(), LT(), etc. the stream does not pull from the lexer. + * + * The only difference between this stream and BufferedTokenStream superclass + * is that this stream knows how to ignore off channel tokens. There may be + * a performance advantage to using the superclass if you don't pass + * whitespace and comments etc. to the parser on a hidden channel (i.e., + * you set $channel instead of calling skip() in lexer rules.) + * + * @see org.antlr.runtime.UnbufferedTokenStream + * @see org.antlr.runtime.BufferedTokenStream + */ +public class CommonTokenStream extends BufferedTokenStream { + /** Skip tokens on any channel but this one; this is how we skip whitespace... */ + protected int channel = Token.DEFAULT_CHANNEL; + + public CommonTokenStream() { ; } + + public CommonTokenStream(TokenSource tokenSource) { + super(tokenSource); + } + + public CommonTokenStream(TokenSource tokenSource, int channel) { + this(tokenSource); + this.channel = channel; + } + + /** Always leave p on an on-channel token. */ + public void consume() { + if ( p == -1 ) setup(); + p++; + sync(p); + while ( tokens.get(p).getChannel()!=channel ) { + p++; + sync(p); + } + } + + protected Token LB(int k) { + if ( k==0 || (p-k)<0 ) return null; + + int i = p; + int n = 1; + // find k good tokens looking backwards + while ( n<=k ) { + // skip off-channel tokens + i = skipOffTokenChannelsReverse(i-1); + n++; + } + if ( i<0 ) return null; + return tokens.get(i); + } + + public Token LT(int k) { + //System.out.println("enter LT("+k+")"); + if ( p == -1 ) setup(); + if ( k == 0 ) return null; + if ( k < 0 ) return LB(-k); + int i = p; + int n = 1; // we know tokens[p] is a good one + // find k good tokens + while ( n<k ) { + // skip off-channel tokens + i = skipOffTokenChannels(i+1); + n++; + } + if ( i>range ) range = i; + return tokens.get(i); + } + + /** Given a starting index, return the index of the first on-channel + * token. + */ + protected int skipOffTokenChannels(int i) { + sync(i); + while ( tokens.get(i).getChannel()!=channel ) { // also stops at EOF (it's onchannel) + i++; + sync(i); + } + return i; + } + + protected int skipOffTokenChannelsReverse(int i) { + while ( i>=0 && ((Token)tokens.get(i)).getChannel()!=channel ) { + i--; + } + return i; + } + + protected void setup() { + p = 0; + sync(0); + int i = 0; + while ( tokens.get(i).getChannel()!=channel ) { + i++; + sync(i); + } + p = i; + } + + /** Count EOF just once. */ + public int getNumberOfOnChannelTokens() { + int n = 0; + fill(); + for (int i = 0; i < tokens.size(); i++) { + Token t = tokens.get(i); + if ( t.getChannel()==channel ) n++; + if ( t.getType()==Token.EOF ) break; + } + return n; + } + + /** Reset this token stream by setting its token source. */ + public void setTokenSource(TokenSource tokenSource) { + super.setTokenSource(tokenSource); + channel = Token.DEFAULT_CHANNEL; + } +} diff --git a/src/org/antlr/runtime/DFA.java b/src/org/antlr/runtime/DFA.java new file mode 100755 index 0000000..d1bebbc --- /dev/null +++ b/src/org/antlr/runtime/DFA.java @@ -0,0 +1,229 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A DFA implemented as a set of transition tables. + * + * Any state that has a semantic predicate edge is special; those states + * are generated with if-then-else structures in a specialStateTransition() + * which is generated by cyclicDFA template. + * + * There are at most 32767 states (16-bit signed short). + * Could get away with byte sometimes but would have to generate different + * types and the simulation code too. For a point of reference, the Java + * lexer's Tokens rule DFA has 326 states roughly. + */ +public class DFA { + protected short[] eot; + protected short[] eof; + protected char[] min; + protected char[] max; + protected short[] accept; + protected short[] special; + protected short[][] transition; + + protected int decisionNumber; + + /** Which recognizer encloses this DFA? Needed to check backtracking */ + protected BaseRecognizer recognizer; + + public static final boolean debug = false; + + /** From the input stream, predict what alternative will succeed + * using this DFA (representing the covering regular approximation + * to the underlying CFL). Return an alternative number 1..n. Throw + * an exception upon error. + */ + public int predict(IntStream input) + throws RecognitionException + { + if ( debug ) { + System.err.println("Enter DFA.predict for decision "+decisionNumber); + } + int mark = input.mark(); // remember where decision started in input + int s = 0; // we always start at s0 + try { + while ( true ) { + if ( debug ) System.err.println("DFA "+decisionNumber+" state "+s+" LA(1)="+(char)input.LA(1)+"("+input.LA(1)+ + "), index="+input.index()); + int specialState = special[s]; + if ( specialState>=0 ) { + if ( debug ) { + System.err.println("DFA "+decisionNumber+ + " state "+s+" is special state "+specialState); + } + s = specialStateTransition(specialState,input); + if ( debug ) { + System.err.println("DFA "+decisionNumber+ + " returns from special state "+specialState+" to "+s); + } + if ( s==-1 ) { + noViableAlt(s,input); + return 0; + } + input.consume(); + continue; + } + if ( accept[s] >= 1 ) { + if ( debug ) System.err.println("accept; predict "+accept[s]+" from state "+s); + return accept[s]; + } + // look for a normal char transition + char c = (char)input.LA(1); // -1 == \uFFFF, all tokens fit in 65000 space + if (c>=min[s] && c<=max[s]) { + int snext = transition[s][c-min[s]]; // move to next state + if ( snext < 0 ) { + // was in range but not a normal transition + // must check EOT, which is like the else clause. + // eot[s]>=0 indicates that an EOT edge goes to another + // state. + if ( eot[s]>=0 ) { // EOT Transition to accept state? + if ( debug ) System.err.println("EOT transition"); + s = eot[s]; + input.consume(); + // TODO: I had this as return accept[eot[s]] + // which assumed here that the EOT edge always + // went to an accept...faster to do this, but + // what about predicated edges coming from EOT + // target? + continue; + } + noViableAlt(s,input); + return 0; + } + s = snext; + input.consume(); + continue; + } + if ( eot[s]>=0 ) { // EOT Transition? + if ( debug ) System.err.println("EOT transition"); + s = eot[s]; + input.consume(); + continue; + } + if ( c==(char)Token.EOF && eof[s]>=0 ) { // EOF Transition to accept state? + if ( debug ) System.err.println("accept via EOF; predict "+accept[eof[s]]+" from "+eof[s]); + return accept[eof[s]]; + } + // not in range and not EOF/EOT, must be invalid symbol + if ( debug ) { + System.err.println("min["+s+"]="+min[s]); + System.err.println("max["+s+"]="+max[s]); + System.err.println("eot["+s+"]="+eot[s]); + System.err.println("eof["+s+"]="+eof[s]); + for (int p=0; p<transition[s].length; p++) { + System.err.print(transition[s][p]+" "); + } + System.err.println(); + } + noViableAlt(s,input); + return 0; + } + } + finally { + input.rewind(mark); + } + } + + protected void noViableAlt(int s, IntStream input) throws NoViableAltException { + if (recognizer.state.backtracking>0) { + recognizer.state.failed=true; + return; + } + NoViableAltException nvae = + new NoViableAltException(getDescription(), + decisionNumber, + s, + input); + error(nvae); + throw nvae; + } + + /** A hook for debugging interface */ + protected void error(NoViableAltException nvae) { ; } + + public int specialStateTransition(int s, IntStream input) + throws NoViableAltException + { + return -1; + } + + public String getDescription() { + return "n/a"; + } + + /** Given a String that has a run-length-encoding of some unsigned shorts + * like "\1\2\3\9", convert to short[] {2,9,9,9}. We do this to avoid + * static short[] which generates so much init code that the class won't + * compile. :( + */ + public static short[] unpackEncodedString(String encodedString) { + // walk first to find how big it is. + int size = 0; + for (int i=0; i<encodedString.length(); i+=2) { + size += encodedString.charAt(i); + } + short[] data = new short[size]; + int di = 0; + for (int i=0; i<encodedString.length(); i+=2) { + char n = encodedString.charAt(i); + char v = encodedString.charAt(i+1); + // add v n times to data + for (int j=1; j<=n; j++) { + data[di++] = (short)v; + } + } + return data; + } + + /** Hideous duplication of code, but I need different typed arrays out :( */ + public static char[] unpackEncodedStringToUnsignedChars(String encodedString) { + // walk first to find how big it is. + int size = 0; + for (int i=0; i<encodedString.length(); i+=2) { + size += encodedString.charAt(i); + } + char[] data = new char[size]; + int di = 0; + for (int i=0; i<encodedString.length(); i+=2) { + char n = encodedString.charAt(i); + char v = encodedString.charAt(i+1); + // add v n times to data + for (int j=1; j<=n; j++) { + data[di++] = v; + } + } + return data; + } + + /* + public int specialTransition(int state, int symbol) { + return 0; + } + */ +} diff --git a/src/org/antlr/runtime/EarlyExitException.java b/src/org/antlr/runtime/EarlyExitException.java new file mode 100755 index 0000000..1f9c1ec --- /dev/null +++ b/src/org/antlr/runtime/EarlyExitException.java @@ -0,0 +1,41 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** The recognizer did not match anything for a (..)+ loop. */ +public class EarlyExitException extends RecognitionException { + public int decisionNumber; + + /** Used for remote debugger deserialization */ + public EarlyExitException() {;} + + public EarlyExitException(int decisionNumber, IntStream input) { + super(input); + this.decisionNumber = decisionNumber; + } +} diff --git a/src/org/antlr/runtime/FailedPredicateException.java b/src/org/antlr/runtime/FailedPredicateException.java new file mode 100755 index 0000000..5bef1bd --- /dev/null +++ b/src/org/antlr/runtime/FailedPredicateException.java @@ -0,0 +1,54 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A semantic predicate failed during validation. Validation of predicates + * occurs when normally parsing the alternative just like matching a token. + * Disambiguating predicate evaluation occurs when we hoist a predicate into + * a prediction decision. + */ +public class FailedPredicateException extends RecognitionException { + public String ruleName; + public String predicateText; + + /** Used for remote debugger deserialization */ + public FailedPredicateException() {;} + + public FailedPredicateException(IntStream input, + String ruleName, + String predicateText) + { + super(input); + this.ruleName = ruleName; + this.predicateText = predicateText; + } + + public String toString() { + return "FailedPredicateException("+ruleName+",{"+predicateText+"}?)"; + } +} diff --git a/src/org/antlr/runtime/IntStream.java b/src/org/antlr/runtime/IntStream.java new file mode 100755 index 0000000..01c7841 --- /dev/null +++ b/src/org/antlr/runtime/IntStream.java @@ -0,0 +1,122 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A simple stream of integers used when all I care about is the char + * or token type sequence (such as interpretation). + */ +public interface IntStream { + void consume(); + + /** Get int at current input pointer + i ahead where i=1 is next int. + * Negative indexes are allowed. LA(-1) is previous token (token + * just matched). LA(-i) where i is before first token should + * yield -1, invalid char / EOF. + */ + int LA(int i); + + /** Tell the stream to start buffering if it hasn't already. Return + * current input position, index(), or some other marker so that + * when passed to rewind() you get back to the same spot. + * rewind(mark()) should not affect the input cursor. The Lexer + * track line/col info as well as input index so its markers are + * not pure input indexes. Same for tree node streams. + */ + int mark(); + + /** Return the current input symbol index 0..n where n indicates the + * last symbol has been read. The index is the symbol about to be + * read not the most recently read symbol. + */ + int index(); + + /** Reset the stream so that next call to index would return marker. + * The marker will usually be index() but it doesn't have to be. It's + * just a marker to indicate what state the stream was in. This is + * essentially calling release() and seek(). If there are markers + * created after this marker argument, this routine must unroll them + * like a stack. Assume the state the stream was in when this marker + * was created. + */ + void rewind(int marker); + + /** Rewind to the input position of the last marker. + * Used currently only after a cyclic DFA and just + * before starting a sem/syn predicate to get the + * input position back to the start of the decision. + * Do not "pop" the marker off the state. mark(i) + * and rewind(i) should balance still. It is + * like invoking rewind(last marker) but it should not "pop" + * the marker off. It's like seek(last marker's input position). + */ + void rewind(); + + /** You may want to commit to a backtrack but don't want to force the + * stream to keep bookkeeping objects around for a marker that is + * no longer necessary. This will have the same behavior as + * rewind() except it releases resources without the backward seek. + * This must throw away resources for all markers back to the marker + * argument. So if you're nested 5 levels of mark(), and then release(2) + * you have to release resources for depths 2..5. + */ + void release(int marker); + + /** Set the input cursor to the position indicated by index. This is + * normally used to seek ahead in the input stream. No buffering is + * required to do this unless you know your stream will use seek to + * move backwards such as when backtracking. + * + * This is different from rewind in its multi-directional + * requirement and in that its argument is strictly an input cursor (index). + * + * For char streams, seeking forward must update the stream state such + * as line number. For seeking backwards, you will be presumably + * backtracking using the mark/rewind mechanism that restores state and + * so this method does not need to update state when seeking backwards. + * + * Currently, this method is only used for efficient backtracking using + * memoization, but in the future it may be used for incremental parsing. + * + * The index is 0..n-1. A seek to position i means that LA(1) will + * return the ith symbol. So, seeking to 0 means LA(1) will return the + * first element in the stream. + */ + void seek(int index); + + /** Only makes sense for streams that buffer everything up probably, but + * might be useful to display the entire stream or for testing. This + * value includes a single EOF. + */ + int size(); + + /** Where are you getting symbols from? Normally, implementations will + * pass the buck all the way to the lexer who can ask its input stream + * for the file name or whatever. + */ + public String getSourceName(); +} diff --git a/src/org/antlr/runtime/LegacyCommonTokenStream.java b/src/org/antlr/runtime/LegacyCommonTokenStream.java new file mode 100755 index 0000000..f9c5e39 --- /dev/null +++ b/src/org/antlr/runtime/LegacyCommonTokenStream.java @@ -0,0 +1,394 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.util.*; + +/** The most common stream of tokens is one where every token is buffered up + * and tokens are prefiltered for a certain channel (the parser will only + * see these tokens and cannot change the filter channel number during the + * parse). + * + * TODO: how to access the full token stream? How to track all tokens matched per rule? + */ +public class LegacyCommonTokenStream implements TokenStream { + protected TokenSource tokenSource; + + /** Record every single token pulled from the source so we can reproduce + * chunks of it later. + */ + protected List tokens; + + /** Map<tokentype, channel> to override some Tokens' channel numbers */ + protected Map channelOverrideMap; + + /** Set<tokentype>; discard any tokens with this type */ + protected Set discardSet; + + /** Skip tokens on any channel but this one; this is how we skip whitespace... */ + protected int channel = Token.DEFAULT_CHANNEL; + + /** By default, track all incoming tokens */ + protected boolean discardOffChannelTokens = false; + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + protected int range = -1; // how deep have we gone? + + /** The index into the tokens list of the current token (next token + * to consume). p==-1 indicates that the tokens list is empty + */ + protected int p = -1; + + public LegacyCommonTokenStream() { + tokens = new ArrayList(500); + } + + public LegacyCommonTokenStream(TokenSource tokenSource) { + this(); + this.tokenSource = tokenSource; + } + + public LegacyCommonTokenStream(TokenSource tokenSource, int channel) { + this(tokenSource); + this.channel = channel; + } + + /** Reset this token stream by setting its token source. */ + public void setTokenSource(TokenSource tokenSource) { + this.tokenSource = tokenSource; + tokens.clear(); + p = -1; + channel = Token.DEFAULT_CHANNEL; + } + + /** Load all tokens from the token source and put in tokens. + * This is done upon first LT request because you might want to + * set some token type / channel overrides before filling buffer. + */ + protected void fillBuffer() { + int index = 0; + Token t = tokenSource.nextToken(); + while ( t!=null && t.getType()!=CharStream.EOF ) { + boolean discard = false; + // is there a channel override for token type? + if ( channelOverrideMap!=null ) { + Integer channelI = (Integer) + channelOverrideMap.get(new Integer(t.getType())); + if ( channelI!=null ) { + t.setChannel(channelI.intValue()); + } + } + if ( discardSet!=null && + discardSet.contains(new Integer(t.getType())) ) + { + discard = true; + } + else if ( discardOffChannelTokens && t.getChannel()!=this.channel ) { + discard = true; + } + if ( !discard ) { + t.setTokenIndex(index); + tokens.add(t); + index++; + } + t = tokenSource.nextToken(); + } + // leave p pointing at first token on channel + p = 0; + p = skipOffTokenChannels(p); + } + + /** Move the input pointer to the next incoming token. The stream + * must become active with LT(1) available. consume() simply + * moves the input pointer so that LT(1) points at the next + * input symbol. Consume at least one token. + * + * Walk past any token not on the channel the parser is listening to. + */ + public void consume() { + if ( p<tokens.size() ) { + p++; + p = skipOffTokenChannels(p); // leave p on valid token + } + } + + /** Given a starting index, return the index of the first on-channel + * token. + */ + protected int skipOffTokenChannels(int i) { + int n = tokens.size(); + while ( i<n && ((Token)tokens.get(i)).getChannel()!=channel ) { + i++; + } + return i; + } + + protected int skipOffTokenChannelsReverse(int i) { + while ( i>=0 && ((Token)tokens.get(i)).getChannel()!=channel ) { + i--; + } + return i; + } + + /** A simple filter mechanism whereby you can tell this token stream + * to force all tokens of type ttype to be on channel. For example, + * when interpreting, we cannot exec actions so we need to tell + * the stream to force all WS and NEWLINE to be a different, ignored + * channel. + */ + public void setTokenTypeChannel(int ttype, int channel) { + if ( channelOverrideMap==null ) { + channelOverrideMap = new HashMap(); + } + channelOverrideMap.put(new Integer(ttype), new Integer(channel)); + } + + public void discardTokenType(int ttype) { + if ( discardSet==null ) { + discardSet = new HashSet(); + } + discardSet.add(new Integer(ttype)); + } + + public void discardOffChannelTokens(boolean discardOffChannelTokens) { + this.discardOffChannelTokens = discardOffChannelTokens; + } + + public List getTokens() { + if ( p == -1 ) { + fillBuffer(); + } + return tokens; + } + + public List getTokens(int start, int stop) { + return getTokens(start, stop, (BitSet)null); + } + + /** Given a start and stop index, return a List of all tokens in + * the token type BitSet. Return null if no tokens were found. This + * method looks at both on and off channel tokens. + */ + public List getTokens(int start, int stop, BitSet types) { + if ( p == -1 ) { + fillBuffer(); + } + if ( stop>=tokens.size() ) { + stop=tokens.size()-1; + } + if ( start<0 ) { + start=0; + } + if ( start>stop ) { + return null; + } + + // list = tokens[start:stop]:{Token t, t.getType() in types} + List filteredTokens = new ArrayList(); + for (int i=start; i<=stop; i++) { + Token t = (Token)tokens.get(i); + if ( types==null || types.member(t.getType()) ) { + filteredTokens.add(t); + } + } + if ( filteredTokens.size()==0 ) { + filteredTokens = null; + } + return filteredTokens; + } + + public List getTokens(int start, int stop, List types) { + return getTokens(start,stop,new BitSet(types)); + } + + public List getTokens(int start, int stop, int ttype) { + return getTokens(start,stop,BitSet.of(ttype)); + } + + /** Get the ith token from the current position 1..n where k=1 is the + * first symbol of lookahead. + */ + public Token LT(int k) { + if ( p == -1 ) { + fillBuffer(); + } + if ( k==0 ) { + return null; + } + if ( k<0 ) { + return LB(-k); + } + //System.out.print("LT(p="+p+","+k+")="); + if ( (p+k-1) >= tokens.size() ) { + return (Token)tokens.get(tokens.size()-1); + } + //System.out.println(tokens.get(p+k-1)); + int i = p; + int n = 1; + // find k good tokens + while ( n<k ) { + // skip off-channel tokens + i = skipOffTokenChannels(i+1); // leave p on valid token + n++; + } + if ( i>=tokens.size() ) { + return (Token)tokens.get(tokens.size()-1); // must be EOF + } + + if ( i>range ) range = i; + return (Token)tokens.get(i); + } + + /** Look backwards k tokens on-channel tokens */ + protected Token LB(int k) { + //System.out.print("LB(p="+p+","+k+") "); + if ( p == -1 ) { + fillBuffer(); + } + if ( k==0 ) { + return null; + } + if ( (p-k)<0 ) { + return null; + } + + int i = p; + int n = 1; + // find k good tokens looking backwards + while ( n<=k ) { + // skip off-channel tokens + i = skipOffTokenChannelsReverse(i-1); // leave p on valid token + n++; + } + if ( i<0 ) { + return null; + } + return (Token)tokens.get(i); + } + + /** Return absolute token i; ignore which channel the tokens are on; + * that is, count all tokens not just on-channel tokens. + */ + public Token get(int i) { + return (Token)tokens.get(i); + } + + /** Get all tokens from start..stop inclusively */ + public List get(int start, int stop) { + if ( p == -1 ) fillBuffer(); + if ( start<0 || stop<0 ) return null; + return tokens.subList(start, stop); + } + + public int LA(int i) { + return LT(i).getType(); + } + + public int mark() { + if ( p == -1 ) { + fillBuffer(); + } + lastMarker = index(); + return lastMarker; + } + + public void release(int marker) { + // no resources to release + } + + public int size() { + return tokens.size(); + } + + public int index() { + return p; + } + + public int range() { + return range; + } + + public void rewind(int marker) { + seek(marker); + } + + public void rewind() { + seek(lastMarker); + } + + public void reset() { + p = 0; + lastMarker = 0; + } + + public void seek(int index) { + p = index; + } + + public TokenSource getTokenSource() { + return tokenSource; + } + + public String getSourceName() { + return getTokenSource().getSourceName(); + } + + public String toString() { + if ( p == -1 ) { + fillBuffer(); + } + return toString(0, tokens.size()-1); + } + + public String toString(int start, int stop) { + if ( start<0 || stop<0 ) { + return null; + } + if ( p == -1 ) { + fillBuffer(); + } + if ( stop>=tokens.size() ) { + stop = tokens.size()-1; + } + StringBuffer buf = new StringBuffer(); + for (int i = start; i <= stop; i++) { + Token t = (Token)tokens.get(i); + buf.append(t.getText()); + } + return buf.toString(); + } + + public String toString(Token start, Token stop) { + if ( start!=null && stop!=null ) { + return toString(start.getTokenIndex(), stop.getTokenIndex()); + } + return null; + } +} diff --git a/src/org/antlr/runtime/Lexer.java b/src/org/antlr/runtime/Lexer.java new file mode 100755 index 0000000..6037bcf --- /dev/null +++ b/src/org/antlr/runtime/Lexer.java @@ -0,0 +1,340 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A lexer is recognizer that draws input symbols from a character stream. + * lexer grammars result in a subclass of this object. A Lexer object + * uses simplified match() and error recovery mechanisms in the interest + * of speed. + */ +public abstract class Lexer extends BaseRecognizer implements TokenSource { + /** Where is the lexer drawing characters from? */ + protected CharStream input; + + public Lexer() { + } + + public Lexer(CharStream input) { + this.input = input; + } + + public Lexer(CharStream input, RecognizerSharedState state) { + super(state); + this.input = input; + } + + public void reset() { + super.reset(); // reset all recognizer state variables + // wack Lexer state variables + if ( input!=null ) { + input.seek(0); // rewind the input + } + if ( state==null ) { + return; // no shared state work to do + } + state.token = null; + state.type = Token.INVALID_TOKEN_TYPE; + state.channel = Token.DEFAULT_CHANNEL; + state.tokenStartCharIndex = -1; + state.tokenStartCharPositionInLine = -1; + state.tokenStartLine = -1; + state.text = null; + } + + /** Return a token from this source; i.e., match a token on the char + * stream. + */ + public Token nextToken() { + while (true) { + state.token = null; + state.channel = Token.DEFAULT_CHANNEL; + state.tokenStartCharIndex = input.index(); + state.tokenStartCharPositionInLine = input.getCharPositionInLine(); + state.tokenStartLine = input.getLine(); + state.text = null; + if ( input.LA(1)==CharStream.EOF ) { + Token eof = new CommonToken((CharStream)input,Token.EOF, + Token.DEFAULT_CHANNEL, + input.index(),input.index()); + eof.setLine(getLine()); + eof.setCharPositionInLine(getCharPositionInLine()); + return eof; + } + try { + mTokens(); + if ( state.token==null ) { + emit(); + } + else if ( state.token==Token.SKIP_TOKEN ) { + continue; + } + return state.token; + } + catch (NoViableAltException nva) { + reportError(nva); + recover(nva); // throw out current char and try again + } + catch (RecognitionException re) { + reportError(re); + // match() routine has already called recover() + } + } + } + + /** Instruct the lexer to skip creating a token for current lexer rule + * and look for another token. nextToken() knows to keep looking when + * a lexer rule finishes with token set to SKIP_TOKEN. Recall that + * if token==null at end of any token rule, it creates one for you + * and emits it. + */ + public void skip() { + state.token = Token.SKIP_TOKEN; + } + + /** This is the lexer entry point that sets instance var 'token' */ + public abstract void mTokens() throws RecognitionException; + + /** Set the char stream and reset the lexer */ + public void setCharStream(CharStream input) { + this.input = null; + reset(); + this.input = input; + } + + public CharStream getCharStream() { + return this.input; + } + + public String getSourceName() { + return input.getSourceName(); + } + + /** Currently does not support multiple emits per nextToken invocation + * for efficiency reasons. Subclass and override this method and + * nextToken (to push tokens into a list and pull from that list rather + * than a single variable as this implementation does). + */ + public void emit(Token token) { + state.token = token; + } + + /** The standard method called to automatically emit a token at the + * outermost lexical rule. The token object should point into the + * char buffer start..stop. If there is a text override in 'text', + * use that to set the token's text. Override this method to emit + * custom Token objects. + * + * If you are building trees, then you should also override + * Parser or TreeParser.getMissingSymbol(). + */ + public Token emit() { + Token t = new CommonToken(input, state.type, state.channel, state.tokenStartCharIndex, getCharIndex()-1); + t.setLine(state.tokenStartLine); + t.setText(state.text); + t.setCharPositionInLine(state.tokenStartCharPositionInLine); + emit(t); + return t; + } + + public void match(String s) throws MismatchedTokenException { + int i = 0; + while ( i<s.length() ) { + if ( input.LA(1)!=s.charAt(i) ) { + if ( state.backtracking>0 ) { + state.failed = true; + return; + } + MismatchedTokenException mte = + new MismatchedTokenException(s.charAt(i), input); + recover(mte); + throw mte; + } + i++; + input.consume(); + state.failed = false; + } + } + + public void matchAny() { + input.consume(); + } + + public void match(int c) throws MismatchedTokenException { + if ( input.LA(1)!=c ) { + if ( state.backtracking>0 ) { + state.failed = true; + return; + } + MismatchedTokenException mte = + new MismatchedTokenException(c, input); + recover(mte); // don't really recover; just consume in lexer + throw mte; + } + input.consume(); + state.failed = false; + } + + public void matchRange(int a, int b) + throws MismatchedRangeException + { + if ( input.LA(1)<a || input.LA(1)>b ) { + if ( state.backtracking>0 ) { + state.failed = true; + return; + } + MismatchedRangeException mre = + new MismatchedRangeException(a,b,input); + recover(mre); + throw mre; + } + input.consume(); + state.failed = false; + } + + public int getLine() { + return input.getLine(); + } + + public int getCharPositionInLine() { + return input.getCharPositionInLine(); + } + + /** What is the index of the current character of lookahead? */ + public int getCharIndex() { + return input.index(); + } + + /** Return the text matched so far for the current token or any + * text override. + */ + public String getText() { + if ( state.text!=null ) { + return state.text; + } + return input.substring(state.tokenStartCharIndex,getCharIndex()-1); + } + + /** Set the complete text of this token; it wipes any previous + * changes to the text. + */ + public void setText(String text) { + state.text = text; + } + + public void reportError(RecognitionException e) { + /** TODO: not thought about recovery in lexer yet. + * + // if we've already reported an error and have not matched a token + // yet successfully, don't report any errors. + if ( errorRecovery ) { + //System.err.print("[SPURIOUS] "); + return; + } + errorRecovery = true; + */ + + displayRecognitionError(this.getTokenNames(), e); + } + + public String getErrorMessage(RecognitionException e, String[] tokenNames) { + String msg = null; + if ( e instanceof MismatchedTokenException ) { + MismatchedTokenException mte = (MismatchedTokenException)e; + msg = "mismatched character "+getCharErrorDisplay(e.c)+" expecting "+getCharErrorDisplay(mte.expecting); + } + else if ( e instanceof NoViableAltException ) { + NoViableAltException nvae = (NoViableAltException)e; + // for development, can add "decision=<<"+nvae.grammarDecisionDescription+">>" + // and "(decision="+nvae.decisionNumber+") and + // "state "+nvae.stateNumber + msg = "no viable alternative at character "+getCharErrorDisplay(e.c); + } + else if ( e instanceof EarlyExitException ) { + EarlyExitException eee = (EarlyExitException)e; + // for development, can add "(decision="+eee.decisionNumber+")" + msg = "required (...)+ loop did not match anything at character "+getCharErrorDisplay(e.c); + } + else if ( e instanceof MismatchedNotSetException ) { + MismatchedNotSetException mse = (MismatchedNotSetException)e; + msg = "mismatched character "+getCharErrorDisplay(e.c)+" expecting set "+mse.expecting; + } + else if ( e instanceof MismatchedSetException ) { + MismatchedSetException mse = (MismatchedSetException)e; + msg = "mismatched character "+getCharErrorDisplay(e.c)+" expecting set "+mse.expecting; + } + else if ( e instanceof MismatchedRangeException ) { + MismatchedRangeException mre = (MismatchedRangeException)e; + msg = "mismatched character "+getCharErrorDisplay(e.c)+" expecting set "+ + getCharErrorDisplay(mre.a)+".."+getCharErrorDisplay(mre.b); + } + else { + msg = super.getErrorMessage(e, tokenNames); + } + return msg; + } + + public String getCharErrorDisplay(int c) { + String s = String.valueOf((char)c); + switch ( c ) { + case Token.EOF : + s = "<EOF>"; + break; + case '\n' : + s = "\\n"; + break; + case '\t' : + s = "\\t"; + break; + case '\r' : + s = "\\r"; + break; + } + return "'"+s+"'"; + } + + /** Lexers can normally match any char in it's vocabulary after matching + * a token, so do the easy thing and just kill a character and hope + * it all works out. You can instead use the rule invocation stack + * to do sophisticated error recovery if you are in a fragment rule. + */ + public void recover(RecognitionException re) { + //System.out.println("consuming char "+(char)input.LA(1)+" during recovery"); + //re.printStackTrace(); + input.consume(); + } + + public void traceIn(String ruleName, int ruleIndex) { + String inputSymbol = ((char)input.LT(1))+" line="+getLine()+":"+getCharPositionInLine(); + super.traceIn(ruleName, ruleIndex, inputSymbol); + } + + public void traceOut(String ruleName, int ruleIndex) { + String inputSymbol = ((char)input.LT(1))+" line="+getLine()+":"+getCharPositionInLine(); + super.traceOut(ruleName, ruleIndex, inputSymbol); + } +} diff --git a/src/org/antlr/runtime/MismatchedNotSetException.java b/src/org/antlr/runtime/MismatchedNotSetException.java new file mode 100755 index 0000000..49ceb27 --- /dev/null +++ b/src/org/antlr/runtime/MismatchedNotSetException.java @@ -0,0 +1,41 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +public class MismatchedNotSetException extends MismatchedSetException { + /** Used for remote debugger deserialization */ + public MismatchedNotSetException() {;} + + public MismatchedNotSetException(BitSet expecting, IntStream input) { + super(expecting, input); + } + + public String toString() { + return "MismatchedNotSetException("+getUnexpectedType()+"!="+expecting+")"; + } +} diff --git a/src/org/antlr/runtime/MismatchedRangeException.java b/src/org/antlr/runtime/MismatchedRangeException.java new file mode 100755 index 0000000..23b3d87 --- /dev/null +++ b/src/org/antlr/runtime/MismatchedRangeException.java @@ -0,0 +1,45 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +public class MismatchedRangeException extends RecognitionException { + public int a,b; + + /** Used for remote debugger deserialization */ + public MismatchedRangeException() {;} + + public MismatchedRangeException(int a, int b, IntStream input) { + super(input); + this.a = a; + this.b = b; + } + + public String toString() { + return "MismatchedNotSetException("+getUnexpectedType()+" not in ["+a+","+b+"])"; + } +} diff --git a/src/org/antlr/runtime/MismatchedSetException.java b/src/org/antlr/runtime/MismatchedSetException.java new file mode 100755 index 0000000..9bfa530 --- /dev/null +++ b/src/org/antlr/runtime/MismatchedSetException.java @@ -0,0 +1,44 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +public class MismatchedSetException extends RecognitionException { + public BitSet expecting; + + /** Used for remote debugger deserialization */ + public MismatchedSetException() {;} + + public MismatchedSetException(BitSet expecting, IntStream input) { + super(input); + this.expecting = expecting; + } + + public String toString() { + return "MismatchedSetException("+getUnexpectedType()+"!="+expecting+")"; + } +} diff --git a/src/org/antlr/runtime/MismatchedTokenException.java b/src/org/antlr/runtime/MismatchedTokenException.java new file mode 100755 index 0000000..07ae814 --- /dev/null +++ b/src/org/antlr/runtime/MismatchedTokenException.java @@ -0,0 +1,45 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A mismatched char or Token or tree node */ +public class MismatchedTokenException extends RecognitionException { + public int expecting = Token.INVALID_TOKEN_TYPE; + + /** Used for remote debugger deserialization */ + public MismatchedTokenException() {;} + + public MismatchedTokenException(int expecting, IntStream input) { + super(input); + this.expecting = expecting; + } + + public String toString() { + return "MismatchedTokenException("+getUnexpectedType()+"!="+expecting+")"; + } +} diff --git a/src/org/antlr/runtime/MismatchedTreeNodeException.java b/src/org/antlr/runtime/MismatchedTreeNodeException.java new file mode 100755 index 0000000..99c834d --- /dev/null +++ b/src/org/antlr/runtime/MismatchedTreeNodeException.java @@ -0,0 +1,49 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import org.antlr.runtime.tree.TreeNodeStream; +import org.antlr.runtime.tree.Tree; + +/** + */ +public class MismatchedTreeNodeException extends RecognitionException { + public int expecting; + + public MismatchedTreeNodeException() { + } + + public MismatchedTreeNodeException(int expecting, TreeNodeStream input) { + super(input); + this.expecting = expecting; + } + + public String toString() { + return "MismatchedTreeNodeException("+getUnexpectedType()+"!="+expecting+")"; + } +} diff --git a/src/org/antlr/runtime/MissingTokenException.java b/src/org/antlr/runtime/MissingTokenException.java new file mode 100755 index 0000000..9eda1f2 --- /dev/null +++ b/src/org/antlr/runtime/MissingTokenException.java @@ -0,0 +1,56 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** We were expecting a token but it's not found. The current token + * is actually what we wanted next. Used for tree node errors too. + */ +public class MissingTokenException extends MismatchedTokenException { + public Object inserted; + /** Used for remote debugger deserialization */ + public MissingTokenException() {;} + + public MissingTokenException(int expecting, IntStream input, Object inserted) { + super(expecting, input); + this.inserted = inserted; + } + + public int getMissingType() { + return expecting; + } + + public String toString() { + if ( inserted!=null && token!=null ) { + return "MissingTokenException(inserted "+inserted+" at "+token.getText()+")"; + } + if ( token!=null ) { + return "MissingTokenException(at "+token.getText()+")"; + } + return "MissingTokenException"; + } +} diff --git a/src/org/antlr/runtime/NoViableAltException.java b/src/org/antlr/runtime/NoViableAltException.java new file mode 100755 index 0000000..889045f --- /dev/null +++ b/src/org/antlr/runtime/NoViableAltException.java @@ -0,0 +1,57 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +public class NoViableAltException extends RecognitionException { + public String grammarDecisionDescription; + public int decisionNumber; + public int stateNumber; + + /** Used for remote debugger deserialization */ + public NoViableAltException() {;} + + public NoViableAltException(String grammarDecisionDescription, + int decisionNumber, + int stateNumber, + IntStream input) + { + super(input); + this.grammarDecisionDescription = grammarDecisionDescription; + this.decisionNumber = decisionNumber; + this.stateNumber = stateNumber; + } + + public String toString() { + if ( input instanceof CharStream ) { + return "NoViableAltException('"+(char)getUnexpectedType()+"'@["+grammarDecisionDescription+"])"; + } + else { + return "NoViableAltException("+getUnexpectedType()+"@["+grammarDecisionDescription+"])"; + } + } +} diff --git a/src/org/antlr/runtime/Parser.java b/src/org/antlr/runtime/Parser.java new file mode 100755 index 0000000..9cf937a --- /dev/null +++ b/src/org/antlr/runtime/Parser.java @@ -0,0 +1,98 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A parser for TokenStreams. "parser grammars" result in a subclass + * of this. + */ +public class Parser extends BaseRecognizer { + public TokenStream input; + + public Parser(TokenStream input) { + super(); // highlight that we go to super to set state object + setTokenStream(input); + } + + public Parser(TokenStream input, RecognizerSharedState state) { + super(state); // share the state object with another parser + this.input = input; + } + + public void reset() { + super.reset(); // reset all recognizer state variables + if ( input!=null ) { + input.seek(0); // rewind the input + } + } + + protected Object getCurrentInputSymbol(IntStream input) { + return ((TokenStream)input).LT(1); + } + + protected Object getMissingSymbol(IntStream input, + RecognitionException e, + int expectedTokenType, + BitSet follow) + { + String tokenText = null; + if ( expectedTokenType==Token.EOF ) tokenText = "<missing EOF>"; + else tokenText = "<missing "+getTokenNames()[expectedTokenType]+">"; + CommonToken t = new CommonToken(expectedTokenType, tokenText); + Token current = ((TokenStream)input).LT(1); + if ( current.getType() == Token.EOF ) { + current = ((TokenStream)input).LT(-1); + } + t.line = current.getLine(); + t.charPositionInLine = current.getCharPositionInLine(); + t.channel = DEFAULT_TOKEN_CHANNEL; + return t; + } + + /** Set the token stream and reset the parser */ + public void setTokenStream(TokenStream input) { + this.input = null; + reset(); + this.input = input; + } + + public TokenStream getTokenStream() { + return input; + } + + public String getSourceName() { + return input.getSourceName(); + } + + public void traceIn(String ruleName, int ruleIndex) { + super.traceIn(ruleName, ruleIndex, input.LT(1)); + } + + public void traceOut(String ruleName, int ruleIndex) { + super.traceOut(ruleName, ruleIndex, input.LT(1)); + } +} diff --git a/src/org/antlr/runtime/ParserRuleReturnScope.java b/src/org/antlr/runtime/ParserRuleReturnScope.java new file mode 100755 index 0000000..9237e4e --- /dev/null +++ b/src/org/antlr/runtime/ParserRuleReturnScope.java @@ -0,0 +1,52 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** Rules that return more than a single value must return an object + * containing all the values. Besides the properties defined in + * RuleLabelScope.predefinedRulePropertiesScope there may be user-defined + * return values. This class simply defines the minimum properties that + * are always defined and methods to access the others that might be + * available depending on output option such as template and tree. + * + * Note text is not an actual property of the return value, it is computed + * from start and stop using the input stream's toString() method. I + * could add a ctor to this so that we can pass in and store the input + * stream, but I'm not sure we want to do that. It would seem to be undefined + * to get the .text property anyway if the rule matches tokens from multiple + * input streams. + * + * I do not use getters for fields of objects that are used simply to + * group values such as this aggregate. The getters/setters are there to + * satisfy the superclass interface. + */ +public class ParserRuleReturnScope extends RuleReturnScope { + public Token start, stop; + public Object getStart() { return start; } + public Object getStop() { return stop; } +} diff --git a/src/org/antlr/runtime/RecognitionException.java b/src/org/antlr/runtime/RecognitionException.java new file mode 100755 index 0000000..3e79f99 --- /dev/null +++ b/src/org/antlr/runtime/RecognitionException.java @@ -0,0 +1,180 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import org.antlr.runtime.tree.*; + +/** The root of the ANTLR exception hierarchy. + * + * To avoid English-only error messages and to generally make things + * as flexible as possible, these exceptions are not created with strings, + * but rather the information necessary to generate an error. Then + * the various reporting methods in Parser and Lexer can be overridden + * to generate a localized error message. For example, MismatchedToken + * exceptions are built with the expected token type. + * So, don't expect getMessage() to return anything. + * + * Note that as of Java 1.4, you can access the stack trace, which means + * that you can compute the complete trace of rules from the start symbol. + * This gives you considerable context information with which to generate + * useful error messages. + * + * ANTLR generates code that throws exceptions upon recognition error and + * also generates code to catch these exceptions in each rule. If you + * want to quit upon first error, you can turn off the automatic error + * handling mechanism using rulecatch action, but you still need to + * override methods mismatch and recoverFromMismatchSet. + * + * In general, the recognition exceptions can track where in a grammar a + * problem occurred and/or what was the expected input. While the parser + * knows its state (such as current input symbol and line info) that + * state can change before the exception is reported so current token index + * is computed and stored at exception time. From this info, you can + * perhaps print an entire line of input not just a single token, for example. + * Better to just say the recognizer had a problem and then let the parser + * figure out a fancy report. + */ +public class RecognitionException extends Exception { + /** What input stream did the error occur in? */ + public transient IntStream input; + + /** What is index of token/char were we looking at when the error occurred? */ + public int index; + + /** The current Token when an error occurred. Since not all streams + * can retrieve the ith Token, we have to track the Token object. + * For parsers. Even when it's a tree parser, token might be set. + */ + public Token token; + + /** If this is a tree parser exception, node is set to the node with + * the problem. + */ + public Object node; + + /** The current char when an error occurred. For lexers. */ + public int c; + + /** Track the line at which the error occurred in case this is + * generated from a lexer. We need to track this since the + * unexpected char doesn't carry the line info. + */ + public int line; + + public int charPositionInLine; + + /** If you are parsing a tree node stream, you will encounter som + * imaginary nodes w/o line/col info. We now search backwards looking + * for most recent token with line/col info, but notify getErrorHeader() + * that info is approximate. + */ + public boolean approximateLineInfo; + + /** Used for remote debugger deserialization */ + public RecognitionException() { + } + + public RecognitionException(IntStream input) { + this.input = input; + this.index = input.index(); + if ( input instanceof TokenStream ) { + this.token = ((TokenStream)input).LT(1); + this.line = token.getLine(); + this.charPositionInLine = token.getCharPositionInLine(); + } + if ( input instanceof TreeNodeStream ) { + extractInformationFromTreeNodeStream(input); + } + else if ( input instanceof CharStream ) { + this.c = input.LA(1); + this.line = ((CharStream)input).getLine(); + this.charPositionInLine = ((CharStream)input).getCharPositionInLine(); + } + else { + this.c = input.LA(1); + } + } + + protected void extractInformationFromTreeNodeStream(IntStream input) { + TreeNodeStream nodes = (TreeNodeStream)input; + this.node = nodes.LT(1); + TreeAdaptor adaptor = nodes.getTreeAdaptor(); + Token payload = adaptor.getToken(node); + if ( payload!=null ) { + this.token = payload; + if ( payload.getLine()<= 0 ) { + // imaginary node; no line/pos info; scan backwards + int i = -1; + Object priorNode = nodes.LT(i); + while ( priorNode!=null ) { + Token priorPayload = adaptor.getToken(priorNode); + if ( priorPayload!=null && priorPayload.getLine()>0 ) { + // we found the most recent real line / pos info + this.line = priorPayload.getLine(); + this.charPositionInLine = priorPayload.getCharPositionInLine(); + this.approximateLineInfo = true; + break; + } + --i; + priorNode = nodes.LT(i); + } + } + else { // node created from real token + this.line = payload.getLine(); + this.charPositionInLine = payload.getCharPositionInLine(); + } + } + else if ( this.node instanceof Tree) { + this.line = ((Tree)this.node).getLine(); + this.charPositionInLine = ((Tree)this.node).getCharPositionInLine(); + if ( this.node instanceof CommonTree) { + this.token = ((CommonTree)this.node).token; + } + } + else { + int type = adaptor.getType(this.node); + String text = adaptor.getText(this.node); + this.token = new CommonToken(type, text); + } + } + + /** Return the token type or char of the unexpected input element */ + public int getUnexpectedType() { + if ( input instanceof TokenStream ) { + return token.getType(); + } + else if ( input instanceof TreeNodeStream ) { + TreeNodeStream nodes = (TreeNodeStream)input; + TreeAdaptor adaptor = nodes.getTreeAdaptor(); + return adaptor.getType(node); + } + else { + return c; + } + } +} diff --git a/src/org/antlr/runtime/RecognizerSharedState.java b/src/org/antlr/runtime/RecognizerSharedState.java new file mode 100755 index 0000000..068ac3b --- /dev/null +++ b/src/org/antlr/runtime/RecognizerSharedState.java @@ -0,0 +1,144 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */package org.antlr.runtime; + +import java.util.Map; + +/** The set of fields needed by an abstract recognizer to recognize input + * and recover from errors etc... As a separate state object, it can be + * shared among multiple grammars; e.g., when one grammar imports another. + * + * These fields are publically visible but the actual state pointer per + * parser is protected. + */ +public class RecognizerSharedState { + /** Track the set of token types that can follow any rule invocation. + * Stack grows upwards. When it hits the max, it grows 2x in size + * and keeps going. + */ + public BitSet[] following = new BitSet[BaseRecognizer.INITIAL_FOLLOW_STACK_SIZE]; + public int _fsp = -1; + + /** This is true when we see an error and before having successfully + * matched a token. Prevents generation of more than one error message + * per error. + */ + public boolean errorRecovery = false; + + /** The index into the input stream where the last error occurred. + * This is used to prevent infinite loops where an error is found + * but no token is consumed during recovery...another error is found, + * ad naseum. This is a failsafe mechanism to guarantee that at least + * one token/tree node is consumed for two errors. + */ + public int lastErrorIndex = -1; + + /** In lieu of a return value, this indicates that a rule or token + * has failed to match. Reset to false upon valid token match. + */ + public boolean failed = false; + + /** Did the recognizer encounter a syntax error? Track how many. */ + public int syntaxErrors = 0; + + /** If 0, no backtracking is going on. Safe to exec actions etc... + * If >0 then it's the level of backtracking. + */ + public int backtracking = 0; + + /** An array[size num rules] of Map<Integer,Integer> that tracks + * the stop token index for each rule. ruleMemo[ruleIndex] is + * the memoization table for ruleIndex. For key ruleStartIndex, you + * get back the stop token for associated rule or MEMO_RULE_FAILED. + * + * This is only used if rule memoization is on (which it is by default). + */ + public Map[] ruleMemo; + + + // LEXER FIELDS (must be in same state object to avoid casting + // constantly in generated code and Lexer object) :( + + + /** The goal of all lexer rules/methods is to create a token object. + * This is an instance variable as multiple rules may collaborate to + * create a single token. nextToken will return this object after + * matching lexer rule(s). If you subclass to allow multiple token + * emissions, then set this to the last token to be matched or + * something nonnull so that the auto token emit mechanism will not + * emit another token. + */ + public Token token; + + /** What character index in the stream did the current token start at? + * Needed, for example, to get the text for current token. Set at + * the start of nextToken. + */ + public int tokenStartCharIndex = -1; + + /** The line on which the first character of the token resides */ + public int tokenStartLine; + + /** The character position of first character within the line */ + public int tokenStartCharPositionInLine; + + /** The channel number for the current token */ + public int channel; + + /** The token type for the current token */ + public int type; + + /** You can set the text for the current token to override what is in + * the input char buffer. Use setText() or can set this instance var. + */ + public String text; + + public RecognizerSharedState() {;} + + public RecognizerSharedState(RecognizerSharedState state) { + if ( this.following.length < state.following.length ) { + this.following = new BitSet[state.following.length]; + } + System.arraycopy(state.following, 0, this.following, 0, state.following.length); + this._fsp = state._fsp; + this.errorRecovery = state.errorRecovery; + this.lastErrorIndex = state.lastErrorIndex; + this.failed = state.failed; + this.syntaxErrors = state.syntaxErrors; + this.backtracking = state.backtracking; + if ( state.ruleMemo!=null ) { + this.ruleMemo = new Map[state.ruleMemo.length]; + System.arraycopy(state.ruleMemo, 0, this.ruleMemo, 0, state.ruleMemo.length); + } + this.token = state.token; + this.tokenStartCharIndex = state.tokenStartCharIndex; + this.tokenStartCharPositionInLine = state.tokenStartCharPositionInLine; + this.channel = state.channel; + this.type = state.type; + this.text = state.text; + } +} diff --git a/src/org/antlr/runtime/RuleReturnScope.java b/src/org/antlr/runtime/RuleReturnScope.java new file mode 100755 index 0000000..85702ca --- /dev/null +++ b/src/org/antlr/runtime/RuleReturnScope.java @@ -0,0 +1,42 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** Rules can return start/stop info as well as possible trees and templates */ +public class RuleReturnScope { + /** Return the start token or tree */ + public Object getStart() { return null; } + /** Return the stop token or tree */ + public Object getStop() { return null; } + /** Has a value potentially if output=AST; */ + public Object getTree() { return null; } + /** Has a value potentially if output=template; Don't use StringTemplate + * type as it then causes a dependency with ST lib. + */ + public Object getTemplate() { return null; } +} diff --git a/src/org/antlr/runtime/SerializedGrammar.java b/src/org/antlr/runtime/SerializedGrammar.java new file mode 100755 index 0000000..a609053 --- /dev/null +++ b/src/org/antlr/runtime/SerializedGrammar.java @@ -0,0 +1,198 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.runtime; + +import java.io.IOException; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.util.List; +import java.util.ArrayList; + +public class SerializedGrammar { + public static final String COOKIE = "$ANTLR"; + public static final int FORMAT_VERSION = 1; + //public static org.antlr.tool.Grammar gr; // TESTING ONLY; remove later + + public String name; + public char type; // in {l, p, t, c} + public List rules; + + class Rule { + String name; + Block block; + public Rule(String name, Block block) { + this.name = name; + this.block = block; + } + public String toString() { + return name+":"+block; + } + } + + class Block { + List[] alts; + public Block(List[] alts) { + this.alts = alts; + } + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0; i < alts.length; i++) { + List alt = alts[i]; + if ( i>0 ) buf.append("|"); + buf.append(alt.toString()); + } + buf.append(")"); + return buf.toString(); + } + } + + class TokenRef { + int ttype; + public TokenRef(int ttype) { this.ttype = ttype; } + public String toString() { return String.valueOf(ttype); } + } + + class RuleRef { + int ruleIndex; + public RuleRef(int ruleIndex) { this.ruleIndex = ruleIndex; } + public String toString() { return String.valueOf(ruleIndex); } + } + + public SerializedGrammar(String filename) throws IOException { + System.out.println("loading "+filename); + FileInputStream fis = new FileInputStream(filename); + BufferedInputStream bos = new BufferedInputStream(fis); + DataInputStream in = new DataInputStream(bos); + readFile(in); + in.close(); + } + + protected void readFile(DataInputStream in) throws IOException { + String cookie = readString(in); // get $ANTLR + if ( !cookie.equals(COOKIE) ) throw new IOException("not a serialized grammar file"); + int version = in.readByte(); + char grammarType = (char)in.readByte(); + this.type = grammarType; + String grammarName = readString(in); + this.name = grammarName; + System.out.println(grammarType+" grammar "+grammarName); + int numRules = in.readShort(); + System.out.println("num rules = "+numRules); + rules = readRules(in, numRules); + } + + protected List readRules(DataInputStream in, int numRules) throws IOException { + List rules = new ArrayList(); + for (int i=0; i<numRules; i++) { + Rule r = readRule(in); + rules.add(r); + } + return rules; + } + + protected Rule readRule(DataInputStream in) throws IOException { + byte R = in.readByte(); + if ( R!='R' ) throw new IOException("missing R on start of rule"); + String name = readString(in); + System.out.println("rule: "+name); + byte B = in.readByte(); + Block b = readBlock(in); + byte period = in.readByte(); + if ( period!='.' ) throw new IOException("missing . on end of rule"); + return new Rule(name, b); + } + + protected Block readBlock(DataInputStream in) throws IOException { + int nalts = in.readShort(); + List[] alts = new List[nalts]; + //System.out.println("enter block n="+nalts); + for (int i=0; i<nalts; i++) { + List alt = readAlt(in); + alts[i] = alt; + } + //System.out.println("exit block"); + return new Block(alts); + } + + protected List readAlt(DataInputStream in) throws IOException { + List alt = new ArrayList(); + byte A = in.readByte(); + if ( A!='A' ) throw new IOException("missing A on start of alt"); + byte cmd = in.readByte(); + while ( cmd!=';' ) { + switch (cmd) { + case 't' : + int ttype = in.readShort(); + alt.add(new TokenRef(ttype)); + //System.out.println("read token "+gr.getTokenDisplayName(ttype)); + break; + case 'r' : + int ruleIndex = in.readShort(); + alt.add(new RuleRef(ruleIndex)); + //System.out.println("read rule "+gr.getRuleName(ruleIndex)); + break; + case '.' : // wildcard + break; + case '-' : // range + int from = in.readChar(); + int to = in.readChar(); + break; + case '~' : // not + int notThisTokenType = in.readShort(); + break; + case 'B' : // nested block + Block b = readBlock(in); + alt.add(b); + break; + } + cmd = in.readByte(); + } + //System.out.println("exit alt"); + return alt; + } + + protected String readString(DataInputStream in) throws IOException { + byte c = in.readByte(); + StringBuffer buf = new StringBuffer(); + while ( c!=';' ) { + buf.append((char)c); + c = in.readByte(); + } + return buf.toString(); + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append(type+" grammar "+name); + buf.append(rules); + return buf.toString(); + } +} diff --git a/src/org/antlr/runtime/Token.java b/src/org/antlr/runtime/Token.java new file mode 100755 index 0000000..b8eb95e --- /dev/null +++ b/src/org/antlr/runtime/Token.java @@ -0,0 +1,92 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +public interface Token { + public static final int EOR_TOKEN_TYPE = 1; + + /** imaginary tree navigation type; traverse "get child" link */ + public static final int DOWN = 2; + /** imaginary tree navigation type; finish with a child list */ + public static final int UP = 3; + + public static final int MIN_TOKEN_TYPE = UP+1; + + public static final int EOF = CharStream.EOF; + // TODO: remove once we go ANTLR v3.3 + public static final Token EOF_TOKEN = new CommonToken(EOF); + + public static final int INVALID_TOKEN_TYPE = 0; + public static final Token INVALID_TOKEN = new CommonToken(INVALID_TOKEN_TYPE); + + /** In an action, a lexer rule can set token to this SKIP_TOKEN and ANTLR + * will avoid creating a token for this symbol and try to fetch another. + */ + public static final Token SKIP_TOKEN = new CommonToken(INVALID_TOKEN_TYPE); + + /** All tokens go to the parser (unless skip() is called in that rule) + * on a particular "channel". The parser tunes to a particular channel + * so that whitespace etc... can go to the parser on a "hidden" channel. + */ + public static final int DEFAULT_CHANNEL = 0; + + /** Anything on different channel than DEFAULT_CHANNEL is not parsed + * by parser. + */ + public static final int HIDDEN_CHANNEL = 99; + + /** Get the text of the token */ + public String getText(); + public void setText(String text); + + public int getType(); + public void setType(int ttype); + /** The line number on which this token was matched; line=1..n */ + public int getLine(); + public void setLine(int line); + + /** The index of the first character relative to the beginning of the line 0..n-1 */ + public int getCharPositionInLine(); + public void setCharPositionInLine(int pos); + + public int getChannel(); + public void setChannel(int channel); + + /** An index from 0..n-1 of the token object in the input stream. + * This must be valid in order to use the ANTLRWorks debugger. + */ + public int getTokenIndex(); + public void setTokenIndex(int index); + + /** From what character stream was this token created? You don't have to + * implement but it's nice to know where a Token comes from if you have + * include files etc... on the input. + */ + public CharStream getInputStream(); + public void setInputStream(CharStream input); +} diff --git a/src/org/antlr/runtime/TokenRewriteStream.java b/src/org/antlr/runtime/TokenRewriteStream.java new file mode 100755 index 0000000..8437441 --- /dev/null +++ b/src/org/antlr/runtime/TokenRewriteStream.java @@ -0,0 +1,590 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.util.*; + +/** Useful for dumping out the input stream after doing some + * augmentation or other manipulations. + * + * You can insert stuff, replace, and delete chunks. Note that the + * operations are done lazily--only if you convert the buffer to a + * String. This is very efficient because you are not moving data around + * all the time. As the buffer of tokens is converted to strings, the + * toString() method(s) check to see if there is an operation at the + * current index. If so, the operation is done and then normal String + * rendering continues on the buffer. This is like having multiple Turing + * machine instruction streams (programs) operating on a single input tape. :) + * + * Since the operations are done lazily at toString-time, operations do not + * screw up the token index values. That is, an insert operation at token + * index i does not change the index values for tokens i+1..n-1. + * + * Because operations never actually alter the buffer, you may always get + * the original token stream back without undoing anything. Since + * the instructions are queued up, you can easily simulate transactions and + * roll back any changes if there is an error just by removing instructions. + * For example, + * + * CharStream input = new ANTLRFileStream("input"); + * TLexer lex = new TLexer(input); + * TokenRewriteStream tokens = new TokenRewriteStream(lex); + * T parser = new T(tokens); + * parser.startRule(); + * + * Then in the rules, you can execute + * Token t,u; + * ... + * input.insertAfter(t, "text to put after t");} + * input.insertAfter(u, "text after u");} + * System.out.println(tokens.toString()); + * + * Actually, you have to cast the 'input' to a TokenRewriteStream. :( + * + * You can also have multiple "instruction streams" and get multiple + * rewrites from a single pass over the input. Just name the instruction + * streams and use that name again when printing the buffer. This could be + * useful for generating a C file and also its header file--all from the + * same buffer: + * + * tokens.insertAfter("pass1", t, "text to put after t");} + * tokens.insertAfter("pass2", u, "text after u");} + * System.out.println(tokens.toString("pass1")); + * System.out.println(tokens.toString("pass2")); + * + * If you don't use named rewrite streams, a "default" stream is used as + * the first example shows. + */ +public class TokenRewriteStream extends CommonTokenStream { + public static final String DEFAULT_PROGRAM_NAME = "default"; + public static final int PROGRAM_INIT_SIZE = 100; + public static final int MIN_TOKEN_INDEX = 0; + + // Define the rewrite operation hierarchy + + class RewriteOperation { + /** What index into rewrites List are we? */ + protected int instructionIndex; + /** Token buffer index. */ + protected int index; + protected Object text; + + protected RewriteOperation(int index) { + this.index = index; + } + + protected RewriteOperation(int index, Object text) { + this.index = index; + this.text = text; + } + /** Execute the rewrite operation by possibly adding to the buffer. + * Return the index of the next token to operate on. + */ + public int execute(StringBuffer buf) { + return index; + } + public String toString() { + String opName = getClass().getName(); + int $index = opName.indexOf('$'); + opName = opName.substring($index+1, opName.length()); + return "<"+opName+"@"+tokens.get(index)+ + ":\""+text+"\">"; + } + } + + class InsertBeforeOp extends RewriteOperation { + public InsertBeforeOp(int index, Object text) { + super(index,text); + } + public int execute(StringBuffer buf) { + buf.append(text); + if ( tokens.get(index).getType()!=Token.EOF ) { + buf.append(tokens.get(index).getText()); + } + return index+1; + } + } + + /** I'm going to try replacing range from x..y with (y-x)+1 ReplaceOp + * instructions. + */ + class ReplaceOp extends RewriteOperation { + protected int lastIndex; + public ReplaceOp(int from, int to, Object text) { + super(from,text); + lastIndex = to; + } + public int execute(StringBuffer buf) { + if ( text!=null ) { + buf.append(text); + } + return lastIndex+1; + } + public String toString() { + if ( text==null ) { + return "<DeleteOp@"+tokens.get(index)+ + ".."+tokens.get(lastIndex)+">"; + } + return "<ReplaceOp@"+tokens.get(index)+ + ".."+tokens.get(lastIndex)+":\""+text+"\">"; + } + } + + /** You may have multiple, named streams of rewrite operations. + * I'm calling these things "programs." + * Maps String (name) -> rewrite (List) + */ + protected Map programs = null; + + /** Map String (program name) -> Integer index */ + protected Map lastRewriteTokenIndexes = null; + + public TokenRewriteStream() { + init(); + } + + protected void init() { + programs = new HashMap(); + programs.put(DEFAULT_PROGRAM_NAME, new ArrayList(PROGRAM_INIT_SIZE)); + lastRewriteTokenIndexes = new HashMap(); + } + + public TokenRewriteStream(TokenSource tokenSource) { + super(tokenSource); + init(); + } + + public TokenRewriteStream(TokenSource tokenSource, int channel) { + super(tokenSource, channel); + init(); + } + + public void rollback(int instructionIndex) { + rollback(DEFAULT_PROGRAM_NAME, instructionIndex); + } + + /** Rollback the instruction stream for a program so that + * the indicated instruction (via instructionIndex) is no + * longer in the stream. UNTESTED! + */ + public void rollback(String programName, int instructionIndex) { + List is = (List)programs.get(programName); + if ( is!=null ) { + programs.put(programName, is.subList(MIN_TOKEN_INDEX,instructionIndex)); + } + } + + public void deleteProgram() { + deleteProgram(DEFAULT_PROGRAM_NAME); + } + + /** Reset the program so that no instructions exist */ + public void deleteProgram(String programName) { + rollback(programName, MIN_TOKEN_INDEX); + } + + public void insertAfter(Token t, Object text) { + insertAfter(DEFAULT_PROGRAM_NAME, t, text); + } + + public void insertAfter(int index, Object text) { + insertAfter(DEFAULT_PROGRAM_NAME, index, text); + } + + public void insertAfter(String programName, Token t, Object text) { + insertAfter(programName,t.getTokenIndex(), text); + } + + public void insertAfter(String programName, int index, Object text) { + // to insert after, just insert before next index (even if past end) + insertBefore(programName,index+1, text); + } + + public void insertBefore(Token t, Object text) { + insertBefore(DEFAULT_PROGRAM_NAME, t, text); + } + + public void insertBefore(int index, Object text) { + insertBefore(DEFAULT_PROGRAM_NAME, index, text); + } + + public void insertBefore(String programName, Token t, Object text) { + insertBefore(programName, t.getTokenIndex(), text); + } + + public void insertBefore(String programName, int index, Object text) { + RewriteOperation op = new InsertBeforeOp(index,text); + List rewrites = getProgram(programName); + op.instructionIndex = rewrites.size(); + rewrites.add(op); + } + + public void replace(int index, Object text) { + replace(DEFAULT_PROGRAM_NAME, index, index, text); + } + + public void replace(int from, int to, Object text) { + replace(DEFAULT_PROGRAM_NAME, from, to, text); + } + + public void replace(Token indexT, Object text) { + replace(DEFAULT_PROGRAM_NAME, indexT, indexT, text); + } + + public void replace(Token from, Token to, Object text) { + replace(DEFAULT_PROGRAM_NAME, from, to, text); + } + + public void replace(String programName, int from, int to, Object text) { + if ( from > to || from<0 || to<0 || to >= tokens.size() ) { + throw new IllegalArgumentException("replace: range invalid: "+from+".."+to+"(size="+tokens.size()+")"); + } + RewriteOperation op = new ReplaceOp(from, to, text); + List rewrites = getProgram(programName); + op.instructionIndex = rewrites.size(); + rewrites.add(op); + } + + public void replace(String programName, Token from, Token to, Object text) { + replace(programName, + from.getTokenIndex(), + to.getTokenIndex(), + text); + } + + public void delete(int index) { + delete(DEFAULT_PROGRAM_NAME, index, index); + } + + public void delete(int from, int to) { + delete(DEFAULT_PROGRAM_NAME, from, to); + } + + public void delete(Token indexT) { + delete(DEFAULT_PROGRAM_NAME, indexT, indexT); + } + + public void delete(Token from, Token to) { + delete(DEFAULT_PROGRAM_NAME, from, to); + } + + public void delete(String programName, int from, int to) { + replace(programName,from,to,null); + } + + public void delete(String programName, Token from, Token to) { + replace(programName,from,to,null); + } + + public int getLastRewriteTokenIndex() { + return getLastRewriteTokenIndex(DEFAULT_PROGRAM_NAME); + } + + protected int getLastRewriteTokenIndex(String programName) { + Integer I = (Integer)lastRewriteTokenIndexes.get(programName); + if ( I==null ) { + return -1; + } + return I.intValue(); + } + + protected void setLastRewriteTokenIndex(String programName, int i) { + lastRewriteTokenIndexes.put(programName, new Integer(i)); + } + + protected List getProgram(String name) { + List is = (List)programs.get(name); + if ( is==null ) { + is = initializeProgram(name); + } + return is; + } + + private List initializeProgram(String name) { + List is = new ArrayList(PROGRAM_INIT_SIZE); + programs.put(name, is); + return is; + } + + public String toOriginalString() { + fill(); + return toOriginalString(MIN_TOKEN_INDEX, size()-1); + } + + public String toOriginalString(int start, int end) { + StringBuffer buf = new StringBuffer(); + for (int i=start; i>=MIN_TOKEN_INDEX && i<=end && i<tokens.size(); i++) { + if ( get(i).getType()!=Token.EOF ) buf.append(get(i).getText()); + } + return buf.toString(); + } + + public String toString() { + fill(); + return toString(MIN_TOKEN_INDEX, size()-1); + } + + public String toString(String programName) { + fill(); + return toString(programName, MIN_TOKEN_INDEX, size()-1); + } + + public String toString(int start, int end) { + return toString(DEFAULT_PROGRAM_NAME, start, end); + } + + public String toString(String programName, int start, int end) { + List rewrites = (List)programs.get(programName); + + // ensure start/end are in range + if ( end>tokens.size()-1 ) end = tokens.size()-1; + if ( start<0 ) start = 0; + + if ( rewrites==null || rewrites.size()==0 ) { + return toOriginalString(start,end); // no instructions to execute + } + StringBuffer buf = new StringBuffer(); + + // First, optimize instruction stream + Map indexToOp = reduceToSingleOperationPerIndex(rewrites); + + // Walk buffer, executing instructions and emitting tokens + int i = start; + while ( i <= end && i < tokens.size() ) { + RewriteOperation op = (RewriteOperation)indexToOp.get(new Integer(i)); + indexToOp.remove(new Integer(i)); // remove so any left have index size-1 + Token t = (Token) tokens.get(i); + if ( op==null ) { + // no operation at that index, just dump token + if ( t.getType()!=Token.EOF ) buf.append(t.getText()); + i++; // move to next token + } + else { + i = op.execute(buf); // execute operation and skip + } + } + + // include stuff after end if it's last index in buffer + // So, if they did an insertAfter(lastValidIndex, "foo"), include + // foo if end==lastValidIndex. + if ( end==tokens.size()-1 ) { + // Scan any remaining operations after last token + // should be included (they will be inserts). + Iterator it = indexToOp.values().iterator(); + while (it.hasNext()) { + RewriteOperation op = (RewriteOperation)it.next(); + if ( op.index >= tokens.size()-1 ) buf.append(op.text); + } + } + return buf.toString(); + } + + /** We need to combine operations and report invalid operations (like + * overlapping replaces that are not completed nested). Inserts to + * same index need to be combined etc... Here are the cases: + * + * I.i.u I.j.v leave alone, nonoverlapping + * I.i.u I.i.v combine: Iivu + * + * R.i-j.u R.x-y.v | i-j in x-y delete first R + * R.i-j.u R.i-j.v delete first R + * R.i-j.u R.x-y.v | x-y in i-j ERROR + * R.i-j.u R.x-y.v | boundaries overlap ERROR + * + * Delete special case of replace (text==null): + * D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) + * + * I.i.u R.x-y.v | i in (x+1)-y delete I (since insert before + * we're not deleting i) + * I.i.u R.x-y.v | i not in (x+1)-y leave alone, nonoverlapping + * R.x-y.v I.i.u | i in x-y ERROR + * R.x-y.v I.x.u R.x-y.uv (combine, delete I) + * R.x-y.v I.i.u | i not in x-y leave alone, nonoverlapping + * + * I.i.u = insert u before op @ index i + * R.x-y.u = replace x-y indexed tokens with u + * + * First we need to examine replaces. For any replace op: + * + * 1. wipe out any insertions before op within that range. + * 2. Drop any replace op before that is contained completely within + * that range. + * 3. Throw exception upon boundary overlap with any previous replace. + * + * Then we can deal with inserts: + * + * 1. for any inserts to same index, combine even if not adjacent. + * 2. for any prior replace with same left boundary, combine this + * insert with replace and delete this replace. + * 3. throw exception if index in same range as previous replace + * + * Don't actually delete; make op null in list. Easier to walk list. + * Later we can throw as we add to index -> op map. + * + * Note that I.2 R.2-2 will wipe out I.2 even though, technically, the + * inserted stuff would be before the replace range. But, if you + * add tokens in front of a method body '{' and then delete the method + * body, I think the stuff before the '{' you added should disappear too. + * + * Return a map from token index to operation. + */ + protected Map reduceToSingleOperationPerIndex(List rewrites) { +// System.out.println("rewrites="+rewrites); + + // WALK REPLACES + for (int i = 0; i < rewrites.size(); i++) { + RewriteOperation op = (RewriteOperation)rewrites.get(i); + if ( op==null ) continue; + if ( !(op instanceof ReplaceOp) ) continue; + ReplaceOp rop = (ReplaceOp)rewrites.get(i); + // Wipe prior inserts within range + List inserts = getKindOfOps(rewrites, InsertBeforeOp.class, i); + for (int j = 0; j < inserts.size(); j++) { + InsertBeforeOp iop = (InsertBeforeOp) inserts.get(j); + if ( iop.index == rop.index ) { + // E.g., insert before 2, delete 2..2; update replace + // text to include insert before, kill insert + rewrites.set(iop.instructionIndex, null); + rop.text = iop.text.toString() + (rop.text!=null?rop.text.toString():""); + } + else if ( iop.index > rop.index && iop.index <= rop.lastIndex ) { + // delete insert as it's a no-op. + rewrites.set(iop.instructionIndex, null); + } + } + // Drop any prior replaces contained within + List prevReplaces = getKindOfOps(rewrites, ReplaceOp.class, i); + for (int j = 0; j < prevReplaces.size(); j++) { + ReplaceOp prevRop = (ReplaceOp) prevReplaces.get(j); + if ( prevRop.index>=rop.index && prevRop.lastIndex <= rop.lastIndex ) { + // delete replace as it's a no-op. + rewrites.set(prevRop.instructionIndex, null); + continue; + } + // throw exception unless disjoint or identical + boolean disjoint = + prevRop.lastIndex<rop.index || prevRop.index > rop.lastIndex; + boolean same = + prevRop.index==rop.index && prevRop.lastIndex==rop.lastIndex; + // Delete special case of replace (text==null): + // D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right) + if ( prevRop.text==null && rop.text==null && !disjoint ) { + //System.out.println("overlapping deletes: "+prevRop+", "+rop); + rewrites.set(prevRop.instructionIndex, null); // kill first delete + rop.index = Math.min(prevRop.index, rop.index); + rop.lastIndex = Math.max(prevRop.lastIndex, rop.lastIndex); + System.out.println("new rop "+rop); + } + else if ( !disjoint && !same ) { + throw new IllegalArgumentException("replace op boundaries of "+rop+ + " overlap with previous "+prevRop); + } + } + } + + // WALK INSERTS + for (int i = 0; i < rewrites.size(); i++) { + RewriteOperation op = (RewriteOperation)rewrites.get(i); + if ( op==null ) continue; + if ( !(op instanceof InsertBeforeOp) ) continue; + InsertBeforeOp iop = (InsertBeforeOp)rewrites.get(i); + // combine current insert with prior if any at same index + List prevInserts = getKindOfOps(rewrites, InsertBeforeOp.class, i); + for (int j = 0; j < prevInserts.size(); j++) { + InsertBeforeOp prevIop = (InsertBeforeOp) prevInserts.get(j); + if ( prevIop.index == iop.index ) { // combine objects + // convert to strings...we're in process of toString'ing + // whole token buffer so no lazy eval issue with any templates + iop.text = catOpText(iop.text,prevIop.text); + // delete redundant prior insert + rewrites.set(prevIop.instructionIndex, null); + } + } + // look for replaces where iop.index is in range; error + List prevReplaces = getKindOfOps(rewrites, ReplaceOp.class, i); + for (int j = 0; j < prevReplaces.size(); j++) { + ReplaceOp rop = (ReplaceOp) prevReplaces.get(j); + if ( iop.index == rop.index ) { + rop.text = catOpText(iop.text,rop.text); + rewrites.set(i, null); // delete current insert + continue; + } + if ( iop.index >= rop.index && iop.index <= rop.lastIndex ) { + throw new IllegalArgumentException("insert op "+iop+ + " within boundaries of previous "+rop); + } + } + } + // System.out.println("rewrites after="+rewrites); + Map m = new HashMap(); + for (int i = 0; i < rewrites.size(); i++) { + RewriteOperation op = (RewriteOperation)rewrites.get(i); + if ( op==null ) continue; // ignore deleted ops + if ( m.get(new Integer(op.index))!=null ) { + throw new Error("should only be one op per index"); + } + m.put(new Integer(op.index), op); + } + //System.out.println("index to op: "+m); + return m; + } + + protected String catOpText(Object a, Object b) { + String x = ""; + String y = ""; + if ( a!=null ) x = a.toString(); + if ( b!=null ) y = b.toString(); + return x+y; + } + protected List getKindOfOps(List rewrites, Class kind) { + return getKindOfOps(rewrites, kind, rewrites.size()); + } + + /** Get all operations before an index of a particular kind */ + protected List getKindOfOps(List rewrites, Class kind, int before) { + List ops = new ArrayList(); + for (int i=0; i<before && i<rewrites.size(); i++) { + RewriteOperation op = (RewriteOperation)rewrites.get(i); + if ( op==null ) continue; // ignore deleted + if ( op.getClass() == kind ) ops.add(op); + } + return ops; + } + + public String toDebugString() { + return toDebugString(MIN_TOKEN_INDEX, size()-1); + } + + public String toDebugString(int start, int end) { + StringBuffer buf = new StringBuffer(); + for (int i=start; i>=MIN_TOKEN_INDEX && i<=end && i<tokens.size(); i++) { + buf.append(get(i)); + } + return buf.toString(); + } +} diff --git a/src/org/antlr/runtime/TokenSource.java b/src/org/antlr/runtime/TokenSource.java new file mode 100755 index 0000000..1f4dc64 --- /dev/null +++ b/src/org/antlr/runtime/TokenSource.java @@ -0,0 +1,54 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** A source of tokens must provide a sequence of tokens via nextToken() + * and also must reveal it's source of characters; CommonToken's text is + * computed from a CharStream; it only store indices into the char stream. + * + * Errors from the lexer are never passed to the parser. Either you want + * to keep going or you do not upon token recognition error. If you do not + * want to continue lexing then you do not want to continue parsing. Just + * throw an exception not under RecognitionException and Java will naturally + * toss you all the way out of the recognizers. If you want to continue + * lexing then you should not throw an exception to the parser--it has already + * requested a token. Keep lexing until you get a valid one. Just report + * errors and keep going, looking for a valid token. + */ +public interface TokenSource { + /** Return a Token object from your input stream (usually a CharStream). + * Do not fail/return upon lexing error; keep chewing on the characters + * until you get a good one; errors are not passed through to the parser. + */ + public Token nextToken(); + + /** Where are you getting tokens from? normally the implication will simply + * ask lexers input stream. + */ + public String getSourceName(); +} diff --git a/src/org/antlr/runtime/TokenStream.java b/src/org/antlr/runtime/TokenStream.java new file mode 100755 index 0000000..1b43c14 --- /dev/null +++ b/src/org/antlr/runtime/TokenStream.java @@ -0,0 +1,75 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import java.util.List; + +/** A stream of tokens accessing tokens from a TokenSource */ +public interface TokenStream extends IntStream { + /** Get Token at current input pointer + i ahead where i=1 is next Token. + * i<0 indicates tokens in the past. So -1 is previous token and -2 is + * two tokens ago. LT(0) is undefined. For i>=n, return Token.EOFToken. + * Return null for LT(0) and any index that results in an absolute address + * that is negative. + */ + public Token LT(int k); + + /** How far ahead has the stream been asked to look? The return + * value is a valid index from 0..n-1. + */ + int range(); + + /** Get a token at an absolute index i; 0..n-1. This is really only + * needed for profiling and debugging and token stream rewriting. + * If you don't want to buffer up tokens, then this method makes no + * sense for you. Naturally you can't use the rewrite stream feature. + * I believe DebugTokenStream can easily be altered to not use + * this method, removing the dependency. + */ + public Token get(int i); + + /** Where is this stream pulling tokens from? This is not the name, but + * the object that provides Token objects. + */ + public TokenSource getTokenSource(); + + /** Return the text of all tokens from start to stop, inclusive. + * If the stream does not buffer all the tokens then it can just + * return "" or null; Users should not access $ruleLabel.text in + * an action of course in that case. + */ + public String toString(int start, int stop); + + /** Because the user is not required to use a token with an index stored + * in it, we must provide a means for two token objects themselves to + * indicate the start/end location. Most often this will just delegate + * to the other toString(int,int). This is also parallel with + * the TreeNodeStream.toString(Object,Object). + */ + public String toString(Token start, Token stop); +} diff --git a/src/org/antlr/runtime/UnbufferedTokenStream.java b/src/org/antlr/runtime/UnbufferedTokenStream.java new file mode 100755 index 0000000..0b0e979 --- /dev/null +++ b/src/org/antlr/runtime/UnbufferedTokenStream.java @@ -0,0 +1,82 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +import org.antlr.runtime.misc.LookaheadStream; + +import java.util.List; +import java.util.NoSuchElementException; + +/** A token stream that pulls tokens from the code source on-demand and + * without tracking a complete buffer of the tokens. This stream buffers + * the minimum number of tokens possible. It's the same as + * OnDemandTokenStream except that OnDemandTokenStream buffers all tokens. + * + * You can't use this stream if you pass whitespace or other off-channel + * tokens to the parser. The stream can't ignore off-channel tokens. + * + * You can only look backwards 1 token: LT(-1). + * + * Use this when you need to read from a socket or other infinite stream. + * + * @see BufferedTokenStream + * @see CommonTokenStream + */ +public class UnbufferedTokenStream extends LookaheadStream<Token> implements TokenStream { + protected TokenSource tokenSource; + protected int tokenIndex = 0; // simple counter to set token index in tokens + + /** Skip tokens on any channel but this one; this is how we skip whitespace... */ + protected int channel = Token.DEFAULT_CHANNEL; + + public UnbufferedTokenStream(TokenSource tokenSource) { + this.tokenSource = tokenSource; + } + + public Token nextElement() { + Token t = tokenSource.nextToken(); + t.setTokenIndex(tokenIndex++); + return t; + } + + public boolean isEOF(Token o) { return o.getType() == Token.EOF; } + + public TokenSource getTokenSource() { return tokenSource; } + + public String toString(int start, int stop) { return "n/a"; } + + public String toString(Token start, Token stop) { return "n/a"; } + + public int LA(int i) { return LT(i).getType(); } + + public Token get(int i) { + throw new UnsupportedOperationException("Absolute token indexes are meaningless in an unbuffered stream"); + } + + public String getSourceName() { return tokenSource.getSourceName(); } +} diff --git a/src/org/antlr/runtime/UnwantedTokenException.java b/src/org/antlr/runtime/UnwantedTokenException.java new file mode 100755 index 0000000..feb7445 --- /dev/null +++ b/src/org/antlr/runtime/UnwantedTokenException.java @@ -0,0 +1,53 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime; + +/** An extra token while parsing a TokenStream */ +public class UnwantedTokenException extends MismatchedTokenException { + /** Used for remote debugger deserialization */ + public UnwantedTokenException() {;} + + public UnwantedTokenException(int expecting, IntStream input) { + super(expecting, input); + } + + public Token getUnexpectedToken() { + return token; + } + + public String toString() { + String exp = ", expected "+expecting; + if ( expecting==Token.INVALID_TOKEN_TYPE ) { + exp = ""; + } + if ( token==null ) { + return "UnwantedTokenException(found="+null+exp+")"; + } + return "UnwantedTokenException(found="+token.getText()+exp+")"; + } +} diff --git a/src/org/antlr/runtime/debug/BlankDebugEventListener.java b/src/org/antlr/runtime/debug/BlankDebugEventListener.java new file mode 100755 index 0000000..d70aa26 --- /dev/null +++ b/src/org/antlr/runtime/debug/BlankDebugEventListener.java @@ -0,0 +1,77 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; + +/** A blank listener that does nothing; useful for real classes so + * they don't have to have lots of blank methods and are less + * sensitive to updates to debug interface. + */ +public class BlankDebugEventListener implements DebugEventListener { + public void enterRule(String grammarFileName, String ruleName) {} + public void exitRule(String grammarFileName, String ruleName) {} + public void enterAlt(int alt) {} + public void enterSubRule(int decisionNumber) {} + public void exitSubRule(int decisionNumber) {} + public void enterDecision(int decisionNumber, boolean couldBacktrack) {} + public void exitDecision(int decisionNumber) {} + public void location(int line, int pos) {} + public void consumeToken(Token token) {} + public void consumeHiddenToken(Token token) {} + public void LT(int i, Token t) {} + public void mark(int i) {} + public void rewind(int i) {} + public void rewind() {} + public void beginBacktrack(int level) {} + public void endBacktrack(int level, boolean successful) {} + public void recognitionException(RecognitionException e) {} + public void beginResync() {} + public void endResync() {} + public void semanticPredicate(boolean result, String predicate) {} + public void commence() {} + public void terminate() {} + + // Tree parsing stuff + + public void consumeNode(Object t) {} + public void LT(int i, Object t) {} + + // AST Stuff + + public void nilNode(Object t) {} + public void errorNode(Object t) {} + public void createNode(Object t) {} + public void createNode(Object node, Token token) {} + public void becomeRoot(Object newRoot, Object oldRoot) {} + public void addChild(Object root, Object child) {} + public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {} +} + + diff --git a/src/org/antlr/runtime/debug/DebugEventHub.java b/src/org/antlr/runtime/debug/DebugEventHub.java new file mode 100755 index 0000000..7bfe6a8 --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugEventHub.java @@ -0,0 +1,292 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.Token; +import org.antlr.runtime.RecognitionException; + +import java.util.List; +import java.util.ArrayList; + +/** Broadcast debug events to multiple listeners. Lets you debug and still + * use the event mechanism to build parse trees etc... Not thread-safe. + * Don't add events in one thread while parser fires events in another. + * + * @see also DebugEventRepeater + */ +public class DebugEventHub implements DebugEventListener { + protected List listeners = new ArrayList(); + + public DebugEventHub(DebugEventListener listener) { + listeners.add(listener); + } + + public DebugEventHub(DebugEventListener a, DebugEventListener b) { + listeners.add(a); + listeners.add(b); + } + + /** Add another listener to broadcast events too. Not thread-safe. + * Don't add events in one thread while parser fires events in another. + */ + public void addListener(DebugEventListener listener) { + listeners.add(listener); + } + + /* To avoid a mess like this: + public void enterRule(final String ruleName) { + broadcast(new Code(){ + public void exec(DebugEventListener listener) {listener.enterRule(ruleName);}} + ); + } + I am dup'ing the for-loop in each. Where are Java closures!? blech! + */ + + public void enterRule(String grammarFileName, String ruleName) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.enterRule(grammarFileName,ruleName); + } + } + + public void exitRule(String grammarFileName, String ruleName) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.exitRule(grammarFileName, ruleName); + } + } + + public void enterAlt(int alt) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.enterAlt(alt); + } + } + + public void enterSubRule(int decisionNumber) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.enterSubRule(decisionNumber); + } + } + + public void exitSubRule(int decisionNumber) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.exitSubRule(decisionNumber); + } + } + + public void enterDecision(int decisionNumber, boolean couldBacktrack) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.enterDecision(decisionNumber, couldBacktrack); + } + } + + public void exitDecision(int decisionNumber) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.exitDecision(decisionNumber); + } + } + + public void location(int line, int pos) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.location(line, pos); + } + } + + public void consumeToken(Token token) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.consumeToken(token); + } + } + + public void consumeHiddenToken(Token token) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.consumeHiddenToken(token); + } + } + + public void LT(int index, Token t) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.LT(index, t); + } + } + + public void mark(int index) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.mark(index); + } + } + + public void rewind(int index) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.rewind(index); + } + } + + public void rewind() { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.rewind(); + } + } + + public void beginBacktrack(int level) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.beginBacktrack(level); + } + } + + public void endBacktrack(int level, boolean successful) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.endBacktrack(level, successful); + } + } + + public void recognitionException(RecognitionException e) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.recognitionException(e); + } + } + + public void beginResync() { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.beginResync(); + } + } + + public void endResync() { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.endResync(); + } + } + + public void semanticPredicate(boolean result, String predicate) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.semanticPredicate(result, predicate); + } + } + + public void commence() { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.commence(); + } + } + + public void terminate() { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.terminate(); + } + } + + + // Tree parsing stuff + + public void consumeNode(Object t) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.consumeNode(t); + } + } + + public void LT(int index, Object t) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.LT(index, t); + } + } + + + // AST Stuff + + public void nilNode(Object t) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.nilNode(t); + } + } + + public void errorNode(Object t) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.errorNode(t); + } + } + + public void createNode(Object t) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.createNode(t); + } + } + + public void createNode(Object node, Token token) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.createNode(node, token); + } + } + + public void becomeRoot(Object newRoot, Object oldRoot) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.becomeRoot(newRoot, oldRoot); + } + } + + public void addChild(Object root, Object child) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.addChild(root, child); + } + } + + public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) { + for (int i = 0; i < listeners.size(); i++) { + DebugEventListener listener = (DebugEventListener)listeners.get(i); + listener.setTokenBoundaries(t, tokenStartIndex, tokenStopIndex); + } + } +} diff --git a/src/org/antlr/runtime/debug/DebugEventListener.java b/src/org/antlr/runtime/debug/DebugEventListener.java new file mode 100755 index 0000000..163b5cd --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugEventListener.java @@ -0,0 +1,323 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; + +/** All debugging events that a recognizer can trigger. + * + * I did not create a separate AST debugging interface as it would create + * lots of extra classes and DebugParser has a dbg var defined, which makes + * it hard to change to ASTDebugEventListener. I looked hard at this issue + * and it is easier to understand as one monolithic event interface for all + * possible events. Hopefully, adding ST debugging stuff won't be bad. Leave + * for future. 4/26/2006. + */ +public interface DebugEventListener { + /** Moved to version 2 for v3.1: added grammar name to enter/exit Rule */ + public static final String PROTOCOL_VERSION = "2"; + + /** serialized version of true */ + public static final int TRUE = 1; + public static final int FALSE = 0; + + /** The parser has just entered a rule. No decision has been made about + * which alt is predicted. This is fired AFTER init actions have been + * executed. Attributes are defined and available etc... + * The grammarFileName allows composite grammars to jump around among + * multiple grammar files. + */ + public void enterRule(String grammarFileName, String ruleName); + + /** Because rules can have lots of alternatives, it is very useful to + * know which alt you are entering. This is 1..n for n alts. + */ + public void enterAlt(int alt); + + /** This is the last thing executed before leaving a rule. It is + * executed even if an exception is thrown. This is triggered after + * error reporting and recovery have occurred (unless the exception is + * not caught in this rule). This implies an "exitAlt" event. + * The grammarFileName allows composite grammars to jump around among + * multiple grammar files. + */ + public void exitRule(String grammarFileName, String ruleName); + + /** Track entry into any (...) subrule other EBNF construct */ + public void enterSubRule(int decisionNumber); + + public void exitSubRule(int decisionNumber); + + /** Every decision, fixed k or arbitrary, has an enter/exit event + * so that a GUI can easily track what LT/consume events are + * associated with prediction. You will see a single enter/exit + * subrule but multiple enter/exit decision events, one for each + * loop iteration. + */ + public void enterDecision(int decisionNumber, boolean couldBacktrack); + + public void exitDecision(int decisionNumber); + + /** An input token was consumed; matched by any kind of element. + * Trigger after the token was matched by things like match(), matchAny(). + */ + public void consumeToken(Token t); + + /** An off-channel input token was consumed. + * Trigger after the token was matched by things like match(), matchAny(). + * (unless of course the hidden token is first stuff in the input stream). + */ + public void consumeHiddenToken(Token t); + + /** Somebody (anybody) looked ahead. Note that this actually gets + * triggered by both LA and LT calls. The debugger will want to know + * which Token object was examined. Like consumeToken, this indicates + * what token was seen at that depth. A remote debugger cannot look + * ahead into a file it doesn't have so LT events must pass the token + * even if the info is redundant. + */ + public void LT(int i, Token t); + + /** The parser is going to look arbitrarily ahead; mark this location, + * the token stream's marker is sent in case you need it. + */ + public void mark(int marker); + + /** After an arbitrairly long lookahead as with a cyclic DFA (or with + * any backtrack), this informs the debugger that stream should be + * rewound to the position associated with marker. + */ + public void rewind(int marker); + + /** Rewind to the input position of the last marker. + * Used currently only after a cyclic DFA and just + * before starting a sem/syn predicate to get the + * input position back to the start of the decision. + * Do not "pop" the marker off the state. mark(i) + * and rewind(i) should balance still. + */ + public void rewind(); + + public void beginBacktrack(int level); + + public void endBacktrack(int level, boolean successful); + + /** To watch a parser move through the grammar, the parser needs to + * inform the debugger what line/charPos it is passing in the grammar. + * For now, this does not know how to switch from one grammar to the + * other and back for island grammars etc... + * + * This should also allow breakpoints because the debugger can stop + * the parser whenever it hits this line/pos. + */ + public void location(int line, int pos); + + /** A recognition exception occurred such as NoViableAltException. I made + * this a generic event so that I can alter the exception hierachy later + * without having to alter all the debug objects. + * + * Upon error, the stack of enter rule/subrule must be properly unwound. + * If no viable alt occurs it is within an enter/exit decision, which + * also must be rewound. Even the rewind for each mark must be unwount. + * In the Java target this is pretty easy using try/finally, if a bit + * ugly in the generated code. The rewind is generated in DFA.predict() + * actually so no code needs to be generated for that. For languages + * w/o this "finally" feature (C++?), the target implementor will have + * to build an event stack or something. + * + * Across a socket for remote debugging, only the RecognitionException + * data fields are transmitted. The token object or whatever that + * caused the problem was the last object referenced by LT. The + * immediately preceding LT event should hold the unexpected Token or + * char. + * + * Here is a sample event trace for grammar: + * + * b : C ({;}A|B) // {;} is there to prevent A|B becoming a set + * | D + * ; + * + * The sequence for this rule (with no viable alt in the subrule) for + * input 'c c' (there are 3 tokens) is: + * + * commence + * LT(1) + * enterRule b + * location 7 1 + * enter decision 3 + * LT(1) + * exit decision 3 + * enterAlt1 + * location 7 5 + * LT(1) + * consumeToken [c/<4>,1:0] + * location 7 7 + * enterSubRule 2 + * enter decision 2 + * LT(1) + * LT(1) + * recognitionException NoViableAltException 2 1 2 + * exit decision 2 + * exitSubRule 2 + * beginResync + * LT(1) + * consumeToken [c/<4>,1:1] + * LT(1) + * endResync + * LT(-1) + * exitRule b + * terminate + */ + public void recognitionException(RecognitionException e); + + /** Indicates the recognizer is about to consume tokens to resynchronize + * the parser. Any consume events from here until the recovered event + * are not part of the parse--they are dead tokens. + */ + public void beginResync(); + + /** Indicates that the recognizer has finished consuming tokens in order + * to resychronize. There may be multiple beginResync/endResync pairs + * before the recognizer comes out of errorRecovery mode (in which + * multiple errors are suppressed). This will be useful + * in a gui where you want to probably grey out tokens that are consumed + * but not matched to anything in grammar. Anything between + * a beginResync/endResync pair was tossed out by the parser. + */ + public void endResync(); + + /** A semantic predicate was evaluate with this result and action text */ + public void semanticPredicate(boolean result, String predicate); + + /** Announce that parsing has begun. Not technically useful except for + * sending events over a socket. A GUI for example will launch a thread + * to connect and communicate with a remote parser. The thread will want + * to notify the GUI when a connection is made. ANTLR parsers + * trigger this upon entry to the first rule (the ruleLevel is used to + * figure this out). + */ + public void commence(); + + /** Parsing is over; successfully or not. Mostly useful for telling + * remote debugging listeners that it's time to quit. When the rule + * invocation level goes to zero at the end of a rule, we are done + * parsing. + */ + public void terminate(); + + + // T r e e P a r s i n g + + /** Input for a tree parser is an AST, but we know nothing for sure + * about a node except its type and text (obtained from the adaptor). + * This is the analog of the consumeToken method. Again, the ID is + * the hashCode usually of the node so it only works if hashCode is + * not implemented. If the type is UP or DOWN, then + * the ID is not really meaningful as it's fixed--there is + * just one UP node and one DOWN navigation node. + * @param t + */ + public void consumeNode(Object t); + + /** The tree parser lookedahead. If the type is UP or DOWN, + * then the ID is not really meaningful as it's fixed--there is + * just one UP node and one DOWN navigation node. + */ + public void LT(int i, Object t); + + + // A S T E v e n t s + + /** A nil was created (even nil nodes have a unique ID... + * they are not "null" per se). As of 4/28/2006, this + * seems to be uniquely triggered when starting a new subtree + * such as when entering a subrule in automatic mode and when + * building a tree in rewrite mode. + * + * If you are receiving this event over a socket via + * RemoteDebugEventSocketListener then only t.ID is set. + */ + public void nilNode(Object t); + + /** Upon syntax error, recognizers bracket the error with an error node + * if they are building ASTs. + * @param t + */ + public void errorNode(Object t); + + /** Announce a new node built from token elements such as type etc... + * + * If you are receiving this event over a socket via + * RemoteDebugEventSocketListener then only t.ID, type, text are + * set. + */ + public void createNode(Object t); + + /** Announce a new node built from an existing token. + * + * If you are receiving this event over a socket via + * RemoteDebugEventSocketListener then only node.ID and token.tokenIndex + * are set. + */ + public void createNode(Object node, Token token); + + /** Make a node the new root of an existing root. See + * + * Note: the newRootID parameter is possibly different + * than the TreeAdaptor.becomeRoot() newRoot parameter. + * In our case, it will always be the result of calling + * TreeAdaptor.becomeRoot() and not root_n or whatever. + * + * The listener should assume that this event occurs + * only when the current subrule (or rule) subtree is + * being reset to newRootID. + * + * If you are receiving this event over a socket via + * RemoteDebugEventSocketListener then only IDs are set. + * + * @see org.antlr.runtime.tree.TreeAdaptor.becomeRoot() + */ + public void becomeRoot(Object newRoot, Object oldRoot); + + /** Make childID a child of rootID. + * + * If you are receiving this event over a socket via + * RemoteDebugEventSocketListener then only IDs are set. + * + * @see org.antlr.runtime.tree.TreeAdaptor.addChild() + */ + public void addChild(Object root, Object child); + + /** Set the token start/stop token index for a subtree root or node. + * + * If you are receiving this event over a socket via + * RemoteDebugEventSocketListener then only t.ID is set. + */ + public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex); +} diff --git a/src/org/antlr/runtime/debug/DebugEventRepeater.java b/src/org/antlr/runtime/debug/DebugEventRepeater.java new file mode 100755 index 0000000..8fb6b66 --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugEventRepeater.java @@ -0,0 +1,88 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.Token; +import org.antlr.runtime.RecognitionException; + +/** A simple event repeater (proxy) that delegates all functionality to the + * listener sent into the ctor. Useful if you want to listen in on a few + * debug events w/o interrupting the debugger. Just subclass the repeater + * and override the methods you want to listen in on. Remember to call + * the method in this class so the event will continue on to the original + * recipient. + * + * @see DebugEventHub + */ +public class DebugEventRepeater implements DebugEventListener { + protected DebugEventListener listener; + + public DebugEventRepeater(DebugEventListener listener) { + this.listener = listener; + } + + public void enterRule(String grammarFileName, String ruleName) { listener.enterRule(grammarFileName, ruleName); } + public void exitRule(String grammarFileName, String ruleName) { listener.exitRule(grammarFileName, ruleName); } + public void enterAlt(int alt) { listener.enterAlt(alt); } + public void enterSubRule(int decisionNumber) { listener.enterSubRule(decisionNumber); } + public void exitSubRule(int decisionNumber) { listener.exitSubRule(decisionNumber); } + public void enterDecision(int decisionNumber, boolean couldBacktrack) { listener.enterDecision(decisionNumber, couldBacktrack); } + public void exitDecision(int decisionNumber) { listener.exitDecision(decisionNumber); } + public void location(int line, int pos) { listener.location(line, pos); } + public void consumeToken(Token token) { listener.consumeToken(token); } + public void consumeHiddenToken(Token token) { listener.consumeHiddenToken(token); } + public void LT(int i, Token t) { listener.LT(i, t); } + public void mark(int i) { listener.mark(i); } + public void rewind(int i) { listener.rewind(i); } + public void rewind() { listener.rewind(); } + public void beginBacktrack(int level) { listener.beginBacktrack(level); } + public void endBacktrack(int level, boolean successful) { listener.endBacktrack(level, successful); } + public void recognitionException(RecognitionException e) { listener.recognitionException(e); } + public void beginResync() { listener.beginResync(); } + public void endResync() { listener.endResync(); } + public void semanticPredicate(boolean result, String predicate) { listener.semanticPredicate(result, predicate); } + public void commence() { listener.commence(); } + public void terminate() { listener.terminate(); } + + // Tree parsing stuff + + public void consumeNode(Object t) { listener.consumeNode(t); } + public void LT(int i, Object t) { listener.LT(i, t); } + + // AST Stuff + + public void nilNode(Object t) { listener.nilNode(t); } + public void errorNode(Object t) { listener.errorNode(t); } + public void createNode(Object t) { listener.createNode(t); } + public void createNode(Object node, Token token) { listener.createNode(node, token); } + public void becomeRoot(Object newRoot, Object oldRoot) { listener.becomeRoot(newRoot, oldRoot); } + public void addChild(Object root, Object child) { listener.addChild(root, child); } + public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) { + listener.setTokenBoundaries(t, tokenStartIndex, tokenStopIndex); + } +} diff --git a/src/org/antlr/runtime/debug/DebugEventSocketProxy.java b/src/org/antlr/runtime/debug/DebugEventSocketProxy.java new file mode 100755 index 0000000..3b480ad --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugEventSocketProxy.java @@ -0,0 +1,354 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.BaseRecognizer; +import org.antlr.runtime.tree.TreeAdaptor; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; + +/** A proxy debug event listener that forwards events over a socket to + * a debugger (or any other listener) using a simple text-based protocol; + * one event per line. ANTLRWorks listens on server socket with a + * RemoteDebugEventSocketListener instance. These two objects must therefore + * be kept in sync. New events must be handled on both sides of socket. + */ +public class DebugEventSocketProxy extends BlankDebugEventListener { + public static final int DEFAULT_DEBUGGER_PORT = 49100; // was 49153 + protected int port = DEFAULT_DEBUGGER_PORT; + protected ServerSocket serverSocket; + protected Socket socket; + protected String grammarFileName; + protected PrintWriter out; + protected BufferedReader in; + + /** Who am i debugging? */ + protected BaseRecognizer recognizer; + + /** Almost certainly the recognizer will have adaptor set, but + * we don't know how to cast it (Parser or TreeParser) to get + * the adaptor field. Must be set with a constructor. :( + */ + protected TreeAdaptor adaptor; + + public DebugEventSocketProxy(BaseRecognizer recognizer, TreeAdaptor adaptor) { + this(recognizer, DEFAULT_DEBUGGER_PORT, adaptor); + } + + public DebugEventSocketProxy(BaseRecognizer recognizer, int port, TreeAdaptor adaptor) { + this.grammarFileName = recognizer.getGrammarFileName(); + this.adaptor = adaptor; + this.port = port; + } + + public void handshake() throws IOException { + if ( serverSocket==null ) { + serverSocket = new ServerSocket(port); + socket = serverSocket.accept(); + socket.setTcpNoDelay(true); + OutputStream os = socket.getOutputStream(); + OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8"); + out = new PrintWriter(new BufferedWriter(osw)); + InputStream is = socket.getInputStream(); + InputStreamReader isr = new InputStreamReader(is, "UTF8"); + in = new BufferedReader(isr); + out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION); + out.println("grammar \""+ grammarFileName); + out.flush(); + ack(); + } + } + + public void commence() { + // don't bother sending event; listener will trigger upon connection + } + + public void terminate() { + transmit("terminate"); + out.close(); + try { + socket.close(); + } + catch (IOException ioe) { + ioe.printStackTrace(System.err); + } + } + + protected void ack() { + try { + in.readLine(); + } + catch (IOException ioe) { + ioe.printStackTrace(System.err); + } + } + + protected void transmit(String event) { + out.println(event); + out.flush(); + ack(); + } + + public void enterRule(String grammarFileName, String ruleName) { + transmit("enterRule\t"+grammarFileName+"\t"+ruleName); + } + + public void enterAlt(int alt) { + transmit("enterAlt\t"+alt); + } + + public void exitRule(String grammarFileName, String ruleName) { + transmit("exitRule\t"+grammarFileName+"\t"+ruleName); + } + + public void enterSubRule(int decisionNumber) { + transmit("enterSubRule\t"+decisionNumber); + } + + public void exitSubRule(int decisionNumber) { + transmit("exitSubRule\t"+decisionNumber); + } + + public void enterDecision(int decisionNumber, boolean couldBacktrack) { + transmit("enterDecision\t"+decisionNumber+"\t"+couldBacktrack); + } + + public void exitDecision(int decisionNumber) { + transmit("exitDecision\t"+decisionNumber); + } + + public void consumeToken(Token t) { + String buf = serializeToken(t); + transmit("consumeToken\t"+buf); + } + + public void consumeHiddenToken(Token t) { + String buf = serializeToken(t); + transmit("consumeHiddenToken\t"+buf); + } + + public void LT(int i, Token t) { + if(t != null) + transmit("LT\t"+i+"\t"+serializeToken(t)); + } + + public void mark(int i) { + transmit("mark\t"+i); + } + + public void rewind(int i) { + transmit("rewind\t"+i); + } + + public void rewind() { + transmit("rewind"); + } + + public void beginBacktrack(int level) { + transmit("beginBacktrack\t"+level); + } + + public void endBacktrack(int level, boolean successful) { + transmit("endBacktrack\t"+level+"\t"+(successful?TRUE:FALSE)); + } + + public void location(int line, int pos) { + transmit("location\t"+line+"\t"+pos); + } + + public void recognitionException(RecognitionException e) { + StringBuffer buf = new StringBuffer(50); + buf.append("exception\t"); + buf.append(e.getClass().getName()); + // dump only the data common to all exceptions for now + buf.append("\t"); + buf.append(e.index); + buf.append("\t"); + buf.append(e.line); + buf.append("\t"); + buf.append(e.charPositionInLine); + transmit(buf.toString()); + } + + public void beginResync() { + transmit("beginResync"); + } + + public void endResync() { + transmit("endResync"); + } + + public void semanticPredicate(boolean result, String predicate) { + StringBuffer buf = new StringBuffer(50); + buf.append("semanticPredicate\t"); + buf.append(result); + serializeText(buf, predicate); + transmit(buf.toString()); + } + + // A S T P a r s i n g E v e n t s + + public void consumeNode(Object t) { + StringBuffer buf = new StringBuffer(50); + buf.append("consumeNode"); + serializeNode(buf, t); + transmit(buf.toString()); + } + + public void LT(int i, Object t) { + int ID = adaptor.getUniqueID(t); + String text = adaptor.getText(t); + int type = adaptor.getType(t); + StringBuffer buf = new StringBuffer(50); + buf.append("LN\t"); // lookahead node; distinguish from LT in protocol + buf.append(i); + serializeNode(buf, t); + transmit(buf.toString()); + } + + protected void serializeNode(StringBuffer buf, Object t) { + int ID = adaptor.getUniqueID(t); + String text = adaptor.getText(t); + int type = adaptor.getType(t); + buf.append("\t"); + buf.append(ID); + buf.append("\t"); + buf.append(type); + Token token = adaptor.getToken(t); + int line = -1; + int pos = -1; + if ( token!=null ) { + line = token.getLine(); + pos = token.getCharPositionInLine(); + } + buf.append("\t"); + buf.append(line); + buf.append("\t"); + buf.append(pos); + int tokenIndex = adaptor.getTokenStartIndex(t); + buf.append("\t"); + buf.append(tokenIndex); + serializeText(buf, text); + } + + + // A S T E v e n t s + + public void nilNode(Object t) { + int ID = adaptor.getUniqueID(t); + transmit("nilNode\t"+ID); + } + + public void errorNode(Object t) { + int ID = adaptor.getUniqueID(t); + String text = t.toString(); + StringBuffer buf = new StringBuffer(50); + buf.append("errorNode\t"); + buf.append(ID); + buf.append("\t"); + buf.append(Token.INVALID_TOKEN_TYPE); + serializeText(buf, text); + transmit(buf.toString()); + } + + public void createNode(Object t) { + int ID = adaptor.getUniqueID(t); + String text = adaptor.getText(t); + int type = adaptor.getType(t); + StringBuffer buf = new StringBuffer(50); + buf.append("createNodeFromTokenElements\t"); + buf.append(ID); + buf.append("\t"); + buf.append(type); + serializeText(buf, text); + transmit(buf.toString()); + } + + public void createNode(Object node, Token token) { + int ID = adaptor.getUniqueID(node); + int tokenIndex = token.getTokenIndex(); + transmit("createNode\t"+ID+"\t"+tokenIndex); + } + + public void becomeRoot(Object newRoot, Object oldRoot) { + int newRootID = adaptor.getUniqueID(newRoot); + int oldRootID = adaptor.getUniqueID(oldRoot); + transmit("becomeRoot\t"+newRootID+"\t"+oldRootID); + } + + public void addChild(Object root, Object child) { + int rootID = adaptor.getUniqueID(root); + int childID = adaptor.getUniqueID(child); + transmit("addChild\t"+rootID+"\t"+childID); + } + + public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) { + int ID = adaptor.getUniqueID(t); + transmit("setTokenBoundaries\t"+ID+"\t"+tokenStartIndex+"\t"+tokenStopIndex); + } + + + // support + + public void setTreeAdaptor(TreeAdaptor adaptor) { this.adaptor = adaptor; } + public TreeAdaptor getTreeAdaptor() { return adaptor; } + + protected String serializeToken(Token t) { + StringBuffer buf = new StringBuffer(50); + buf.append(t.getTokenIndex()); buf.append('\t'); + buf.append(t.getType()); buf.append('\t'); + buf.append(t.getChannel()); buf.append('\t'); + buf.append(t.getLine()); buf.append('\t'); + buf.append(t.getCharPositionInLine()); + serializeText(buf, t.getText()); + return buf.toString(); + } + + protected void serializeText(StringBuffer buf, String text) { + buf.append("\t\""); + if ( text==null ) { + text = ""; + } + // escape \n and \r all text for token appears to exist on one line + // this escape is slow but easy to understand + text = escapeNewlines(text); + buf.append(text); + } + + protected String escapeNewlines(String txt) { + txt = txt.replaceAll("%","%25"); // escape all escape char ;) + txt = txt.replaceAll("\n","%0A"); // escape \n + txt = txt.replaceAll("\r","%0D"); // escape \r + return txt; + } +} + diff --git a/src/org/antlr/runtime/debug/DebugParser.java b/src/org/antlr/runtime/debug/DebugParser.java new file mode 100755 index 0000000..49d78e7 --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugParser.java @@ -0,0 +1,98 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.*; + +import java.io.IOException; + +public class DebugParser extends Parser { + /** Who to notify when events in the parser occur. */ + protected DebugEventListener dbg = null; + + /** Used to differentiate between fixed lookahead and cyclic DFA decisions + * while profiling. + */ + public boolean isCyclicDecision = false; + + /** Create a normal parser except wrap the token stream in a debug + * proxy that fires consume events. + */ + public DebugParser(TokenStream input, DebugEventListener dbg, RecognizerSharedState state) { + super(input instanceof DebugTokenStream?input:new DebugTokenStream(input,dbg), state); + setDebugListener(dbg); + } + + public DebugParser(TokenStream input, RecognizerSharedState state) { + super(input instanceof DebugTokenStream?input:new DebugTokenStream(input,null), state); + } + + public DebugParser(TokenStream input, DebugEventListener dbg) { + this(input instanceof DebugTokenStream?input:new DebugTokenStream(input,dbg), dbg, null); + } + + /** Provide a new debug event listener for this parser. Notify the + * input stream too that it should send events to this listener. + */ + public void setDebugListener(DebugEventListener dbg) { + if ( input instanceof DebugTokenStream ) { + ((DebugTokenStream)input).setDebugListener(dbg); + } + this.dbg = dbg; + } + + public DebugEventListener getDebugListener() { + return dbg; + } + + public void reportError(IOException e) { + System.err.println(e); + e.printStackTrace(System.err); + } + + public void beginResync() { + dbg.beginResync(); + } + + public void endResync() { + dbg.endResync(); + } + + public void beginBacktrack(int level) { + dbg.beginBacktrack(level); + } + + public void endBacktrack(int level, boolean successful) { + dbg.endBacktrack(level,successful); + } + + public void reportError(RecognitionException e) { + super.reportError(e); + dbg.recognitionException(e); + } +} diff --git a/src/org/antlr/runtime/debug/DebugTokenStream.java b/src/org/antlr/runtime/debug/DebugTokenStream.java new file mode 100755 index 0000000..9a7a75f --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugTokenStream.java @@ -0,0 +1,156 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.*; + +import java.util.List; + +public class DebugTokenStream implements TokenStream { + protected DebugEventListener dbg; + public TokenStream input; + protected boolean initialStreamState = true; + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + public DebugTokenStream(TokenStream input, DebugEventListener dbg) { + this.input = input; + setDebugListener(dbg); + // force TokenStream to get at least first valid token + // so we know if there are any hidden tokens first in the stream + input.LT(1); + } + + public void setDebugListener(DebugEventListener dbg) { + this.dbg = dbg; + } + + public void consume() { + if ( initialStreamState ) { + consumeInitialHiddenTokens(); + } + int a = input.index(); + Token t = input.LT(1); + input.consume(); + int b = input.index(); + dbg.consumeToken(t); + if ( b>a+1 ) { + // then we consumed more than one token; must be off channel tokens + for (int i=a+1; i<b; i++) { + dbg.consumeHiddenToken(input.get(i)); + } + } + } + + /* consume all initial off-channel tokens */ + protected void consumeInitialHiddenTokens() { + int firstOnChannelTokenIndex = input.index(); + for (int i=0; i<firstOnChannelTokenIndex; i++) { + dbg.consumeHiddenToken(input.get(i)); + } + initialStreamState = false; + } + + public Token LT(int i) { + if ( initialStreamState ) { + consumeInitialHiddenTokens(); + } + dbg.LT(i, input.LT(i)); + return input.LT(i); + } + + public int LA(int i) { + if ( initialStreamState ) { + consumeInitialHiddenTokens(); + } + dbg.LT(i, input.LT(i)); + return input.LA(i); + } + + public Token get(int i) { + return input.get(i); + } + + public int mark() { + lastMarker = input.mark(); + dbg.mark(lastMarker); + return lastMarker; + } + + public int index() { + return input.index(); + } + + public int range() { + return input.range(); + } + + public void rewind(int marker) { + dbg.rewind(marker); + input.rewind(marker); + } + + public void rewind() { + dbg.rewind(); + input.rewind(lastMarker); + } + + public void release(int marker) { + } + + public void seek(int index) { + // TODO: implement seek in dbg interface + // db.seek(index); + input.seek(index); + } + + public int size() { + return input.size(); + } + + public TokenSource getTokenSource() { + return input.getTokenSource(); + } + + public String getSourceName() { + return getTokenSource().getSourceName(); + } + + public String toString() { + return input.toString(); + } + + public String toString(int start, int stop) { + return input.toString(start,stop); + } + + public String toString(Token start, Token stop) { + return input.toString(start,stop); + } +} diff --git a/src/org/antlr/runtime/debug/DebugTreeAdaptor.java b/src/org/antlr/runtime/debug/DebugTreeAdaptor.java new file mode 100755 index 0000000..c72a2b6 --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugTreeAdaptor.java @@ -0,0 +1,250 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.tree.TreeAdaptor; + +/** A TreeAdaptor proxy that fires debugging events to a DebugEventListener + * delegate and uses the TreeAdaptor delegate to do the actual work. All + * AST events are triggered by this adaptor; no code gen changes are needed + * in generated rules. Debugging events are triggered *after* invoking + * tree adaptor routines. + * + * Trees created with actions in rewrite actions like "-> ^(ADD {foo} {bar})" + * cannot be tracked as they might not use the adaptor to create foo, bar. + * The debug listener has to deal with tree node IDs for which it did + * not see a createNode event. A single <unknown> node is sufficient even + * if it represents a whole tree. + */ +public class DebugTreeAdaptor implements TreeAdaptor { + protected DebugEventListener dbg; + protected TreeAdaptor adaptor; + + public DebugTreeAdaptor(DebugEventListener dbg, TreeAdaptor adaptor) { + this.dbg = dbg; + this.adaptor = adaptor; + } + + public Object create(Token payload) { + if ( payload.getTokenIndex() < 0 ) { + // could be token conjured up during error recovery + return create(payload.getType(), payload.getText()); + } + Object node = adaptor.create(payload); + dbg.createNode(node, payload); + return node; + } + + public Object errorNode(TokenStream input, Token start, Token stop, + RecognitionException e) + { + Object node = adaptor.errorNode(input, start, stop, e); + if ( node!=null ) { + dbg.errorNode(node); + } + return node; + } + + public Object dupTree(Object tree) { + Object t = adaptor.dupTree(tree); + // walk the tree and emit create and add child events + // to simulate what dupTree has done. dupTree does not call this debug + // adapter so I must simulate. + simulateTreeConstruction(t); + return t; + } + + /** ^(A B C): emit create A, create B, add child, ...*/ + protected void simulateTreeConstruction(Object t) { + dbg.createNode(t); + int n = adaptor.getChildCount(t); + for (int i=0; i<n; i++) { + Object child = adaptor.getChild(t, i); + simulateTreeConstruction(child); + dbg.addChild(t, child); + } + } + + public Object dupNode(Object treeNode) { + Object d = adaptor.dupNode(treeNode); + dbg.createNode(d); + return d; + } + + public Object nil() { + Object node = adaptor.nil(); + dbg.nilNode(node); + return node; + } + + public boolean isNil(Object tree) { + return adaptor.isNil(tree); + } + + public void addChild(Object t, Object child) { + if ( t==null || child==null ) { + return; + } + adaptor.addChild(t,child); + dbg.addChild(t, child); + } + + public Object becomeRoot(Object newRoot, Object oldRoot) { + Object n = adaptor.becomeRoot(newRoot, oldRoot); + dbg.becomeRoot(newRoot, oldRoot); + return n; + } + + public Object rulePostProcessing(Object root) { + return adaptor.rulePostProcessing(root); + } + + public void addChild(Object t, Token child) { + Object n = this.create(child); + this.addChild(t, n); + } + + public Object becomeRoot(Token newRoot, Object oldRoot) { + Object n = this.create(newRoot); + adaptor.becomeRoot(n, oldRoot); + dbg.becomeRoot(newRoot, oldRoot); + return n; + } + + public Object create(int tokenType, Token fromToken) { + Object node = adaptor.create(tokenType, fromToken); + dbg.createNode(node); + return node; + } + + public Object create(int tokenType, Token fromToken, String text) { + Object node = adaptor.create(tokenType, fromToken, text); + dbg.createNode(node); + return node; + } + + public Object create(int tokenType, String text) { + Object node = adaptor.create(tokenType, text); + dbg.createNode(node); + return node; + } + + public int getType(Object t) { + return adaptor.getType(t); + } + + public void setType(Object t, int type) { + adaptor.setType(t, type); + } + + public String getText(Object t) { + return adaptor.getText(t); + } + + public void setText(Object t, String text) { + adaptor.setText(t, text); + } + + public Token getToken(Object t) { + return adaptor.getToken(t); + } + + public void setTokenBoundaries(Object t, Token startToken, Token stopToken) { + adaptor.setTokenBoundaries(t, startToken, stopToken); + if ( t!=null && startToken!=null && stopToken!=null ) { + dbg.setTokenBoundaries( + t, startToken.getTokenIndex(), + stopToken.getTokenIndex()); + } + } + + public int getTokenStartIndex(Object t) { + return adaptor.getTokenStartIndex(t); + } + + public int getTokenStopIndex(Object t) { + return adaptor.getTokenStopIndex(t); + } + + public Object getChild(Object t, int i) { + return adaptor.getChild(t, i); + } + + public void setChild(Object t, int i, Object child) { + adaptor.setChild(t, i, child); + } + + public Object deleteChild(Object t, int i) { + return deleteChild(t, i); + } + + public int getChildCount(Object t) { + return adaptor.getChildCount(t); + } + + public int getUniqueID(Object node) { + return adaptor.getUniqueID(node); + } + + public Object getParent(Object t) { + return adaptor.getParent(t); + } + + public int getChildIndex(Object t) { + return adaptor.getChildIndex(t); + } + + public void setParent(Object t, Object parent) { + adaptor.setParent(t, parent); + } + + public void setChildIndex(Object t, int index) { + adaptor.setChildIndex(t, index); + } + + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) { + adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t); + } + + // support + + public DebugEventListener getDebugListener() { + return dbg; + } + + public void setDebugListener(DebugEventListener dbg) { + this.dbg = dbg; + } + + public TreeAdaptor getTreeAdaptor() { + return adaptor; + } +} diff --git a/src/org/antlr/runtime/debug/DebugTreeNodeStream.java b/src/org/antlr/runtime/debug/DebugTreeNodeStream.java new file mode 100755 index 0000000..92ff009 --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugTreeNodeStream.java @@ -0,0 +1,155 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.tree.TreeAdaptor; +import org.antlr.runtime.tree.TreeNodeStream; +import org.antlr.runtime.TokenStream; + +/** Debug any tree node stream. The constructor accepts the stream + * and a debug listener. As node stream calls come in, debug events + * are triggered. + */ +public class DebugTreeNodeStream implements TreeNodeStream { + protected DebugEventListener dbg; + protected TreeAdaptor adaptor; + protected TreeNodeStream input; + protected boolean initialStreamState = true; + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + public DebugTreeNodeStream(TreeNodeStream input, + DebugEventListener dbg) + { + this.input = input; + this.adaptor = input.getTreeAdaptor(); + this.input.setUniqueNavigationNodes(true); + setDebugListener(dbg); + } + + public void setDebugListener(DebugEventListener dbg) { + this.dbg = dbg; + } + + public TreeAdaptor getTreeAdaptor() { + return adaptor; + } + + public void consume() { + Object node = input.LT(1); + input.consume(); + dbg.consumeNode(node); + } + + public Object get(int i) { + return input.get(i); + } + + public Object LT(int i) { + Object node = input.LT(i); + int ID = adaptor.getUniqueID(node); + String text = adaptor.getText(node); + int type = adaptor.getType(node); + dbg.LT(i, node); + return node; + } + + public int LA(int i) { + Object node = input.LT(i); + int ID = adaptor.getUniqueID(node); + String text = adaptor.getText(node); + int type = adaptor.getType(node); + dbg.LT(i, node); + return type; + } + + public int mark() { + lastMarker = input.mark(); + dbg.mark(lastMarker); + return lastMarker; + } + + public int index() { + return input.index(); + } + + public void rewind(int marker) { + dbg.rewind(marker); + input.rewind(marker); + } + + public void rewind() { + dbg.rewind(); + input.rewind(lastMarker); + } + + public void release(int marker) { + } + + public void seek(int index) { + // TODO: implement seek in dbg interface + // db.seek(index); + input.seek(index); + } + + public int size() { + return input.size(); + } + + public void reset() { ; } + + public Object getTreeSource() { + return input; + } + + public String getSourceName() { + return getTokenStream().getSourceName(); + } + + public TokenStream getTokenStream() { + return input.getTokenStream(); + } + + /** It is normally this object that instructs the node stream to + * create unique nav nodes, but to satisfy interface, we have to + * define it. It might be better to ignore the parameter but + * there might be a use for it later, so I'll leave. + */ + public void setUniqueNavigationNodes(boolean uniqueNavigationNodes) { + input.setUniqueNavigationNodes(uniqueNavigationNodes); + } + + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) { + input.replaceChildren(parent, startChildIndex, stopChildIndex, t); + } + + public String toString(Object start, Object stop) { + return input.toString(start,stop); + } +} diff --git a/src/org/antlr/runtime/debug/DebugTreeParser.java b/src/org/antlr/runtime/debug/DebugTreeParser.java new file mode 100755 index 0000000..6e1ece8 --- /dev/null +++ b/src/org/antlr/runtime/debug/DebugTreeParser.java @@ -0,0 +1,109 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.*; +import org.antlr.runtime.tree.TreeNodeStream; +import org.antlr.runtime.tree.TreeParser; + +import java.io.IOException; + +public class DebugTreeParser extends TreeParser { + /** Who to notify when events in the parser occur. */ + protected DebugEventListener dbg = null; + + /** Used to differentiate between fixed lookahead and cyclic DFA decisions + * while profiling. + */ + public boolean isCyclicDecision = false; + + /** Create a normal parser except wrap the token stream in a debug + * proxy that fires consume events. + */ + public DebugTreeParser(TreeNodeStream input, DebugEventListener dbg, RecognizerSharedState state) { + super(input instanceof DebugTreeNodeStream?input:new DebugTreeNodeStream(input,dbg), state); + setDebugListener(dbg); + } + + public DebugTreeParser(TreeNodeStream input, RecognizerSharedState state) { + super(input instanceof DebugTreeNodeStream?input:new DebugTreeNodeStream(input,null), state); + } + + public DebugTreeParser(TreeNodeStream input, DebugEventListener dbg) { + this(input instanceof DebugTreeNodeStream?input:new DebugTreeNodeStream(input,dbg), dbg, null); + } + + /** Provide a new debug event listener for this parser. Notify the + * input stream too that it should send events to this listener. + */ + public void setDebugListener(DebugEventListener dbg) { + if ( input instanceof DebugTreeNodeStream ) { + ((DebugTreeNodeStream)input).setDebugListener(dbg); + } + this.dbg = dbg; + } + + public DebugEventListener getDebugListener() { + return dbg; + } + + public void reportError(IOException e) { + System.err.println(e); + e.printStackTrace(System.err); + } + + public void reportError(RecognitionException e) { + dbg.recognitionException(e); + } + + protected Object getMissingSymbol(IntStream input, + RecognitionException e, + int expectedTokenType, + BitSet follow) + { + Object o = super.getMissingSymbol(input, e, expectedTokenType, follow); + dbg.consumeNode(o); + return o; + } + + public void beginResync() { + dbg.beginResync(); + } + + public void endResync() { + dbg.endResync(); + } + + public void beginBacktrack(int level) { + dbg.beginBacktrack(level); + } + + public void endBacktrack(int level, boolean successful) { + dbg.endBacktrack(level,successful); + } +} diff --git a/src/org/antlr/runtime/debug/ParseTreeBuilder.java b/src/org/antlr/runtime/debug/ParseTreeBuilder.java new file mode 100755 index 0000000..13c6ed0 --- /dev/null +++ b/src/org/antlr/runtime/debug/ParseTreeBuilder.java @@ -0,0 +1,109 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.ParseTree; + +import java.util.Stack; +import java.util.ArrayList; +import java.util.List; + +/** This parser listener tracks rule entry/exit and token matches + * to build a simple parse tree using ParseTree nodes. + */ +public class ParseTreeBuilder extends BlankDebugEventListener { + public static final String EPSILON_PAYLOAD = "<epsilon>"; + + Stack callStack = new Stack(); + List hiddenTokens = new ArrayList(); + int backtracking = 0; + + public ParseTreeBuilder(String grammarName) { + ParseTree root = create("<grammar "+grammarName+">"); + callStack.push(root); + } + + public ParseTree getTree() { + return (ParseTree)callStack.elementAt(0); + } + + /** What kind of node to create. You might want to override + * so I factored out creation here. + */ + public ParseTree create(Object payload) { + return new ParseTree(payload); + } + + public ParseTree epsilonNode() { + return create(EPSILON_PAYLOAD); + } + + /** Backtracking or cyclic DFA, don't want to add nodes to tree */ + public void enterDecision(int d, boolean couldBacktrack) { backtracking++; } + public void exitDecision(int i) { backtracking--; } + + public void enterRule(String filename, String ruleName) { + if ( backtracking>0 ) return; + ParseTree parentRuleNode = (ParseTree)callStack.peek(); + ParseTree ruleNode = create(ruleName); + parentRuleNode.addChild(ruleNode); + callStack.push(ruleNode); + } + + public void exitRule(String filename, String ruleName) { + if ( backtracking>0 ) return; + ParseTree ruleNode = (ParseTree)callStack.peek(); + if ( ruleNode.getChildCount()==0 ) { + ruleNode.addChild(epsilonNode()); + } + callStack.pop(); + } + + public void consumeToken(Token token) { + if ( backtracking>0 ) return; + ParseTree ruleNode = (ParseTree)callStack.peek(); + ParseTree elementNode = create(token); + elementNode.hiddenTokens = this.hiddenTokens; + this.hiddenTokens = new ArrayList(); + ruleNode.addChild(elementNode); + } + + public void consumeHiddenToken(Token token) { + if ( backtracking>0 ) return; + hiddenTokens.add(token); + } + + public void recognitionException(RecognitionException e) { + if ( backtracking>0 ) return; + ParseTree ruleNode = (ParseTree)callStack.peek(); + ParseTree errorNode = create(e); + ruleNode.addChild(errorNode); + } +} diff --git a/src/org/antlr/runtime/debug/Profiler.java b/src/org/antlr/runtime/debug/Profiler.java new file mode 100755 index 0000000..aea9a17 --- /dev/null +++ b/src/org/antlr/runtime/debug/Profiler.java @@ -0,0 +1,734 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.*; +import org.antlr.runtime.misc.DoubleKeyMap; + +import java.util.*; + +/** Using the debug event interface, track what is happening in the parser + * and record statistics about the runtime. + */ +public class Profiler extends BlankDebugEventListener { + public static final String DATA_SEP = "\t"; + public static final String newline = System.getProperty("line.separator"); + + static boolean dump = false; + + public static class ProfileStats { + public String Version; + public String name; + public int numRuleInvocations; + public int numUniqueRulesInvoked; + public int numDecisionEvents; + public int numDecisionsCovered; + public int numDecisionsThatPotentiallyBacktrack; + public int numDecisionsThatDoBacktrack; + public int maxRuleInvocationDepth; + public float avgkPerDecisionEvent; + public float avgkPerBacktrackingDecisionEvent; + public float averageDecisionPercentBacktracks; + public int numBacktrackOccurrences; // doesn't count gated DFA edges + + public int numFixedDecisions; + public int minDecisionMaxFixedLookaheads; + public int maxDecisionMaxFixedLookaheads; + public int avgDecisionMaxFixedLookaheads; + public int stddevDecisionMaxFixedLookaheads; + public int numCyclicDecisions; + public int minDecisionMaxCyclicLookaheads; + public int maxDecisionMaxCyclicLookaheads; + public int avgDecisionMaxCyclicLookaheads; + public int stddevDecisionMaxCyclicLookaheads; +// int Stats.min(toArray(decisionMaxSynPredLookaheads); +// int Stats.max(toArray(decisionMaxSynPredLookaheads); +// int Stats.avg(toArray(decisionMaxSynPredLookaheads); +// int Stats.stddev(toArray(decisionMaxSynPredLookaheads); + public int numSemanticPredicates; + public int numTokens; + public int numHiddenTokens; + public int numCharsMatched; + public int numHiddenCharsMatched; + public int numReportedErrors; + public int numMemoizationCacheHits; + public int numMemoizationCacheMisses; + public int numGuessingRuleInvocations; + public int numMemoizationCacheEntries; + } + + public static class DecisionDescriptor { + public int decision; + public String fileName; + public String ruleName; + public int line; + public int pos; + public boolean couldBacktrack; + + public int n; + public float avgk; // avg across all decision events + public int maxk; + public int numBacktrackOccurrences; + public int numSemPredEvals; + } + + // all about a specific exec of a single decision + public static class DecisionEvent { + public DecisionDescriptor decision; + public int startIndex; + public int k; + public boolean backtracks; // doesn't count gated DFA edges + public boolean evalSemPred; + public long startTime; + public long stopTime; + public int numMemoizationCacheHits; + public int numMemoizationCacheMisses; + } + + /** Because I may change the stats, I need to track that for later + * computations to be consistent. + */ + public static final String Version = "3"; + public static final String RUNTIME_STATS_FILENAME = "runtime.stats"; + + /** Ack, should not store parser; can't do remote stuff. Well, we pass + * input stream around too so I guess it's ok. + */ + public DebugParser parser = null; + + // working variables + + protected int ruleLevel = 0; + //protected int decisionLevel = 0; + protected Token lastRealTokenTouchedInDecision; + protected Set<String> uniqueRules = new HashSet<String>(); + protected Stack<String> currentGrammarFileName = new Stack(); + protected Stack<String> currentRuleName = new Stack(); + protected Stack<Integer> currentLine = new Stack(); + protected Stack<Integer> currentPos = new Stack(); + + // Vector<DecisionStats> + //protected Vector decisions = new Vector(200); // need setSize + protected DoubleKeyMap<String,Integer, DecisionDescriptor> decisions = + new DoubleKeyMap<String,Integer, DecisionDescriptor>(); + + // Record a DecisionData for each decision we hit while parsing + protected List<DecisionEvent> decisionEvents = new ArrayList<DecisionEvent>(); + protected Stack<DecisionEvent> decisionStack = new Stack<DecisionEvent>(); + + protected int backtrackDepth; + + ProfileStats stats = new ProfileStats(); + + public Profiler() { + } + + public Profiler(DebugParser parser) { + this.parser = parser; + } + + public void enterRule(String grammarFileName, String ruleName) { +// System.out.println("enterRule "+grammarFileName+":"+ruleName); + ruleLevel++; + stats.numRuleInvocations++; + uniqueRules.add(grammarFileName+":"+ruleName); + stats.maxRuleInvocationDepth = Math.max(stats.maxRuleInvocationDepth, ruleLevel); + currentGrammarFileName.push( grammarFileName ); + currentRuleName.push( ruleName ); + } + + public void exitRule(String grammarFileName, String ruleName) { + ruleLevel--; + currentGrammarFileName.pop(); + currentRuleName.pop(); + } + + /** Track memoization; this is not part of standard debug interface + * but is triggered by profiling. Code gen inserts an override + * for this method in the recognizer, which triggers this method. + * Called from alreadyParsedRule(). + */ + public void examineRuleMemoization(IntStream input, + int ruleIndex, + int stopIndex, // index or MEMO_RULE_UNKNOWN... + String ruleName) + { + if (dump) System.out.println("examine memo "+ruleName+" at "+input.index()+": "+stopIndex); + if ( stopIndex==BaseRecognizer.MEMO_RULE_UNKNOWN ) { + //System.out.println("rule "+ruleIndex+" missed @ "+input.index()); + stats.numMemoizationCacheMisses++; + stats.numGuessingRuleInvocations++; // we'll have to enter + currentDecision().numMemoizationCacheMisses++; + } + else { + // regardless of rule success/failure, if in cache, we have a cache hit + //System.out.println("rule "+ruleIndex+" hit @ "+input.index()); + stats.numMemoizationCacheHits++; + currentDecision().numMemoizationCacheHits++; + } + } + + /** Warning: doesn't track success/failure, just unique recording event */ + public void memoize(IntStream input, + int ruleIndex, + int ruleStartIndex, + String ruleName) + { + // count how many entries go into table + if (dump) System.out.println("memoize "+ruleName); + stats.numMemoizationCacheEntries++; + } + + @Override + public void location(int line, int pos) { + currentLine.push(line); + currentPos.push(pos); + } + + public void enterDecision(int decisionNumber, boolean couldBacktrack) { + lastRealTokenTouchedInDecision = null; + stats.numDecisionEvents++; + int startingLookaheadIndex = parser.getTokenStream().index(); + TokenStream input = parser.getTokenStream(); + if ( dump ) System.out.println("enterDecision canBacktrack="+couldBacktrack+" "+ decisionNumber + + " backtrack depth " + backtrackDepth + + " @ " + input.get(input.index()) + + " rule " +locationDescription()); + String g = (String) currentGrammarFileName.peek(); + DecisionDescriptor descriptor = decisions.get(g, decisionNumber); + if ( descriptor == null ) { + descriptor = new DecisionDescriptor(); + decisions.put(g, decisionNumber, descriptor); + descriptor.decision = decisionNumber; + descriptor.fileName = (String)currentGrammarFileName.peek(); + descriptor.ruleName = (String)currentRuleName.peek(); + descriptor.line = (Integer)currentLine.peek(); + descriptor.pos = (Integer)currentPos.peek(); + descriptor.couldBacktrack = couldBacktrack; + } + descriptor.n++; + + DecisionEvent d = new DecisionEvent(); + decisionStack.push(d); + d.decision = descriptor; + d.startTime = System.currentTimeMillis(); + d.startIndex = startingLookaheadIndex; + } + + public void exitDecision(int decisionNumber) { + DecisionEvent d = decisionStack.pop(); + d.stopTime = System.currentTimeMillis(); + + int lastTokenIndex = lastRealTokenTouchedInDecision.getTokenIndex(); + int numHidden = getNumberOfHiddenTokens(d.startIndex, lastTokenIndex); + int depth = lastTokenIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1 + d.k = depth; + d.decision.maxk = Math.max(d.decision.maxk, depth); + + if (dump) System.out.println("exitDecision "+decisionNumber+" in "+d.decision.ruleName+ + " lookahead "+d.k +" max token "+lastRealTokenTouchedInDecision); + decisionEvents.add(d); // done with decision; track all + } + + public void consumeToken(Token token) { + if (dump) System.out.println("consume token "+token); + if ( !inDecision() ) { + stats.numTokens++; + return; + } + if ( lastRealTokenTouchedInDecision==null || + lastRealTokenTouchedInDecision.getTokenIndex() < token.getTokenIndex() ) + { + lastRealTokenTouchedInDecision = token; + } + DecisionEvent d = currentDecision(); + // compute lookahead depth + int thisRefIndex = token.getTokenIndex(); + int numHidden = getNumberOfHiddenTokens(d.startIndex, thisRefIndex); + int depth = thisRefIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1 + //d.maxk = Math.max(d.maxk, depth); + if (dump) System.out.println("consume "+thisRefIndex+" "+depth+" tokens ahead in "+ + d.decision.ruleName+"-"+d.decision.decision+" start index "+d.startIndex); + } + + /** The parser is in a decision if the decision depth > 0. This + * works for backtracking also, which can have nested decisions. + */ + public boolean inDecision() { + return decisionStack.size()>0; + } + + public void consumeHiddenToken(Token token) { + //System.out.println("consume hidden token "+token); + if ( !inDecision() ) stats.numHiddenTokens++; + } + + /** Track refs to lookahead if in a fixed/nonfixed decision. + */ + public void LT(int i, Token t) { + if ( inDecision() && i>0 ) { + DecisionEvent d = currentDecision(); + if (dump) System.out.println("LT("+i+")="+t+" index "+t.getTokenIndex()+" relative to "+d.decision.ruleName+"-"+ + d.decision.decision+" start index "+d.startIndex); + if ( lastRealTokenTouchedInDecision==null || + lastRealTokenTouchedInDecision.getTokenIndex() < t.getTokenIndex() ) + { + lastRealTokenTouchedInDecision = t; + if (dump) System.out.println("set last token "+lastRealTokenTouchedInDecision); + } + // get starting index off stack +// int stackTop = lookaheadStack.size()-1; +// Integer startingIndex = (Integer)lookaheadStack.get(stackTop); +// // compute lookahead depth +// int thisRefIndex = parser.getTokenStream().index(); +// int numHidden = +// getNumberOfHiddenTokens(startingIndex.intValue(), thisRefIndex); +// int depth = i + thisRefIndex - startingIndex.intValue() - numHidden; +// /* +// System.out.println("LT("+i+") @ index "+thisRefIndex+" is depth "+depth+ +// " max is "+maxLookaheadInCurrentDecision); +// */ +// if ( depth>maxLookaheadInCurrentDecision ) { +// maxLookaheadInCurrentDecision = depth; +// } +// d.maxk = currentDecision()/ + } + } + + /** Track backtracking decisions. You'll see a fixed or cyclic decision + * and then a backtrack. + * + * enter rule + * ... + * enter decision + * LA and possibly consumes (for cyclic DFAs) + * begin backtrack level + * mark m + * rewind m + * end backtrack level, success + * exit decision + * ... + * exit rule + */ + public void beginBacktrack(int level) { + if (dump) System.out.println("enter backtrack "+level); + backtrackDepth++; + DecisionEvent e = currentDecision(); + if ( e.decision.couldBacktrack ) { + stats.numBacktrackOccurrences++; + e.decision.numBacktrackOccurrences++; + e.backtracks = true; + } + } + + /** Successful or not, track how much lookahead synpreds use */ + public void endBacktrack(int level, boolean successful) { + if (dump) System.out.println("exit backtrack "+level+": "+successful); + backtrackDepth--; + } + + @Override + public void mark(int i) { + if (dump) System.out.println("mark "+i); + } + + @Override + public void rewind(int i) { + if (dump) System.out.println("rewind "+i); + } + + @Override + public void rewind() { + if (dump) System.out.println("rewind"); + } + + + + protected DecisionEvent currentDecision() { + return decisionStack.peek(); + } + + public void recognitionException(RecognitionException e) { + stats.numReportedErrors++; + } + + public void semanticPredicate(boolean result, String predicate) { + stats.numSemanticPredicates++; + if ( inDecision() ) { + DecisionEvent d = currentDecision(); + d.evalSemPred = true; + d.decision.numSemPredEvals++; + if (dump) System.out.println("eval "+predicate+" in "+d.decision.ruleName+"-"+ + d.decision.decision); + } + } + + public void terminate() { + for (DecisionEvent e : decisionEvents) { + //System.out.println("decision "+e.decision.decision+": k="+e.k); + e.decision.avgk += e.k; + stats.avgkPerDecisionEvent += e.k; + if ( e.backtracks ) { // doesn't count gated syn preds on DFA edges + stats.avgkPerBacktrackingDecisionEvent += e.k; + } + } + stats.averageDecisionPercentBacktracks = 0.0f; + for (DecisionDescriptor d : decisions.values()) { + stats.numDecisionsCovered++; + d.avgk /= (double)d.n; + if ( d.couldBacktrack ) { + stats.numDecisionsThatPotentiallyBacktrack++; + float percentBacktracks = d.numBacktrackOccurrences / (float)d.n; + //System.out.println("dec "+d.decision+" backtracks "+percentBacktracks*100+"%"); + stats.averageDecisionPercentBacktracks += percentBacktracks; + } + // ignore rules that backtrack along gated DFA edges + if ( d.numBacktrackOccurrences > 0 ) { + stats.numDecisionsThatDoBacktrack++; + } + } + stats.averageDecisionPercentBacktracks /= stats.numDecisionsThatPotentiallyBacktrack; + stats.averageDecisionPercentBacktracks *= 100; // it's a percentage + stats.avgkPerDecisionEvent /= stats.numDecisionEvents; + stats.avgkPerBacktrackingDecisionEvent /= (double)stats.numBacktrackOccurrences; + + System.err.println(toString()); + System.err.println(getDecisionStatsDump()); + +// String stats = toNotifyString(); +// try { +// Stats.writeReport(RUNTIME_STATS_FILENAME,stats); +// } +// catch (IOException ioe) { +// System.err.println(ioe); +// ioe.printStackTrace(System.err); +// } + } + + public void setParser(DebugParser parser) { + this.parser = parser; + } + + // R E P O R T I N G + + public String toNotifyString() { + StringBuffer buf = new StringBuffer(); + buf.append(Version); + buf.append('\t'); + buf.append(parser.getClass().getName()); +// buf.append('\t'); +// buf.append(numRuleInvocations); +// buf.append('\t'); +// buf.append(maxRuleInvocationDepth); +// buf.append('\t'); +// buf.append(numFixedDecisions); +// buf.append('\t'); +// buf.append(Stats.min(decisionMaxFixedLookaheads)); +// buf.append('\t'); +// buf.append(Stats.max(decisionMaxFixedLookaheads)); +// buf.append('\t'); +// buf.append(Stats.avg(decisionMaxFixedLookaheads)); +// buf.append('\t'); +// buf.append(Stats.stddev(decisionMaxFixedLookaheads)); +// buf.append('\t'); +// buf.append(numCyclicDecisions); +// buf.append('\t'); +// buf.append(Stats.min(decisionMaxCyclicLookaheads)); +// buf.append('\t'); +// buf.append(Stats.max(decisionMaxCyclicLookaheads)); +// buf.append('\t'); +// buf.append(Stats.avg(decisionMaxCyclicLookaheads)); +// buf.append('\t'); +// buf.append(Stats.stddev(decisionMaxCyclicLookaheads)); +// buf.append('\t'); +// buf.append(numBacktrackDecisions); +// buf.append('\t'); +// buf.append(Stats.min(toArray(decisionMaxSynPredLookaheads))); +// buf.append('\t'); +// buf.append(Stats.max(toArray(decisionMaxSynPredLookaheads))); +// buf.append('\t'); +// buf.append(Stats.avg(toArray(decisionMaxSynPredLookaheads))); +// buf.append('\t'); +// buf.append(Stats.stddev(toArray(decisionMaxSynPredLookaheads))); +// buf.append('\t'); +// buf.append(numSemanticPredicates); +// buf.append('\t'); +// buf.append(parser.getTokenStream().size()); +// buf.append('\t'); +// buf.append(numHiddenTokens); +// buf.append('\t'); +// buf.append(numCharsMatched); +// buf.append('\t'); +// buf.append(numHiddenCharsMatched); +// buf.append('\t'); +// buf.append(numberReportedErrors); +// buf.append('\t'); +// buf.append(numMemoizationCacheHits); +// buf.append('\t'); +// buf.append(numMemoizationCacheMisses); +// buf.append('\t'); +// buf.append(numGuessingRuleInvocations); +// buf.append('\t'); +// buf.append(numMemoizationCacheEntries); + return buf.toString(); + } + + public String toString() { + return toString(getReport()); + } + + public ProfileStats getReport() { +// TokenStream input = parser.getTokenStream(); +// for (int i=0; i<input.size()&& lastRealTokenTouchedInDecision !=null&&i<= lastRealTokenTouchedInDecision.getTokenIndex(); i++) { +// Token t = input.get(i); +// if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) { +// stats.numHiddenTokens++; +// stats.numHiddenCharsMatched += t.getText().length(); +// } +// } + stats.Version = Version; + stats.name = parser.getClass().getName(); + stats.numUniqueRulesInvoked = uniqueRules.size(); + //stats.numCharsMatched = lastTokenConsumed.getStopIndex() + 1; + return stats; + } + + public DoubleKeyMap getDecisionStats() { + return decisions; + } + + public List getDecisionEvents() { + return decisionEvents; + } + + public static String toString(ProfileStats stats) { + StringBuffer buf = new StringBuffer(); + buf.append("ANTLR Runtime Report; Profile Version "); + buf.append(stats.Version); + buf.append(newline); + buf.append("parser name "); + buf.append(stats.name); + buf.append(newline); + buf.append("Number of rule invocations "); + buf.append(stats.numRuleInvocations); + buf.append(newline); + buf.append("Number of unique rules visited "); + buf.append(stats.numUniqueRulesInvoked); + buf.append(newline); + buf.append("Number of decision events "); + buf.append(stats.numDecisionEvents); + buf.append(newline); + buf.append("Overall average k per decision event "); + buf.append(stats.avgkPerDecisionEvent); + buf.append(newline); + buf.append("Number of backtracking occurrences (can be multiple per decision) "); + buf.append(stats.numBacktrackOccurrences); + buf.append(newline); + buf.append("Overall average k per decision event that backtracks "); + buf.append(stats.avgkPerBacktrackingDecisionEvent); + buf.append(newline); + buf.append("Number of rule invocations while backtracking "); + buf.append(stats.numGuessingRuleInvocations); + buf.append(newline); + buf.append("num decisions that potentially backtrack "); + buf.append(stats.numDecisionsThatPotentiallyBacktrack); + buf.append(newline); + buf.append("num decisions that do backtrack "); + buf.append(stats.numDecisionsThatDoBacktrack); + buf.append(newline); + buf.append("num decisions that potentially backtrack but don't "); + buf.append(stats.numDecisionsThatPotentiallyBacktrack - stats.numDecisionsThatDoBacktrack); + buf.append(newline); + buf.append("average % of time a potentially backtracking decision backtracks "); + buf.append(stats.averageDecisionPercentBacktracks); + buf.append(newline); + buf.append("num unique decisions covered "); + buf.append(stats.numDecisionsCovered); + buf.append(newline); + buf.append("max rule invocation nesting depth "); + buf.append(stats.maxRuleInvocationDepth); + buf.append(newline); + +// buf.append("number of fixed lookahead decisions "); +// buf.append(); +// buf.append('\n'); +// buf.append("min lookahead used in a fixed lookahead decision "); +// buf.append(); +// buf.append('\n'); +// buf.append("max lookahead used in a fixed lookahead decision "); +// buf.append(); +// buf.append('\n'); +// buf.append("average lookahead depth used in fixed lookahead decisions "); +// buf.append(); +// buf.append('\n'); +// buf.append("standard deviation of depth used in fixed lookahead decisions "); +// buf.append(); +// buf.append('\n'); +// buf.append("number of arbitrary lookahead decisions "); +// buf.append(); +// buf.append('\n'); +// buf.append("min lookahead used in an arbitrary lookahead decision "); +// buf.append(); +// buf.append('\n'); +// buf.append("max lookahead used in an arbitrary lookahead decision "); +// buf.append(); +// buf.append('\n'); +// buf.append("average lookahead depth used in arbitrary lookahead decisions "); +// buf.append(); +// buf.append('\n'); +// buf.append("standard deviation of depth used in arbitrary lookahead decisions "); +// buf.append(); +// buf.append('\n'); +// buf.append("number of evaluated syntactic predicates "); +// buf.append(); +// buf.append('\n'); +// buf.append("min lookahead used in a syntactic predicate "); +// buf.append(); +// buf.append('\n'); +// buf.append("max lookahead used in a syntactic predicate "); +// buf.append(); +// buf.append('\n'); +// buf.append("average lookahead depth used in syntactic predicates "); +// buf.append(); +// buf.append('\n'); +// buf.append("standard deviation of depth used in syntactic predicates "); +// buf.append(); +// buf.append('\n'); + buf.append("rule memoization cache size "); + buf.append(stats.numMemoizationCacheEntries); + buf.append(newline); + buf.append("number of rule memoization cache hits "); + buf.append(stats.numMemoizationCacheHits); + buf.append(newline); + buf.append("number of rule memoization cache misses "); + buf.append(stats.numMemoizationCacheMisses); + buf.append(newline); +// buf.append("number of evaluated semantic predicates "); +// buf.append(); +// buf.append(newline); + buf.append("number of tokens "); + buf.append(stats.numTokens); + buf.append(newline); + buf.append("number of hidden tokens "); + buf.append(stats.numHiddenTokens); + buf.append(newline); + buf.append("number of char "); + buf.append(stats.numCharsMatched); + buf.append(newline); + buf.append("number of hidden char "); + buf.append(stats.numHiddenCharsMatched); + buf.append(newline); + buf.append("number of syntax errors "); + buf.append(stats.numReportedErrors); + buf.append(newline); + return buf.toString(); + } + + public String getDecisionStatsDump() { + StringBuffer buf = new StringBuffer(); + buf.append("location"); + buf.append(DATA_SEP); + buf.append("n"); + buf.append(DATA_SEP); + buf.append("avgk"); + buf.append(DATA_SEP); + buf.append("maxk"); + buf.append(DATA_SEP); + buf.append("synpred"); + buf.append(DATA_SEP); + buf.append("sempred"); + buf.append(DATA_SEP); + buf.append("canbacktrack"); + buf.append("\n"); + for (String fileName : decisions.keySet()) { + for (int d : decisions.keySet(fileName)) { + DecisionDescriptor s = decisions.get(fileName, d); + buf.append(s.decision); + buf.append("@"); + buf.append(locationDescription(s.fileName,s.ruleName,s.line,s.pos)); // decision number + buf.append(DATA_SEP); + buf.append(s.n); + buf.append(DATA_SEP); + buf.append(String.format("%.2f",s.avgk)); + buf.append(DATA_SEP); + buf.append(s.maxk); + buf.append(DATA_SEP); + buf.append(s.numBacktrackOccurrences); + buf.append(DATA_SEP); + buf.append(s.numSemPredEvals); + buf.append(DATA_SEP); + buf.append(s.couldBacktrack ?"1":"0"); + buf.append(newline); + } + } + return buf.toString(); + } + + protected int[] trim(int[] X, int n) { + if ( n<X.length ) { + int[] trimmed = new int[n]; + System.arraycopy(X,0,trimmed,0,n); + X = trimmed; + } + return X; + } + + protected int[] toArray(List a) { + int[] x = new int[a.size()]; + for (int i = 0; i < a.size(); i++) { + Integer I = (Integer) a.get(i); + x[i] = I.intValue(); + } + return x; + } + + /** Get num hidden tokens between i..j inclusive */ + public int getNumberOfHiddenTokens(int i, int j) { + int n = 0; + TokenStream input = parser.getTokenStream(); + for (int ti = i; ti<input.size() && ti <= j; ti++) { + Token t = input.get(ti); + if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) { + n++; + } + } + return n; + } + + protected String locationDescription() { + return locationDescription( + currentGrammarFileName.peek(), + currentRuleName.peek(), + currentLine.peek(), + currentPos.peek()); + } + + protected String locationDescription(String file, String rule, int line, int pos) { + return file+":"+line+":"+pos+"(" + rule + ")"; + } +} diff --git a/src/org/antlr/runtime/debug/RemoteDebugEventSocketListener.java b/src/org/antlr/runtime/debug/RemoteDebugEventSocketListener.java new file mode 100755 index 0000000..933fdae --- /dev/null +++ b/src/org/antlr/runtime/debug/RemoteDebugEventSocketListener.java @@ -0,0 +1,527 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.tree.BaseTree; +import org.antlr.runtime.tree.Tree; + +import java.io.*; +import java.net.ConnectException; +import java.net.Socket; +import java.util.StringTokenizer; + +public class RemoteDebugEventSocketListener implements Runnable { + static final int MAX_EVENT_ELEMENTS = 8; + DebugEventListener listener; + String machine; + int port; + Socket channel = null; + PrintWriter out; + BufferedReader in; + String event; + /** Version of ANTLR (dictates events) */ + public String version; + public String grammarFileName; + /** Track the last token index we saw during a consume. If same, then + * set a flag that we have a problem. + */ + int previousTokenIndex = -1; + boolean tokenIndexesInvalid = false; + + public static class ProxyToken implements Token { + int index; + int type; + int channel; + int line; + int charPos; + String text; + public ProxyToken(int index) { this.index = index; } + public ProxyToken(int index, int type, int channel, + int line, int charPos, String text) + { + this.index = index; + this.type = type; + this.channel = channel; + this.line = line; + this.charPos = charPos; + this.text = text; + } + public String getText() { + return text; + } + public void setText(String text) { + this.text = text; + } + public int getType() { + return type; + } + public void setType(int ttype) { + this.type = ttype; + } + public int getLine() { + return line; + } + public void setLine(int line) { + this.line = line; + } + public int getCharPositionInLine() { + return charPos; + } + public void setCharPositionInLine(int pos) { + this.charPos = pos; + } + public int getChannel() { + return channel; + } + public void setChannel(int channel) { + this.channel = channel; + } + public int getTokenIndex() { + return index; + } + public void setTokenIndex(int index) { + this.index = index; + } + public CharStream getInputStream() { + return null; + } + public void setInputStream(CharStream input) { + } + public String toString() { + String channelStr = ""; + if ( channel!=Token.DEFAULT_CHANNEL ) { + channelStr=",channel="+channel; + } + return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]"; + } + } + + public static class ProxyTree extends BaseTree { + public int ID; + public int type; + public int line = 0; + public int charPos = -1; + public int tokenIndex = -1; + public String text; + + public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) { + this.ID = ID; + this.type = type; + this.line = line; + this.charPos = charPos; + this.tokenIndex = tokenIndex; + this.text = text; + } + + public ProxyTree(int ID) { this.ID = ID; } + + public int getTokenStartIndex() { return tokenIndex; } + public void setTokenStartIndex(int index) { } + public int getTokenStopIndex() { return 0; } + public void setTokenStopIndex(int index) { } + public Tree dupNode() { return null; } + public int getType() { return type; } + public String getText() { return text; } + public String toString() { + return "fix this"; + } + } + + public RemoteDebugEventSocketListener(DebugEventListener listener, + String machine, + int port) throws IOException + { + this.listener = listener; + this.machine = machine; + this.port = port; + + if( !openConnection() ) { + throw new ConnectException(); + } + } + + protected void eventHandler() { + try { + handshake(); + event = in.readLine(); + while ( event!=null ) { + dispatch(event); + ack(); + event = in.readLine(); + } + } + catch (Exception e) { + System.err.println(e); + e.printStackTrace(System.err); + } + finally { + closeConnection(); + } + } + + protected boolean openConnection() { + boolean success = false; + try { + channel = new Socket(machine, port); + channel.setTcpNoDelay(true); + OutputStream os = channel.getOutputStream(); + OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8"); + out = new PrintWriter(new BufferedWriter(osw)); + InputStream is = channel.getInputStream(); + InputStreamReader isr = new InputStreamReader(is, "UTF8"); + in = new BufferedReader(isr); + success = true; + } catch(Exception e) { + System.err.println(e); + } + return success; + } + + protected void closeConnection() { + try { + in.close(); in = null; + out.close(); out = null; + channel.close(); channel=null; + } + catch (Exception e) { + System.err.println(e); + e.printStackTrace(System.err); + } + finally { + if ( in!=null ) { + try {in.close();} catch (IOException ioe) { + System.err.println(ioe); + } + } + if ( out!=null ) { + out.close(); + } + if ( channel!=null ) { + try {channel.close();} catch (IOException ioe) { + System.err.println(ioe); + } + } + } + + } + + protected void handshake() throws IOException { + String antlrLine = in.readLine(); + String[] antlrElements = getEventElements(antlrLine); + version = antlrElements[1]; + String grammarLine = in.readLine(); + String[] grammarElements = getEventElements(grammarLine); + grammarFileName = grammarElements[1]; + ack(); + listener.commence(); // inform listener after handshake + } + + protected void ack() { + out.println("ack"); + out.flush(); + } + + protected void dispatch(String line) { + //System.out.println("event: "+line); + String[] elements = getEventElements(line); + if ( elements==null || elements[0]==null ) { + System.err.println("unknown debug event: "+line); + return; + } + if ( elements[0].equals("enterRule") ) { + listener.enterRule(elements[1], elements[2]); + } + else if ( elements[0].equals("exitRule") ) { + listener.exitRule(elements[1], elements[2]); + } + else if ( elements[0].equals("enterAlt") ) { + listener.enterAlt(Integer.parseInt(elements[1])); + } + else if ( elements[0].equals("enterSubRule") ) { + listener.enterSubRule(Integer.parseInt(elements[1])); + } + else if ( elements[0].equals("exitSubRule") ) { + listener.exitSubRule(Integer.parseInt(elements[1])); + } + else if ( elements[0].equals("enterDecision") ) { + listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true")); + } + else if ( elements[0].equals("exitDecision") ) { + listener.exitDecision(Integer.parseInt(elements[1])); + } + else if ( elements[0].equals("location") ) { + listener.location(Integer.parseInt(elements[1]), + Integer.parseInt(elements[2])); + } + else if ( elements[0].equals("consumeToken") ) { + ProxyToken t = deserializeToken(elements, 1); + if ( t.getTokenIndex() == previousTokenIndex ) { + tokenIndexesInvalid = true; + } + previousTokenIndex = t.getTokenIndex(); + listener.consumeToken(t); + } + else if ( elements[0].equals("consumeHiddenToken") ) { + ProxyToken t = deserializeToken(elements, 1); + if ( t.getTokenIndex() == previousTokenIndex ) { + tokenIndexesInvalid = true; + } + previousTokenIndex = t.getTokenIndex(); + listener.consumeHiddenToken(t); + } + else if ( elements[0].equals("LT") ) { + Token t = deserializeToken(elements, 2); + listener.LT(Integer.parseInt(elements[1]), t); + } + else if ( elements[0].equals("mark") ) { + listener.mark(Integer.parseInt(elements[1])); + } + else if ( elements[0].equals("rewind") ) { + if ( elements[1]!=null ) { + listener.rewind(Integer.parseInt(elements[1])); + } + else { + listener.rewind(); + } + } + else if ( elements[0].equals("beginBacktrack") ) { + listener.beginBacktrack(Integer.parseInt(elements[1])); + } + else if ( elements[0].equals("endBacktrack") ) { + int level = Integer.parseInt(elements[1]); + int successI = Integer.parseInt(elements[2]); + listener.endBacktrack(level, successI==DebugEventListener.TRUE); + } + else if ( elements[0].equals("exception") ) { + String excName = elements[1]; + String indexS = elements[2]; + String lineS = elements[3]; + String posS = elements[4]; + Class excClass = null; + try { + excClass = Class.forName(excName); + RecognitionException e = + (RecognitionException)excClass.newInstance(); + e.index = Integer.parseInt(indexS); + e.line = Integer.parseInt(lineS); + e.charPositionInLine = Integer.parseInt(posS); + listener.recognitionException(e); + } + catch (ClassNotFoundException cnfe) { + System.err.println("can't find class "+cnfe); + cnfe.printStackTrace(System.err); + } + catch (InstantiationException ie) { + System.err.println("can't instantiate class "+ie); + ie.printStackTrace(System.err); + } + catch (IllegalAccessException iae) { + System.err.println("can't access class "+iae); + iae.printStackTrace(System.err); + } + } + else if ( elements[0].equals("beginResync") ) { + listener.beginResync(); + } + else if ( elements[0].equals("endResync") ) { + listener.endResync(); + } + else if ( elements[0].equals("terminate") ) { + listener.terminate(); + } + else if ( elements[0].equals("semanticPredicate") ) { + Boolean result = Boolean.valueOf(elements[1]); + String predicateText = elements[2]; + predicateText = unEscapeNewlines(predicateText); + listener.semanticPredicate(result.booleanValue(), + predicateText); + } + else if ( elements[0].equals("consumeNode") ) { + ProxyTree node = deserializeNode(elements, 1); + listener.consumeNode(node); + } + else if ( elements[0].equals("LN") ) { + int i = Integer.parseInt(elements[1]); + ProxyTree node = deserializeNode(elements, 2); + listener.LT(i, node); + } + else if ( elements[0].equals("createNodeFromTokenElements") ) { + int ID = Integer.parseInt(elements[1]); + int type = Integer.parseInt(elements[2]); + String text = elements[3]; + text = unEscapeNewlines(text); + ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); + listener.createNode(node); + } + else if ( elements[0].equals("createNode") ) { + int ID = Integer.parseInt(elements[1]); + int tokenIndex = Integer.parseInt(elements[2]); + // create dummy node/token filled with ID, tokenIndex + ProxyTree node = new ProxyTree(ID); + ProxyToken token = new ProxyToken(tokenIndex); + listener.createNode(node, token); + } + else if ( elements[0].equals("nilNode") ) { + int ID = Integer.parseInt(elements[1]); + ProxyTree node = new ProxyTree(ID); + listener.nilNode(node); + } + else if ( elements[0].equals("errorNode") ) { + // TODO: do we need a special tree here? + int ID = Integer.parseInt(elements[1]); + int type = Integer.parseInt(elements[2]); + String text = elements[3]; + text = unEscapeNewlines(text); + ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text); + listener.errorNode(node); + } + else if ( elements[0].equals("becomeRoot") ) { + int newRootID = Integer.parseInt(elements[1]); + int oldRootID = Integer.parseInt(elements[2]); + ProxyTree newRoot = new ProxyTree(newRootID); + ProxyTree oldRoot = new ProxyTree(oldRootID); + listener.becomeRoot(newRoot, oldRoot); + } + else if ( elements[0].equals("addChild") ) { + int rootID = Integer.parseInt(elements[1]); + int childID = Integer.parseInt(elements[2]); + ProxyTree root = new ProxyTree(rootID); + ProxyTree child = new ProxyTree(childID); + listener.addChild(root, child); + } + else if ( elements[0].equals("setTokenBoundaries") ) { + int ID = Integer.parseInt(elements[1]); + ProxyTree node = new ProxyTree(ID); + listener.setTokenBoundaries( + node, + Integer.parseInt(elements[2]), + Integer.parseInt(elements[3])); + } + else { + System.err.println("unknown debug event: "+line); + } + } + + protected ProxyTree deserializeNode(String[] elements, int offset) { + int ID = Integer.parseInt(elements[offset+0]); + int type = Integer.parseInt(elements[offset+1]); + int tokenLine = Integer.parseInt(elements[offset+2]); + int charPositionInLine = Integer.parseInt(elements[offset+3]); + int tokenIndex = Integer.parseInt(elements[offset+4]); + String text = elements[offset+5]; + text = unEscapeNewlines(text); + return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text); + } + + protected ProxyToken deserializeToken(String[] elements, + int offset) + { + String indexS = elements[offset+0]; + String typeS = elements[offset+1]; + String channelS = elements[offset+2]; + String lineS = elements[offset+3]; + String posS = elements[offset+4]; + String text = elements[offset+5]; + text = unEscapeNewlines(text); + int index = Integer.parseInt(indexS); + ProxyToken t = + new ProxyToken(index, + Integer.parseInt(typeS), + Integer.parseInt(channelS), + Integer.parseInt(lineS), + Integer.parseInt(posS), + text); + return t; + } + + /** Create a thread to listen to the remote running recognizer */ + public void start() { + Thread t = new Thread(this); + t.start(); + } + + public void run() { + eventHandler(); + } + + // M i s c + + public String[] getEventElements(String event) { + if ( event==null ) { + return null; + } + String[] elements = new String[MAX_EVENT_ELEMENTS]; + String str = null; // a string element if present (must be last) + try { + int firstQuoteIndex = event.indexOf('"'); + if ( firstQuoteIndex>=0 ) { + // treat specially; has a string argument like "a comment\n + // Note that the string is terminated by \n not end quote. + // Easier to parse that way. + String eventWithoutString = event.substring(0,firstQuoteIndex); + str = event.substring(firstQuoteIndex+1,event.length()); + event = eventWithoutString; + } + StringTokenizer st = new StringTokenizer(event, "\t", false); + int i = 0; + while ( st.hasMoreTokens() ) { + if ( i>=MAX_EVENT_ELEMENTS ) { + // ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event); + return elements; + } + elements[i] = st.nextToken(); + i++; + } + if ( str!=null ) { + elements[i] = str; + } + } + catch (Exception e) { + e.printStackTrace(System.err); + } + return elements; + } + + protected String unEscapeNewlines(String txt) { + // this unescape is slow but easy to understand + txt = txt.replaceAll("%0A","\n"); // unescape \n + txt = txt.replaceAll("%0D","\r"); // unescape \r + txt = txt.replaceAll("%25","%"); // undo escaped escape chars + return txt; + } + + public boolean tokenIndexesAreInvalid() { + return false; + //return tokenIndexesInvalid; + } + +} + diff --git a/src/org/antlr/runtime/debug/TraceDebugEventListener.java b/src/org/antlr/runtime/debug/TraceDebugEventListener.java new file mode 100755 index 0000000..de9366d --- /dev/null +++ b/src/org/antlr/runtime/debug/TraceDebugEventListener.java @@ -0,0 +1,96 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.TreeAdaptor; + +/** Print out (most of) the events... Useful for debugging, testing... */ +public class TraceDebugEventListener extends BlankDebugEventListener { + TreeAdaptor adaptor; + + public TraceDebugEventListener(TreeAdaptor adaptor) { + this.adaptor = adaptor; + } + + public void enterRule(String ruleName) { System.out.println("enterRule "+ruleName); } + public void exitRule(String ruleName) { System.out.println("exitRule "+ruleName); } + public void enterSubRule(int decisionNumber) { System.out.println("enterSubRule"); } + public void exitSubRule(int decisionNumber) { System.out.println("exitSubRule"); } + public void location(int line, int pos) {System.out.println("location "+line+":"+pos);} + + // Tree parsing stuff + + public void consumeNode(Object t) { + int ID = adaptor.getUniqueID(t); + String text = adaptor.getText(t); + int type = adaptor.getType(t); + System.out.println("consumeNode "+ID+" "+text+" "+type); + } + + public void LT(int i, Object t) { + int ID = adaptor.getUniqueID(t); + String text = adaptor.getText(t); + int type = adaptor.getType(t); + System.out.println("LT "+i+" "+ID+" "+text+" "+type); + } + + + // AST stuff + public void nilNode(Object t) {System.out.println("nilNode "+adaptor.getUniqueID(t));} + + public void createNode(Object t) { + int ID = adaptor.getUniqueID(t); + String text = adaptor.getText(t); + int type = adaptor.getType(t); + System.out.println("create "+ID+": "+text+", "+type); + } + + public void createNode(Object node, Token token) { + int ID = adaptor.getUniqueID(node); + String text = adaptor.getText(node); + int tokenIndex = token.getTokenIndex(); + System.out.println("create "+ID+": "+tokenIndex); + } + + public void becomeRoot(Object newRoot, Object oldRoot) { + System.out.println("becomeRoot "+adaptor.getUniqueID(newRoot)+", "+ + adaptor.getUniqueID(oldRoot)); + } + + public void addChild(Object root, Object child) { + System.out.println("addChild "+adaptor.getUniqueID(root)+", "+ + adaptor.getUniqueID(child)); + } + + public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) { + System.out.println("setTokenBoundaries "+adaptor.getUniqueID(t)+", "+ + tokenStartIndex+", "+tokenStopIndex); + } +} + diff --git a/src/org/antlr/runtime/debug/Tracer.java b/src/org/antlr/runtime/debug/Tracer.java new file mode 100755 index 0000000..c2c73da --- /dev/null +++ b/src/org/antlr/runtime/debug/Tracer.java @@ -0,0 +1,65 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.debug; + +import org.antlr.runtime.IntStream; +import org.antlr.runtime.TokenStream; + +/** The default tracer mimics the traceParser behavior of ANTLR 2.x. + * This listens for debugging events from the parser and implies + * that you cannot debug and trace at the same time. + */ +public class Tracer extends BlankDebugEventListener { + public IntStream input; + protected int level = 0; + + public Tracer(IntStream input) { + this.input = input; + } + + public void enterRule(String ruleName) { + for (int i=1; i<=level; i++) {System.out.print(" ");} + System.out.println("> "+ruleName+" lookahead(1)="+getInputSymbol(1)); + level++; + } + + public void exitRule(String ruleName) { + level--; + for (int i=1; i<=level; i++) {System.out.print(" ");} + System.out.println("< "+ruleName+" lookahead(1)="+getInputSymbol(1)); + } + + public Object getInputSymbol(int k) { + if ( input instanceof TokenStream ) { + return ((TokenStream)input).LT(k); + } + return new Character((char)input.LA(k)); + } +} + + diff --git a/src/org/antlr/runtime/misc/DoubleKeyMap.java b/src/org/antlr/runtime/misc/DoubleKeyMap.java new file mode 100755 index 0000000..b69ae32 --- /dev/null +++ b/src/org/antlr/runtime/misc/DoubleKeyMap.java @@ -0,0 +1,62 @@ +package org.antlr.runtime.misc; + +import java.util.*; + +/** Sometimes we need to map a key to a value but key is two pieces of data. + * This nested hash table saves creating a single key each time we access + * map; avoids mem creation. + */ +public class DoubleKeyMap<Key1, Key2, Value> { + Map<Key1, Map<Key2, Value>> data = new LinkedHashMap<Key1, Map<Key2, Value>>(); + + public Value put(Key1 k1, Key2 k2, Value v) { + Map<Key2, Value> data2 = data.get(k1); + Value prev = null; + if ( data2==null ) { + data2 = new LinkedHashMap<Key2, Value>(); + data.put(k1, data2); + } + else { + prev = data2.get(k2); + } + data2.put(k2, v); + return prev; + } + + public Value get(Key1 k1, Key2 k2) { + Map<Key2, Value> data2 = data.get(k1); + if ( data2==null ) return null; + return data2.get(k2); + } + + public Map<Key2, Value> get(Key1 k1) { return data.get(k1); } + + /** Get all values associated with primary key */ + public Collection<Value> values(Key1 k1) { + Map<Key2, Value> data2 = data.get(k1); + if ( data2==null ) return null; + return data2.values(); + } + + /** get all primary keys */ + public Set<Key1> keySet() { + return data.keySet(); + } + + /** get all secondary keys associated with a primary key */ + public Set<Key2> keySet(Key1 k1) { + Map<Key2, Value> data2 = data.get(k1); + if ( data2==null ) return null; + return data2.keySet(); + } + + public Collection<Value> values() { + Set<Value> s = new HashSet<Value>(); + for (Map<Key2, Value> k2 : data.values()) { + for (Value v : k2.values()) { + s.add(v); + } + } + return s; + } +} diff --git a/src/org/antlr/runtime/misc/FastQueue.java b/src/org/antlr/runtime/misc/FastQueue.java new file mode 100755 index 0000000..08843dd --- /dev/null +++ b/src/org/antlr/runtime/misc/FastQueue.java @@ -0,0 +1,100 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.misc; + +import java.util.List; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +/** A queue that can dequeue and get(i) in O(1) and grow arbitrarily large. + * A linked list is fast at dequeue but slow at get(i). An array is + * the reverse. This is O(1) for both operations. + * + * List grows until you dequeue last element at end of buffer. Then + * it resets to start filling at 0 again. If adds/removes are balanced, the + * buffer will not grow too large. + * + * No iterator stuff as that's not how we'll use it. + */ +public class FastQueue<T> { + /** dynamically-sized buffer of elements */ + protected List<T> data = new ArrayList<T>(); + /** index of next element to fill */ + protected int p = 0; + protected int range = -1; // how deep have we gone? + + public void reset() { clear(); } + public void clear() { p = 0; data.clear(); } + + /** Get and remove first element in queue */ + public T remove() { + T o = elementAt(0); + p++; + // have we hit end of buffer? + if ( p == data.size() ) { + // if so, it's an opportunity to start filling at index 0 again + clear(); // size goes to 0, but retains memory + } + return o; + } + + public void add(T o) { data.add(o); } + + public int size() { return data.size() - p; } + + public int range() { return range; } + + public T head() { return elementAt(0); } + + /** Return element i elements ahead of current element. i==0 gets + * current element. This is not an absolute index into the data list + * since p defines the start of the real list. + */ + public T elementAt(int i) { + int absIndex = p + i; + if ( absIndex >= data.size() ) { + throw new NoSuchElementException("queue index "+ absIndex +" > last index "+(data.size()-1)); + } + if ( absIndex < 0 ) { + throw new NoSuchElementException("queue index "+ absIndex +" < 0"); + } + if ( absIndex>range ) range = absIndex; + return data.get(absIndex); + } + + /** Return string of current buffer contents; non-destructive */ + public String toString() { + StringBuffer buf = new StringBuffer(); + int n = size(); + for (int i=0; i<n; i++) { + buf.append(elementAt(i)); + if ( (i+1)<n ) buf.append(" "); + } + return buf.toString(); + } +}
\ No newline at end of file diff --git a/src/org/antlr/runtime/misc/IntArray.java b/src/org/antlr/runtime/misc/IntArray.java new file mode 100755 index 0000000..bc484aa --- /dev/null +++ b/src/org/antlr/runtime/misc/IntArray.java @@ -0,0 +1,87 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.misc; + +/** A dynamic array that uses int not Integer objects. In principle this + * is more efficient in time, but certainly in space. + * + * This is simple enough that you can access the data array directly, + * but make sure that you append elements only with add() so that you + * get dynamic sizing. Make sure to call ensureCapacity() when you are + * manually adding new elements. + * + * Doesn't impl List because it doesn't return objects and I mean this + * really as just an array not a List per se. Manipulate the elements + * at will. This has stack methods too. + * + * When runtime can be 1.5, I'll make this generic. + */ +public class IntArray { + public static final int INITIAL_SIZE = 10; + public int[] data; + protected int p = -1; + + public void add(int v) { + ensureCapacity(p+1); + data[++p] = v; + } + + public void push(int v) { + add(v); + } + + public int pop() { + int v = data[p]; + p--; + return v; + } + + /** This only tracks elements added via push/add. */ + public int size() { + return p; + } + + public void clear() { + p = -1; + } + + public void ensureCapacity(int index) { + if ( data==null ) { + data = new int[INITIAL_SIZE]; + } + else if ( (index+1)>=data.length ) { + int newSize = data.length*2; + if ( index>newSize ) { + newSize = index+1; + } + int[] newData = new int[newSize]; + System.arraycopy(data, 0, newData, 0, data.length); + data = newData; + } + } +} diff --git a/src/org/antlr/runtime/misc/LookaheadStream.java b/src/org/antlr/runtime/misc/LookaheadStream.java new file mode 100755 index 0000000..6f19c44 --- /dev/null +++ b/src/org/antlr/runtime/misc/LookaheadStream.java @@ -0,0 +1,161 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.misc; + +import org.antlr.runtime.Token; + +import java.util.NoSuchElementException; + +/** A lookahead queue that knows how to mark/release locations + * in the buffer for backtracking purposes. Any markers force the FastQueue + * superclass to keep all tokens until no more markers; then can reset + * to avoid growing a huge buffer. + */ +public abstract class LookaheadStream<T> extends FastQueue<T> { + public static final int UNINITIALIZED_EOF_ELEMENT_INDEX = Integer.MAX_VALUE; + + /** Absolute token index. It's the index of the symbol about to be + * read via LT(1). Goes from 0 to numtokens. + */ + protected int currentElementIndex = 0; + + protected T prevElement; + + /** Track object returned by nextElement upon end of stream; + * Return it later when they ask for LT passed end of input. + */ + public T eof = null; + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + /** tracks how deep mark() calls are nested */ + protected int markDepth = 0; + + public void reset() { + super.reset(); + currentElementIndex = 0; + p = 0; + prevElement=null; + } + + /** Implement nextElement to supply a stream of elements to this + * lookahead buffer. Return eof upon end of the stream we're pulling from. + */ + public abstract T nextElement(); + + public abstract boolean isEOF(T o); + + /** Get and remove first element in queue; override FastQueue.remove(); + * it's the same, just checks for backtracking. + */ + public T remove() { + T o = elementAt(0); + p++; + // have we hit end of buffer and not backtracking? + if ( p == data.size() && markDepth==0 ) { + // if so, it's an opportunity to start filling at index 0 again + clear(); // size goes to 0, but retains memory + } + return o; + } + + /** Make sure we have at least one element to remove, even if EOF */ + public void consume() { + syncAhead(1); + prevElement = remove(); + currentElementIndex++; + } + + /** Make sure we have 'need' elements from current position p. Last valid + * p index is data.size()-1. p+need-1 is the data index 'need' elements + * ahead. If we need 1 element, (p+1-1)==p must be < data.size(). + */ + protected void syncAhead(int need) { + int n = (p+need-1) - data.size() + 1; // how many more elements we need? + if ( n > 0 ) fill(n); // out of elements? + } + + /** add n elements to buffer */ + public void fill(int n) { + for (int i=1; i<=n; i++) { + T o = nextElement(); + if ( isEOF(o) ) eof = o; + data.add(o); + } + } + + /** Size of entire stream is unknown; we only know buffer size from FastQueue */ + public int size() { throw new UnsupportedOperationException("streams are of unknown size"); } + + public T LT(int k) { + if ( k==0 ) { + return null; + } + if ( k<0 ) return LB(-k); + //System.out.print("LT(p="+p+","+k+")="); + syncAhead(k); + if ( (p+k-1) > data.size() ) return eof; + return elementAt(k-1); + } + + public int index() { return currentElementIndex; } + + public int mark() { + markDepth++; + lastMarker = p; // track where we are in buffer not absolute token index + return lastMarker; + } + + public void release(int marker) { + // no resources to release + } + + public void rewind(int marker) { + markDepth--; + seek(marker); // assume marker is top + // release(marker); // waste of call; it does nothing in this class + } + + public void rewind() { + seek(lastMarker); // rewind but do not release marker + } + + /** Seek to a 0-indexed position within data buffer. Can't handle + * case where you seek beyond end of existing buffer. Normally used + * to seek backwards in the buffer. Does not force loading of nodes. + * Doesn't see to absolute position in input stream since this stream + * is unbuffered. Seeks only into our moving window of elements. + */ + public void seek(int index) { p = index; } + + protected T LB(int k) { + if ( k==1 ) return prevElement; + throw new NoSuchElementException("can't look backwards more than one token in this stream"); + } +}
\ No newline at end of file diff --git a/src/org/antlr/runtime/misc/Stats.java b/src/org/antlr/runtime/misc/Stats.java new file mode 100755 index 0000000..9fdd21e --- /dev/null +++ b/src/org/antlr/runtime/misc/Stats.java @@ -0,0 +1,189 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.misc; + +import java.io.*; +import java.util.List; + +/** Stats routines needed by profiler etc... + + // note that these routines return 0.0 if no values exist in the X[] + // which is not "correct", but it is useful so I don't generate NaN + // in my output + + */ +public class Stats { + public static final String ANTLRWORKS_DIR = "antlrworks"; + + /** Compute the sample (unbiased estimator) standard deviation following: + * + * Computing Deviations: Standard Accuracy + * Tony F. Chan and John Gregg Lewis + * Stanford University + * Communications of ACM September 1979 of Volume 22 the ACM Number 9 + * + * The "two-pass" method from the paper; supposed to have better + * numerical properties than the textbook summation/sqrt. To me + * this looks like the textbook method, but I ain't no numerical + * methods guy. + */ + public static double stddev(int[] X) { + int m = X.length; + if ( m<=1 ) { + return 0; + } + double xbar = avg(X); + double s2 = 0.0; + for (int i=0; i<m; i++){ + s2 += (X[i] - xbar)*(X[i] - xbar); + } + s2 = s2/(m-1); + return Math.sqrt(s2); + } + + /** Compute the sample mean */ + public static double avg(int[] X) { + double xbar = 0.0; + int m = X.length; + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + xbar += X[i]; + } + if ( xbar>=0.0 ) { + return xbar / m; + } + return 0.0; + } + + public static int min(int[] X) { + int min = Integer.MAX_VALUE; + int m = X.length; + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + if ( X[i] < min ) { + min = X[i]; + } + } + return min; + } + + public static int max(int[] X) { + int max = Integer.MIN_VALUE; + int m = X.length; + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + if ( X[i] > max ) { + max = X[i]; + } + } + return max; + } + + /** Compute the sample mean */ + public static double avg(List<Integer> X) { + double xbar = 0.0; + int m = X.size(); + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + xbar += X.get(i); + } + if ( xbar>=0.0 ) { + return xbar / m; + } + return 0.0; + } + + public static int min(List<Integer> X) { + int min = Integer.MAX_VALUE; + int m = X.size(); + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + if ( X.get(i) < min ) { + min = X.get(i); + } + } + return min; + } + + public static int max(List<Integer> X) { + int max = Integer.MIN_VALUE; + int m = X.size(); + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + if ( X.get(i) > max ) { + max = X.get(i); + } + } + return max; + } + + public static int sum(int[] X) { + int s = 0; + int m = X.length; + if ( m==0 ) { + return 0; + } + for (int i=0; i<m; i++){ + s += X[i]; + } + return s; + } + + public static void writeReport(String filename, String data) throws IOException { + String absoluteFilename = getAbsoluteFileName(filename); + File f = new File(absoluteFilename); + File parent = f.getParentFile(); + parent.mkdirs(); // ensure parent dir exists + // write file + FileOutputStream fos = new FileOutputStream(f, true); // append + BufferedOutputStream bos = new BufferedOutputStream(fos); + PrintStream ps = new PrintStream(bos); + ps.println(data); + ps.close(); + bos.close(); + fos.close(); + } + + public static String getAbsoluteFileName(String filename) { + return System.getProperty("user.home")+File.separator+ + ANTLRWORKS_DIR +File.separator+ + filename; + } +} diff --git a/src/org/antlr/runtime/tree/.DS_Store b/src/org/antlr/runtime/tree/.DS_Store Binary files differnew file mode 100644 index 0000000..5008ddf --- /dev/null +++ b/src/org/antlr/runtime/tree/.DS_Store diff --git a/src/org/antlr/runtime/tree/BaseTree.java b/src/org/antlr/runtime/tree/BaseTree.java new file mode 100755 index 0000000..ebffdb3 --- /dev/null +++ b/src/org/antlr/runtime/tree/BaseTree.java @@ -0,0 +1,349 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import java.util.ArrayList; +import java.util.List; + +/** A generic tree implementation with no payload. You must subclass to + * actually have any user data. ANTLR v3 uses a list of children approach + * instead of the child-sibling approach in v2. A flat tree (a list) is + * an empty node whose children represent the list. An empty, but + * non-null node is called "nil". + */ +public abstract class BaseTree implements Tree { + protected List children; + + public BaseTree() { + } + + /** Create a new node from an existing node does nothing for BaseTree + * as there are no fields other than the children list, which cannot + * be copied as the children are not considered part of this node. + */ + public BaseTree(Tree node) { + } + + public Tree getChild(int i) { + if ( children==null || i>=children.size() ) { + return null; + } + return (Tree)children.get(i); + } + + /** Get the children internal List; note that if you directly mess with + * the list, do so at your own risk. + */ + public List getChildren() { + return children; + } + + public Tree getFirstChildWithType(int type) { + for (int i = 0; children!=null && i < children.size(); i++) { + Tree t = (Tree) children.get(i); + if ( t.getType()==type ) { + return t; + } + } + return null; + } + + public int getChildCount() { + if ( children==null ) { + return 0; + } + return children.size(); + } + + /** Add t as child of this node. + * + * Warning: if t has no children, but child does + * and child isNil then this routine moves children to t via + * t.children = child.children; i.e., without copying the array. + */ + public void addChild(Tree t) { + //System.out.println("add child "+t.toStringTree()+" "+this.toStringTree()); + //System.out.println("existing children: "+children); + if ( t==null ) { + return; // do nothing upon addChild(null) + } + BaseTree childTree = (BaseTree)t; + if ( childTree.isNil() ) { // t is an empty node possibly with children + if ( this.children!=null && this.children == childTree.children ) { + throw new RuntimeException("attempt to add child list to itself"); + } + // just add all of childTree's children to this + if ( childTree.children!=null ) { + if ( this.children!=null ) { // must copy, this has children already + int n = childTree.children.size(); + for (int i = 0; i < n; i++) { + Tree c = (Tree)childTree.children.get(i); + this.children.add(c); + // handle double-link stuff for each child of nil root + c.setParent(this); + c.setChildIndex(children.size()-1); + } + } + else { + // no children for this but t has children; just set pointer + // call general freshener routine + this.children = childTree.children; + this.freshenParentAndChildIndexes(); + } + } + } + else { // child is not nil (don't care about children) + if ( children==null ) { + children = createChildrenList(); // create children list on demand + } + children.add(t); + childTree.setParent(this); + childTree.setChildIndex(children.size()-1); + } + // System.out.println("now children are: "+children); + } + + /** Add all elements of kids list as children of this node */ + public void addChildren(List kids) { + for (int i = 0; i < kids.size(); i++) { + Tree t = (Tree) kids.get(i); + addChild(t); + } + } + + public void setChild(int i, Tree t) { + if ( t==null ) { + return; + } + if ( t.isNil() ) { + throw new IllegalArgumentException("Can't set single child to a list"); + } + if ( children==null ) { + children = createChildrenList(); + } + children.set(i, t); + t.setParent(this); + t.setChildIndex(i); + } + + public Object deleteChild(int i) { + if ( children==null ) { + return null; + } + Tree killed = (Tree)children.remove(i); + // walk rest and decrement their child indexes + this.freshenParentAndChildIndexes(i); + return killed; + } + + /** Delete children from start to stop and replace with t even if t is + * a list (nil-root tree). num of children can increase or decrease. + * For huge child lists, inserting children can force walking rest of + * children to set their childindex; could be slow. + */ + public void replaceChildren(int startChildIndex, int stopChildIndex, Object t) { + /* + System.out.println("replaceChildren "+startChildIndex+", "+stopChildIndex+ + " with "+((BaseTree)t).toStringTree()); + System.out.println("in="+toStringTree()); + */ + if ( children==null ) { + throw new IllegalArgumentException("indexes invalid; no children in list"); + } + int replacingHowMany = stopChildIndex - startChildIndex + 1; + int replacingWithHowMany; + BaseTree newTree = (BaseTree)t; + List newChildren = null; + // normalize to a list of children to add: newChildren + if ( newTree.isNil() ) { + newChildren = newTree.children; + } + else { + newChildren = new ArrayList(1); + newChildren.add(newTree); + } + replacingWithHowMany = newChildren.size(); + int numNewChildren = newChildren.size(); + int delta = replacingHowMany - replacingWithHowMany; + // if same number of nodes, do direct replace + if ( delta == 0 ) { + int j = 0; // index into new children + for (int i=startChildIndex; i<=stopChildIndex; i++) { + BaseTree child = (BaseTree)newChildren.get(j); + children.set(i, child); + child.setParent(this); + child.setChildIndex(i); + j++; + } + } + else if ( delta > 0 ) { // fewer new nodes than there were + // set children and then delete extra + for (int j=0; j<numNewChildren; j++) { + children.set(startChildIndex+j, newChildren.get(j)); + } + int indexToDelete = startChildIndex+numNewChildren; + for (int c=indexToDelete; c<=stopChildIndex; c++) { + // delete same index, shifting everybody down each time + children.remove(indexToDelete); + } + freshenParentAndChildIndexes(startChildIndex); + } + else { // more new nodes than were there before + // fill in as many children as we can (replacingHowMany) w/o moving data + for (int j=0; j<replacingHowMany; j++) { + children.set(startChildIndex+j, newChildren.get(j)); + } + int numToInsert = replacingWithHowMany-replacingHowMany; + for (int j=replacingHowMany; j<replacingWithHowMany; j++) { + children.add(startChildIndex+j, newChildren.get(j)); + } + freshenParentAndChildIndexes(startChildIndex); + } + //System.out.println("out="+toStringTree()); + } + + /** Override in a subclass to change the impl of children list */ + protected List createChildrenList() { + return new ArrayList(); + } + + public boolean isNil() { + return false; + } + + /** Set the parent and child index values for all child of t */ + public void freshenParentAndChildIndexes() { + freshenParentAndChildIndexes(0); + } + + public void freshenParentAndChildIndexes(int offset) { + int n = getChildCount(); + for (int c = offset; c < n; c++) { + Tree child = (Tree)getChild(c); + child.setChildIndex(c); + child.setParent(this); + } + } + + public void sanityCheckParentAndChildIndexes() { + sanityCheckParentAndChildIndexes(null, -1); + } + + public void sanityCheckParentAndChildIndexes(Tree parent, int i) { + if ( parent!=this.getParent() ) { + throw new IllegalStateException("parents don't match; expected "+parent+" found "+this.getParent()); + } + if ( i!=this.getChildIndex() ) { + throw new IllegalStateException("child indexes don't match; expected "+i+" found "+this.getChildIndex()); + } + int n = this.getChildCount(); + for (int c = 0; c < n; c++) { + CommonTree child = (CommonTree)this.getChild(c); + child.sanityCheckParentAndChildIndexes(this, c); + } + } + + /** BaseTree doesn't track child indexes. */ + public int getChildIndex() { + return 0; + } + public void setChildIndex(int index) { + } + + /** BaseTree doesn't track parent pointers. */ + public Tree getParent() { + return null; + } + + public void setParent(Tree t) { + } + + /** Walk upwards looking for ancestor with this token type. */ + public boolean hasAncestor(int ttype) { return getAncestor(ttype)!=null; } + + /** Walk upwards and get first ancestor with this token type. */ + public Tree getAncestor(int ttype) { + Tree t = this; + t = t.getParent(); + while ( t!=null ) { + if ( t.getType()==ttype ) return t; + t = t.getParent(); + } + return null; + } + + /** Return a list of all ancestors of this node. The first node of + * list is the root and the last is the parent of this node. + */ + public List getAncestors() { + if ( getParent()==null ) return null; + List ancestors = new ArrayList(); + Tree t = this; + t = t.getParent(); + while ( t!=null ) { + ancestors.add(0, t); // insert at start + t = t.getParent(); + } + return ancestors; + } + + /** Print out a whole tree not just a node */ + public String toStringTree() { + if ( children==null || children.size()==0 ) { + return this.toString(); + } + StringBuffer buf = new StringBuffer(); + if ( !isNil() ) { + buf.append("("); + buf.append(this.toString()); + buf.append(' '); + } + for (int i = 0; children!=null && i < children.size(); i++) { + Tree t = (Tree)children.get(i); + if ( i>0 ) { + buf.append(' '); + } + buf.append(t.toStringTree()); + } + if ( !isNil() ) { + buf.append(")"); + } + return buf.toString(); + } + + public int getLine() { + return 0; + } + + public int getCharPositionInLine() { + return 0; + } + + /** Override to say how a node (not a tree) should look as text */ + public abstract String toString(); +} diff --git a/src/org/antlr/runtime/tree/BaseTreeAdaptor.java b/src/org/antlr/runtime/tree/BaseTreeAdaptor.java new file mode 100755 index 0000000..33140b1 --- /dev/null +++ b/src/org/antlr/runtime/tree/BaseTreeAdaptor.java @@ -0,0 +1,279 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.RecognitionException; + +import java.util.HashMap; +import java.util.Map; + +/** A TreeAdaptor that works with any Tree implementation. */ +public abstract class BaseTreeAdaptor implements TreeAdaptor { + /** System.identityHashCode() is not always unique; we have to + * track ourselves. That's ok, it's only for debugging, though it's + * expensive: we have to create a hashtable with all tree nodes in it. + */ + protected Map treeToUniqueIDMap; + protected int uniqueNodeID = 1; + + public Object nil() { + return create(null); + } + + /** create tree node that holds the start and stop tokens associated + * with an error. + * + * If you specify your own kind of tree nodes, you will likely have to + * override this method. CommonTree returns Token.INVALID_TOKEN_TYPE + * if no token payload but you might have to set token type for diff + * node type. + * + * You don't have to subclass CommonErrorNode; you will likely need to + * subclass your own tree node class to avoid class cast exception. + */ + public Object errorNode(TokenStream input, Token start, Token stop, + RecognitionException e) + { + CommonErrorNode t = new CommonErrorNode(input, start, stop, e); + //System.out.println("returning error node '"+t+"' @index="+input.index()); + return t; + } + + public boolean isNil(Object tree) { + return ((Tree)tree).isNil(); + } + + public Object dupTree(Object tree) { + return dupTree(tree, null); + } + + /** This is generic in the sense that it will work with any kind of + * tree (not just Tree interface). It invokes the adaptor routines + * not the tree node routines to do the construction. + */ + public Object dupTree(Object t, Object parent) { + if ( t==null ) { + return null; + } + Object newTree = dupNode(t); + // ensure new subtree root has parent/child index set + setChildIndex(newTree, getChildIndex(t)); // same index in new tree + setParent(newTree, parent); + int n = getChildCount(t); + for (int i = 0; i < n; i++) { + Object child = getChild(t, i); + Object newSubTree = dupTree(child, t); + addChild(newTree, newSubTree); + } + return newTree; + } + + /** Add a child to the tree t. If child is a flat tree (a list), make all + * in list children of t. Warning: if t has no children, but child does + * and child isNil then you can decide it is ok to move children to t via + * t.children = child.children; i.e., without copying the array. Just + * make sure that this is consistent with have the user will build + * ASTs. + */ + public void addChild(Object t, Object child) { + if ( t!=null && child!=null ) { + ((Tree)t).addChild((Tree)child); + } + } + + /** If oldRoot is a nil root, just copy or move the children to newRoot. + * If not a nil root, make oldRoot a child of newRoot. + * + * old=^(nil a b c), new=r yields ^(r a b c) + * old=^(a b c), new=r yields ^(r ^(a b c)) + * + * If newRoot is a nil-rooted single child tree, use the single + * child as the new root node. + * + * old=^(nil a b c), new=^(nil r) yields ^(r a b c) + * old=^(a b c), new=^(nil r) yields ^(r ^(a b c)) + * + * If oldRoot was null, it's ok, just return newRoot (even if isNil). + * + * old=null, new=r yields r + * old=null, new=^(nil r) yields ^(nil r) + * + * Return newRoot. Throw an exception if newRoot is not a + * simple node or nil root with a single child node--it must be a root + * node. If newRoot is ^(nil x) return x as newRoot. + * + * Be advised that it's ok for newRoot to point at oldRoot's + * children; i.e., you don't have to copy the list. We are + * constructing these nodes so we should have this control for + * efficiency. + */ + public Object becomeRoot(Object newRoot, Object oldRoot) { + //System.out.println("becomeroot new "+newRoot.toString()+" old "+oldRoot); + Tree newRootTree = (Tree)newRoot; + Tree oldRootTree = (Tree)oldRoot; + if ( oldRoot==null ) { + return newRoot; + } + // handle ^(nil real-node) + if ( newRootTree.isNil() ) { + int nc = newRootTree.getChildCount(); + if ( nc==1 ) newRootTree = (Tree)newRootTree.getChild(0); + else if ( nc >1 ) { + // TODO: make tree run time exceptions hierarchy + throw new RuntimeException("more than one node as root (TODO: make exception hierarchy)"); + } + } + // add oldRoot to newRoot; addChild takes care of case where oldRoot + // is a flat list (i.e., nil-rooted tree). All children of oldRoot + // are added to newRoot. + newRootTree.addChild(oldRootTree); + return newRootTree; + } + + /** Transform ^(nil x) to x and nil to null */ + public Object rulePostProcessing(Object root) { + //System.out.println("rulePostProcessing: "+((Tree)root).toStringTree()); + Tree r = (Tree)root; + if ( r!=null && r.isNil() ) { + if ( r.getChildCount()==0 ) { + r = null; + } + else if ( r.getChildCount()==1 ) { + r = (Tree)r.getChild(0); + // whoever invokes rule will set parent and child index + r.setParent(null); + r.setChildIndex(-1); + } + } + return r; + } + + public Object becomeRoot(Token newRoot, Object oldRoot) { + return becomeRoot(create(newRoot), oldRoot); + } + + public Object create(int tokenType, Token fromToken) { + fromToken = createToken(fromToken); + //((ClassicToken)fromToken).setType(tokenType); + fromToken.setType(tokenType); + Tree t = (Tree)create(fromToken); + return t; + } + + public Object create(int tokenType, Token fromToken, String text) { + if (fromToken == null) return create(tokenType, text); + fromToken = createToken(fromToken); + fromToken.setType(tokenType); + fromToken.setText(text); + Tree t = (Tree)create(fromToken); + return t; + } + + public Object create(int tokenType, String text) { + Token fromToken = createToken(tokenType, text); + Tree t = (Tree)create(fromToken); + return t; + } + + public int getType(Object t) { + return ((Tree)t).getType(); + } + + public void setType(Object t, int type) { + throw new NoSuchMethodError("don't know enough about Tree node"); + } + + public String getText(Object t) { + return ((Tree)t).getText(); + } + + public void setText(Object t, String text) { + throw new NoSuchMethodError("don't know enough about Tree node"); + } + + public Object getChild(Object t, int i) { + return ((Tree)t).getChild(i); + } + + public void setChild(Object t, int i, Object child) { + ((Tree)t).setChild(i, (Tree)child); + } + + public Object deleteChild(Object t, int i) { + return ((Tree)t).deleteChild(i); + } + + public int getChildCount(Object t) { + return ((Tree)t).getChildCount(); + } + + public int getUniqueID(Object node) { + if ( treeToUniqueIDMap==null ) { + treeToUniqueIDMap = new HashMap(); + } + Integer prevID = (Integer)treeToUniqueIDMap.get(node); + if ( prevID!=null ) { + return prevID.intValue(); + } + int ID = uniqueNodeID; + treeToUniqueIDMap.put(node, new Integer(ID)); + uniqueNodeID++; + return ID; + // GC makes these nonunique: + // return System.identityHashCode(node); + } + + /** Tell me how to create a token for use with imaginary token nodes. + * For example, there is probably no input symbol associated with imaginary + * token DECL, but you need to create it as a payload or whatever for + * the DECL node as in ^(DECL type ID). + * + * If you care what the token payload objects' type is, you should + * override this method and any other createToken variant. + */ + public abstract Token createToken(int tokenType, String text); + + /** Tell me how to create a token for use with imaginary token nodes. + * For example, there is probably no input symbol associated with imaginary + * token DECL, but you need to create it as a payload or whatever for + * the DECL node as in ^(DECL type ID). + * + * This is a variant of createToken where the new token is derived from + * an actual real input token. Typically this is for converting '{' + * tokens to BLOCK etc... You'll see + * + * r : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ; + * + * If you care what the token payload objects' type is, you should + * override this method and any other createToken variant. + */ + public abstract Token createToken(Token fromToken); +} + diff --git a/src/org/antlr/runtime/tree/BufferedTreeNodeStream.java b/src/org/antlr/runtime/tree/BufferedTreeNodeStream.java new file mode 100755 index 0000000..d9a2a7e --- /dev/null +++ b/src/org/antlr/runtime/tree/BufferedTreeNodeStream.java @@ -0,0 +1,478 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.misc.IntArray; +import java.util.*; + +/** A buffered stream of tree nodes. Nodes can be from a tree of ANY kind. + * + * This node stream sucks all nodes out of the tree specified in + * the constructor during construction and makes pointers into + * the tree using an array of Object pointers. The stream necessarily + * includes pointers to DOWN and UP and EOF nodes. + * + * This stream knows how to mark/release for backtracking. + * + * This stream is most suitable for tree interpreters that need to + * jump around a lot or for tree parsers requiring speed (at cost of memory). + * There is some duplicated functionality here with UnBufferedTreeNodeStream + * but just in bookkeeping, not tree walking etc... + * + * TARGET DEVELOPERS: + * + * This is the old CommonTreeNodeStream that buffered up entire node stream. + * No need to implement really as new CommonTreeNodeStream is much better + * and covers what we need. + * + * @see CommonTreeNodeStream + */ +public class BufferedTreeNodeStream implements TreeNodeStream { + public static final int DEFAULT_INITIAL_BUFFER_SIZE = 100; + public static final int INITIAL_CALL_STACK_SIZE = 10; + + protected class StreamIterator implements Iterator { + int i = 0; + public boolean hasNext() { + return i<nodes.size(); + } + + public Object next() { + int current = i; + i++; + if ( current < nodes.size() ) { + return nodes.get(current); + } + return eof; + } + + public void remove() { + throw new RuntimeException("cannot remove nodes from stream"); + } + } + + // all these navigation nodes are shared and hence they + // cannot contain any line/column info + + protected Object down; + protected Object up; + protected Object eof; + + /** The complete mapping from stream index to tree node. + * This buffer includes pointers to DOWN, UP, and EOF nodes. + * It is built upon ctor invocation. The elements are type + * Object as we don't what the trees look like. + * + * Load upon first need of the buffer so we can set token types + * of interest for reverseIndexing. Slows us down a wee bit to + * do all of the if p==-1 testing everywhere though. + */ + protected List nodes; + + /** Pull nodes from which tree? */ + protected Object root; + + /** IF this tree (root) was created from a token stream, track it. */ + protected TokenStream tokens; + + /** What tree adaptor was used to build these trees */ + TreeAdaptor adaptor; + + /** Reuse same DOWN, UP navigation nodes unless this is true */ + protected boolean uniqueNavigationNodes = false; + + /** The index into the nodes list of the current node (next node + * to consume). If -1, nodes array not filled yet. + */ + protected int p = -1; + + /** Track the last mark() call result value for use in rewind(). */ + protected int lastMarker; + + /** Stack of indexes used for push/pop calls */ + protected IntArray calls; + + public BufferedTreeNodeStream(Object tree) { + this(new CommonTreeAdaptor(), tree); + } + + public BufferedTreeNodeStream(TreeAdaptor adaptor, Object tree) { + this(adaptor, tree, DEFAULT_INITIAL_BUFFER_SIZE); + } + + public BufferedTreeNodeStream(TreeAdaptor adaptor, Object tree, int initialBufferSize) { + this.root = tree; + this.adaptor = adaptor; + nodes = new ArrayList(initialBufferSize); + down = adaptor.create(Token.DOWN, "DOWN"); + up = adaptor.create(Token.UP, "UP"); + eof = adaptor.create(Token.EOF, "EOF"); + } + + /** Walk tree with depth-first-search and fill nodes buffer. + * Don't do DOWN, UP nodes if its a list (t is isNil). + */ + protected void fillBuffer() { + fillBuffer(root); + //System.out.println("revIndex="+tokenTypeToStreamIndexesMap); + p = 0; // buffer of nodes intialized now + } + + public void fillBuffer(Object t) { + boolean nil = adaptor.isNil(t); + if ( !nil ) { + nodes.add(t); // add this node + } + // add DOWN node if t has children + int n = adaptor.getChildCount(t); + if ( !nil && n>0 ) { + addNavigationNode(Token.DOWN); + } + // and now add all its children + for (int c=0; c<n; c++) { + Object child = adaptor.getChild(t,c); + fillBuffer(child); + } + // add UP node if t has children + if ( !nil && n>0 ) { + addNavigationNode(Token.UP); + } + } + + /** What is the stream index for node? 0..n-1 + * Return -1 if node not found. + */ + protected int getNodeIndex(Object node) { + if ( p==-1 ) { + fillBuffer(); + } + for (int i = 0; i < nodes.size(); i++) { + Object t = (Object) nodes.get(i); + if ( t==node ) { + return i; + } + } + return -1; + } + + /** As we flatten the tree, we use UP, DOWN nodes to represent + * the tree structure. When debugging we need unique nodes + * so instantiate new ones when uniqueNavigationNodes is true. + */ + protected void addNavigationNode(final int ttype) { + Object navNode = null; + if ( ttype==Token.DOWN ) { + if ( hasUniqueNavigationNodes() ) { + navNode = adaptor.create(Token.DOWN, "DOWN"); + } + else { + navNode = down; + } + } + else { + if ( hasUniqueNavigationNodes() ) { + navNode = adaptor.create(Token.UP, "UP"); + } + else { + navNode = up; + } + } + nodes.add(navNode); + } + + public Object get(int i) { + if ( p==-1 ) { + fillBuffer(); + } + return nodes.get(i); + } + + public Object LT(int k) { + if ( p==-1 ) { + fillBuffer(); + } + if ( k==0 ) { + return null; + } + if ( k<0 ) { + return LB(-k); + } + //System.out.print("LT(p="+p+","+k+")="); + if ( (p+k-1) >= nodes.size() ) { + return eof; + } + return nodes.get(p+k-1); + } + + public Object getCurrentSymbol() { return LT(1); } + +/* + public Object getLastTreeNode() { + int i = index(); + if ( i>=size() ) { + i--; // if at EOF, have to start one back + } + System.out.println("start last node: "+i+" size=="+nodes.size()); + while ( i>=0 && + (adaptor.getType(get(i))==Token.EOF || + adaptor.getType(get(i))==Token.UP || + adaptor.getType(get(i))==Token.DOWN) ) + { + i--; + } + System.out.println("stop at node: "+i+" "+nodes.get(i)); + return nodes.get(i); + } +*/ + + /** Look backwards k nodes */ + protected Object LB(int k) { + if ( k==0 ) { + return null; + } + if ( (p-k)<0 ) { + return null; + } + return nodes.get(p-k); + } + + public Object getTreeSource() { + return root; + } + + public String getSourceName() { + return getTokenStream().getSourceName(); + } + + public TokenStream getTokenStream() { + return tokens; + } + + public void setTokenStream(TokenStream tokens) { + this.tokens = tokens; + } + + public TreeAdaptor getTreeAdaptor() { + return adaptor; + } + + public void setTreeAdaptor(TreeAdaptor adaptor) { + this.adaptor = adaptor; + } + + public boolean hasUniqueNavigationNodes() { + return uniqueNavigationNodes; + } + + public void setUniqueNavigationNodes(boolean uniqueNavigationNodes) { + this.uniqueNavigationNodes = uniqueNavigationNodes; + } + + public void consume() { + if ( p==-1 ) { + fillBuffer(); + } + p++; + } + + public int LA(int i) { + return adaptor.getType(LT(i)); + } + + public int mark() { + if ( p==-1 ) { + fillBuffer(); + } + lastMarker = index(); + return lastMarker; + } + + public void release(int marker) { + // no resources to release + } + + public int index() { + return p; + } + + public void rewind(int marker) { + seek(marker); + } + + public void rewind() { + seek(lastMarker); + } + + public void seek(int index) { + if ( p==-1 ) { + fillBuffer(); + } + p = index; + } + + /** Make stream jump to a new location, saving old location. + * Switch back with pop(). + */ + public void push(int index) { + if ( calls==null ) { + calls = new IntArray(); + } + calls.push(p); // save current index + seek(index); + } + + /** Seek back to previous index saved during last push() call. + * Return top of stack (return index). + */ + public int pop() { + int ret = calls.pop(); + seek(ret); + return ret; + } + + public void reset() { + p = 0; + lastMarker = 0; + if (calls != null) { + calls.clear(); + } + } + + public int size() { + if ( p==-1 ) { + fillBuffer(); + } + return nodes.size(); + } + + public Iterator iterator() { + if ( p==-1 ) { + fillBuffer(); + } + return new StreamIterator(); + } + + // TREE REWRITE INTERFACE + + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) { + if ( parent!=null ) { + adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t); + } + } + + /** Used for testing, just return the token type stream */ + public String toTokenTypeString() { + if ( p==-1 ) { + fillBuffer(); + } + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < nodes.size(); i++) { + Object t = (Object) nodes.get(i); + buf.append(" "); + buf.append(adaptor.getType(t)); + } + return buf.toString(); + } + + /** Debugging */ + public String toTokenString(int start, int stop) { + if ( p==-1 ) { + fillBuffer(); + } + StringBuffer buf = new StringBuffer(); + for (int i = start; i < nodes.size() && i <= stop; i++) { + Object t = (Object) nodes.get(i); + buf.append(" "); + buf.append(adaptor.getToken(t)); + } + return buf.toString(); + } + + public String toString(Object start, Object stop) { + System.out.println("toString"); + if ( start==null || stop==null ) { + return null; + } + if ( p==-1 ) { + fillBuffer(); + } + //System.out.println("stop: "+stop); + if ( start instanceof CommonTree ) + System.out.print("toString: "+((CommonTree)start).getToken()+", "); + else + System.out.println(start); + if ( stop instanceof CommonTree ) + System.out.println(((CommonTree)stop).getToken()); + else + System.out.println(stop); + // if we have the token stream, use that to dump text in order + if ( tokens!=null ) { + int beginTokenIndex = adaptor.getTokenStartIndex(start); + int endTokenIndex = adaptor.getTokenStopIndex(stop); + // if it's a tree, use start/stop index from start node + // else use token range from start/stop nodes + if ( adaptor.getType(stop)==Token.UP ) { + endTokenIndex = adaptor.getTokenStopIndex(start); + } + else if ( adaptor.getType(stop)==Token.EOF ) { + endTokenIndex = size()-2; // don't use EOF + } + return tokens.toString(beginTokenIndex, endTokenIndex); + } + // walk nodes looking for start + Object t = null; + int i = 0; + for (; i < nodes.size(); i++) { + t = nodes.get(i); + if ( t==start ) { + break; + } + } + // now walk until we see stop, filling string buffer with text + StringBuffer buf = new StringBuffer(); + t = nodes.get(i); + while ( t!=stop ) { + String text = adaptor.getText(t); + if ( text==null ) { + text = " "+String.valueOf(adaptor.getType(t)); + } + buf.append(text); + i++; + t = nodes.get(i); + } + // include stop node too + String text = adaptor.getText(stop); + if ( text==null ) { + text = " "+String.valueOf(adaptor.getType(stop)); + } + buf.append(text); + return buf.toString(); + } +} diff --git a/src/org/antlr/runtime/tree/CommonErrorNode.java b/src/org/antlr/runtime/tree/CommonErrorNode.java new file mode 100755 index 0000000..26b9933 --- /dev/null +++ b/src/org/antlr/runtime/tree/CommonErrorNode.java @@ -0,0 +1,108 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.*; + +/** A node representing erroneous token range in token stream */ +public class CommonErrorNode extends CommonTree { + public IntStream input; + public Token start; + public Token stop; + public RecognitionException trappedException; + + public CommonErrorNode(TokenStream input, Token start, Token stop, + RecognitionException e) + { + //System.out.println("start: "+start+", stop: "+stop); + if ( stop==null || + (stop.getTokenIndex() < start.getTokenIndex() && + stop.getType()!=Token.EOF) ) + { + // sometimes resync does not consume a token (when LT(1) is + // in follow set. So, stop will be 1 to left to start. adjust. + // Also handle case where start is the first token and no token + // is consumed during recovery; LT(-1) will return null. + stop = start; + } + this.input = input; + this.start = start; + this.stop = stop; + this.trappedException = e; + } + + public boolean isNil() { + return false; + } + + public int getType() { + return Token.INVALID_TOKEN_TYPE; + } + + public String getText() { + String badText = null; + if ( start instanceof Token ) { + int i = ((Token)start).getTokenIndex(); + int j = ((Token)stop).getTokenIndex(); + if ( ((Token)stop).getType() == Token.EOF ) { + j = ((TokenStream)input).size(); + } + badText = ((TokenStream)input).toString(i, j); + } + else if ( start instanceof Tree ) { + badText = ((TreeNodeStream)input).toString(start, stop); + } + else { + // people should subclass if they alter the tree type so this + // next one is for sure correct. + badText = "<unknown>"; + } + return badText; + } + + public String toString() { + if ( trappedException instanceof MissingTokenException ) { + return "<missing type: "+ + ((MissingTokenException)trappedException).getMissingType()+ + ">"; + } + else if ( trappedException instanceof UnwantedTokenException ) { + return "<extraneous: "+ + ((UnwantedTokenException)trappedException).getUnexpectedToken()+ + ", resync="+getText()+">"; + } + else if ( trappedException instanceof MismatchedTokenException ) { + return "<mismatched token: "+trappedException.token+", resync="+getText()+">"; + } + else if ( trappedException instanceof NoViableAltException ) { + return "<unexpected: "+trappedException.token+ + ", resync="+getText()+">"; + } + return "<error: "+getText()+">"; + } +} diff --git a/src/org/antlr/runtime/tree/CommonTree.java b/src/org/antlr/runtime/tree/CommonTree.java new file mode 100755 index 0000000..91c59de --- /dev/null +++ b/src/org/antlr/runtime/tree/CommonTree.java @@ -0,0 +1,185 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; + +/** A tree node that is wrapper for a Token object. After 3.0 release + * while building tree rewrite stuff, it became clear that computing + * parent and child index is very difficult and cumbersome. Better to + * spend the space in every tree node. If you don't want these extra + * fields, it's easy to cut them out in your own BaseTree subclass. + */ +public class CommonTree extends BaseTree { + /** A single token is the payload */ + public Token token; + + /** What token indexes bracket all tokens associated with this node + * and below? + */ + protected int startIndex=-1, stopIndex=-1; + + /** Who is the parent node of this node; if null, implies node is root */ + public CommonTree parent; + + /** What index is this node in the child list? Range: 0..n-1 */ + public int childIndex = -1; + + public CommonTree() { } + + public CommonTree(CommonTree node) { + super(node); + this.token = node.token; + this.startIndex = node.startIndex; + this.stopIndex = node.stopIndex; + } + + public CommonTree(Token t) { + this.token = t; + } + + public Token getToken() { + return token; + } + + public Tree dupNode() { + return new CommonTree(this); + } + + public boolean isNil() { + return token==null; + } + + public int getType() { + if ( token==null ) { + return Token.INVALID_TOKEN_TYPE; + } + return token.getType(); + } + + public String getText() { + if ( token==null ) { + return null; + } + return token.getText(); + } + + public int getLine() { + if ( token==null || token.getLine()==0 ) { + if ( getChildCount()>0 ) { + return getChild(0).getLine(); + } + return 0; + } + return token.getLine(); + } + + public int getCharPositionInLine() { + if ( token==null || token.getCharPositionInLine()==-1 ) { + if ( getChildCount()>0 ) { + return getChild(0).getCharPositionInLine(); + } + return 0; + } + return token.getCharPositionInLine(); + } + + public int getTokenStartIndex() { + if ( startIndex==-1 && token!=null ) { + return token.getTokenIndex(); + } + return startIndex; + } + + public void setTokenStartIndex(int index) { + startIndex = index; + } + + public int getTokenStopIndex() { + if ( stopIndex==-1 && token!=null ) { + return token.getTokenIndex(); + } + return stopIndex; + } + + public void setTokenStopIndex(int index) { + stopIndex = index; + } + + /** For every node in this subtree, make sure it's start/stop token's + * are set. Walk depth first, visit bottom up. Only updates nodes + * with at least one token index < 0. + */ + public void setUnknownTokenBoundaries() { + if ( children==null ) { + if ( startIndex<0 || stopIndex<0 ) { + startIndex = stopIndex = token.getTokenIndex(); + } + return; + } + for (int i=0; i<children.size(); i++) { + ((CommonTree)children.get(i)).setUnknownTokenBoundaries(); + } + if ( startIndex>=0 && stopIndex>=0 ) return; // already set + if ( children.size() > 0 ) { + CommonTree firstChild = (CommonTree)children.get(0); + CommonTree lastChild = (CommonTree)children.get(children.size()-1); + startIndex = firstChild.getTokenStartIndex(); + stopIndex = lastChild.getTokenStopIndex(); + } + } + + public int getChildIndex() { + return childIndex; + } + + public Tree getParent() { + return parent; + } + + public void setParent(Tree t) { + this.parent = (CommonTree)t; + } + + public void setChildIndex(int index) { + this.childIndex = index; + } + + public String toString() { + if ( isNil() ) { + return "nil"; + } + if ( getType()==Token.INVALID_TOKEN_TYPE ) { + return "<errornode>"; + } + if ( token==null ) { + return null; + } + return token.getText(); + } +} diff --git a/src/org/antlr/runtime/tree/CommonTreeAdaptor.java b/src/org/antlr/runtime/tree/CommonTreeAdaptor.java new file mode 100755 index 0000000..ebf560b --- /dev/null +++ b/src/org/antlr/runtime/tree/CommonTreeAdaptor.java @@ -0,0 +1,168 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; + +/** A TreeAdaptor that works with any Tree implementation. It provides + * really just factory methods; all the work is done by BaseTreeAdaptor. + * If you would like to have different tokens created than ClassicToken + * objects, you need to override this and then set the parser tree adaptor to + * use your subclass. + * + * To get your parser to build nodes of a different type, override + * create(Token), errorNode(), and to be safe, YourTreeClass.dupNode(). + * dupNode is called to duplicate nodes during rewrite operations. + */ +public class CommonTreeAdaptor extends BaseTreeAdaptor { + /** Duplicate a node. This is part of the factory; + * override if you want another kind of node to be built. + * + * I could use reflection to prevent having to override this + * but reflection is slow. + */ + public Object dupNode(Object t) { + if ( t==null ) return null; + return ((Tree)t).dupNode(); + } + + public Object create(Token payload) { + return new CommonTree(payload); + } + + /** Tell me how to create a token for use with imaginary token nodes. + * For example, there is probably no input symbol associated with imaginary + * token DECL, but you need to create it as a payload or whatever for + * the DECL node as in ^(DECL type ID). + * + * If you care what the token payload objects' type is, you should + * override this method and any other createToken variant. + */ + public Token createToken(int tokenType, String text) { + return new CommonToken(tokenType, text); + } + + /** Tell me how to create a token for use with imaginary token nodes. + * For example, there is probably no input symbol associated with imaginary + * token DECL, but you need to create it as a payload or whatever for + * the DECL node as in ^(DECL type ID). + * + * This is a variant of createToken where the new token is derived from + * an actual real input token. Typically this is for converting '{' + * tokens to BLOCK etc... You'll see + * + * r : lc='{' ID+ '}' -> ^(BLOCK[$lc] ID+) ; + * + * If you care what the token payload objects' type is, you should + * override this method and any other createToken variant. + */ + public Token createToken(Token fromToken) { + return new CommonToken(fromToken); + } + + /** Track start/stop token for subtree root created for a rule. + * Only works with Tree nodes. For rules that match nothing, + * seems like this will yield start=i and stop=i-1 in a nil node. + * Might be useful info so I'll not force to be i..i. + */ + public void setTokenBoundaries(Object t, Token startToken, Token stopToken) { + if ( t==null ) return; + int start = 0; + int stop = 0; + if ( startToken!=null ) start = startToken.getTokenIndex(); + if ( stopToken!=null ) stop = stopToken.getTokenIndex(); + ((Tree)t).setTokenStartIndex(start); + ((Tree)t).setTokenStopIndex(stop); + } + + public int getTokenStartIndex(Object t) { + if ( t==null ) return -1; + return ((Tree)t).getTokenStartIndex(); + } + + public int getTokenStopIndex(Object t) { + if ( t==null ) return -1; + return ((Tree)t).getTokenStopIndex(); + } + + public String getText(Object t) { + if ( t==null ) return null; + return ((Tree)t).getText(); + } + + public int getType(Object t) { + if ( t==null ) return Token.INVALID_TOKEN_TYPE; + return ((Tree)t).getType(); + } + + /** What is the Token associated with this node? If + * you are not using CommonTree, then you must + * override this in your own adaptor. + */ + public Token getToken(Object t) { + if ( t instanceof CommonTree ) { + return ((CommonTree)t).getToken(); + } + return null; // no idea what to do + } + + public Object getChild(Object t, int i) { + if ( t==null ) return null; + return ((Tree)t).getChild(i); + } + + public int getChildCount(Object t) { + if ( t==null ) return 0; + return ((Tree)t).getChildCount(); + } + + public Object getParent(Object t) { + if ( t==null ) return null; + return ((Tree)t).getParent(); + } + + public void setParent(Object t, Object parent) { + if ( t!=null ) ((Tree)t).setParent((Tree)parent); + } + + public int getChildIndex(Object t) { + if ( t==null ) return 0; + return ((Tree)t).getChildIndex(); + } + + public void setChildIndex(Object t, int index) { + if ( t!=null ) ((Tree)t).setChildIndex(index); + } + + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) { + if ( parent!=null ) { + ((Tree)parent).replaceChildren(startChildIndex, stopChildIndex, t); + } + } +} diff --git a/src/org/antlr/runtime/tree/CommonTreeNodeStream.java b/src/org/antlr/runtime/tree/CommonTreeNodeStream.java new file mode 100755 index 0000000..05dbbdd --- /dev/null +++ b/src/org/antlr/runtime/tree/CommonTreeNodeStream.java @@ -0,0 +1,171 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.misc.LookaheadStream; +import org.antlr.runtime.misc.IntArray; + +import java.util.*; + +public class CommonTreeNodeStream extends LookaheadStream<Object> implements TreeNodeStream { + public static final int DEFAULT_INITIAL_BUFFER_SIZE = 100; + public static final int INITIAL_CALL_STACK_SIZE = 10; + + /** Pull nodes from which tree? */ + protected Object root; + + /** If this tree (root) was created from a token stream, track it. */ + protected TokenStream tokens; + + /** What tree adaptor was used to build these trees */ + TreeAdaptor adaptor; + + /** The tree iterator we using */ + protected TreeIterator it; + + /** Stack of indexes used for push/pop calls */ + protected IntArray calls; + + /** Tree (nil A B C) trees like flat A B C streams */ + protected boolean hasNilRoot = false; + + /** Tracks tree depth. Level=0 means we're at root node level. */ + protected int level = 0; + + public CommonTreeNodeStream(Object tree) { + this(new CommonTreeAdaptor(), tree); + } + + public CommonTreeNodeStream(TreeAdaptor adaptor, Object tree) { + this.root = tree; + this.adaptor = adaptor; + it = new TreeIterator(adaptor,root); + } + + public void reset() { + super.reset(); + it.reset(); + hasNilRoot = false; + level = 0; + if ( calls != null ) calls.clear(); + } + + /** Pull elements from tree iterator. Track tree level 0..max_level. + * If nil rooted tree, don't give initial nil and DOWN nor final UP. + */ + public Object nextElement() { + Object t = it.next(); + //System.out.println("pulled "+adaptor.getType(t)); + if ( t == it.up ) { + level--; + if ( level==0 && hasNilRoot ) return it.next(); // don't give last UP; get EOF + } + else if ( t == it.down ) level++; + if ( level==0 && adaptor.isNil(t) ) { // if nil root, scarf nil, DOWN + hasNilRoot = true; + t = it.next(); // t is now DOWN, so get first real node next + level++; + t = it.next(); + } + return t; + } + + public boolean isEOF(Object o) { return adaptor.getType(o) == Token.EOF; } + + public void setUniqueNavigationNodes(boolean uniqueNavigationNodes) { } + + public Object getTreeSource() { return root; } + + public String getSourceName() { return getTokenStream().getSourceName(); } + + public TokenStream getTokenStream() { return tokens; } + + public void setTokenStream(TokenStream tokens) { this.tokens = tokens; } + + public TreeAdaptor getTreeAdaptor() { return adaptor; } + + public void setTreeAdaptor(TreeAdaptor adaptor) { this.adaptor = adaptor; } + + public Object get(int i) { + throw new UnsupportedOperationException("Absolute node indexes are meaningless in an unbuffered stream"); + } + + public int LA(int i) { return adaptor.getType(LT(i)); } + + /** Make stream jump to a new location, saving old location. + * Switch back with pop(). + */ + public void push(int index) { + if ( calls==null ) { + calls = new IntArray(); + } + calls.push(p); // save current index + seek(index); + } + + /** Seek back to previous index saved during last push() call. + * Return top of stack (return index). + */ + public int pop() { + int ret = calls.pop(); + seek(ret); + return ret; + } + + // TREE REWRITE INTERFACE + + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) { + if ( parent!=null ) { + adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t); + } + } + + public String toString(Object start, Object stop) { + // we'll have to walk from start to stop in tree; we're not keeping + // a complete node stream buffer + return "n/a"; + } + + /** For debugging; destructive: moves tree iterator to end. */ + public String toTokenTypeString() { + reset(); + StringBuffer buf = new StringBuffer(); + Object o = LT(1); + int type = adaptor.getType(o); + while ( type!=Token.EOF ) { + buf.append(" "); + buf.append(type); + consume(); + o = LT(1); + type = adaptor.getType(o); + } + return buf.toString(); + } +} diff --git a/src/org/antlr/runtime/tree/ParseTree.java b/src/org/antlr/runtime/tree/ParseTree.java new file mode 100755 index 0000000..5811c55 --- /dev/null +++ b/src/org/antlr/runtime/tree/ParseTree.java @@ -0,0 +1,119 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; + +import java.util.List; + +/** A record of the rules used to match a token sequence. The tokens + * end up as the leaves of this tree and rule nodes are the interior nodes. + * This really adds no functionality, it is just an alias for CommonTree + * that is more meaningful (specific) and holds a String to display for a node. + */ +public class ParseTree extends BaseTree { + public Object payload; + public List hiddenTokens; + + public ParseTree(Object label) { + this.payload = label; + } + + public Tree dupNode() { + return null; + } + + public int getType() { + return 0; + } + + public String getText() { + return toString(); + } + + public int getTokenStartIndex() { + return 0; + } + + public void setTokenStartIndex(int index) { + } + + public int getTokenStopIndex() { + return 0; + } + + public void setTokenStopIndex(int index) { + } + + public String toString() { + if ( payload instanceof Token ) { + Token t = (Token)payload; + if ( t.getType() == Token.EOF ) { + return "<EOF>"; + } + return t.getText(); + } + return payload.toString(); + } + + /** Emit a token and all hidden nodes before. EOF node holds all + * hidden tokens after last real token. + */ + public String toStringWithHiddenTokens() { + StringBuffer buf = new StringBuffer(); + if ( hiddenTokens!=null ) { + for (int i = 0; i < hiddenTokens.size(); i++) { + Token hidden = (Token) hiddenTokens.get(i); + buf.append(hidden.getText()); + } + } + String nodeText = this.toString(); + if ( !nodeText.equals("<EOF>") ) buf.append(nodeText); + return buf.toString(); + } + + /** Print out the leaves of this tree, which means printing original + * input back out. + */ + public String toInputString() { + StringBuffer buf = new StringBuffer(); + _toStringLeaves(buf); + return buf.toString(); + } + + public void _toStringLeaves(StringBuffer buf) { + if ( payload instanceof Token ) { // leaf node token? + buf.append(this.toStringWithHiddenTokens()); + return; + } + for (int i = 0; children!=null && i < children.size(); i++) { + ParseTree t = (ParseTree)children.get(i); + t._toStringLeaves(buf); + } + } +} diff --git a/src/org/antlr/runtime/tree/RewriteCardinalityException.java b/src/org/antlr/runtime/tree/RewriteCardinalityException.java new file mode 100755 index 0000000..7f909cd --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteCardinalityException.java @@ -0,0 +1,47 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +/** Base class for all exceptions thrown during AST rewrite construction. + * This signifies a case where the cardinality of two or more elements + * in a subrule are different: (ID INT)+ where |ID|!=|INT| + */ +public class RewriteCardinalityException extends RuntimeException { + public String elementDescription; + + public RewriteCardinalityException(String elementDescription) { + this.elementDescription = elementDescription; + } + + public String getMessage() { + if ( elementDescription!=null ) { + return elementDescription; + } + return null; + } +} diff --git a/src/org/antlr/runtime/tree/RewriteEarlyExitException.java b/src/org/antlr/runtime/tree/RewriteEarlyExitException.java new file mode 100755 index 0000000..9dbcc76 --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteEarlyExitException.java @@ -0,0 +1,39 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +/** No elements within a (...)+ in a rewrite rule */ +public class RewriteEarlyExitException extends RewriteCardinalityException { + public RewriteEarlyExitException() { + super(null); + } + public RewriteEarlyExitException(String elementDescription) { + super(elementDescription); + } + +} diff --git a/src/org/antlr/runtime/tree/RewriteEmptyStreamException.java b/src/org/antlr/runtime/tree/RewriteEmptyStreamException.java new file mode 100755 index 0000000..8f3a3fc --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteEmptyStreamException.java @@ -0,0 +1,35 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +/** Ref to ID or expr but no tokens in ID stream or subtrees in expr stream */ +public class RewriteEmptyStreamException extends RewriteCardinalityException { + public RewriteEmptyStreamException(String elementDescription) { + super(elementDescription); + } +} diff --git a/src/org/antlr/runtime/tree/RewriteRuleElementStream.java b/src/org/antlr/runtime/tree/RewriteRuleElementStream.java new file mode 100755 index 0000000..61f1860 --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteRuleElementStream.java @@ -0,0 +1,210 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import java.util.ArrayList; +import java.util.List; + +/** A generic list of elements tracked in an alternative to be used in + * a -> rewrite rule. We need to subclass to fill in the next() method, + * which returns either an AST node wrapped around a token payload or + * an existing subtree. + * + * Once you start next()ing, do not try to add more elements. It will + * break the cursor tracking I believe. + * + * @see org.antlr.runtime.tree.RewriteRuleSubtreeStream + * @see org.antlr.runtime.tree.RewriteRuleTokenStream + * + * TODO: add mechanism to detect/puke on modification after reading from stream + */ +public abstract class RewriteRuleElementStream { + /** Cursor 0..n-1. If singleElement!=null, cursor is 0 until you next(), + * which bumps it to 1 meaning no more elements. + */ + protected int cursor = 0; + + /** Track single elements w/o creating a list. Upon 2nd add, alloc list */ + protected Object singleElement; + + /** The list of tokens or subtrees we are tracking */ + protected List elements; + + /** Once a node / subtree has been used in a stream, it must be dup'd + * from then on. Streams are reset after subrules so that the streams + * can be reused in future subrules. So, reset must set a dirty bit. + * If dirty, then next() always returns a dup. + * + * I wanted to use "naughty bit" here, but couldn't think of a way + * to use "naughty". + * + * TODO: unused? + */ + protected boolean dirty = false; + + /** The element or stream description; usually has name of the token or + * rule reference that this list tracks. Can include rulename too, but + * the exception would track that info. + */ + protected String elementDescription; + protected TreeAdaptor adaptor; + + public RewriteRuleElementStream(TreeAdaptor adaptor, String elementDescription) { + this.elementDescription = elementDescription; + this.adaptor = adaptor; + } + + /** Create a stream with one element */ + public RewriteRuleElementStream(TreeAdaptor adaptor, + String elementDescription, + Object oneElement) + { + this(adaptor, elementDescription); + add(oneElement); + } + + /** Create a stream, but feed off an existing list */ + public RewriteRuleElementStream(TreeAdaptor adaptor, + String elementDescription, + List elements) + { + this(adaptor, elementDescription); + this.singleElement = null; + this.elements = elements; + } + + /** Reset the condition of this stream so that it appears we have + * not consumed any of its elements. Elements themselves are untouched. + * Once we reset the stream, any future use will need duplicates. Set + * the dirty bit. + */ + public void reset() { + cursor = 0; + dirty = true; + } + + public void add(Object el) { + //System.out.println("add '"+elementDescription+"' is "+el); + if ( el==null ) { + return; + } + if ( elements!=null ) { // if in list, just add + elements.add(el); + return; + } + if ( singleElement == null ) { // no elements yet, track w/o list + singleElement = el; + return; + } + // adding 2nd element, move to list + elements = new ArrayList(5); + elements.add(singleElement); + singleElement = null; + elements.add(el); + } + + /** Return the next element in the stream. If out of elements, throw + * an exception unless size()==1. If size is 1, then return elements[0]. + * Return a duplicate node/subtree if stream is out of elements and + * size==1. If we've already used the element, dup (dirty bit set). + */ + public Object nextTree() { + int n = size(); + if ( dirty || (cursor>=n && n==1) ) { + // if out of elements and size is 1, dup + Object el = _next(); + return dup(el); + } + // test size above then fetch + Object el = _next(); + return el; + } + + /** do the work of getting the next element, making sure that it's + * a tree node or subtree. Deal with the optimization of single- + * element list versus list of size > 1. Throw an exception + * if the stream is empty or we're out of elements and size>1. + * protected so you can override in a subclass if necessary. + */ + protected Object _next() { + int n = size(); + if ( n ==0 ) { + throw new RewriteEmptyStreamException(elementDescription); + } + if ( cursor>= n) { // out of elements? + if ( n ==1 ) { // if size is 1, it's ok; return and we'll dup + return toTree(singleElement); + } + // out of elements and size was not 1, so we can't dup + throw new RewriteCardinalityException(elementDescription); + } + // we have elements + if ( singleElement!=null ) { + cursor++; // move cursor even for single element list + return toTree(singleElement); + } + // must have more than one in list, pull from elements + Object o = toTree(elements.get(cursor)); + cursor++; + return o; + } + + /** When constructing trees, sometimes we need to dup a token or AST + * subtree. Dup'ing a token means just creating another AST node + * around it. For trees, you must call the adaptor.dupTree() unless + * the element is for a tree root; then it must be a node dup. + */ + protected abstract Object dup(Object el); + + /** Ensure stream emits trees; tokens must be converted to AST nodes. + * AST nodes can be passed through unmolested. + */ + protected Object toTree(Object el) { + return el; + } + + public boolean hasNext() { + return (singleElement != null && cursor < 1) || + (elements!=null && cursor < elements.size()); + } + + public int size() { + int n = 0; + if ( singleElement != null ) { + n = 1; + } + if ( elements!=null ) { + return elements.size(); + } + return n; + } + + public String getDescription() { + return elementDescription; + } +} diff --git a/src/org/antlr/runtime/tree/RewriteRuleNodeStream.java b/src/org/antlr/runtime/tree/RewriteRuleNodeStream.java new file mode 100755 index 0000000..713e9ff --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteRuleNodeStream.java @@ -0,0 +1,70 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import java.util.List; + +/** Queues up nodes matched on left side of -> in a tree parser. This is + * the analog of RewriteRuleTokenStream for normal parsers. + */ +public class RewriteRuleNodeStream extends RewriteRuleElementStream { + + public RewriteRuleNodeStream(TreeAdaptor adaptor, String elementDescription) { + super(adaptor, elementDescription); + } + + /** Create a stream with one element */ + public RewriteRuleNodeStream(TreeAdaptor adaptor, + String elementDescription, + Object oneElement) + { + super(adaptor, elementDescription, oneElement); + } + + /** Create a stream, but feed off an existing list */ + public RewriteRuleNodeStream(TreeAdaptor adaptor, + String elementDescription, + List elements) + { + super(adaptor, elementDescription, elements); + } + + public Object nextNode() { + return _next(); + } + + protected Object toTree(Object el) { + return adaptor.dupNode(el); + } + + protected Object dup(Object el) { + // we dup every node, so don't have to worry about calling dup; short- + // circuited next() so it doesn't call. + throw new UnsupportedOperationException("dup can't be called for a node stream."); + } +} diff --git a/src/org/antlr/runtime/tree/RewriteRuleSubtreeStream.java b/src/org/antlr/runtime/tree/RewriteRuleSubtreeStream.java new file mode 100755 index 0000000..5189f21 --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteRuleSubtreeStream.java @@ -0,0 +1,88 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import java.util.List; + +public class RewriteRuleSubtreeStream extends RewriteRuleElementStream { + + public RewriteRuleSubtreeStream(TreeAdaptor adaptor, String elementDescription) { + super(adaptor, elementDescription); + } + + /** Create a stream with one element */ + public RewriteRuleSubtreeStream(TreeAdaptor adaptor, + String elementDescription, + Object oneElement) + { + super(adaptor, elementDescription, oneElement); + } + + /** Create a stream, but feed off an existing list */ + public RewriteRuleSubtreeStream(TreeAdaptor adaptor, + String elementDescription, + List elements) + { + super(adaptor, elementDescription, elements); + } + + /** Treat next element as a single node even if it's a subtree. + * This is used instead of next() when the result has to be a + * tree root node. Also prevents us from duplicating recently-added + * children; e.g., ^(type ID)+ adds ID to type and then 2nd iteration + * must dup the type node, but ID has been added. + * + * Referencing a rule result twice is ok; dup entire tree as + * we can't be adding trees as root; e.g., expr expr. + * + * Hideous code duplication here with super.next(). Can't think of + * a proper way to refactor. This needs to always call dup node + * and super.next() doesn't know which to call: dup node or dup tree. + */ + public Object nextNode() { + //System.out.println("nextNode: elements="+elements+", singleElement="+((Tree)singleElement).toStringTree()); + int n = size(); + if ( dirty || (cursor>=n && n==1) ) { + // if out of elements and size is 1, dup (at most a single node + // since this is for making root nodes). + Object el = _next(); + return adaptor.dupNode(el); + } + // test size above then fetch + Object tree = _next(); + while (adaptor.isNil(tree) && adaptor.getChildCount(tree) == 1) + tree = adaptor.getChild(tree, 0); + //System.out.println("_next="+((Tree)tree).toStringTree()); + Object el = adaptor.dupNode(tree); // dup just the root (want node here) + return el; + } + + protected Object dup(Object el) { + return adaptor.dupTree(el); + } +}
\ No newline at end of file diff --git a/src/org/antlr/runtime/tree/RewriteRuleTokenStream.java b/src/org/antlr/runtime/tree/RewriteRuleTokenStream.java new file mode 100755 index 0000000..4cd7b08 --- /dev/null +++ b/src/org/antlr/runtime/tree/RewriteRuleTokenStream.java @@ -0,0 +1,76 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; + +import java.util.List; + +public class RewriteRuleTokenStream extends RewriteRuleElementStream { + + public RewriteRuleTokenStream(TreeAdaptor adaptor, String elementDescription) { + super(adaptor, elementDescription); + } + + /** Create a stream with one element */ + public RewriteRuleTokenStream(TreeAdaptor adaptor, + String elementDescription, + Object oneElement) + { + super(adaptor, elementDescription, oneElement); + } + + /** Create a stream, but feed off an existing list */ + public RewriteRuleTokenStream(TreeAdaptor adaptor, + String elementDescription, + List elements) + { + super(adaptor, elementDescription, elements); + } + + /** Get next token from stream and make a node for it */ + public Object nextNode() { + Token t = (Token)_next(); + return adaptor.create(t); + } + + public Token nextToken() { + return (Token)_next(); + } + + /** Don't convert to a tree unless they explicitly call nextTree. + * This way we can do hetero tree nodes in rewrite. + */ + protected Object toTree(Object el) { + return el; + } + + protected Object dup(Object el) { + throw new UnsupportedOperationException("dup can't be called for a token stream."); + } +} diff --git a/src/org/antlr/runtime/tree/Tree.java b/src/org/antlr/runtime/tree/Tree.java new file mode 100755 index 0000000..7875be3 --- /dev/null +++ b/src/org/antlr/runtime/tree/Tree.java @@ -0,0 +1,127 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; + +import java.util.List; + +/** What does a tree look like? ANTLR has a number of support classes + * such as CommonTreeNodeStream that work on these kinds of trees. You + * don't have to make your trees implement this interface, but if you do, + * you'll be able to use more support code. + * + * NOTE: When constructing trees, ANTLR can build any kind of tree; it can + * even use Token objects as trees if you add a child list to your tokens. + * + * This is a tree node without any payload; just navigation and factory stuff. + */ +public interface Tree { + public static final Tree INVALID_NODE = new CommonTree(Token.INVALID_TOKEN); + + Tree getChild(int i); + + int getChildCount(); + + // Tree tracks parent and child index now > 3.0 + + public Tree getParent(); + + public void setParent(Tree t); + + /** Is there is a node above with token type ttype? */ + public boolean hasAncestor(int ttype); + + /** Walk upwards and get first ancestor with this token type. */ + public Tree getAncestor(int ttype); + + /** Return a list of all ancestors of this node. The first node of + * list is the root and the last is the parent of this node. + */ + public List getAncestors(); + + /** This node is what child index? 0..n-1 */ + public int getChildIndex(); + + public void setChildIndex(int index); + + /** Set the parent and child index values for all children */ + public void freshenParentAndChildIndexes(); + + /** Add t as a child to this node. If t is null, do nothing. If t + * is nil, add all children of t to this' children. + */ + void addChild(Tree t); + + /** Set ith child (0..n-1) to t; t must be non-null and non-nil node */ + public void setChild(int i, Tree t); + + public Object deleteChild(int i); + + /** Delete children from start to stop and replace with t even if t is + * a list (nil-root tree). num of children can increase or decrease. + * For huge child lists, inserting children can force walking rest of + * children to set their childindex; could be slow. + */ + public void replaceChildren(int startChildIndex, int stopChildIndex, Object t); + + /** Indicates the node is a nil node but may still have children, meaning + * the tree is a flat list. + */ + boolean isNil(); + + /** What is the smallest token index (indexing from 0) for this node + * and its children? + */ + int getTokenStartIndex(); + + void setTokenStartIndex(int index); + + /** What is the largest token index (indexing from 0) for this node + * and its children? + */ + int getTokenStopIndex(); + + void setTokenStopIndex(int index); + + Tree dupNode(); + + /** Return a token type; needed for tree parsing */ + int getType(); + + String getText(); + + /** In case we don't have a token payload, what is the line for errors? */ + int getLine(); + + int getCharPositionInLine(); + + String toStringTree(); + + String toString(); +} diff --git a/src/org/antlr/runtime/tree/TreeAdaptor.java b/src/org/antlr/runtime/tree/TreeAdaptor.java new file mode 100755 index 0000000..8e889f8 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeAdaptor.java @@ -0,0 +1,263 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.RecognitionException; + +/** How to create and navigate trees. Rather than have a separate factory + * and adaptor, I've merged them. Makes sense to encapsulate. + * + * This takes the place of the tree construction code generated in the + * generated code in 2.x and the ASTFactory. + * + * I do not need to know the type of a tree at all so they are all + * generic Objects. This may increase the amount of typecasting needed. :( + */ +public interface TreeAdaptor { + // C o n s t r u c t i o n + + /** Create a tree node from Token object; for CommonTree type trees, + * then the token just becomes the payload. This is the most + * common create call. + * + * Override if you want another kind of node to be built. + */ + public Object create(Token payload); + + /** Duplicate a single tree node. + * Override if you want another kind of node to be built. + */ + public Object dupNode(Object treeNode); + + /** Duplicate tree recursively, using dupNode() for each node */ + public Object dupTree(Object tree); + + /** Return a nil node (an empty but non-null node) that can hold + * a list of element as the children. If you want a flat tree (a list) + * use "t=adaptor.nil(); t.addChild(x); t.addChild(y);" + */ + public Object nil(); + + /** Return a tree node representing an error. This node records the + * tokens consumed during error recovery. The start token indicates the + * input symbol at which the error was detected. The stop token indicates + * the last symbol consumed during recovery. + * + * You must specify the input stream so that the erroneous text can + * be packaged up in the error node. The exception could be useful + * to some applications; default implementation stores ptr to it in + * the CommonErrorNode. + * + * This only makes sense during token parsing, not tree parsing. + * Tree parsing should happen only when parsing and tree construction + * succeed. + */ + public Object errorNode(TokenStream input, Token start, Token stop, RecognitionException e); + + /** Is tree considered a nil node used to make lists of child nodes? */ + public boolean isNil(Object tree); + + /** Add a child to the tree t. If child is a flat tree (a list), make all + * in list children of t. Warning: if t has no children, but child does + * and child isNil then you can decide it is ok to move children to t via + * t.children = child.children; i.e., without copying the array. Just + * make sure that this is consistent with have the user will build + * ASTs. Do nothing if t or child is null. + */ + public void addChild(Object t, Object child); + + /** If oldRoot is a nil root, just copy or move the children to newRoot. + * If not a nil root, make oldRoot a child of newRoot. + * + * old=^(nil a b c), new=r yields ^(r a b c) + * old=^(a b c), new=r yields ^(r ^(a b c)) + * + * If newRoot is a nil-rooted single child tree, use the single + * child as the new root node. + * + * old=^(nil a b c), new=^(nil r) yields ^(r a b c) + * old=^(a b c), new=^(nil r) yields ^(r ^(a b c)) + * + * If oldRoot was null, it's ok, just return newRoot (even if isNil). + * + * old=null, new=r yields r + * old=null, new=^(nil r) yields ^(nil r) + * + * Return newRoot. Throw an exception if newRoot is not a + * simple node or nil root with a single child node--it must be a root + * node. If newRoot is ^(nil x) return x as newRoot. + * + * Be advised that it's ok for newRoot to point at oldRoot's + * children; i.e., you don't have to copy the list. We are + * constructing these nodes so we should have this control for + * efficiency. + */ + public Object becomeRoot(Object newRoot, Object oldRoot); + + /** Given the root of the subtree created for this rule, post process + * it to do any simplifications or whatever you want. A required + * behavior is to convert ^(nil singleSubtree) to singleSubtree + * as the setting of start/stop indexes relies on a single non-nil root + * for non-flat trees. + * + * Flat trees such as for lists like "idlist : ID+ ;" are left alone + * unless there is only one ID. For a list, the start/stop indexes + * are set in the nil node. + * + * This method is executed after all rule tree construction and right + * before setTokenBoundaries(). + */ + public Object rulePostProcessing(Object root); + + /** For identifying trees. + * + * How to identify nodes so we can say "add node to a prior node"? + * Even becomeRoot is an issue. Use System.identityHashCode(node) + * usually. + */ + public int getUniqueID(Object node); + + + // R e w r i t e R u l e s + + /** Create a node for newRoot make it the root of oldRoot. + * If oldRoot is a nil root, just copy or move the children to newRoot. + * If not a nil root, make oldRoot a child of newRoot. + * + * Return node created for newRoot. + * + * Be advised: when debugging ASTs, the DebugTreeAdaptor manually + * calls create(Token child) and then plain becomeRoot(node, node) + * because it needs to trap calls to create, but it can't since it delegates + * to not inherits from the TreeAdaptor. + */ + public Object becomeRoot(Token newRoot, Object oldRoot); + + /** Create a new node derived from a token, with a new token type. + * This is invoked from an imaginary node ref on right side of a + * rewrite rule as IMAG[$tokenLabel]. + * + * This should invoke createToken(Token). + */ + public Object create(int tokenType, Token fromToken); + + /** Same as create(tokenType,fromToken) except set the text too. + * This is invoked from an imaginary node ref on right side of a + * rewrite rule as IMAG[$tokenLabel, "IMAG"]. + * + * This should invoke createToken(Token). + */ + public Object create(int tokenType, Token fromToken, String text); + + /** Create a new node derived from a token, with a new token type. + * This is invoked from an imaginary node ref on right side of a + * rewrite rule as IMAG["IMAG"]. + * + * This should invoke createToken(int,String). + */ + public Object create(int tokenType, String text); + + + // C o n t e n t + + /** For tree parsing, I need to know the token type of a node */ + public int getType(Object t); + + /** Node constructors can set the type of a node */ + public void setType(Object t, int type); + + public String getText(Object t); + + /** Node constructors can set the text of a node */ + public void setText(Object t, String text); + + /** Return the token object from which this node was created. + * Currently used only for printing an error message. + * The error display routine in BaseRecognizer needs to + * display where the input the error occurred. If your + * tree of limitation does not store information that can + * lead you to the token, you can create a token filled with + * the appropriate information and pass that back. See + * BaseRecognizer.getErrorMessage(). + */ + public Token getToken(Object t); + + /** Where are the bounds in the input token stream for this node and + * all children? Each rule that creates AST nodes will call this + * method right before returning. Flat trees (i.e., lists) will + * still usually have a nil root node just to hold the children list. + * That node would contain the start/stop indexes then. + */ + public void setTokenBoundaries(Object t, Token startToken, Token stopToken); + + /** Get the token start index for this subtree; return -1 if no such index */ + public int getTokenStartIndex(Object t); + + /** Get the token stop index for this subtree; return -1 if no such index */ + public int getTokenStopIndex(Object t); + + + // N a v i g a t i o n / T r e e P a r s i n g + + /** Get a child 0..n-1 node */ + public Object getChild(Object t, int i); + + /** Set ith child (0..n-1) to t; t must be non-null and non-nil node */ + public void setChild(Object t, int i, Object child); + + /** Remove ith child and shift children down from right. */ + public Object deleteChild(Object t, int i); + + /** How many children? If 0, then this is a leaf node */ + public int getChildCount(Object t); + + /** Who is the parent node of this node; if null, implies node is root. + * If your node type doesn't handle this, it's ok but the tree rewrites + * in tree parsers need this functionality. + */ + public Object getParent(Object t); + public void setParent(Object t, Object parent); + + /** What index is this node in the child list? Range: 0..n-1 + * If your node type doesn't handle this, it's ok but the tree rewrites + * in tree parsers need this functionality. + */ + public int getChildIndex(Object t); + public void setChildIndex(Object t, int index); + + /** Replace from start to stop child index of parent with t, which might + * be a list. Number of children may be different + * after this call. + * + * If parent is null, don't do anything; must be at root of overall tree. + * Can't replace whatever points to the parent externally. Do nothing. + */ + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t); +} diff --git a/src/org/antlr/runtime/tree/TreeFilter.java b/src/org/antlr/runtime/tree/TreeFilter.java new file mode 100755 index 0000000..b6a7e05 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeFilter.java @@ -0,0 +1,135 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.RecognizerSharedState; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.TokenStream; + +/** + Cut-n-paste from material I'm not using in the book anymore (edit later + to make sense): + + Now, how are we going to test these tree patterns against every +subtree in our original tree? In what order should we visit nodes? +For this application, it turns out we need a simple ``apply once'' +rule application strategy and a ``down then up'' tree traversal +strategy. Let's look at rule application first. + +As we visit each node, we need to see if any of our patterns match. If +a pattern matches, we execute the associated tree rewrite and move on +to the next node. In other words, we only look for a single rule +application opportunity (we'll see below that we sometimes need to +repeatedly apply rules). The following method applies a rule in a @cl +TreeParser (derived from a tree grammar) to a tree: + +here is where weReferenced code/walking/patterns/TreePatternMatcher.java + +It uses reflection to lookup the appropriate rule within the generated +tree parser class (@cl Simplify in this case). Most of the time, the +rule will not match the tree. To avoid issuing syntax errors and +attempting error recovery, it bumps up the backtracking level. Upon +failure, the invoked rule immediately returns. If you don't plan on +using this technique in your own ANTLR-based application, don't sweat +the details. This method boils down to ``call a rule to match a tree, +executing any embedded actions and rewrite rules.'' + +At this point, we know how to define tree grammar rules and how to +apply them to a particular subtree. The final piece of the tree +pattern matcher is the actual tree traversal. We have to get the +correct node visitation order. In particular, we need to perform the +scalar-vector multiply transformation on the way down (preorder) and +we need to reduce multiply-by-zero subtrees on the way up (postorder). + +To implement a top-down visitor, we do a depth first walk of the tree, +executing an action in the preorder position. To get a bottom-up +visitor, we execute an action in the postorder position. ANTLR +provides a standard @cl TreeVisitor class with a depth first search @v +visit method. That method executes either a @m pre or @m post method +or both. In our case, we need to call @m applyOnce in both. On the way +down, we'll look for @r vmult patterns. On the way up, +we'll look for @r mult0 patterns. + */ +public class TreeFilter extends TreeParser { + public interface fptr { + public void rule() throws RecognitionException; + } + + protected TokenStream originalTokenStream; + protected TreeAdaptor originalAdaptor; + + public TreeFilter(TreeNodeStream input) { + this(input, new RecognizerSharedState()); + } + public TreeFilter(TreeNodeStream input, RecognizerSharedState state) { + super(input, state); + originalAdaptor = input.getTreeAdaptor(); + originalTokenStream = input.getTokenStream(); + } + + public void applyOnce(Object t, fptr whichRule) { + if ( t==null ) return; + try { + // share TreeParser object but not parsing-related state + state = new RecognizerSharedState(); + input = new CommonTreeNodeStream(originalAdaptor, t); + ((CommonTreeNodeStream)input).setTokenStream(originalTokenStream); + setBacktrackingLevel(1); + whichRule.rule(); + setBacktrackingLevel(0); + } + catch (RecognitionException e) { ; } + } + + public void downup(Object t) { + TreeVisitor v = new TreeVisitor(new CommonTreeAdaptor()); + TreeVisitorAction actions = new TreeVisitorAction() { + public Object pre(Object t) { applyOnce(t, topdown_fptr); return t; } + public Object post(Object t) { applyOnce(t, bottomup_fptr); return t; } + }; + v.visit(t, actions); + } + + fptr topdown_fptr = new fptr() { + public void rule() throws RecognitionException { + topdown(); + } + }; + + fptr bottomup_fptr = new fptr() { + public void rule() throws RecognitionException { + bottomup(); + } + }; + + // methods the downup strategy uses to do the up and down rules. + // to override, just define tree grammar rule topdown and turn on + // filter=true. + public void topdown() throws RecognitionException {;} + public void bottomup() throws RecognitionException {;} +} diff --git a/src/org/antlr/runtime/tree/TreeIterator.java b/src/org/antlr/runtime/tree/TreeIterator.java new file mode 100755 index 0000000..43ead6d --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeIterator.java @@ -0,0 +1,132 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.misc.FastQueue; + +import java.util.Iterator; + +/** Return a node stream from a doubly-linked tree whose nodes + * know what child index they are. No remove() is supported. + * + * Emit navigation nodes (DOWN, UP, and EOF) to let show tree structure. + */ +public class TreeIterator implements Iterator { + protected TreeAdaptor adaptor; + protected Object root; + protected Object tree; + protected boolean firstTime = true; + + // navigation nodes to return during walk and at end + public Object up; + public Object down; + public Object eof; + + /** If we emit UP/DOWN nodes, we need to spit out multiple nodes per + * next() call. + */ + protected FastQueue nodes; + + public TreeIterator(Object tree) { + this(new CommonTreeAdaptor(),tree); + } + + public TreeIterator(TreeAdaptor adaptor, Object tree) { + this.adaptor = adaptor; + this.tree = tree; + this.root = tree; + nodes = new FastQueue(); + down = adaptor.create(Token.DOWN, "DOWN"); + up = adaptor.create(Token.UP, "UP"); + eof = adaptor.create(Token.EOF, "EOF"); + } + + public void reset() { + firstTime = true; + tree = root; + nodes.clear(); + } + + public boolean hasNext() { + if ( firstTime ) return root!=null; + if ( nodes!=null && nodes.size()>0 ) return true; + if ( tree==null ) return false; + if ( adaptor.getChildCount(tree)>0 ) return true; + return adaptor.getParent(tree)!=null; // back at root? + } + + public Object next() { + if ( firstTime ) { // initial condition + firstTime = false; + if ( adaptor.getChildCount(tree)==0 ) { // single node tree (special) + nodes.add(eof); + return tree; + } + return tree; + } + // if any queued up, use those first + if ( nodes!=null && nodes.size()>0 ) return nodes.remove(); + + // no nodes left? + if ( tree==null ) return eof; + + // next node will be child 0 if any children + if ( adaptor.getChildCount(tree)>0 ) { + tree = adaptor.getChild(tree, 0); + nodes.add(tree); // real node is next after DOWN + return down; + } + // if no children, look for next sibling of tree or ancestor + Object parent = adaptor.getParent(tree); + // while we're out of siblings, keep popping back up towards root + while ( parent!=null && + adaptor.getChildIndex(tree)+1 >= adaptor.getChildCount(parent) ) + { + nodes.add(up); // we're moving back up + tree = parent; + parent = adaptor.getParent(tree); + } + // no nodes left? + if ( parent==null ) { + tree = null; // back at root? nothing left then + nodes.add(eof); // add to queue, might have UP nodes in there + return nodes.remove(); + } + + // must have found a node with an unvisited sibling + // move to it and return it + int nextSiblingIndex = adaptor.getChildIndex(tree) + 1; + tree = adaptor.getChild(parent, nextSiblingIndex); + nodes.add(tree); // add to queue, might have UP nodes in there + return nodes.remove(); + } + + public void remove() { throw new UnsupportedOperationException(); } +} diff --git a/src/org/antlr/runtime/tree/TreeNodeStream.java b/src/org/antlr/runtime/tree/TreeNodeStream.java new file mode 100755 index 0000000..df0ad34 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeNodeStream.java @@ -0,0 +1,106 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.IntStream; +import org.antlr.runtime.TokenStream; + +/** A stream of tree nodes, accessing nodes from a tree of some kind */ +public interface TreeNodeStream extends IntStream { + /** Get a tree node at an absolute index i; 0..n-1. + * If you don't want to buffer up nodes, then this method makes no + * sense for you. + */ + public Object get(int i); + + /** Get tree node at current input pointer + i ahead where i=1 is next node. + * i<0 indicates nodes in the past. So LT(-1) is previous node, but + * implementations are not required to provide results for k < -1. + * LT(0) is undefined. For i>=n, return null. + * Return null for LT(0) and any index that results in an absolute address + * that is negative. + * + * This is analogus to the LT() method of the TokenStream, but this + * returns a tree node instead of a token. Makes code gen identical + * for both parser and tree grammars. :) + */ + public Object LT(int k); + + /** Where is this stream pulling nodes from? This is not the name, but + * the object that provides node objects. + */ + public Object getTreeSource(); + + /** If the tree associated with this stream was created from a TokenStream, + * you can specify it here. Used to do rule $text attribute in tree + * parser. Optional unless you use tree parser rule text attribute + * or output=template and rewrite=true options. + */ + public TokenStream getTokenStream(); + + /** What adaptor can tell me how to interpret/navigate nodes and + * trees. E.g., get text of a node. + */ + public TreeAdaptor getTreeAdaptor(); + + /** As we flatten the tree, we use UP, DOWN nodes to represent + * the tree structure. When debugging we need unique nodes + * so we have to instantiate new ones. When doing normal tree + * parsing, it's slow and a waste of memory to create unique + * navigation nodes. Default should be false; + */ + public void setUniqueNavigationNodes(boolean uniqueNavigationNodes); + + /** Reset the tree node stream in such a way that it acts like + * a freshly constructed stream. + */ + public void reset(); + + /** Return the text of all nodes from start to stop, inclusive. + * If the stream does not buffer all the nodes then it can still + * walk recursively from start until stop. You can always return + * null or "" too, but users should not access $ruleLabel.text in + * an action of course in that case. + */ + public String toString(Object start, Object stop); + + + // REWRITING TREES (used by tree parser) + + /** Replace from start to stop child index of parent with t, which might + * be a list. Number of children may be different + * after this call. The stream is notified because it is walking the + * tree and might need to know you are monkeying with the underlying + * tree. Also, it might be able to modify the node stream to avoid + * restreaming for future phases. + * + * If parent is null, don't do anything; must be at root of overall tree. + * Can't replace whatever points to the parent externally. Do nothing. + */ + public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t); +} diff --git a/src/org/antlr/runtime/tree/TreeParser.java b/src/org/antlr/runtime/tree/TreeParser.java new file mode 100755 index 0000000..e568bc9 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeParser.java @@ -0,0 +1,169 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.*; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** A parser for a stream of tree nodes. "tree grammars" result in a subclass + * of this. All the error reporting and recovery is shared with Parser via + * the BaseRecognizer superclass. +*/ +public class TreeParser extends BaseRecognizer { + public static final int DOWN = Token.DOWN; + public static final int UP = Token.UP; + + // precompiled regex used by inContext + static String dotdot = ".*[^.]\\.\\.[^.].*"; + static String doubleEtc = ".*\\.\\.\\.\\s+\\.\\.\\..*"; + static Pattern dotdotPattern = Pattern.compile(dotdot); + static Pattern doubleEtcPattern = Pattern.compile(doubleEtc); + + protected TreeNodeStream input; + + public TreeParser(TreeNodeStream input) { + super(); // highlight that we go to super to set state object + setTreeNodeStream(input); + } + + public TreeParser(TreeNodeStream input, RecognizerSharedState state) { + super(state); // share the state object with another parser + setTreeNodeStream(input); + } + + public void reset() { + super.reset(); // reset all recognizer state variables + if ( input!=null ) { + input.seek(0); // rewind the input + } + } + + /** Set the input stream */ + public void setTreeNodeStream(TreeNodeStream input) { + this.input = input; + } + + public TreeNodeStream getTreeNodeStream() { + return input; + } + + public String getSourceName() { + return input.getSourceName(); + } + + protected Object getCurrentInputSymbol(IntStream input) { + return ((TreeNodeStream)input).LT(1); + } + + protected Object getMissingSymbol(IntStream input, + RecognitionException e, + int expectedTokenType, + BitSet follow) + { + String tokenText = + "<missing "+getTokenNames()[expectedTokenType]+">"; + TreeAdaptor adaptor = ((TreeNodeStream)e.input).getTreeAdaptor(); + return adaptor.create(new CommonToken(expectedTokenType, tokenText)); + } + + /** Match '.' in tree parser has special meaning. Skip node or + * entire tree if node has children. If children, scan until + * corresponding UP node. + */ + public void matchAny(IntStream ignore) { // ignore stream, copy of input + state.errorRecovery = false; + state.failed = false; + Object look = input.LT(1); + if ( input.getTreeAdaptor().getChildCount(look)==0 ) { + input.consume(); // not subtree, consume 1 node and return + return; + } + // current node is a subtree, skip to corresponding UP. + // must count nesting level to get right UP + int level=0; + int tokenType = input.getTreeAdaptor().getType(look); + while ( tokenType!=Token.EOF && !(tokenType==UP && level==0) ) { + input.consume(); + look = input.LT(1); + tokenType = input.getTreeAdaptor().getType(look); + if ( tokenType == DOWN ) { + level++; + } + else if ( tokenType == UP ) { + level--; + } + } + input.consume(); // consume UP + } + + /** We have DOWN/UP nodes in the stream that have no line info; override. + * plus we want to alter the exception type. Don't try to recover + * from tree parser errors inline... + */ + protected Object recoverFromMismatchedToken(IntStream input, + int ttype, + BitSet follow) + throws RecognitionException + { + throw new MismatchedTreeNodeException(ttype, (TreeNodeStream)input); + } + + /** Prefix error message with the grammar name because message is + * always intended for the programmer because the parser built + * the input tree not the user. + */ + public String getErrorHeader(RecognitionException e) { + return getGrammarFileName()+": node from "+ + (e.approximateLineInfo?"after ":"")+"line "+e.line+":"+e.charPositionInLine; + } + + /** Tree parsers parse nodes they usually have a token object as + * payload. Set the exception token and do the default behavior. + */ + public String getErrorMessage(RecognitionException e, String[] tokenNames) { + if ( this instanceof TreeParser ) { + TreeAdaptor adaptor = ((TreeNodeStream)e.input).getTreeAdaptor(); + e.token = adaptor.getToken(e.node); + if ( e.token==null ) { // could be an UP/DOWN node + e.token = new CommonToken(adaptor.getType(e.node), + adaptor.getText(e.node)); + } + } + return super.getErrorMessage(e, tokenNames); + } + + public void traceIn(String ruleName, int ruleIndex) { + super.traceIn(ruleName, ruleIndex, input.LT(1)); + } + + public void traceOut(String ruleName, int ruleIndex) { + super.traceOut(ruleName, ruleIndex, input.LT(1)); + } +} diff --git a/src/org/antlr/runtime/tree/TreePatternLexer.java b/src/org/antlr/runtime/tree/TreePatternLexer.java new file mode 100755 index 0000000..2677c4e --- /dev/null +++ b/src/org/antlr/runtime/tree/TreePatternLexer.java @@ -0,0 +1,135 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +public class TreePatternLexer { + public static final int EOF = -1; + public static final int BEGIN = 1; + public static final int END = 2; + public static final int ID = 3; + public static final int ARG = 4; + public static final int PERCENT = 5; + public static final int COLON = 6; + public static final int DOT = 7; + + /** The tree pattern to lex like "(A B C)" */ + protected String pattern; + + /** Index into input string */ + protected int p = -1; + + /** Current char */ + protected int c; + + /** How long is the pattern in char? */ + protected int n; + + /** Set when token type is ID or ARG (name mimics Java's StreamTokenizer) */ + public StringBuffer sval = new StringBuffer(); + + public boolean error = false; + + public TreePatternLexer(String pattern) { + this.pattern = pattern; + this.n = pattern.length(); + consume(); + } + + public int nextToken() { + sval.setLength(0); // reset, but reuse buffer + while ( c != EOF ) { + if ( c==' ' || c=='\n' || c=='\r' || c=='\t' ) { + consume(); + continue; + } + if ( (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_' ) { + sval.append((char)c); + consume(); + while ( (c>='a' && c<='z') || (c>='A' && c<='Z') || + (c>='0' && c<='9') || c=='_' ) + { + sval.append((char)c); + consume(); + } + return ID; + } + if ( c=='(' ) { + consume(); + return BEGIN; + } + if ( c==')' ) { + consume(); + return END; + } + if ( c=='%' ) { + consume(); + return PERCENT; + } + if ( c==':' ) { + consume(); + return COLON; + } + if ( c=='.' ) { + consume(); + return DOT; + } + if ( c=='[' ) { // grab [x] as a string, returning x + consume(); + while ( c!=']' ) { + if ( c=='\\' ) { + consume(); + if ( c!=']' ) { + sval.append('\\'); + } + sval.append((char)c); + } + else { + sval.append((char)c); + } + consume(); + } + consume(); + return ARG; + } + consume(); + error = true; + return EOF; + } + return EOF; + } + + protected void consume() { + p++; + if ( p>=n ) { + c = EOF; + } + else { + c = pattern.charAt(p); + } + } +}
\ No newline at end of file diff --git a/src/org/antlr/runtime/tree/TreePatternParser.java b/src/org/antlr/runtime/tree/TreePatternParser.java new file mode 100755 index 0000000..14983ab --- /dev/null +++ b/src/org/antlr/runtime/tree/TreePatternParser.java @@ -0,0 +1,154 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; +import org.antlr.runtime.CommonToken; + +public class TreePatternParser { + protected TreePatternLexer tokenizer; + protected int ttype; + protected TreeWizard wizard; + protected TreeAdaptor adaptor; + + public TreePatternParser(TreePatternLexer tokenizer, TreeWizard wizard, TreeAdaptor adaptor) { + this.tokenizer = tokenizer; + this.wizard = wizard; + this.adaptor = adaptor; + ttype = tokenizer.nextToken(); // kickstart + } + + public Object pattern() { + if ( ttype==TreePatternLexer.BEGIN ) { + return parseTree(); + } + else if ( ttype==TreePatternLexer.ID ) { + Object node = parseNode(); + if ( ttype==TreePatternLexer.EOF ) { + return node; + } + return null; // extra junk on end + } + return null; + } + + public Object parseTree() { + if ( ttype != TreePatternLexer.BEGIN ) { + throw new RuntimeException("no BEGIN"); + } + ttype = tokenizer.nextToken(); + Object root = parseNode(); + if ( root==null ) { + return null; + } + while ( ttype==TreePatternLexer.BEGIN || + ttype==TreePatternLexer.ID || + ttype==TreePatternLexer.PERCENT || + ttype==TreePatternLexer.DOT ) + { + if ( ttype==TreePatternLexer.BEGIN ) { + Object subtree = parseTree(); + adaptor.addChild(root, subtree); + } + else { + Object child = parseNode(); + if ( child==null ) { + return null; + } + adaptor.addChild(root, child); + } + } + if ( ttype != TreePatternLexer.END ) { + throw new RuntimeException("no END"); + } + ttype = tokenizer.nextToken(); + return root; + } + + public Object parseNode() { + // "%label:" prefix + String label = null; + if ( ttype == TreePatternLexer.PERCENT ) { + ttype = tokenizer.nextToken(); + if ( ttype != TreePatternLexer.ID ) { + return null; + } + label = tokenizer.sval.toString(); + ttype = tokenizer.nextToken(); + if ( ttype != TreePatternLexer.COLON ) { + return null; + } + ttype = tokenizer.nextToken(); // move to ID following colon + } + + // Wildcard? + if ( ttype == TreePatternLexer.DOT ) { + ttype = tokenizer.nextToken(); + Token wildcardPayload = new CommonToken(0, "."); + TreeWizard.TreePattern node = + new TreeWizard.WildcardTreePattern(wildcardPayload); + if ( label!=null ) { + node.label = label; + } + return node; + } + + // "ID" or "ID[arg]" + if ( ttype != TreePatternLexer.ID ) { + return null; + } + String tokenName = tokenizer.sval.toString(); + ttype = tokenizer.nextToken(); + if ( tokenName.equals("nil") ) { + return adaptor.nil(); + } + String text = tokenName; + // check for arg + String arg = null; + if ( ttype == TreePatternLexer.ARG ) { + arg = tokenizer.sval.toString(); + text = arg; + ttype = tokenizer.nextToken(); + } + + // create node + int treeNodeType = wizard.getTokenType(tokenName); + if ( treeNodeType==Token.INVALID_TOKEN_TYPE ) { + return null; + } + Object node; + node = adaptor.create(treeNodeType, text); + if ( label!=null && node.getClass()==TreeWizard.TreePattern.class ) { + ((TreeWizard.TreePattern)node).label = label; + } + if ( arg!=null && node.getClass()==TreeWizard.TreePattern.class ) { + ((TreeWizard.TreePattern)node).hasTextArg = true; + } + return node; + } +} diff --git a/src/org/antlr/runtime/tree/TreeRewriter.java b/src/org/antlr/runtime/tree/TreeRewriter.java new file mode 100755 index 0000000..91aee93 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeRewriter.java @@ -0,0 +1,120 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.RecognizerSharedState; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.TokenStream; + +public class TreeRewriter extends TreeParser { + public interface fptr { + public Object rule() throws RecognitionException; + } + + protected boolean showTransformations = false; + + protected TokenStream originalTokenStream; + protected TreeAdaptor originalAdaptor; + + public TreeRewriter(TreeNodeStream input) { + this(input, new RecognizerSharedState()); + } + public TreeRewriter(TreeNodeStream input, RecognizerSharedState state) { + super(input, state); + originalAdaptor = input.getTreeAdaptor(); + originalTokenStream = input.getTokenStream(); + } + + public Object applyOnce(Object t, fptr whichRule) { + if ( t==null ) return null; + try { + // share TreeParser object but not parsing-related state + state = new RecognizerSharedState(); + input = new CommonTreeNodeStream(originalAdaptor, t); + ((CommonTreeNodeStream)input).setTokenStream(originalTokenStream); + setBacktrackingLevel(1); + TreeRuleReturnScope r = (TreeRuleReturnScope)whichRule.rule(); + setBacktrackingLevel(0); + if ( failed() ) return t; + if ( showTransformations && + r!=null && !t.equals(r.getTree()) && r.getTree()!=null ) + { + reportTransformation(t, r.getTree()); + } + if ( r!=null && r.getTree()!=null ) return r.getTree(); + else return t; + } + catch (RecognitionException e) { ; } + return t; + } + + public Object applyRepeatedly(Object t, fptr whichRule) { + boolean treeChanged = true; + while ( treeChanged ) { + Object u = applyOnce(t, whichRule); + treeChanged = !t.equals(u); + t = u; + } + return t; + } + + public Object downup(Object t) { return downup(t, false); } + + public Object downup(Object t, boolean showTransformations) { + this.showTransformations = showTransformations; + TreeVisitor v = new TreeVisitor(new CommonTreeAdaptor()); + TreeVisitorAction actions = new TreeVisitorAction() { + public Object pre(Object t) { return applyOnce(t, topdown_fptr); } + public Object post(Object t) { return applyRepeatedly(t, bottomup_ftpr); } + }; + t = v.visit(t, actions); + return t; + } + + /** Override this if you need transformation tracing to go somewhere + * other than stdout or if you're not using Tree-derived trees. + */ + public void reportTransformation(Object oldTree, Object newTree) { + System.out.println(((Tree)oldTree).toStringTree()+" -> "+ + ((Tree)newTree).toStringTree()); + } + + fptr topdown_fptr = new fptr() { + public Object rule() throws RecognitionException { return topdown(); } + }; + + fptr bottomup_ftpr = new fptr() { + public Object rule() throws RecognitionException { return bottomup(); } + }; + + // methods the downup strategy uses to do the up and down rules. + // to override, just define tree grammar rule topdown and turn on + // filter=true. + public Object topdown() throws RecognitionException { return null; } + public Object bottomup() throws RecognitionException { return null; } +} diff --git a/src/org/antlr/runtime/tree/TreeRuleReturnScope.java b/src/org/antlr/runtime/tree/TreeRuleReturnScope.java new file mode 100755 index 0000000..4ea65c0 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeRuleReturnScope.java @@ -0,0 +1,41 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.RuleReturnScope; + +/** This is identical to the ParserRuleReturnScope except that + * the start property is a tree nodes not Token object + * when you are parsing trees. To be generic the tree node types + * have to be Object. + */ +public class TreeRuleReturnScope extends RuleReturnScope { + /** First node or root node of tree matched for this rule. */ + public Object start; + public Object getStart() { return start; } +} diff --git a/src/org/antlr/runtime/tree/TreeVisitor.java b/src/org/antlr/runtime/tree/TreeVisitor.java new file mode 100755 index 0000000..8c5a717 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeVisitor.java @@ -0,0 +1,69 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.runtime.tree; + +/** Do a depth first walk of a tree, applying pre() and post() actions + * as we discover and finish nodes. + */ +public class TreeVisitor { + protected TreeAdaptor adaptor; + + public TreeVisitor(TreeAdaptor adaptor) { + this.adaptor = adaptor; + } + public TreeVisitor() { this(new CommonTreeAdaptor()); } + + /** Visit every node in tree t and trigger an action for each node + * before/after having visited all of its children. + * Execute both actions even if t has no children. + * If a child visit yields a new child, it can update its + * parent's child list or just return the new child. The + * child update code works even if the child visit alters its parent + * and returns the new tree. + * + * Return result of applying post action to this node. + */ + public Object visit(Object t, TreeVisitorAction action) { + // System.out.println("visit "+((Tree)t).toStringTree()); + boolean isNil = adaptor.isNil(t); + if ( action!=null && !isNil ) { + t = action.pre(t); // if rewritten, walk children of new t + } + for (int i=0; i<adaptor.getChildCount(t); i++) { + Object child = adaptor.getChild(t, i); + Object visitResult = visit(child, action); + Object childAfterVisit = adaptor.getChild(t, i); + if ( visitResult != childAfterVisit ) { // result & child differ? + adaptor.setChild(t, i, visitResult); + } + } + if ( action!=null && !isNil ) t = action.post(t); + return t; + } +} diff --git a/src/org/antlr/runtime/tree/TreeVisitorAction.java b/src/org/antlr/runtime/tree/TreeVisitorAction.java new file mode 100755 index 0000000..ef0f93c --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeVisitorAction.java @@ -0,0 +1,47 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.antlr.runtime.tree; + +/** How to execute code for node t when a visitor visits node t. Execute + * pre() before visiting children and execute post() after visiting children. + */ +public interface TreeVisitorAction { + /** Execute an action before visiting children of t. Return t or + * a rewritten t. It is up to the visitor to decide what to do + * with the return value. Children of returned value will be + * visited if using TreeVisitor.visit(). + */ + public Object pre(Object t); + + /** Execute an action after visiting children of t. Return t or + * a rewritten t. It is up to the visitor to decide what to do + * with the return value. + */ + public Object post(Object t); +} diff --git a/src/org/antlr/runtime/tree/TreeWizard.java b/src/org/antlr/runtime/tree/TreeWizard.java new file mode 100755 index 0000000..666cfd6 --- /dev/null +++ b/src/org/antlr/runtime/tree/TreeWizard.java @@ -0,0 +1,531 @@ +/* + [The "BSD license"] + Copyright (c) 2005-2009 Terence Parr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.antlr.runtime.tree; + +import org.antlr.runtime.Token; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Build and navigate trees with this object. Must know about the names + * of tokens so you have to pass in a map or array of token names (from which + * this class can build the map). I.e., Token DECL means nothing unless the + * class can translate it to a token type. + * + * In order to create nodes and navigate, this class needs a TreeAdaptor. + * + * This class can build a token type -> node index for repeated use or for + * iterating over the various nodes with a particular type. + * + * This class works in conjunction with the TreeAdaptor rather than moving + * all this functionality into the adaptor. An adaptor helps build and + * navigate trees using methods. This class helps you do it with string + * patterns like "(A B C)". You can create a tree from that pattern or + * match subtrees against it. + */ +public class TreeWizard { + protected TreeAdaptor adaptor; + protected Map tokenNameToTypeMap; + + public interface ContextVisitor { + // TODO: should this be called visit or something else? + public void visit(Object t, Object parent, int childIndex, Map labels); + } + + public static abstract class Visitor implements ContextVisitor { + public void visit(Object t, Object parent, int childIndex, Map labels) { + visit(t); + } + public abstract void visit(Object t); + } + + /** When using %label:TOKENNAME in a tree for parse(), we must + * track the label. + */ + public static class TreePattern extends CommonTree { + public String label; + public boolean hasTextArg; + public TreePattern(Token payload) { + super(payload); + } + public String toString() { + if ( label!=null ) { + return "%"+label+":"+super.toString(); + } + else { + return super.toString(); + } + } + } + + public static class WildcardTreePattern extends TreePattern { + public WildcardTreePattern(Token payload) { + super(payload); + } + } + + /** This adaptor creates TreePattern objects for use during scan() */ + public static class TreePatternTreeAdaptor extends CommonTreeAdaptor { + public Object create(Token payload) { + return new TreePattern(payload); + } + } + + // TODO: build indexes for the wizard + + /** During fillBuffer(), we can make a reverse index from a set + * of token types of interest to the list of indexes into the + * node stream. This lets us convert a node pointer to a + * stream index semi-efficiently for a list of interesting + * nodes such as function definition nodes (you'll want to seek + * to their bodies for an interpreter). Also useful for doing + * dynamic searches; i.e., go find me all PLUS nodes. + protected Map tokenTypeToStreamIndexesMap; + + /** If tokenTypesToReverseIndex set to INDEX_ALL then indexing + * occurs for all token types. + public static final Set INDEX_ALL = new HashSet(); + + /** A set of token types user would like to index for faster lookup. + * If this is INDEX_ALL, then all token types are tracked. If null, + * then none are indexed. + protected Set tokenTypesToReverseIndex = null; + */ + + public TreeWizard(TreeAdaptor adaptor) { + this.adaptor = adaptor; + } + + public TreeWizard(TreeAdaptor adaptor, Map tokenNameToTypeMap) { + this.adaptor = adaptor; + this.tokenNameToTypeMap = tokenNameToTypeMap; + } + + public TreeWizard(TreeAdaptor adaptor, String[] tokenNames) { + this.adaptor = adaptor; + this.tokenNameToTypeMap = computeTokenTypes(tokenNames); + } + + public TreeWizard(String[] tokenNames) { + this(new CommonTreeAdaptor(), tokenNames); + } + + /** Compute a Map<String, Integer> that is an inverted index of + * tokenNames (which maps int token types to names). + */ + public Map computeTokenTypes(String[] tokenNames) { + Map m = new HashMap(); + if ( tokenNames==null ) { + return m; + } + for (int ttype = Token.MIN_TOKEN_TYPE; ttype < tokenNames.length; ttype++) { + String name = tokenNames[ttype]; + m.put(name, new Integer(ttype)); + } + return m; + } + + /** Using the map of token names to token types, return the type. */ + public int getTokenType(String tokenName) { + if ( tokenNameToTypeMap==null ) { + return Token.INVALID_TOKEN_TYPE; + } + Integer ttypeI = (Integer)tokenNameToTypeMap.get(tokenName); + if ( ttypeI!=null ) { + return ttypeI.intValue(); + } + return Token.INVALID_TOKEN_TYPE; + } + + /** Walk the entire tree and make a node name to nodes mapping. + * For now, use recursion but later nonrecursive version may be + * more efficient. Returns Map<Integer, List> where the List is + * of your AST node type. The Integer is the token type of the node. + * + * TODO: save this index so that find and visit are faster + */ + public Map index(Object t) { + Map m = new HashMap(); + _index(t, m); + return m; + } + + /** Do the work for index */ + protected void _index(Object t, Map m) { + if ( t==null ) { + return; + } + int ttype = adaptor.getType(t); + List elements = (List)m.get(new Integer(ttype)); + if ( elements==null ) { + elements = new ArrayList(); + m.put(new Integer(ttype), elements); + } + elements.add(t); + int n = adaptor.getChildCount(t); + for (int i=0; i<n; i++) { + Object child = adaptor.getChild(t, i); + _index(child, m); + } + } + + /** Return a List of tree nodes with token type ttype */ + public List find(Object t, int ttype) { + final List nodes = new ArrayList(); + visit(t, ttype, new TreeWizard.Visitor() { + public void visit(Object t) { + nodes.add(t); + } + }); + return nodes; + } + + /** Return a List of subtrees matching pattern. */ + public List find(Object t, String pattern) { + final List subtrees = new ArrayList(); + // Create a TreePattern from the pattern + TreePatternLexer tokenizer = new TreePatternLexer(pattern); + TreePatternParser parser = + new TreePatternParser(tokenizer, this, new TreePatternTreeAdaptor()); + final TreePattern tpattern = (TreePattern)parser.pattern(); + // don't allow invalid patterns + if ( tpattern==null || + tpattern.isNil() || + tpattern.getClass()==WildcardTreePattern.class ) + { + return null; + } + int rootTokenType = tpattern.getType(); + visit(t, rootTokenType, new TreeWizard.ContextVisitor() { + public void visit(Object t, Object parent, int childIndex, Map labels) { + if ( _parse(t, tpattern, null) ) { + subtrees.add(t); + } + } + }); + return subtrees; + } + + public Object findFirst(Object t, int ttype) { + return null; + } + + public Object findFirst(Object t, String pattern) { + return null; + } + + /** Visit every ttype node in t, invoking the visitor. This is a quicker + * version of the general visit(t, pattern) method. The labels arg + * of the visitor action method is never set (it's null) since using + * a token type rather than a pattern doesn't let us set a label. + */ + public void visit(Object t, int ttype, ContextVisitor visitor) { + _visit(t, null, 0, ttype, visitor); + } + + /** Do the recursive work for visit */ + protected void _visit(Object t, Object parent, int childIndex, int ttype, ContextVisitor visitor) { + if ( t==null ) { + return; + } + if ( adaptor.getType(t)==ttype ) { + visitor.visit(t, parent, childIndex, null); + } + int n = adaptor.getChildCount(t); + for (int i=0; i<n; i++) { + Object child = adaptor.getChild(t, i); + _visit(child, t, i, ttype, visitor); + } + } + + /** For all subtrees that match the pattern, execute the visit action. + * The implementation uses the root node of the pattern in combination + * with visit(t, ttype, visitor) so nil-rooted patterns are not allowed. + * Patterns with wildcard roots are also not allowed. + */ + public void visit(Object t, final String pattern, final ContextVisitor visitor) { + // Create a TreePattern from the pattern + TreePatternLexer tokenizer = new TreePatternLexer(pattern); + TreePatternParser parser = + new TreePatternParser(tokenizer, this, new TreePatternTreeAdaptor()); + final TreePattern tpattern = (TreePattern)parser.pattern(); + // don't allow invalid patterns + if ( tpattern==null || + tpattern.isNil() || + tpattern.getClass()==WildcardTreePattern.class ) + { + return; + } + final Map labels = new HashMap(); // reused for each _parse + int rootTokenType = tpattern.getType(); + visit(t, rootTokenType, new TreeWizard.ContextVisitor() { + public void visit(Object t, Object parent, int childIndex, Map unusedlabels) { + // the unusedlabels arg is null as visit on token type doesn't set. + labels.clear(); + if ( _parse(t, tpattern, labels) ) { + visitor.visit(t, parent, childIndex, labels); + } + } + }); + } + + /** Given a pattern like (ASSIGN %lhs:ID %rhs:.) with optional labels + * on the various nodes and '.' (dot) as the node/subtree wildcard, + * return true if the pattern matches and fill the labels Map with + * the labels pointing at the appropriate nodes. Return false if + * the pattern is malformed or the tree does not match. + * + * If a node specifies a text arg in pattern, then that must match + * for that node in t. + * + * TODO: what's a better way to indicate bad pattern? Exceptions are a hassle + */ + public boolean parse(Object t, String pattern, Map labels) { + TreePatternLexer tokenizer = new TreePatternLexer(pattern); + TreePatternParser parser = + new TreePatternParser(tokenizer, this, new TreePatternTreeAdaptor()); + TreePattern tpattern = (TreePattern)parser.pattern(); + /* + System.out.println("t="+((Tree)t).toStringTree()); + System.out.println("scant="+tpattern.toStringTree()); + */ + boolean matched = _parse(t, tpattern, labels); + return matched; + } + + public boolean parse(Object t, String pattern) { + return parse(t, pattern, null); + } + + /** Do the work for parse. Check to see if the t2 pattern fits the + * structure and token types in t1. Check text if the pattern has + * text arguments on nodes. Fill labels map with pointers to nodes + * in tree matched against nodes in pattern with labels. + */ + protected boolean _parse(Object t1, TreePattern tpattern, Map labels) { + // make sure both are non-null + if ( t1==null || tpattern==null ) { + return false; + } + // check roots (wildcard matches anything) + if ( tpattern.getClass() != WildcardTreePattern.class ) { + if ( adaptor.getType(t1) != tpattern.getType() ) return false; + // if pattern has text, check node text + if ( tpattern.hasTextArg && !adaptor.getText(t1).equals(tpattern.getText()) ) { + return false; + } + } + if ( tpattern.label!=null && labels!=null ) { + // map label in pattern to node in t1 + labels.put(tpattern.label, t1); + } + // check children + int n1 = adaptor.getChildCount(t1); + int n2 = tpattern.getChildCount(); + if ( n1 != n2 ) { + return false; + } + for (int i=0; i<n1; i++) { + Object child1 = adaptor.getChild(t1, i); + TreePattern child2 = (TreePattern)tpattern.getChild(i); + if ( !_parse(child1, child2, labels) ) { + return false; + } + } + return true; + } + + /** Create a tree or node from the indicated tree pattern that closely + * follows ANTLR tree grammar tree element syntax: + * + * (root child1 ... child2). + * + * You can also just pass in a node: ID + * + * Any node can have a text argument: ID[foo] + * (notice there are no quotes around foo--it's clear it's a string). + * + * nil is a special name meaning "give me a nil node". Useful for + * making lists: (nil A B C) is a list of A B C. + */ + public Object create(String pattern) { + TreePatternLexer tokenizer = new TreePatternLexer(pattern); + TreePatternParser parser = new TreePatternParser(tokenizer, this, adaptor); + Object t = parser.pattern(); + return t; + } + + /** Compare t1 and t2; return true if token types/text, structure match exactly. + * The trees are examined in their entirety so that (A B) does not match + * (A B C) nor (A (B C)). + // TODO: allow them to pass in a comparator + * TODO: have a version that is nonstatic so it can use instance adaptor + * + * I cannot rely on the tree node's equals() implementation as I make + * no constraints at all on the node types nor interface etc... + */ + public static boolean equals(Object t1, Object t2, TreeAdaptor adaptor) { + return _equals(t1, t2, adaptor); + } + + /** Compare type, structure, and text of two trees, assuming adaptor in + * this instance of a TreeWizard. + */ + public boolean equals(Object t1, Object t2) { + return _equals(t1, t2, adaptor); + } + + protected static boolean _equals(Object t1, Object t2, TreeAdaptor adaptor) { + // make sure both are non-null + if ( t1==null || t2==null ) { + return false; + } + // check roots + if ( adaptor.getType(t1) != adaptor.getType(t2) ) { + return false; + } + if ( !adaptor.getText(t1).equals(adaptor.getText(t2)) ) { + return false; + } + // check children + int n1 = adaptor.getChildCount(t1); + int n2 = adaptor.getChildCount(t2); + if ( n1 != n2 ) { + return false; + } + for (int i=0; i<n1; i++) { + Object child1 = adaptor.getChild(t1, i); + Object child2 = adaptor.getChild(t2, i); + if ( !_equals(child1, child2, adaptor) ) { + return false; + } + } + return true; + } + + // TODO: next stuff taken from CommonTreeNodeStream + + /** Given a node, add this to the reverse index tokenTypeToStreamIndexesMap. + * You can override this method to alter how indexing occurs. The + * default is to create a + * + * Map<Integer token type,ArrayList<Integer stream index>> + * + * This data structure allows you to find all nodes with type INT in order. + * + * If you really need to find a node of type, say, FUNC quickly then perhaps + * + * Map<Integertoken type,Map<Object tree node,Integer stream index>> + * + * would be better for you. The interior maps map a tree node to + * the index so you don't have to search linearly for a specific node. + * + * If you change this method, you will likely need to change + * getNodeIndex(), which extracts information. + protected void fillReverseIndex(Object node, int streamIndex) { + //System.out.println("revIndex "+node+"@"+streamIndex); + if ( tokenTypesToReverseIndex==null ) { + return; // no indexing if this is empty (nothing of interest) + } + if ( tokenTypeToStreamIndexesMap==null ) { + tokenTypeToStreamIndexesMap = new HashMap(); // first indexing op + } + int tokenType = adaptor.getType(node); + Integer tokenTypeI = new Integer(tokenType); + if ( !(tokenTypesToReverseIndex==INDEX_ALL || + tokenTypesToReverseIndex.contains(tokenTypeI)) ) + { + return; // tokenType not of interest + } + Integer streamIndexI = new Integer(streamIndex); + ArrayList indexes = (ArrayList)tokenTypeToStreamIndexesMap.get(tokenTypeI); + if ( indexes==null ) { + indexes = new ArrayList(); // no list yet for this token type + indexes.add(streamIndexI); // not there yet, add + tokenTypeToStreamIndexesMap.put(tokenTypeI, indexes); + } + else { + if ( !indexes.contains(streamIndexI) ) { + indexes.add(streamIndexI); // not there yet, add + } + } + } + + /** Track the indicated token type in the reverse index. Call this + * repeatedly for each type or use variant with Set argument to + * set all at once. + * @param tokenType + public void reverseIndex(int tokenType) { + if ( tokenTypesToReverseIndex==null ) { + tokenTypesToReverseIndex = new HashSet(); + } + else if ( tokenTypesToReverseIndex==INDEX_ALL ) { + return; + } + tokenTypesToReverseIndex.add(new Integer(tokenType)); + } + + /** Track the indicated token types in the reverse index. Set + * to INDEX_ALL to track all token types. + public void reverseIndex(Set tokenTypes) { + tokenTypesToReverseIndex = tokenTypes; + } + + /** Given a node pointer, return its index into the node stream. + * This is not its Token stream index. If there is no reverse map + * from node to stream index or the map does not contain entries + * for node's token type, a linear search of entire stream is used. + * + * Return -1 if exact node pointer not in stream. + public int getNodeIndex(Object node) { + //System.out.println("get "+node); + if ( tokenTypeToStreamIndexesMap==null ) { + return getNodeIndexLinearly(node); + } + int tokenType = adaptor.getType(node); + Integer tokenTypeI = new Integer(tokenType); + ArrayList indexes = (ArrayList)tokenTypeToStreamIndexesMap.get(tokenTypeI); + if ( indexes==null ) { + //System.out.println("found linearly; stream index = "+getNodeIndexLinearly(node)); + return getNodeIndexLinearly(node); + } + for (int i = 0; i < indexes.size(); i++) { + Integer streamIndexI = (Integer)indexes.get(i); + Object n = get(streamIndexI.intValue()); + if ( n==node ) { + //System.out.println("found in index; stream index = "+streamIndexI); + return streamIndexI.intValue(); // found it! + } + } + return -1; + } + + */ +} |