ScannerUtil.java

package org.sterling.source.scanner;

import static java.lang.Character.isISOControl;
import static java.lang.Character.isWhitespace;
import static java.util.Arrays.asList;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import org.sterling.SterlingException;
import org.sterling.source.exception.InvalidEscapeSequenceException;
import org.sterling.source.exception.ScannerException;
import org.sterling.source.exception.UnterminatedCharacterException;
import org.sterling.source.exception.UnterminatedStringException;
import org.sterling.source.syntax.NodeKind;

public final class ScannerUtil {

    public static SterlingException invalidEscapeSequence(InputReader reader, NodeKind kind) {
        return new InvalidEscapeSequenceException("Invalid escape sequence in " + kind + " [" + reader.getLocation() + "]");
    }

    public static SterlingException unexpectedInput(InputReader reader, Collection<NodeKind> expectedKinds) {
        while (!reader.expect('\0') && !reader.expect('\n') && isWhitespace(reader.peek())) {
            reader.skip();
        }
        ScannerException exception = new ScannerException("Unexpected " + getName(reader)
            + " '" + getValue(reader) + "', expecting " + listKinds(expectedKinds)
            + " [" + reader.getLocation() + "]");
        reader.reject();
        return exception;
    }

    public static SterlingException unexpectedInput(InputReader reader, NodeKind... expectedKinds) {
        return unexpectedInput(reader, asList(expectedKinds));
    }

    public static SterlingException unterminatedCharacter(InputReader reader, NodeKind kind) {
        return new UnterminatedCharacterException("Unterminated " + kind + " [" + reader.getLocation() + "]");
    }

    public static SterlingException unterminatedString(InputReader reader, NodeKind kind) {
        return new UnterminatedStringException("Unterminated " + kind + " [" + reader.getLocation() + "]");
    }

    private static String getName(InputReader reader) {
        char value = reader.peek();
        return (value == '\0') ? "END OF INPUT" : Character.getName(value);
    }

    private static String getValue(InputReader reader) {
        char value = reader.peek();
        if (' ' != value && (isWhitespace(value) || isISOControl(value))) {
            return "";
        } else {
            return String.valueOf(value);
        }
    }

    private static String listKinds(Collection<NodeKind> kinds) {
        StringBuilder builder = new StringBuilder();
        Deque<NodeKind> queue = new ArrayDeque<>(kinds);
        builder.append(queue.pop());
        while (queue.size() > 1) {
            builder.append(", ");
            builder.append(queue.pop());
        }
        if (queue.size() > 0) {
            builder.append(" or ");
            builder.append(queue.pop());
        }
        return builder.toString();
    }

    private ScannerUtil() {
        // intentionally empty
    }
}