package code.loop.parser;

import java.util.*;
import code.table.*;
import code.symbols.*;

/**
 *@author     jimeng
 *@since    August 2, 2003
 */
public class Ast {

    public static class Term {
        public Expr expr;
        public WhereTail wt;

        public Term(Expr expr, WhereTail wt) {
            this.expr = expr;
            this.wt = wt;
        }

        public String toString() {
            return "" + expr + " " + wt;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }

    }

    public static class WhereTail {
        public Vector variableList;

        public WhereTail(Vector variableList) {
            this.variableList = variableList;
        }

        public String toString() {
            if (variableList.size() == 0) {
                //if no where clause
                return "";
            }
            //there at least one variable defined in where clause
            String s = "where ";
            for (int i = 0; i < variableList.size() - 1; i++) {
                s += variableList.elementAt(i);
                s += ",";
            }
            s += variableList.elementAt(variableList.size() - 1);
            s += " free";
            return s;
        }

    }

    public abstract static class Expr {

        public abstract Object accept(AstVisitor v, Object o);
    }

    public static class IfExpr extends Expr {
        public Expr e1;
        public Expr e2;
        public Expr e3;

        public IfExpr(Expr e1, Expr e2, Expr e3) {
            this.e1 = e1;
            this.e2 = e2;
            this.e3 = e3;
        }

        public String toString() {
            return "if " + e1 + " then " + e2 + " else " + e3;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class InfixOperation extends Expr {
        public Expr e1;
        public Expr e2;
        public String infixId;

        public InfixOperation(Expr e1, String infixId, Expr e2) {
            this.e1 = e1;
            this.infixId = infixId;
            this.e2 = e2;
        }

        public InfixOperation reconstruct(String id, Expr e) throws ParseError {
            if (under(id)) {
                return new InfixOperation(this, id, e);
            } else
                    if (e2 instanceof InfixOperation) {
                e2 = ((Ast.InfixOperation) e2).reconstruct(id, e);
                return this;
            } else {
                e2 = new InfixOperation(e2, id, e);
                return this;
            }
        }

        private boolean under(String id) throws ParseError {
	    try {
		int oldRoot = ModuleTable.getId(infixId);
		int newRoot = ModuleTable.getId(id);
		DataSymbol oldSym = MapTable.getSymbol(oldRoot);
		DataSymbol newSym = MapTable.getSymbol(newRoot);
		if (oldSym.precedence > newSym.precedence) {
		    return true;
		}
		if (oldSym.precedence < newSym.precedence) {
		    return false;
		}
		if ((oldSym.associativity == newSym.associativity) &&
                    (oldSym.associativity == DataSymbol.NonAssoc)) {
		    throw new ParseError("must use parenthesis for nonassoc infixids together: " + infixId + " and " + id);
		}
		if ((oldSym.associativity == newSym.associativity) &&
                    (oldSym.associativity == DataSymbol.Left)) {
		    return true;
		} else if ((oldSym.associativity == newSym.associativity) &&
			   (oldSym.associativity == DataSymbol.Right)) {
		    return false;
		} else {
		    throw new ParseError("must use parenthesis for infixids together that have the same precedence but deifferent associativity: " + infixId + " and " + id);
		}
	    } catch(AmbiguousSymbolException symbolName) {
		throw new ParseError("symbol \""+ symbolName +"\" is ambiguous");
	    }
	}

	public String toString() {
	    return "(" + e1 + " " + infixId + " " + e2 + ")";
	}

	public Object accept(AstVisitor v, Object o) {
	    return v.visit(this, o);
	}
    }

    public static class FunctionExpr extends Expr {
        public Vector basics;

        //????public or not?????

        public FunctionExpr(Vector basics) {
            this.basics = basics;
        }

        public String toString() {
            String s = "";
            for (int i = 0; i < basics.size() - 1; i++) {
                s += basics.elementAt(i);
                s += " ";
            }
            if (basics.size() > 0) {
                s += basics.elementAt(basics.size() - 1);
            } else {
                s += "EMPTY FUNCTION EXPR!!! WRONG !!! IN PARSER !!!";
            }
            return s;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class Uminus extends Expr {
        public Expr e;

        public Uminus(Expr e) {
            this.e = e;
        }

        public String toString() {
            return "-(" + e + ")";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public abstract static class BasicExpr extends Expr {
    }

    public static class PrefixId extends BasicExpr {
        String id;

        public PrefixId(String id) {
            this.id = id;
        }

        public String toString() {
            return id;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class InfixId extends BasicExpr {
        public String id;

        public InfixId(String id) {
            this.id = id;
        }

        public String toString() {
            return "(" + id + ")";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class ParenExpr extends BasicExpr {
        public Expr e;

        public ParenExpr(Expr e) {
            this.e = e;
        }

        public String toString() {
            return "(" + e + ")";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class LeftSection extends BasicExpr {
        public Expr e;
        public String infixId;

        public LeftSection(Expr e, String infixId) {
            this.e = e;
            this.infixId = infixId;
        }

        public String toString() {
            return "(" + e + infixId + ")";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class RightSection extends BasicExpr {
        public String infixId;
        public Expr e;

        public RightSection(String infixId, Expr e) {
            this.infixId = infixId;
            this.e = e;
        }

        public String toString() {
            return "(" + infixId + e + ")";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public abstract static class Literal extends BasicExpr {
    }

    public static class IntLit extends Literal {
        public int i;

        public IntLit(int i) {
            this.i = i;
        }

        public String toString() {
            return "" + i;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class CharLit extends Literal {
        public char c;

        public CharLit(char c) {
            this.c = c;
        }

        public String toString() {
            return "\'" + c + "\'";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class StringLit extends Literal {
        public String s;

        public StringLit(String s) {
            this.s = s;
        }

        public String toString() {
            return "\""+s+"\"";
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class FloatLit extends Literal {
        public float f;

        public FloatLit(float f) {
            this.f = f;
        }

        public String toString() {
            return "" + f;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class List extends BasicExpr {
        public Vector listListing;

        public List(Vector listListing) {
            this.listListing = listListing;
        }

        public String toString() {
            String s = "";
            //if nil
            if (listListing.size() == 0) {
                s += "[]";
            } else {
                s += "[";
                for (int i = 0; i < listListing.size() - 1; i++) {
                    s += listListing.elementAt(i);
                    s += ",";
                }
                s += listListing.elementAt(listListing.size() - 1);
                s += "]";
            }
            return s;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class Tuple extends BasicExpr {
        public Vector tupleListing;

        public Tuple(Vector tupleListing) {
            this.tupleListing = tupleListing;
        }

        public String toString() {
            String s = "";
            //if empty tuple
            if (tupleListing.size() == 0) {
                s += "()";
            } else if (tupleListing.size() > 1) {
                s += "(";
                for (int i = 0; i < tupleListing.size() - 1; i++) {
                    s += tupleListing.elementAt(i);
                    s += ",";
                }
                s += tupleListing.elementAt(tupleListing.size() - 1);
                s += ")";
            }
            //if only one element, it's not tuple, wrongly constructed
            else {
                s += "WRONGLY CONSTRUCTED TUPLE!!! IN PARSER!!!";
            }
            return s;
        }

        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }

    public static class ArithSeq extends BasicExpr {
        public Expr from;
        public Expr then;
        public Expr to;

        public ArithSeq(Expr from, Expr then, Expr to) {
            this.from = from;
            this.then = then;
            this.to = to;
        }

        public String toString() {
            String s = null;
            if ((then == null) && (to == null)) {
                s = "[" + from + "..]";
            }
            if ((then != null) && (to == null)) {
                s = "[" + from + "," + then + "..]";
            }
            if ((then == null) && (to != null)) {
                s = "[" + from + ".." + to + "]";
            }
            if ((then != null) && (to != null)) {
                s = "[" + from + "," + then + ".." + to + "]";
            }
            return s;
        }
        public Object accept(AstVisitor v, Object o) {
            return v.visit(this, o);
        }
    }
}
