/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ package antlr; import antlr.collections.impl.BitSet; /** * This class provides TokenStreamHiddenTokenFilters with the concept of * tokens which can be copied so that they are seen by both the hidden token * stream as well as the parser itself. This is useful when one wants to use * an existing parser (like the Java parser included with ANTLR) that throws * away some tokens to create a parse tree which can be used to spit out * a copy of the code with only minor modifications. * * This code is partially derived from the public domain ANLTR TokenStream */ public class TokenStreamCopyingHiddenTokenFilter extends TokenStreamHiddenTokenFilter implements TokenStream { protected BitSet copyMask; CommonHiddenStreamToken hiddenCopy = null; public TokenStreamCopyingHiddenTokenFilter(TokenStream input) { super(input); copyMask = new BitSet(); } /** * Indicate that all tokens of type tokenType should be copied. The copy * is put in the stream of hidden tokens, and the original is returned in the * stream of normal tokens. * * @param tokenType integer representing the token type to copied */ public void copy(int tokenType) { copyMask.add(tokenType); } /** * Create a clone of the important parts of the given token. Note that this * does NOT copy the hiddenBefore and hiddenAfter fields. * * @param t token to partially clone * @return newly created partial clone */ public CommonHiddenStreamToken partialCloneToken(CommonHiddenStreamToken t) { CommonHiddenStreamToken u = new CommonHiddenStreamToken(t.getType(), t.getText()); u.setColumn(t.getColumn()); u.setLine(t.getLine()); u.setFilename(t.getFilename()); return u; } public void linkAndCopyToken(CommonHiddenStreamToken prev, CommonHiddenStreamToken monitored) { // create a copy of the token in the lookahead for use as hidden token hiddenCopy = partialCloneToken(LA(1)); // attach copy to the previous token, whether hidden or monitored prev.setHiddenAfter(hiddenCopy); // if previous token was hidden, set the hiddenBefore pointer of the // copy to point back to it if (prev != monitored) { hiddenCopy.setHiddenBefore(prev); } // we don't want the non-hidden copy to link back to the hidden // copy on the next pass through this function, so we leave // lastHiddenToken alone //System.err.println("hidden copy: " + hiddenCopy.toString()); return; } private void consumeFirst() throws TokenStreamException { consume(); // get first token of input stream // Handle situation where hidden or discarded tokens // appear first in input stream CommonHiddenStreamToken p=null; // while hidden, copied, or discarded scarf tokens while ( hideMask.member(LA(1).getType()) || discardMask.member(LA(1).getType()) || copyMask.member(LA(1).getType()) ) { // if we've hit one of the tokens that needs to be copied, we copy it // and then break out of the loop, because the parser needs to see it // too // if (copyMask.member(LA(1).getType())) { // copy the token in the lookahead hiddenCopy = partialCloneToken(LA(1)); // if there's an existing token before this, link that and the // copy together if (p != null) { p.setHiddenAfter(hiddenCopy); hiddenCopy.setHiddenBefore(p); // double-link } lastHiddenToken = hiddenCopy; if (firstHidden == null) { firstHidden = hiddenCopy; } // we don't want to consume this token, because it also needs to // be passed through to the parser, so break out of the while look // entirely // break; } else if (hideMask.member(LA(1).getType())) { if (p != null) { p.setHiddenAfter(LA(1)); LA(1).setHiddenBefore(p); // double-link } p = LA(1); lastHiddenToken = p; if (firstHidden == null) { firstHidden = p; // record hidden token if first } } consume(); } } /** Return the next monitored token. * Test the token following the monitored token. * If following is another monitored token, save it * for the next invocation of nextToken (like a single * lookahead token) and return it then. * If following is unmonitored, nondiscarded (hidden) * channel token, add it to the monitored token. * * Note: EOF must be a monitored Token. */ public Token nextToken() throws TokenStreamException { // handle an initial condition; don't want to get lookahead // token of this splitter until first call to nextToken if (LA(1) == null) { consumeFirst(); } //System.err.println(); // we always consume hidden tokens after monitored, thus, // upon entry LA(1) is a monitored token. CommonHiddenStreamToken monitored = LA(1); // point to hidden tokens found during last invocation monitored.setHiddenBefore(lastHiddenToken); lastHiddenToken = null; // Look for hidden tokens, hook them into list emanating // from the monitored tokens. consume(); CommonHiddenStreamToken prev = monitored; // deal with as many not-purely-monitored tokens as possible while ( hideMask.member(LA(1).getType()) || discardMask.member(LA(1).getType()) || copyMask.member(LA(1).getType()) ) { if (copyMask.member(LA(1).getType())) { // copy the token and link it backwards if (hiddenCopy != null) { linkAndCopyToken(hiddenCopy, monitored); } else { linkAndCopyToken(prev, monitored); } // we now need to parse it as a monitored token, so we return, which // avoids the consume() call at the end of this loop. the next call // will parse it as a monitored token. //System.err.println("returned: " + monitored.toString()); return monitored; } else if (hideMask.member(LA(1).getType())) { // attach the hidden token to the monitored in a chain // link forwards prev.setHiddenAfter(LA(1)); // link backwards if (prev != monitored) { //hidden cannot point to monitored tokens LA(1).setHiddenBefore(prev); } else if (hiddenCopy != null) { hiddenCopy.setHiddenAfter(LA(1)); LA(1).setHiddenBefore(hiddenCopy); hiddenCopy = null; } //System.err.println("hidden: " + prev.getHiddenAfter().toString() + "\" after: " + prev.toString()); prev = lastHiddenToken = LA(1); } consume(); } // remember the last hidden token for next time around if (hiddenCopy != null) { lastHiddenToken = hiddenCopy; hiddenCopy = null; } //System.err.println("returned: " + monitored.toString()); return monitored; } }