package code.loop.parser;

import code.stuff.Logger;
import code.stuff.Tracer;
import code.table.AmbiguousSymbolException;
import code.table.UndefinedSymbol;
import code.term.Term;
import code.type.visitor.PrintAsTxtLoadable;
import code.type.typechecker.RenameTypeVisitor;
import code.type.typechecker.TypeError;
import code.type.TypeExpression;
import code.type.visitor.PrintAsTxtLoadable;
import code.type.typechecker.TermGetTypeVisitor;
import code.type.typechecker.TypeError;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.StringReader;

/**
 * @author Jimeng Liu
 * @since July 22, 2003
 */
public class ClpParser {

    private BufferedReader in;
    private BufferedWriter out;

    public ClpParser(BufferedReader in, BufferedWriter out) {
        this.in = in;
        this.out = out;
    }

    public Term parse(String s) throws Exception {
        Term term = null ;
        try {
            AstParser parser_obj
                    = new AstParser(new Yylex(new StringReader(s)));
            Ast.Term astTerm = (Ast.Term) parser_obj.parse().value;
            if (Tracer.clp) Logger.logln("Ast: " + astTerm);

            AstVisitor astTermCreateVisitor = new AstTermCreateVisitor();
            term = (Term) astTerm.accept(astTermCreateVisitor, null);
            if (Tracer.clp) Logger.logln("term: " + term);

            TypeExpression type = TermGetTypeVisitor.getType(term);

            if (Tracer.clp) Logger.logln("type: " + PrintAsTxtLoadable.getString(type));

            return term;
        }

                // TODO: refactor code below
        catch (AmbiguousSymbolException e) {
            out.write("Ambiguous Symbol: " + e.getMessage());
            out.newLine();
            out.flush();
            throw e;
        } catch (ParseError e) {
            out.write("Parse Error: " + e.getMessage());
            out.newLine();
            out.flush();
            throw e;
        } catch (UndefinedSymbol e) {
            out.write("Undefined Symbol: \"" + e.getMessage() + "\"");
            out.newLine();
            out.flush();
            throw e;
        } catch (TypeError e) {
            out.write("Type Error while checking term "+term.toString() + "\n"+e.getMessage());
            out.newLine();
            out.flush();
            throw e;
        } catch (Exception e) {
            out.write("Exception: " + e);
            out.newLine();
            out.flush();
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * Parse the given expression and print its type to the out stream.
     * This method is used ONLY by Command.java and hence it does not throw any exceptions.
     *
     * @param expression The expression whose type should be output to out
     * @return
     */
    public TypeExpression parseTypeOnly(String expression) throws Exception {
        AstParser parser_obj
                = new AstParser(new Yylex(new StringReader(expression)));
        Ast.Term astTerm = (Ast.Term) parser_obj.parse().value;
        if (Tracer.clp) Logger.logln("Ast: " + astTerm);

        AstVisitor astTermCreateVisitor = new AstTermCreateVisitor();
        Term term = (Term) astTerm.accept(astTermCreateVisitor, null);
        if (Tracer.clp) Logger.logln("term: " + term);

        TypeExpression type = TermGetTypeVisitor.getType(term);

        if (Tracer.clp) Logger.logln("type: " + PrintAsTxtLoadable.getString(type));
        return type;
    }

}
