blob: 2aec7ec200b1305ae094086fc17229fbfdee54cf [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.royale.compiler.internal.parsing.as;
import java.util.ArrayList;
import org.apache.royale.compiler.parsing.IASToken;
import org.apache.royale.compiler.parsing.IASToken.ASTokenKind;
/**
* Buffer used that supports streaming of tokens, instead of a pre-computed
* list. This token buffer is used by the ActionScript compiler.
*/
public final class StreamingTokenBuffer extends BaseRepairingTokenBuffer implements IRepairingTokenBuffer
{
/**
* The "rewind buffer size" is the limit of the number of tokens allowed in
* a single syntactic predicate.
*/
private static final int REWIND_BUFFER_SIZE = 10;
private final StreamingASTokenizer tokenizer;
private final ArrayList<ASToken> buffer;
private int bufferSize;
private ASToken previousToken;
public StreamingTokenBuffer(final StreamingASTokenizer tokens)
{
super(tokens.getSourcePath());
tokenizer = tokens;
buffer = new ArrayList<ASToken>();
for (int i = 0; i < REWIND_BUFFER_SIZE; i++)
{
buffer.add(eofToken);
}
bufferSize = 0;
previousToken = eofToken;
}
/**
* @return Path of the token source.
*/
public String getSourcePath()
{
return tokenizer.getSourcePath();
}
@Override
public final boolean insertSemicolon(final boolean isNextToken)
{
if (!insertSemis)
return false;
if (isNextToken)
onSemicolonInserted();
return true;
}
private final void fill(final int distance)
{
for (int pos = 0; pos < distance; pos++)
{
final ASToken next = tokenizer.next();
buffer.add(next);
bufferSize++;
}
}
@Override
public void rewind(final int position)
{
final int backSteps = this.position - position;
if (backSteps > REWIND_BUFFER_SIZE)
{
throw new IllegalStateException(String.format(
"Token buffer can't rewind that far. Max rewind is %d, but got %d.",
REWIND_BUFFER_SIZE,
backSteps));
}
for (int i = 0; i < backSteps; i++)
{
// Left-pad the buffer with EOF tokens to push the look-ahead tokens further.
buffer.add(0, eofToken);
bufferSize++;
}
this.position = position;
}
@Override
public final void consume()
{
if (nextIsSemicolon)
{
nextIsSemicolon = false;
previousToken = SEMICOLON;
}
else
{
position++;
// "fBufferSize" is the number of tokens, not including
// the last EOF in the buffer, or the rewind buffer tokens.
if (bufferSize > 0)
{
assert previousToken != null;
previousToken = buffer.get(REWIND_BUFFER_SIZE);
buffer.remove(1);
bufferSize--;
assert bufferSize >= 0 : "fBufferSize can not be negative";
}
}
}
@Override
protected ASToken lookAheadSkipInsertedSemicolon(int i)
{
assert bufferSize + REWIND_BUFFER_SIZE == buffer.size() : "buffer size out-of-sync";
if (bufferSize < i)
{
fill(i - bufferSize);
}
final ASToken result = buffer.get(REWIND_BUFFER_SIZE - 1 + i);
if (result != null)
result.lock();
return result != null ? result : eofToken;
}
/**
*/
public IASToken[] getTokens(final boolean includeInserted)
{
throw new UnsupportedOperationException();
}
@Override
public ASToken previous()
{
return previousToken != null ? previousToken : eofToken;
}
/**
* Match optional semicolon.
* <p>
* This function implements the first 2 optional semicolon insertion rules
* in the ECMA specification.
*
* @see "ECMA 2.6.2 Chapter 7.9.1 Rules of Automatic Semicolon Insertion"
*/
@Override
public boolean matchOptionalSemicolon()
{
final ASToken nextToken = LT(1);
if (nextToken == null)
{
// Pass -- end of file
}
else if (nextToken.getType() == ASTokenTypes.EOF)
{
// Pass -- end of file
}
else if (nextToken.getType() == ASTokenTypes.TOKEN_SEMICOLON)
{
// Found the semicolon.
consume();
}
else if (nextToken.getTokenKind() == ASTokenKind.SCOPE_CLOSE)
{
// Pass - the "offending token" is a "}".
}
else if (nextToken.getType() == ASTokenTypes.TOKEN_KEYWORD_ELSE)
{
// Pass - the "offending token" is "else".
}
else if (nextToken.getLine() > previous().getLine())
{
// Insert - the "offending token" is on another line.
insertSemicolon(false);
}
else if (!nextToken.getSourcePath().equals(previous().getSourcePath()))
{
// Insert - the "offending token" is in another file.
// The previous token is in an included file.
insertSemicolon(false);
}
else
{
// Failed to insert a virtual semicolon.
return false;
}
return true;
}
}