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

import string

class machine:

	invalid_instruction_error = "Invalid Instruction"

	def __init__ (self):
		# there can be only one!
		self.symbol_table = {
			'callcc' : (
				[['pushcc'],
				 ['varref', (0, 0)],
				 ['invoke']],
				[]),

			'print' : (
				[['print'],
				 ['restore']],
				[]),
			}

	primops = {
		'+'  : lambda a,b: a + b,
		'-'  : lambda a,b: a - b,
		'*'  : lambda a,b: a * b,
		'/'  : lambda a,b: a / b,
		'>'  : lambda a,b: a > b,
		'==' : lambda a,b: a == b,
		'!<' : lambda a,b: a >= b,
		}

	def eval_code (self, code):
		acc = '*'
		acc2 = '*'
		val = []
		lex_env = []

		import pprint
		pprint.pprint (code)

		# (code, pc), val, lex_env, k
		k = ([['halt']], -1), [], [], None

		pc = 0
		
		while 1:
			insn = code[pc]
			opcode = insn[0]

			print opcode
			
			if opcode == 'lit':
				acc = insn[1]
				
			elif opcode == 'varref':
				depth, index = insn[1]
				acc = lex_env[depth][index]

			elif opcode == 'assign':
				depth, index = insn[1]
				lex_env[depth][index] = acc

			elif opcode == 'dynassign':
				[ignore, symbol] = insn
				self.symbol_table[symbol] = acc

			elif opcode == 'define':
				[ignore, symbol] = insn
				self.symbol_table[symbol] = acc

			elif opcode == 'dynref':
				[ignore, symbol] = insn
				acc = self.symbol_table[symbol]

			elif opcode == 'test':
				[ignore, then_code, else_code] = insn
				if acc:
					code, pc = then_code, -1
				else:
					code, pc = else_code, -1

			elif opcode == 'proc':
				[ignore, nargs, body] = insn
				acc = body, lex_env

			elif opcode == 'restore':
				(code, pc), val, lex_env, k = k
				
			elif opcode == 'invoke':
				(code, lex_env), pc = acc, -1
				lex_env = [val] + lex_env
				val = []

			elif opcode == 'save':
				[ignore, code1, code2] = insn
				k = (code2, -1), val, lex_env, k
				code, pc = code1, -1
				
			elif opcode == 'push':
				val = [acc] + val

			elif opcode == 'halt':
				return acc

			elif opcode == 'move_acc2':
				acc2 = acc

			elif opcode == 'primop':
				[ignore, primop] = insn
				acc = self.primops[primop](acc, acc2)

			elif opcode == 'pushcc':
				val = [(
					[['escape'],
					 ['varref', (1,0)],
					 ['loadcc']],
					[[k]]
					)]

			elif opcode == 'loadcc':
				print 'loading continuation from acc', acc
				temp = lex_env[0][0]
				(code, pc), val, lex_env, k = acc
				acc = temp

			elif opcode == 'escape':
				print 'ESCAPE!!!'

			elif opcode == 'print':
				print '==>', lex_env[0]

			else:
				raise self.invalid_instruction_error, insn

			pc = pc + 1
