"""Symbol tables and environments for P65.

	Implements the symbol lookup, through nested environments -
	any non-temporary variable is stored at the top level."""

# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.

from __future__ import nested_scopes
import Ophis.Errors as Err

class Environment:
	"""Environment class.
	Controls the various scopes and global abstract execution variables."""
	def __init__(self):
		self.dicts = [{}]
		self.stack = [0]
		self.pc = 0
		self.segmentdict = {}
		self.segment = "*text-default*"
		self.scopecount = 0
	def __contains__(self, item):
		if item[0] == '_':
			for dict in [self.dicts[i] for i in self.stack]:
				if item in dict: return 1
			return 0
		return item in self.dicts[0]
	def __getitem__(self, item):
		if item[0] == '_':
			for dict in [self.dicts[i] for i in self.stack]:
				if item in dict: return dict[item]
		else:
			if item in self.dicts[0]: return self.dicts[0][item]
		Err.log("Unknown label '%s'" % item)
		return 0
	def __setitem__(self, item, value):
		if item[0] == '_':
			self.dicts[self.stack[0]][item] = value
		else:
			self.dicts[0][item] = value
	def __str__(self):
		return str(self.dicts)
	def getPC(self):
		return self.pc
	def setPC(self, value):
		self.pc = value
	def incPC(self, amount):
		self.pc += amount
	def getsegment(self):
		return self.segment
	def setsegment(self, segment):
		self.segmentdict[self.segment] = self.pc
		self.segment = segment
		self.pc = self.segmentdict.get(segment, 0)
	def reset(self):
		"Clears out program counter, segment, and scoping information"
		self.pc = 0
		self.segmentdict = {}
		self.segment = "*text-default*"
		self.scopecount = 0
		if len(self.stack) > 1:
			Err.log("Unmatched .scope")
		self.stack = [0]
	def newscope(self):
		"Enters a new scope for temporary labels."
		self.scopecount += 1
		self.stack.insert(0, self.scopecount)
		if len(self.dicts) <= self.scopecount: self.dicts.append({})
	def endscope(self):
		"Leaves a scope."
		if len(self.stack) == 1:
			Err.log("Unmatched .scend")
		self.stack.pop(0)

