TokenizerRepeater.java
/*
* Copyright (c) 2015-2020, Stein Eldar Johnsen
*
* 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 net.morimekta.lexer;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
import static net.morimekta.strings.EscapeUtil.javaEscape;
/**
* the tokenizer repeater is meant to repeat a set of tokens in the same
* sequence.
*
* @param <TT> TokenType type.
* @param <T> Token type.
*/
public class TokenizerRepeater<TT, T extends Token<TT>> implements Tokenizer<TT, T> {
private final Queue<T> unread;
private T lastToken;
/**
* @param tokens List of tokens.
*/
public TokenizerRepeater(List<T> tokens) {
if (requireNonNull(tokens, "tokens == null").isEmpty()) {
throw new IllegalArgumentException("Empty token list cannot be repeated");
}
unread = new ArrayDeque<>();
unread.addAll(tokens);
}
@Override
public T parseNextToken() {
if (unread.size() > 0) {
lastToken = unread.poll();
return lastToken;
}
return null;
}
@Override
public T readUntil(CharSequence terminator, TT type, boolean allowEof) throws IOException {
if (unread.size() > 0) {
if (!Objects.equals(unread.peek().type(), type)) {
throw new LexerException("type mismatch: " + type + " != " + lastToken.type());
}
lastToken = requireNonNull(unread.poll());
return lastToken;
}
if (allowEof) return null;
throw new LexerException(currentLine(), currentLineNo(), currentLinePos(), 1,
"End of stream reading until '" + terminator + "'");
}
@Override
public int currentLineNo() {
if (lastToken != null) {
return lastToken.lineNo();
}
T next = requireNonNull(unread.peek());
return next.lineNo();
}
@Override
public int currentLinePos() {
if (lastToken != null) {
return lastToken.linePos() + lastToken.length();
}
T next = requireNonNull(unread.peek());
return next.linePos();
}
@Override
public CharSequence currentLine() {
if (lastToken != null) {
return lastToken.line();
}
T next = requireNonNull(unread.peek());
return next.line();
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" +
unread.stream()
.map(TokenizerRepeater::quote)
.collect(Collectors.joining(", ")) + "}";
}
private static String quote(Token<?> t) {
return "'" + javaEscape(t) + "'";
}
}