com.hazelcast.org.codehaus.janino.TokenStreamImpl Maven / Gradle / Ivy
/*
* Janino - An embedded Java[TM] compiler
*
* Copyright (c) 2001-2010 Arno Unkrig. 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. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 com.hazelcast.org.codehaus.janino;
import java.io.IOException;
import com.hazelcast.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.org.codehaus.commons.compiler.Location;
import com.hazelcast.org.codehaus.commons.compiler.WarningHandler;
import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;
/**
* Standard implementation for the {@link TokenStream}.
*/
public
class TokenStreamImpl implements TokenStream {
private final Scanner scanner;
public
TokenStreamImpl(Scanner scanner) { (this.scanner = scanner).setIgnoreWhiteSpace(true); }
/**
* The optional JAVADOC comment preceding the {@link #nextToken}.
*/
@Nullable private String docComment;
private Token
produceToken() throws CompileException, IOException {
for (;;) {
Token token = this.scanner.produce();
switch (token.type) {
case WHITE_SPACE:
case C_PLUS_PLUS_STYLE_COMMENT:
break;
case C_STYLE_COMMENT:
if (token.value.startsWith("/**")) {
if (TokenStreamImpl.this.docComment != null) {
TokenStreamImpl.this.warning("MDC", "Misplaced doc comment", this.scanner.location());
TokenStreamImpl.this.docComment = null;
}
}
break;
default:
return token;
}
}
}
/**
* Gets the text of the doc comment (a.k.a. "JAVADOC comment") preceding the next token.
*
* @return {@code null} if the next token is not preceded by a doc comment
*/
@Nullable public String
doc() {
String s = this.docComment;
this.docComment = null;
return s;
}
@Nullable private Token previousToken, nextToken, nextButOneToken;
@Override public Token
peek() throws CompileException, IOException {
if (this.nextToken == null) this.nextToken = this.produceToken();
assert this.nextToken != null;
return this.nextToken;
}
@Override public Token
peekNextButOne() throws CompileException, IOException {
if (this.nextToken == null) this.nextToken = this.produceToken();
if (this.nextButOneToken == null) this.nextButOneToken = this.produceToken();
assert this.nextButOneToken != null;
return this.nextButOneToken;
}
@Override public boolean
peek(String suspected) throws CompileException, IOException {
return this.peek().value.equals(suspected);
}
@Override public int
peek(String... suspected) throws CompileException, IOException {
return TokenStreamImpl.indexOf(suspected, this.peek().value);
}
@Override public boolean
peek(TokenType suspected) throws CompileException, IOException {
return this.peek().type == suspected;
}
@Override public int
peek(TokenType... suspected) throws CompileException, IOException {
return TokenStreamImpl.indexOf(suspected, this.peek().type);
}
@Override public boolean
peekNextButOne(String suspected) throws CompileException, IOException {
return this.peekNextButOne().value.equals(suspected);
}
@Override public Token
read() throws CompileException, IOException {
if (this.nextToken == null) return (this.previousToken = this.produceToken());
final Token result = this.nextToken;
assert result != null;
this.previousToken = result;
this.nextToken = this.nextButOneToken;
this.nextButOneToken = null;
return result;
}
@Override public void
read(String expected) throws CompileException, IOException {
String s = this.read().value;
if (!s.equals(expected)) throw this.compileException("'" + expected + "' expected instead of '" + s + "'");
}
@Override public int
read(String... expected) throws CompileException, IOException {
Token t = this.read();
String value = t.value;
int idx = TokenStreamImpl.indexOf(expected, value);
if (idx != -1) return idx;
if (value.startsWith(">")) {
int result = TokenStreamImpl.indexOf(expected, ">");
if (result != -1) {
// The parser is "looking for" token ">", but the next token only STARTS with ">" (e.g. ">>="); split
// that token into TWO tokens (">" and ">=", in the example).
// See JLS8 3.2, "Lexical Transformation", last paragraph.
Location loc = t.getLocation();
this.nextToken = new Token(
loc.getFileName(),
loc.getLineNumber(),
loc.getColumnNumber() + 1,
TokenType.OPERATOR,
value.substring(1)
);
return result;
}
}
throw this.compileException(
"One of '"
+ TokenStreamImpl.join(expected, " ")
+ "' expected instead of '"
+ value
+ "'"
);
}
@Override public String
read(TokenType expected) throws CompileException, IOException {
Token t = this.read();
if (t.type != expected) {
throw new CompileException(expected + " expected instead of '" + t.value + "'", t.getLocation());
}
return t.value;
}
@Override public int
read(TokenType... expected) throws CompileException, IOException {
Token t = this.read();
TokenType type = t.type;
int idx = TokenStreamImpl.indexOf(expected, type);
if (idx != -1) return idx;
throw this.compileException(
"One of '"
+ TokenStreamImpl.join(expected, " ")
+ "' expected instead of '"
+ type
+ "'"
);
}
@Override public boolean
peekRead(String suspected) throws CompileException, IOException {
if (this.nextToken != null) {
if (!this.nextToken.value.equals(suspected)) return false;
this.previousToken = this.nextToken;
this.nextToken = this.nextButOneToken;
this.nextButOneToken = null;
return true;
}
Token result = this.produceToken();
if (result.value.equals(suspected)) {
this.previousToken = result;
return true;
}
this.nextToken = result;
return false;
}
@Override public int
peekRead(String... suspected) throws CompileException, IOException {
if (this.nextToken != null) {
int idx = TokenStreamImpl.indexOf(suspected, this.nextToken.value);
if (idx == -1) return -1;
this.previousToken = this.nextToken;
this.nextToken = this.nextButOneToken;
this.nextButOneToken = null;
return idx;
}
Token t = this.produceToken();
int idx = TokenStreamImpl.indexOf(suspected, t.value);
if (idx != -1) {
this.previousToken = t;
return idx;
}
this.nextToken = t;
return -1;
}
@Override @Nullable public String
peekRead(TokenType suspected) throws CompileException, IOException {
Token nt = this.nextToken;
if (nt != null) {
if (nt.type != suspected) return null;
this.previousToken = this.nextToken;
this.nextToken = this.nextButOneToken;
this.nextButOneToken = null;
return nt.value;
}
nt = this.produceToken();
if (nt.type == suspected) {
this.previousToken = nt;
return nt.value;
}
return (this.nextToken = nt).value;
}
@Override public int
peekRead(TokenType... suspected) throws CompileException, IOException {
if (this.nextToken != null) {
int idx = TokenStreamImpl.indexOf(suspected, this.nextToken.type);
if (idx != -1) {
this.previousToken = this.nextToken;
this.nextToken = this.nextButOneToken;
this.nextButOneToken = null;
}
return idx;
}
Token t = this.produceToken();
int idx = TokenStreamImpl.indexOf(suspected, t.type);
if (idx != -1) {
this.previousToken = t;
return idx;
}
this.nextToken = t;
return -1;
}
@Override public Location
location() {
return this.previousToken != null ? this.previousToken.getLocation() : this.scanner.location();
}
private static int
indexOf(String[] strings, String subject) {
for (int i = 0; i < strings.length; ++i) {
if (strings[i].equals(subject)) return i;
}
return -1;
}
private static int
indexOf(TokenType[] tta, TokenType subject) {
for (int i = 0; i < tta.length; ++i) {
if (tta[i].equals(subject)) return i;
}
return -1;
}
@Override public void
setWarningHandler(@Nullable WarningHandler warningHandler) {
this.warningHandler = warningHandler;
}
// Used for elaborate warning handling.
@Nullable private WarningHandler warningHandler;
@Override public String
toString() { return this.nextToken + "/" + this.nextButOneToken + "/" + this.scanner.location(); }
/**
* Issues a warning with the given message and location and returns. This is done through
* a {@link WarningHandler} that was installed through
* {@link #setWarningHandler(WarningHandler)}.
*
* The {@code handle} argument qualifies the warning and is typically used by
* the {@link WarningHandler} to suppress individual warnings.
*
* @throws CompileException The optionally installed {@link WarningHandler} decided to throw a {@link
* CompileException}
*/
private void
warning(String handle, String message, @Nullable Location location) throws CompileException {
if (this.warningHandler != null) {
this.warningHandler.handleWarning(handle, message, location);
}
}
/**
* Convenience method for throwing a {@link CompileException}.
*/
protected final CompileException
compileException(String message) { return new CompileException(message, this.scanner.location()); }
private static String
join(@Nullable Object[] oa, String glue) {
if (oa == null) return ("(null)");
if (oa.length == 0) return ("(zero length array)");
StringBuilder sb = new StringBuilder().append(oa[0]);
for (int i = 1; i < oa.length; ++i) sb.append(glue).append(oa[i]);
return sb.toString();
}
}