/*
* LinkedListTokenStream.java
*
* Copyright (c) 2006 David Holroyd
*
* 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.
*/
package uk.co.badgersinfoil.metaas.impl.antlr;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenSource;
import org.antlr.runtime.TokenStream;
public class LinkedListTokenStream implements TokenStream {
protected TokenSource tokenSource;
protected LinkedListToken head;
protected LinkedListToken tail;
/** 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 LinkedListToken lastMarker;
/** The current element in the tokens list (next token
* to consume). p==null indicates that the tokens list is empty
*/
protected LinkedListToken p = null;
public LinkedListTokenStream() {
}
public TokenSource getSource() {
return tokenSource;
}
/**
* Reverses the stream 'count' tokens back, causing the tokens to be
* removed from the stream. Can be used to erase tokens which parser
* lookahead has summoned, but which represent input to be handled by
* an 'island grammar'.
*/
public void scrub(int count) {
if (p == null) {
p = tail;
}
for (; count > 0 ; count--) {
p = p.getPrev();
}
p.setNext(null);
tail = p;
p = null;
}
/**
* The given TokenSource must produce tokens of type LinkedListToken
*/
public LinkedListTokenStream(TokenSource tokenSource) {
this.tokenSource = tokenSource;
}
public LinkedListTokenStream(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;
p = null;
channel = Token.DEFAULT_CHANNEL;
}
private LinkedListToken readNextToken() {
LinkedListToken t = (LinkedListToken)tokenSource.nextToken();
while ( t!=null && t.getType()!=CharStream.EOF ) {
boolean discard = false;
if ( discardOffChannelTokens && t.getChannel()!=this.channel ) {
discard = true;
}
if ( !discard ) {
if (head == null && tail == null) {
head = tail = t;
} else {
tail.setNext(t);
t.setPrev(tail);
tail = t;
}
break;
}
t = (LinkedListToken)tokenSource.nextToken();
}
if (t.getType() == CharStream.EOF) {
// prevent ourselves from producing lots of EOF tokens
// if the parser is 'pushy'; also, do the head/tail dance
if (tail!=null && tail.getType()==CharStream.EOF) {
return tail;
} else {
if (head == null && tail == null) {
head = tail = t;
} else {
tail.setNext(t);
t.setPrev(tail);
tail = t;
}
}
}
return skipOffTokenChannels(t);
}
/**
* Returns the token that follows the given token in the stream, or
* null if there's no token following
*/
private LinkedListToken succ(LinkedListToken tok) {
LinkedListToken next = tok.getNext();
if (next == null) {
next = readNextToken();
}
return next;
}
/** 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) {
LinkedListToken tok = head;
for (int c=0; c<i; c++) {
tok = succ(tok);
}
return tok;
}
public TokenSource getTokenSource() {
return tokenSource;
}
public Token LT(int k) {
if (p == null) {
p = readNextToken();
}
if (k == 0) {
return null;
}
if (k < 0) {
return LB(-k);
}
LinkedListToken i = p;
int n = 1;
// find k good tokens
while (n < k) {
LinkedListToken next = succ(i);
if (i == null) {
return Token.EOF_TOKEN;
}
// skip off-channel tokens
i = skipOffTokenChannels(next); // leave p on valid token
n++;
}
if (i == null) {
return Token.EOF_TOKEN;
}
return i;
}
/** Look backwards k tokens on-channel tokens */
protected Token LB(int k) {
if (p == null) {
p = readNextToken();
}
if (k == 0) {
return null;
}
LinkedListToken i = p;
int n = 1;
// find k good tokens looking backwards
while (n <= k) {
LinkedListToken next = i.getPrev();
if (next == null) {
return null;
}
// skip off-channel tokens
i = skipOffTokenChannelsReverse(next); // leave p on valid token
n++;
}
return i;
}
public String toString(int start, int stop) {
LinkedListToken tok = head;
int i = 0;
for (; i<start && tok!=null; i++) {
tok = succ(tok);
}
StringBuffer buf = new StringBuffer();
for (; i<=stop && tok!=null; i++) {
buf.append(tok.getText());
tok = succ(tok);
}
return buf.toString();
}
public String toString(Token start, Token stop) {
LinkedListToken tok = (LinkedListToken)start;
StringBuffer buf = new StringBuffer();
do {
buf.append(tok.getText());
tok = succ(tok);
} while (tok!=null && tok!=stop);
return buf.toString();
}
public void consume() {
do {
p = p.getNext();
} while (p != null && p.getChannel() != channel);
}
public int index() {
int i=0;
for (LinkedListToken tok=head; tok!=p&&tok!=null; tok=tok.getNext()) {
i++;
}
return i;
}
public int LA(int i) {
return LT(i).getType();
}
public int mark() {
// TODO: could store marks in a hash; does it make any difference?
lastMarker = (LinkedListToken)LT(1);
return index();
}
public void release(int marker) {
// no resources to release
}
public void rewind() {
p = lastMarker;
}
public void rewind(int marker) {
seek(marker);
}
public void seek(int index) {
p = head;
for (int i=0; i<index; i++) {
p = succ(p);
}
}
public int size() {
int s = 0;
for (LinkedListToken tok=head; tok!=null; tok=tok.getNext()) {
s++;
}
return s;
}
public void discardOffChannelTokens(boolean discardOffChannelTokens) {
this.discardOffChannelTokens = discardOffChannelTokens;
}
/**
* Given a starting token, return the first on-channel token.
*/
protected LinkedListToken skipOffTokenChannels(LinkedListToken i) {
while (i != null && i.getChannel() != channel) {
i = succ(i);
}
return i;
}
protected LinkedListToken skipOffTokenChannelsReverse(LinkedListToken i) {
while (i != null && i.getChannel() != channel) {
i = i.getPrev();
}
return i;
}
}