blob: 52cac94cac300e4781c6ba93f34c7bd1c0478264 [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 java.util.List;
import antlr.Token;
import org.apache.royale.compiler.common.ISourceLocation;
import org.apache.royale.compiler.common.SourceLocation;
import org.apache.royale.compiler.internal.parsing.TokenBase;
import org.apache.royale.compiler.parsing.ICMToken;
import org.apache.royale.compiler.problems.BadCharacterProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
/**
* Base class for JFlex-based tokenizers (RawScriptTokenizer, RawTagTokenizer,
* RawCSSTokenizer, RawMetadataTokenizer). Tokenizers should use %extends
* BaseRawTokenizer and provide a definitino for getColumn(). yytext()) will be
* generated automatically.
*/
public abstract class BaseRawTokenizer<T extends TokenBase>
{
/**
* Start offset of aggregate
*/
protected int aggregateStart;
/**
* Line number of aggregate
*/
protected int aggregateStartLine;
/**
* Column number of aggregate
*/
protected int aggregateStartColumn;
/**
* Contents of aggregate
*/
protected StringBuilder aggregateContents;
private T lastToken = null;
private final List<ICompilerProblem> problems;
/**
* Token pool to cut down on object creation.
*/
private final T[] tokenPool = initTokenPool();
/**
* Offset into the token pool
*/
private int tokenPoolIndex = 5; //buffer room for tokens set to 5
public BaseRawTokenizer()
{
problems = new ArrayList<ICompilerProblem>();
}
protected abstract T[] initTokenPool();
/**
* Gets the text of the current token. This is implemented by JFlex.
*
* @return The text of current token
*/
protected abstract String yytext();
private String sourcePath;
/**
* Gets the source path for the file being tokenized. This source path is
* used when reporting problems.
*/
public String getSourcePath()
{
return sourcePath;
}
/**
* Sets the source path for the file being tokenized. This source path is
* used when reporting problems.
*/
public void setSourcePath(String sourcePath)
{
this.sourcePath = sourcePath;
}
/**
* Gets the current offset of the tokenizer into the file buffer. This
* should be implemented in the grammar file by returning JFlex's zzchar.
*
* @return The current offset.
*/
protected abstract int getOffset();
/**
* Gets the current line number of the tokenizer. Lines numbers start at 0,
* not 1. This should be implemented in the grammar file by returning
* JFlex's zzline.
*
* @return The current line.
*/
protected abstract int getLine();
/**
* Gets the current column number of the tokenizer. Column numbers start at
* 0, not 1. This should be implemented in the grammar file by returning
* JFlex's zzcolumn.
*
* @return The current column.
*/
protected abstract int getColumn();
protected void addBadCharacterProblem(String badChar)
{
ISourceLocation location = getCurrentSourceLocation(badChar.length());
ICompilerProblem problem = new BadCharacterProblem(location, badChar);
problems.add(problem);
}
/**
* Create a {@code ISourceLocation} object based on the current lexer state.
*
* @param tokenLength Length of the problematic input.
* @return Current source location used to report a syntax problem.
*/
protected final ISourceLocation getCurrentSourceLocation(int tokenLength)
{
return new SourceLocation(
getSourcePath(),
getOffset(),
getOffset() + tokenLength,
getLine(),
getColumn());
}
/**
* @return true if we encountered errors while tokenizing
*/
public boolean hasProblems()
{
return problems.size() > 0;
}
/**
* @return any problems we encountered while parsing
*/
public List<ICompilerProblem> getProblems()
{
return problems;
}
protected T buildToken(int type, int start, int end, int line, int column, CharSequence text)
{
final T token = fetchToken(type, start, start + text.length(), getLine(), getColumn(), text);
setLastToken(token);
return token;
}
public void reset()
{
lastToken = null;
aggregateContents = null;
aggregateStart = -1;
aggregateStartLine = -1;
aggregateStartColumn = -1;
}
public final int getLastTokenType()
{
return lastToken != null ? lastToken.getType() : -1;
}
public final String getLastTokenText()
{
return lastToken != null ? lastToken.getText() : "";
}
protected void setLastToken(T token)
{
lastToken = token;
}
/**
* Initialize a new aggregate with the current yytext() and position
*/
protected final void startAggregate()
{
aggregateStart = getOffset();
aggregateStartLine = getLine();
aggregateStartColumn = getColumn();
aggregateContents = new StringBuilder();
fillBuffer(aggregateContents);
}
protected final void startAggregate(Token token)
{
aggregateStart = ((ICMToken)token).getStart();
aggregateStartLine = ((ICMToken)token).getLine();
aggregateStartColumn = ((ICMToken)token).getColumn();
aggregateContents = new StringBuilder();
aggregateContents.append(token.getText());
}
/**
* Add the current yytext() to the current aggregate
*/
protected void continueAggregate()
{
if (aggregateContents != null)
fillBuffer(aggregateContents);
}
protected final void continueAggregate(Token token)
{
if (aggregateContents != null)
{
aggregateContents.append(token.getText());
}
}
protected final void continueAggregate(String text)
{
if (aggregateContents != null)
{
aggregateContents.append(text);
}
}
protected final void continueAggregate(char c)
{
if (aggregateContents != null)
{
aggregateContents.append(c);
}
}
protected final void continueAggregate(char chars[])
{
if (aggregateContents != null)
{
aggregateContents.append(chars);
}
}
protected abstract void fillBuffer(StringBuilder builder);
protected final boolean hasAggregateContents()
{
return aggregateContents != null;
}
/**
* signals that we should reuse the last token in the token pool without
* filling our buffer
*/
public final void setReuseLastToken()
{
if (tokenPoolIndex > 0)
tokenPoolIndex--;
}
/**
* Builds a token, or reuses a token, based on the underlying token pool
*
* @param type the type of token to build
* @param start the token start
* @param end the token end
* @param line the token line
* @param column the token column
* @param text the token text
* @return a token from the passed in parameters. This token is not safe to
* hold on to, and should be used and discarded before further tokens are
* queried
*/
protected final T fetchToken(final int type, final int start, final int end, final int line, final int column, final CharSequence text)
{
//no token poll. remove this potentially
if (tokenPool.length == 0)
{
return newToken(type, start, end, line, column, text);
}
//if the pool is full, and we are passed the length of the pool, reset our index to zero
if (tokenPoolIndex >= tokenPool.length)
{
tokenPoolIndex = 0;
}
//try to use the new token. Accept it if the token is not null, and it is not locked.
final T potential = tokenPool[tokenPoolIndex];
if (potential != null && !potential.isLocked())
{
potential.reuse(type, start, end, line, column, text);
}
else
//build a new token
{
tokenPool[tokenPoolIndex] = newToken(type, start, end, line, column, text);
}
return tokenPool[tokenPoolIndex++];
}
protected abstract T newToken(int type, int start, int end, int line, int column, CharSequence text);
/**
* Build a token from the current aggregated text and the given type
*
* @param type token type (based on the appropriate XxxTokenTypes interface)
* @return new token
*/
public T buildAggregateToken(final int type)
{
if (aggregateContents == null)
{
return null;
}
final T token = fetchToken(type, aggregateStart, aggregateStart + aggregateContents.length(),
aggregateStartLine, aggregateStartColumn, aggregateContents.toString());
aggregateContents = null;
setLastToken(token);
return token;
}
/**
* Builds a token with the specified type and text, using the current
* getOffset(), getLine(), and getColumn(). The grammar file should not
* override this.
*
* @param type token type (based on the appropriate XxxTokenTypes interface)
* @return new token
*/
public final T buildToken(final int type)
{
final String text = yytext();
final int start = getOffset();
final T token = fetchToken(type, start, start + text.length(), getLine(), getColumn(), text);
setLastToken(token);
return token;
}
/**
* Build a token of the specified type, using the current yytext(),
* getOffset(), getLine(), and getColumn(). The grammar file cannot override
* this.
*
* @param type token type (based on the appropriate XxxTokenTypes interface)
* @return new token
*/
public T buildToken(final int type, final String text)
{
final int start = getOffset();
final T token = fetchToken(type, start, start + text.length(), getLine(), getColumn(), text);
setLastToken(token);
return token;
}
/**
* Get the current context as a string (to help with debugging)
*
* @param line current line number
* @return description of current context
*/
protected String getContext(int line)
{
return yytext() + " (" + line + ")";
}
}