//TypeCheckerLL
import gi.*;
/*\semantics*/import java.util.*;
/*\off*/
class TypeCheckerLL extends LL1_Grammar {

	TypeCheckerLL() throws Exception {
		put("LITERAL", expression("[[:digit:]]+|true|false"));
		put("IDENTIFIER", expression("[[:alpha:]][[:alnum:]]*"));
		put("RELOP", expression("<|=|>|>=|<>|<="));
		put("ADDOP", expression("\\+|-|or"));
		put("MULOP", expression("\\*|div|mod|and"));
		put("SPACE", expression("[[:space:]]+"));
		put("COMMENT", expression("\\{[^}]*\\}"));

		// /*\semantics*/semantic specification/*\off*/
//TypeCheckerLL.Program
/*\semantics*/		final Map<Object,String> symbols = new HashMap<Object,String>();

		Semantics declareProgram = new Semantics() {
			public void f(ParseTree t, int l) {
				symbols.put(t.child[l-1].value, "program");
			}
		};
/*\off*/		put("Program", new Object[][]{
			{"program", "IDENTIFIER", /*\semantics*/declareProgram, /*\off*/";",
				"Decls", "CompoundStmt", "."}
		});
//TypeCheckerLL.Decls
		put("Decls", new Object[][]{
			{},
			{"var", "DeclList"},
		});
		put("DeclList", new Object[][]{
			{"Decl", "DeclList'"}
		});
		put("DeclList'", new Object[][]{
			{},
			{"Decl", "DeclList'"},
		});
//TypeCheckerLL.Decl
/*\semantics*/		Semantics identity = new Semantics() {
			public void f(ParseTree t, int l) {
				t.value = t.child[l-1].value;
			}
		};
		Semantics declareVariable= new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String identifier = (String)t.child[l-2].value;
				String type = (String)t.child[l-1].value;
				String existing = symbols.put(identifier, type);

				if (existing != null) throw new Exception(
					identifier + " already declared of type " + existing);

				t.value = type;
			}
		};
/*\off*/		put("Decl", new Object[][]{
			{"IDENTIFIER", "IdList"/*\semantics*/, declareVariable/*\off*/}
		});
		put("IdList", new Object[][]{
			{":", "Type", /*\semantics*/identity, /*\off*/";"},
			{",", "IDENTIFIER", "IdList"/*\semantics*/, declareVariable/*\off*/},
		});
		put("Type", new Object[][]{
			{"integer"/*\semantics*/, identity/*\off*/},
			{"boolean"/*\semantics*/, identity/*\off*/},
		});
//TypeCheckerLL.CompountStmt
		put("CompoundStmt", new Object[][]{
			{"begin", "StmtList", "end"}
		});
		put("StmtList", new Object[][]{
			{"Stmt", "StmtList'"}
		});
		put("StmtList'", new Object[][]{
			{},
			{";", "Stmt", "StmtList'"},
		});
//TypeCheckerLL.Stmt
/*\semantics*/		Semantics checkAssignment = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String variable = symbols.get(t.child[l-3].value);
				String expression = (String)t.child[l-1].value;

				if (variable == null) throw new Exception(
					"variable undeclared");
				if (!expression.equals(variable)) throw new Exception(
					"assignment of incompatible types");
			}
		};
		Semantics checkCondition = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String type = (String)t.child[l-1].value;

				if (!type.equals("boolean")) throw new Exception(
					"condition expression must be boolean");
			}
		};
/*\off*/		put("Stmt", new Object[][]{
			{"Variable", ":=", "E"/*\semantics*/, checkAssignment/*\off*/},
			{"if", "E", /*\semantics*/checkCondition, /*\off*/"then", "Stmt", "Stmt'"},
			{"while", "E", /*\semantics*/checkCondition, /*\off*/"do", "Stmt"},
			{"CompoundStmt"},
		});
		put("Stmt'", new Object[][]{
			{},
			{"else", "Stmt"},
		});
		put("Variable", new Object[][]{
			{"IDENTIFIER"/*\semantics*/, identity/*\off*/}
		});
//TypeCheckerLL.E
/*\semantics*/		Semantics inherit = new Semantics() {
			public void f(ParseTree t, int l) {
				t.child[l+1].value = t.child[l-1].value;
			}
		};
		Semantics checkRelop = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String left = (String)t.value;
				String right = (String)t.child[l-1].value;

				if (!left.equals("integer") || !right.equals("integer"))
					throw new Exception(
						"relational operands must be integer");

				t.value = "boolean";
			}
		};
/*\off*/		put("E", new Object[][]{
			{"A", /*\semantics*/inherit, /*\off*/"E'"/*\semantics*/, identity/*\off*/}
		});
		put("E'", new Object[][]{
			{},
			{"RELOP", "A"/*\semantics*/, checkRelop/*\off*/},
		});
//TypeCheckerLL.A
/*\semantics*/		Semantics checkSign = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String sign = (String)t.child[l-2].value;
				String type = (String)t.child[l-1].value;

				if (sign.equals("or")) throw new Exception(
					"or is an invalid unary operator");
				if (!type.equals("integer")) throw new Exception(
					"sign operand must be integer");

				t.child[l+1].value = type;
			}
		};
		Semantics checkAddop = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String left = (String)t.value;
				String addop = (String)t.child[l-2].value;
				String right = (String)t.child[l-1].value;

				if (addop.equals("or")) {
					if (!left.equals("boolean") || !right.equals("boolean"))
						throw new Exception(
							"or operands must be boolean");
				}
				else {
					if (!left.equals("integer") || !right.equals("integer"))
						throw new Exception(
							"arithmetic operands must be integer");
				}
				t.child[l+1].value = left;
			}
		};
/*\off*/		put("A", new Object[][]{
			{"T", /*\semantics*/inherit, /*\off*/"A'"/*\semantics*/, identity/*\off*/},
			{"Sign", "T", /*\semantics*/checkSign, /*\off*/"A'"/*\semantics*/, identity/*\off*/},
		});
		put("A'", new Object[][]{
			{},
			{"ADDOP", "T", /*\semantics*/checkAddop, /*\off*/"A'"/*\semantics*/, identity/*\off*/},
		});
		put("Sign", new Object[][]{
			{"ADDOP"/*\semantics*/, identity/*\off*/}
		});
//TypeCheckerLL.T
/*\semantics*/		Semantics checkMulop = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String left = (String)t.value;
				String mulop = (String)t.child[l-2].value;
				String right = (String)t.child[l-1].value;

				if (mulop.equals("and")) {
					if (!left.equals("boolean") || !right.equals("boolean"))
						throw new Exception(
							"and operands must be boolean");
				}
				else {
					if (!left.equals("integer") || !right.equals("integer"))
						throw new Exception(
							"arithmetic operands must be integer");
				}
				t.child[l+1].value = left;
			}
		};
/*\off*/		put("T", new Object[][]{
			{"F", /*\semantics*/inherit, /*\off*/"T'"/*\semantics*/, identity/*\off*/}
		});
		put("T'", new Object[][]{
			{},
			{"MULOP", "F", /*\semantics*/checkMulop, /*\off*/"T'"/*\semantics*/, identity/*\off*/},
		});
//TypeCheckerLL.F
/*\semantics*/		Semantics literalType = new Semantics() {
			public void f(ParseTree t, int l) {
				String literal = (String)t.child[l-1].value;
				t.value = (literal.equals("true") || literal.equals("false"))
					? "boolean"
					: "integer";
			}
		};
		Semantics checkIdentifier = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				t.value = symbols.get(t.child[l-1].value);

				if (t.value == null) throw new Exception(
					"identifier undeclared");
			}
		};
		Semantics checkNot = new Semantics() {
			public void f(ParseTree t, int l) throws Exception {
				String type = (String)t.child[l-1].value;

				if (!type.equals("boolean")) throw new Exception(
					"not operand must be boolean");

				t.value = "boolean";
			}
		};
/*\off*/		put("F", new Object[][]{
			{"LITERAL"/*\semantics*/, literalType/*\off*/},
			{"IDENTIFIER"/*\semantics*/, checkIdentifier/*\off*/},
			{"(", "E", /*\semantics*/identity, /*\off*/")"},
			{"not", "F"/*\semantics*/, checkNot/*\off*/},
		});
//TypeCheckerLL

		debug = PARSE_TREE;
	}

	public static void main(String[] arguments) throws Exception {
		new TypeCheckerLL().interpret(arguments);
	}
}
