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

# the transformer from section 9.4, with some renaming, etc...

class symbol_generator:
	def __init__ (self):
		self.count = 0

	def next (self):
		r = '_g%d' % self.count
		self.count = self.count + 1
		return r

# These transformations could be added directly to the parser, but I
# prefer this: everything's modular: I can make another transformer
# (for say, CPS) and plug it into the output of this one.

class transformer:

	# ========================================
	# Program Transformation
	# ========================================

	# these are pretty straightforward translations of
	# the expansions in section 7.3 of R^4RS.

	# The primitive expression types are:
	#
	#   literal, variable, call, lambda, if, set!
	#
	# everything else can be constructed from these
	# [and call/cc is a primitive procedure!]

	gensym = symbol_generator()

	# walk an expression, recursively transforming derived
	# expressions into primitive ones...

	def expand_expression (self, exp):
		if exp[0] in ['lit', 'varref']:
			return exp
		elif exp[0] in ['assign', 'define']:
			return [exp[0], exp[1], self.expand_expression (exp[2])]
		elif exp[0] == 'app':
			# ['app' <operator_exp> [<operand_exp> ... ]]
			return ['app', self.expand_expression (exp[1]),
					map (
						self.expand_expression,
						exp[2]
						)
					]
		elif exp[0] == 'conditional':
			# ['conditional' <test_exp> <then_exp> <else_exp>]
			return ['conditional'] + map (
				self.expand_expression,
				exp[1:]
				)
		elif exp[0] == 'proc':
			# ['proc' <vars> <exp>]
			return ['proc', exp[1], self.expand_expression (exp[2])]
		elif exp[0] == 'primop':
			[ignore, rator, rand1, rand2] = exp
			return [
				'primop',
				rator,
				self.expand_expression (rand1),
				self.expand_expression (rand2),
				]
		elif exp[0] == 'let':
			return self.expand_let (exp)
		elif exp[0] == 'letrecproc':
			return self.expand_letrecproc (exp)
		elif exp[0] == 'letcont':
			return self.expand_letcont (exp)
		elif exp[0] == 'sequence':
			return self.expand_sequence (exp)
		elif exp[0] == 'while':
			return self.expand_while (exp)
		elif exp[0] == 'list':
			return ['app', ['varref', '__list__'], map (self.expand_expression, exp[1])]
		elif exp[0] == 'array_ref':
			return ['app',
					['varref', '__array_ref__'],
					[self.expand_expression (exp[1]),
					 self.expand_expression (exp[2])
					 ]
					]
		else:
			raise ValueError, "Unknown Expression Type: %s" % exp

	def expand_let (self, exp):
		# ['let ' [ ['decl' <var_1> <init_1>] ...] <body> ]
		# =>
		#  ['app' ['proc', ['var_1' ... 'var_n'], <body> ] [ <init_1> ... <init_n> ]]
		#
		# <init_1..n> and <body> are sub-expressions and must be expanded.
		#
		symbols = map (lambda x: x[1], exp[1])
		inits   = map (self.expand_expression, map (lambda x: x[2], exp[1]))
		body    = self.expand_expression (exp[2])
		return ['app', ['proc', symbols, body], inits]

	def expand_letcont (self, exp):
		# letcont <var> in <body>
		# =>
		#  callcc (proc (<var>) <body>)
		return ['app',
				['varref', 'callcc'],
				[['proc',
				 [exp[1]],
				 self.expand_expression (exp[3])
				 ]]
				]

	# handled directly by the compiler
	def expand_sequence (self, exp):
		return [
			'sequence', map (
				self.expand_expression, 
				exp[1]
				)
			]

	#def expand_sequence (self, exp):
	#	# ['sequence' [<exp_1> ... <exp_n>]]
	#	# =>
	#	# ['app'
	#	#    ['proc' ['ignore' 'thunk'] ['app' ['varref' 'thunk'] [] ] ]
	#	#    <exp_1>
	#	#    ['proc' [] ['sequence' [<exp_2> ... <exp_n>]]]]
	#	# 
	#	if len(exp[1]) == 1:
	#		return self.expand_expression (exp[1][0])
	#	else:
	#		return self.expand_expression (
	#			['app',
	#			 ['proc', ['ignore', 'thunk'], ['app', ['varref', 'thunk'], [] ]],
	#			 [exp[1][0],  # <exp_1>
	#			  ['proc', [], ['sequence', exp[1][1:]]] # <exp_2> ... <exp_n>
	#			  ]
	#			 ]
	#			)

	def expand_while (self, exp):
		# while <test_exp> do <body_exp>
		# =>
		# letrecproc
		#   <gensym> = proc()
		#     if <test_exp>
		#       then
		#         begin
		#           <body_exp>
		#           <gensym>()
		#         end
		#       else
		#         0
		#   in <gensym>()
		#
		gen_proc_name = self.gensym.next()
		return self.expand_letrecproc (
			['letrecproc',
			 [['procdecl', gen_proc_name, [],
			   ['conditional',			# if
				exp[1],					# <text_exp>
				['sequence',			# begin
				 [exp[3],				# <body_exp>
				  ['app', ['varref', gen_proc_name], []]] # <gensym>()
				 ],
				['lit', 0]
				]
			   ]
			  ],
			 ['app', ['varref', gen_proc_name], []]] # <gensym>()
			)
			 

	def expand_letrecproc (self, exp):
		# letrecproc
		#    p1 (f1) = b1;
		#    ...
		#    pn (fn) = bn;
		# =>
		# let
		#   p1 = None
		#   ...
		#   pn = None
		#   in
		#     begin
		#       p1 := proc (f1) b1;
		#       ...
		#       pn := proc (fn) bn
		#       <body>
		#     end

		symbols = map (lambda x: x[1], exp[1])
		formals = map (lambda x: x[2], exp[1])
		bodies  = map (lambda x: x[3], exp[1])
		body = exp[2]
		
		empty_decls = map (lambda s: ['decl', s, ['lit', None]], symbols)
		proc_assigns = map (
			lambda s,f,b: ['assign', s, ['proc', f, b]],
			symbols, formals, bodies
			)
		return self.expand_expression (
			['let', empty_decls,
			 ['sequence', proc_assigns + [body]]]
			)
