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

import grammar
import sys

# at some point we should implement ribcage environments

class environment:
	def __init__ (self, init=None):
		self.bindings = {}
		if init:
			for k,v in init.items():
				self.bindings[k] = v

	def __getitem__ (self, key):
		return self.bindings[key]

	def __setitem__ (self, key, value):
		self.bindings[key] = value

	def items (self):
		return self.bindings.items()

	def has_key (self, key):
		return self.bindings.has_key (key)

class interpreter:

	grammar = grammar.make_grammar()

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

	def eval (self, exp, env):
		try:
			exp = self.grammar.DoParse1 (exp)
			try:
				return self.eval_exp (exp, env)
			except:
				import tb
				tb.printtb (sys.exc_traceback)
				print sys.exc_type, sys.exc_value
		except:
			print 'Parse Error'

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

			try:
				exp = raw_input()
			except EOFError:
				print
				break

			if exp:
				print self.eval (exp, env)

	repl = read_eval_print

	def eval_exp (self, exp, env):
		if exp[0] == 'lit':
			return exp[1]
		elif exp[0] == 'varref':
			if env.has_key (exp[1]):
				return env[exp[1]]
			else:
				raise ValueError, "Unbound variable"
		elif exp[0] == 'app':
			proc = self.eval_exp (exp[1], env)
			args = self.eval_rands (exp[2], env)
			return self.apply_proc (proc, args)
		elif exp[0] == 'conditional':
			test = self.eval_exp (exp[1], env)
			if test != 0:
				return self.eval_exp (exp[2], env)
			else:
				return self.eval_exp (exp[3], env)
		elif exp[0] == 'let':
			# extend our environment with the new bindings
			new_env = environment(env)
			for [ignore,var,decl] in exp[1]:
				new_env[var] = self.eval_exp (decl, env)
			# evaluate the expression in the new environment
			return self.eval_exp (exp[2], new_env)
		else:
			raise SyntaxError, "Invalid Abstract Syntax"

	def eval_rands (self, rands, env):
		return map (
			lambda rand,e=env,s=self: s.eval_exp(rand,e),
			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]
		elif prim_op == 'equal':
			return args[0] == args[1]
		elif prim_op == 'greater':
			return args[0] > args[1]
		elif prim_op == 'lesser':
			return args[0] < args[1]
		elif prim_op == 'zero':
			return args[0] == 0
		else:
			raise "Invalid primitive operator"

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