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

# Compiling for a four-register machine
#
# This is probably a good model for an x86 compiler, since
# there are so few registers available.

import parser
import pprint
import string

TRUE  = 1
FALSE = 0

class compiler:

	parser = parser.make_grammar()

	error = 'Compilation Error'

	# ==================================================

	def compile_exp (self, tail_pos, exp, symbol_table, next_code):
		i = exp[0]

		if i == 'lit':
			[ignore, datum] = exp
			return self.gen_lit (datum, next_code)

		elif i == 'varref':
			[ignore, var] = exp
			addr = self.lexical_address (symbol_table, var)
			if type(addr) == type(''):
				return self.gen_dynref (
					addr,
					next_code
					)
			else:
				return self.gen_varref (
					addr,
					next_code
					)

		elif i == 'assign':
			[ignore, var, value_exp] = exp
			addr = self.lexical_address (symbol_table, var)

			if type(addr) == type(''):
				assign_code = self.gen_dynassign (addr, next_code)
			else:
				assign_code = self.gen_assign (addr, next_code)

			return self.compile_exp (
				FALSE,
				value_exp,
				symbol_table,
				assign_code
				)

		elif i == 'define':
			[ignore, var, value_exp] = exp
			return self.compile_exp (
				FALSE,
				value_exp,
				symbol_table,
				self.gen_define (
					var,
					next_code
					)
				)

		elif i == 'conditional':
			[ignore, test_exp, then_exp, else_exp] = exp
			return self.compile_exp (
				FALSE, test_exp, symbol_table, self.gen_test (
					self.compile_exp (
						tail_pos, then_exp, symbol_table, next_code
						),
					self.compile_exp (
						tail_pos, else_exp, symbol_table, next_code
						),
					)
				)
				
		elif i == 'proc':
			[ignore, formals, body] = exp
			return self.gen_proc (
				len(formals),
				self.compile_exp (
					TRUE,
					body,
					[formals] + symbol_table,
					self.restore_code
					),
				next_code
				)

		elif i == 'app':
			[ignore, rator, rands] = exp
			app_code = self.compile_rands (
				rands,
				symbol_table,
				self.compile_exp (FALSE, rator, symbol_table, self.invoke_code)
				)
			if tail_pos:
				return app_code
			else:
				return self.gen_save (app_code, next_code)

		elif i == 'primop':
			[ignore, primop, sub1, sub2] = exp
			return self.compile_acc2 (
				sub2,
				symbol_table,
				self.compile_exp (
					FALSE,
					sub1,
					symbol_table,
					self.gen_primop (
						primop,
						next_code
						)
					)
				)

		elif i == 'sequence':
			[ignore, sequence] = exp
			if len(sequence) > 0:
				return self.compile_sequence (
					sequence,
					tail_pos,
					symbol_table,
					next_code
					)
			else:
				# empty sequence, do nothing
				return next_code

		else:
			raise self.error, "Invalid abstract syntax: %s" % exp
								
	def compile_rands (self, rands, symbol_table, next_code):
		if rands == []:
			return next_code
		else:
			return self.compile_exp (
				FALSE,
				rands[-1],
				symbol_table,
				self.gen_push (
					self.compile_rands (
						rands[:-1],
						symbol_table,
						next_code
						)
					)
				)
						
	def compile_acc2 (self, exp2, symbol_table, next_code):
		return self.compile_exp (
			FALSE,
			exp2,
			symbol_table,
			self.gen_acc2 (next_code)
			)

	def compile_sequence (self, sequence, tail_pos, symbol_table, next_code):
		if sequence == []:
			return next_code
		else:
			return self.compile_exp (
				(len(sequence) == 1) and tail_pos,
				sequence[0],
				symbol_table,
				self.compile_sequence (
					sequence[1:],
					tail_pos,
					symbol_table,
					next_code
					)
				)


	# ==================================================

	def lexical_address (self, symbol_table, name):
		"compute the lexical address of a name, given a symbol table"
		depth = 0
		for vars in symbol_table:
			i = 0
			for var in vars:
				if name == var:
					return (depth, i)
				i = i + 1
			depth = depth + 1
		return name

import sys
import machine
import transform

class code_compiler (compiler):

	machine = machine.machine()
	transformer = transform.transformer()
	
	def compile (self, exp, symbol_table=None):
		if symbol_table is None:
			symbol_table = []

		exp = self.parser.DoParse1 (exp)
		exp = self.transformer.expand_expression (exp)
		pprint.pprint (exp)
		return self.compile_exp (
			TRUE,
			exp,
			symbol_table,
			[['restore']]
			)

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

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

			code = self.compile (exp)
			self.print_code (code)
			print self.machine.eval_code (code)

	def print_code (self, code, depth=1):
		front = depth * '>'
		for insn in code:
			if insn[0] == 'proc':
				[ignore, nargs, body] = insn
				print '%s %s %d' % (front, 'proc', nargs)
				self.print_code (body, depth+1)
			elif insn[0] == 'test':
				print '%s %s' % (front, 'test')
				print '%s -- then --' % front
				self.print_code (insn[1], depth)
				print '%s -- else --' % front
				self.print_code (insn[2], depth)
			elif insn[0] == 'save':
				print '%s %s' % (front, 'save')
				print '%s -- code1 --' % front
				self.print_code (insn[1], depth)
				print '%s -- code2 --' % front
				self.print_code (insn[2], depth)				
			else:
				print front,
				for part in insn:
					print '%s' % str(part),
				print

	def gen_lit (self, datum, code):
		return [['lit', datum]] + code

	def gen_varref (self, lex_addr, code):
		return [['varref', lex_addr]] + code

	def gen_dynref (self, name, code):
		return [['dynref', name]] + code

	def gen_assign (self, lex_addr, code):
		return [['assign', lex_addr]] + code

	def gen_dynassign (self, name, code):
		return [['dynassign', name]] + code

	def gen_define (self, var, code):
		return [['define', var]] + code

	def gen_test (self, then_code, else_code):
		return [['test', then_code, else_code]]

	def gen_proc (self, nargs, body_code, code):
		return [['proc', nargs, body_code]] + code

	def gen_save (self, code1, code2):
		return [['save', code1, code2]]

	def gen_push (self, code):
		return [['push']] +  code

	def gen_acc2 (self, code):
		return [['move_acc2']] + code

	def gen_primop (self, primop, code):
		return [['primop', primop]] + code

	# simple instruction sequences used as continuations
	invoke_code = [['invoke']]
	restore_code = [['restore']]

if __name__ == '__main__':
	code_compiler().run()

# define fact = proc (x) letrecproc f (n, acc) = if n == 1 then acc else f (n - 1, n * acc) in f (x, 1)

# fun with first-class continuations:

# define count = proc (n) callcc (proc (escape) letrecproc zord (z) = begin print (z) ; if z == 1 then escape(99) else z + zord (z - 1) end in zord (n) )
#
# define escape = 0
# define savecont = proc (x) escape := x
# define p = proc (x) begin print (x); if x == 10 then callcc (savecont) else 0; if x == 0 then 1 else p (x - 1) end
# p (30)
# escape (0)
# letrecproc tak (x, y, z) = if x > y then tak (tak (x - 1, y, z), tak (y - 1, z, x), tak (z - 1, x, y)) else z in tak (18, 12, 6)
