# -*- Mode: Python; tab-width: 4 -*-

import grammar
import sys

class interpreter:
	env = {}

	grammar = grammar.make_grammar()

	def __init__ (self):
		# initialize the environment with our primitives
		for sym in ['+', '-', '*', 'add1', 'sub1', 'minus']:
			self.env[sym] = ['prim-op', sym]

	def eval (self, exp):
		try:
			exp = self.grammar.DoParse1 (exp)
			try:
				return self.eval_exp (exp)
			except:
				print 'Evaluation Error'
		except:
			print 'Parse Error'

	def read_eval_print (self):
		while 1:
			sys.stdout.write ('--> ')
			sys.stdout.flush()

			try:
				exp = raw_input()
			except EOFError:
				break

			if exp:
				print self.eval (exp)

	repl = read_eval_print

	def eval_exp (self, exp):
		if exp[0] == 'lit':
			return exp[1]
		elif exp[0] == 'varref':
			if self.env.has_key (exp[1]):
				return self.env[exp[1]]
			else:
				raise ValueError, "Unbound variable"
		elif exp[0] == 'app':
			proc = self.eval_exp (exp[1])
			args = self.eval_rands (exp[2])
			return self.apply_proc (proc, args)
		else:
			raise SyntaxError, "Invalid Abstract Syntax"

	def eval_rands (self, rands):
		return map (self.eval_exp, rands)

	def apply_proc (self, proc, args):
		if proc[0] == 'prim-op':
			return self.apply_prim_op (proc[1], args)
		else:
			raise ValueError, "Invalid Procedure"

	def apply_prim_op (self, prim_op, args):
		if prim_op == '+':
			return args[0] + args[1]
		elif prim_op == '-':
			return args[0] - args[1]
		elif prim_op == '*':
			return args[0] * args[1]
		elif prim_op == 'add1':
			return args[0] + 1
		elif prim_op == 'sub1':
			return args[0] - 1
		elif prim_op == 'minus':
			return - args[0]
		else:
			raise "Invalid primitive operator"

if __name__ == '__main__':
	i = interpreter()
	i.repl()
