/* **********************************************
 * Duale Hochschule Baden-Württemberg Karlsruhe
 * Prof. Dr. Jörn Eisenbiegler
 * 
 * Vorlesung Übersetzerbau
 * Praxis X Abstiegsparser
 * - Abstiegsparser
 * 
 * **********************************************
 */

package de.dhbw.compiler.xparser;

import de.dhbw.compiler.xparser.api.Parser;

public class XParser extends Parser {
	public XParser(TokenReader in) {
		super(in);
	}

	@Override
	public Tree parseProgram() {
		int oldPosition = in.getPosition();
		Tree program, id, semicolon, block, dot, eof;

		if ((program = parseToken(Token.PROGRAM)) != null
			&& ((id = parseToken(Token.ID)) != null)
			&& ((semicolon = parseToken(Token.SEMICOLON)) != null)
			&& ((block = parseBlock()) != null)
			&& ((dot = parseToken(Token.DOT)) != null)
			&& ((eof = parseToken(Token.EOF)) != null)) {
			Tree tree = new Tree(new Token(Token.APROGRAM));
			tree.addLastChild(program);
			tree.addLastChild(id);
			tree.addLastChild(semicolon);
			tree.addLastChild(block);
			tree.addLastChild(dot);
			tree.addLastChild(eof);

			return tree;
		}

		System.out.println("> Skipping Tree");
		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseBlock() {
		int oldPosition = in.getPosition();

		Tree begin, statList, end;
		if ((begin = parseToken(Token.BEGIN)) != null
			&& (statList = parseStatList()) != null
			&& (end = parseToken(Token.END)) != null) {
			Tree tree = new Tree(new Token(Token.BLOCK));
			tree.addFirstChild(begin);
			tree.addLastChild(statList);
			tree.addLastChild(end);

			return tree;
		}

		System.out.println("parseBlock> Skipping Tree");
		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseStatList() {
		Tree tree = new Tree(new Token(Token.STATLIST));
		Tree stat;

		while ((stat = parseStatWithSemicolon()) != null) {
			tree.addLastChild(stat);
		}
		return tree;
	}

	private Tree parseStatWithSemicolon() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.STATWITHSEMI));
		Tree state, semicolon;

		if (((state = parseStat()) != null)
			&& ((semicolon = parseToken(Token.SEMICOLON)) != null)) {
			tree.addLastChild(state);
			tree.addLastChild(semicolon);
			return tree;
		}

		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseStat() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.STAT));
		Tree numAssign, condStat, block;

		if ((numAssign = parseNumAssign()) != null) {
			tree.addLastChild(numAssign);
			return tree;
		}

		in.setPosition(oldPosition);
		if ((condStat = parseCondStat()) != null) {
			tree.addLastChild(condStat);
			return tree;
		}

		in.setPosition(oldPosition);
		if ((block = parseBlock()) != null) {
			tree.addLastChild(block);
			return tree;
		}

		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseCondStat() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.CONDSTAT));
		Tree ifKey, cond, then, stat1, elseKey, stat2;

		if ((ifKey = parseToken(Token.IF)) != null
			&& (cond = parseCond()) != null
			&& (then = parseToken(Token.THEN)) != null
			&& (stat1 = parseStat()) != null
			&& (elseKey = parseToken(Token.ELSE)) != null
			&& (stat2 = parseStat()) != null) {

			tree.addLastChild(ifKey);
			tree.addLastChild(cond);
			tree.addLastChild(then);
			tree.addLastChild(stat1);
			tree.addLastChild(elseKey);
			tree.addLastChild(stat2);
			return tree;
		}
		in.setPosition(oldPosition);

		if (((ifKey = parseToken(Token.IF)) != null)
			&& (cond = parseCond()) != null
			&& (then = parseToken(Token.THEN)) != null
			&& (stat1 = parseStat()) != null) {
			tree.addLastChild(ifKey);
			tree.addLastChild(cond);
			tree.addLastChild(then);
			tree.addLastChild(stat1);
			return tree;
		}

		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseCond() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.COND));
		Tree a, operator, b;

		if ((a = parseNumExpr()) != null
			&& ((operator = parseToken(Token.EQUALS)) != null
				|| (operator = parseToken(Token.LESS)) != null
				|| (operator = parseToken(Token.MORE)) != null)
			&& (b = parseNumExpr()) != null) {
			tree.addLastChild(a);
			tree.addLastChild(operator);
			tree.addLastChild(b);
			return tree;
		}

		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseNumAssign() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.ASSIGNSTAT));
		Tree id, assign, numExpr;

		if ((id = parseToken(Token.ID)) != null
			&& (assign = parseToken(Token.ASSIGN)) != null
			&& (numExpr = parseNumExpr()) != null) {
			tree.addLastChild(id);
			tree.addLastChild(assign);
			tree.addLastChild(numExpr);
			return tree;
		}

		in.setPosition(oldPosition);
		return null;
	}

	private Tree parseNumExpr() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.EXPR));
		Tree a, b, operator;

		if (((a = parseNumExpr2()) != null)
				&& (((operator = parseToken(Token.PLUS)) != null
				|| (operator = parseToken(Token.MINUS)) != null))
				&& (b = parseNumExpr()) != null) {
			tree.addLastChild(a);
			tree.addLastChild(operator);
			tree.addLastChild(b);
			return tree;
		}
		in.setPosition(oldPosition);

		if ((a = parseNumExpr2()) != null) {
			tree.addLastChild(a);
			return tree;
		}
		in.setPosition(oldPosition);

		return null;
	}

	private Tree parseNumExpr2() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.EXPR2));
		Tree a, b, operator;

		if (((a = parseNumExpr3()) != null)
			&& (((operator = parseToken(Token.MULT)) != null
				  || (operator = parseToken(Token.DIV)) != null))
			&& (b = parseNumExpr2()) != null) {
			tree.addLastChild(a);
			tree.addLastChild(operator);
			tree.addLastChild(b);
			return tree;
		}
		in.setPosition(oldPosition);

		if ((a = parseNumExpr3()) != null) {
			tree.addLastChild(a);
			return tree;
		}
		in.setPosition(oldPosition);

		return null;
	}

	private Tree parseNumExpr3() {
		int oldPosition = in.getPosition();
		Tree tree = new Tree(new Token(Token.EXPR3));
		Tree sign, intConst, id, num, lbr, rbr;

		if ((intConst = parseToken(Token.INTCONST)) != null) {
			tree.addLastChild(intConst);
			return tree;
		}
		in.setPosition(oldPosition);

		if (((sign = parseToken(Token.MINUS)) != null
			&& ((intConst = parseToken(Token.INTCONST))) != null)) {
			tree.addLastChild(sign);
			tree.addLastChild(intConst);
			return tree;
		}
		in.setPosition(oldPosition);

		if ((id = parseToken(Token.ID)) != null) {
			tree.addLastChild(id);
			return tree;
		}

		if (((lbr = parseToken(Token.LBR)) != null) && ((num = parseNumExpr()) != null)
				&& ((rbr = parseToken(Token.RBR)) != null)) {

			tree.addLastChild(lbr);
			tree.addLastChild(num);
			tree.addLastChild(rbr);
			return tree;
		}

		in.setPosition(oldPosition);
		return null;
	}
}
