package fitnesse.wikitext.parser;
import java.util.ArrayList;
import java.util.List;
public class Scanner {
private ScanString input;
private int next;
private TextMaker textMaker;
private SymbolStream symbols;
public Scanner(SourcePage sourcePage, CharSequence input) {
this(
new TextMaker(
new VariableSource() {
@Override
public Maybe<String> findVariable(String name) {
return Maybe.noString;
}
},
sourcePage),
input);
}
public Scanner(TextMaker textMaker, CharSequence input) {
this.input = new ScanString(input, 0);
next = 0;
this.textMaker = textMaker;
symbols = new SymbolStream();
}
public Scanner(Scanner other) {
copy(other);
}
public int getOffset() { return next; }
public void markStart() { input.markStart(next); }
public boolean isEnd() { return symbols.isEnd(); }
public Symbol getCurrent() { return symbols.get(0); }
public Maybe<String> stringFromStart(int start) {
int end = getOffset() - getCurrent().getContent().length();
return start <= end
? new Maybe<>(input.rawSubstring(start, end))
: Maybe.noString;
}
public void copy(Scanner other) {
input = new ScanString(other.input);
next = other.next;
textMaker = other.textMaker;
symbols = new SymbolStream(other.symbols);
}
public Symbol makeLiteral(SymbolType terminator) {
input.setOffset(next);
while (!input.isEnd()) {
SymbolMatch match = terminator.makeMatch(input, symbols);
if (match.isMatch()) {
symbols.add(new Symbol(terminator));
Symbol result = new Symbol(SymbolType.Text, input.substringFrom(next), next);
next = input.getOffset() + match.getMatchLength();
return result;
}
input.moveNext();
}
Symbol result = new Symbol(SymbolType.Text, input.substringFrom(next), next);
next = input.getOffset();
symbols.add(Symbol.emptySymbol);
return result;
}
public void moveNext() {
moveNextIgnoreFirst(new ParseSpecification());
}
public void moveNextIgnoreFirst(ParseSpecification specification) {
Step step = makeNextStep(specification, next);
next = step.nextPosition;
symbols.add(step.token);
}
public List<Symbol> peek(int count, ParseSpecification specification) {
List<Symbol> result = new ArrayList<>(count);
int startPosition = next;
for (int i = 0; i < count; i++) {
Step step = makeNextStep(specification, startPosition);
result.add(step.token);
if (input.isEnd()) break;
startPosition = step.nextPosition;
}
return result;
}
private Step makeNextStep(final ParseSpecification specification, final int startPosition) {
input.setOffset(startPosition);
int newNext = startPosition;
Symbol matchSymbol = null;
while (!input.isEnd()) {
SymbolMatch match = specification.findMatch(input, startPosition, symbols);
if (match.isMatch()) {
matchSymbol = match.getSymbol();
newNext = input.getOffset() + match.getMatchLength();
break;
}
input.moveNext();
}
if (input.getOffset() > startPosition) {
SymbolMatch match = textMaker.make(specification, startPosition, input.substringFrom(startPosition));
return new Step(match.getSymbol(), startPosition + match.getMatchLength());
}
if (input.isEnd()) {
return new Step(Symbol.emptySymbol, input.getOffset());
}
return new Step(matchSymbol, newNext);
}
private static class Step {
public Symbol token;
public int nextPosition;
public Step(Symbol token, int nextPosition) {
this.token = token;
this.nextPosition = nextPosition;
}
}
}