ListScanner.java

package org.sterling.source.scanner;

import static java.util.Arrays.asList;
import static org.sterling.source.syntax.NodeKind.ANYTHING;
import static org.sterling.source.syntax.NodeKind.END_OF_INPUT;
import static org.sterling.source.syntax.Token.token;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import org.sterling.SterlingException;
import org.sterling.source.LocationRange;
import org.sterling.source.exception.ScannerException;
import org.sterling.source.syntax.NodeKind;
import org.sterling.source.syntax.Token;

public class ListScanner implements Scanner {

    private final Deque<Token> tokens;
    private final StateManager<Token> transactions;

    public ListScanner(List<Token> tokens) {
        this.tokens = new ArrayDeque<>(tokens);
        this.transactions = new StateManager<>(this.tokens);
        this.tokens.add(token(END_OF_INPUT, "\0", LocationRange.NULL));
    }

    @Override
    public void begin() {
        transactions.begin();
    }

    @Override
    public void close() {
        transactions.close();
        tokens.clear();
    }

    @Override
    public void end() {
        transactions.end();
    }

    @Override
    public boolean expect(NodeKind kind) {
        Token token = tokens.peek();
        if (token == null) {
            token = Token.NULL;
        }
        return token.is(kind);
    }

    @Override
    public boolean expectOne(NodeKind... kinds) {
        return expectOne(asList(kinds));
    }

    @Override
    public boolean expectOne(Iterable<NodeKind> kinds) {
        return ANYTHING != expected(kinds);
    }

    @Override
    public NodeKind expected(NodeKind... kinds) {
        return expected(asList(kinds));
    }

    @Override
    public NodeKind expected(Iterable<NodeKind> kinds) {
        for (NodeKind kind : kinds) {
            if (expect(kind)) {
                return kind;
            }
        }
        return ANYTHING;
    }

    @Override
    public Token require(NodeKind kind) throws SterlingException {
        if (expect(kind)) {
            Token next = tokens.poll();
            transactions.push(next);
            return next;
        } else {
            throw unexpectedToken();
        }
    }

    @Override
    public Token requireOne(NodeKind... kinds) throws SterlingException {
        return requireOne(asList(kinds));
    }

    @Override
    public Token requireOne(Collection<NodeKind> kinds) throws SterlingException {
        for (NodeKind kind : kinds) {
            if (expect(kind)) {
                return require(kind);
            }
        }
        throw unexpectedToken();
    }

    @Override
    public void restore() {
        transactions.restore();
    }

    private ScannerException unexpectedToken() {
        return new ScannerException("Unexpected token: " + tokens.peek());
    }
}