package parser;

import java.io.InputStream;

import error.LispException;
import scanner.LispScanner;
import sexpression.SExpression;
import token.Eof;
import token.Token;

/**
 * Converts a stream of bytes into internal representations of Lisp s-expressions.
 */
public class LispParser {

    private LispScanner scanner;
    private Token nextToken;
    private LispException delayedException;
    private boolean isNextTokenStored;

    public LispParser(InputStream inputStream, String fileName) {
        scanner = new LispScanner(inputStream, fileName);
        nextToken = null;
        delayedException = null;
        isNextTokenStored = false;
    }

    public boolean isEof() {
        if (!isNextTokenStored)
            storeNextToken();

        return nextToken instanceof Eof;
    }

    private void storeNextToken() {
        try {
            nextToken = scanner.getNextToken();
            isNextTokenStored = true;
        } catch (LispException e) {
            delayedException = e;
        }
    }

    public SExpression getNextSExpression() {
        throwDelayedExceptionIfNecessary();

        if (!isNextTokenStored)
            nextToken = scanner.getNextToken();

        isNextTokenStored = false;

        return nextToken.parseSExpression(scanner::getNextToken);
    }

    private void throwDelayedExceptionIfNecessary() {
        if (delayedException != null) {
            LispException exceptionToThrow = delayedException;
            delayedException = null;

            throw exceptionToThrow;
        }
    }

}
