'''
	Created on 2013-1-25

	@author: desperedo

	miniPascal Compiler Parser
'''

class Type(object):
	def __init__(self):
		self.Type = None;
		self.Array = False;
		self.ArrayLow = None;
		self.ArrayHigh = None;

	def Parse(self, Scanner):
		if Scanner.TokenExpected('Type'):
			self.Type = Scanner.NextToken()[1];
		elif Scanner.TokenExpected('Array'):
			self.Array = True;
			Scanner.NextToken();
			if not Scanner.TokenNeeded('['):
				raise SyntaxError('"[" expected');
			self.ArrayLow = Expression().Parse(Scanner);
			if not Scanner.TokenNeeded('..'):
				raise SyntaxError('".." expected');
			self.ArrayHigh = Expression().Parse(Scanner);
			if not Scanner.TokenNeeded(']'):
				raise SyntaxError('"]" expected');
			if not Scanner.TokenNeeded('Of'):
				raise SyntaxError('"Of" expected');
			self.Type = Type().Parse(Scanner);
		else:
			Token = Scanner.NextToken();
			raise SyntaxError('Type-name expected but "%s".' % (Token[1] or Token[0]));
		return self;

class Constant(object):
	def __init__(self):
		self.Name = '';
		self.Value = None;
		self.Type = Type();

	def Parse(self, Scanner):
		self.Name = Scanner.NextToken()[1];
		if Scanner.TokenExpected(':'):
			Scanner.NextToken();
			self.Type.Parse(Scanner);
		if not Scanner.TokenNeeded('='):
			raise SyntaxError('"=" expected.');
		if self.Type.Type == None:
			self.Type.Type, self.Value = Scanner.NextToken();
		else:
			if self.Type.Array:
				raise SyntaxError('Arrays cannot be as constant.');
			Type, self.Value = Scanner.NextToken();
			if Type.lower() != self.Type.Type.lower():
				raise SyntaxError('Constant type inconsistent.');
		if not Scanner.TokenNeeded(';'):
			raise SyntaxError('";" expected.');
		return self;

class Variable(object):
	def __init__(self):
		self.Names = [];
		self.Array = False;
		self.Type = Type();
		self.ArrayLow = Expression();
		self.ArrayHigh = Expression();

	def Parse(self, Scanner, Parameter = False):
		if Parameter:
			self.Variable = False;
			if Scanner.TokenExpected('Var'):
				self.Variable = True;
				Scanner.NextToken();
		while Scanner.TokenExpected('Identifier'):
			self.Names.append(Scanner.NextToken()[1]);
			if not Scanner.TokenExpected(','):
				if not Scanner.TokenNeeded(':'):
					raise SyntaxError('":" expected.');
				break;
			Scanner.NextToken();
		self.Type.Parse(Scanner);
		if not Parameter and not Scanner.TokenNeeded(';'):
			raise SyntaxError('";" expected.');
		if Parameter and self.Type.Array and self.Variable:
			raise SyntaxError('Arrays cannot be as variable parameters.');
		return self;

class Function(object):
	def __init__(self):
		self.Name = '';
		self.Forward = False;
		self.Parameters = [];
		self.ReturnType = Type();
		self.Statements = Statements();
		self.Declarations = Declarations();

	def Parse(self, Scanner):
		Scanner.NextToken();
		Type, self.Name = Scanner.NextToken();
		if Type != 'Identifier':
			raise SyntaxError('Identifier expected.');
		if Scanner.TokenExpected('('):
			Scanner.NextToken();
			if Scanner.TokensExpected(['Var', 'Identifier']):
				self.Parameters.append(Variable().Parse(Scanner, True));
				while Scanner.TokenExpected(';'):
					Scanner.NextToken();
					self.Parameters.append(Variable().Parse(Scanner, True));
			if not Scanner.TokenNeeded(')'):
				raise SyntaxError('")" expected.');
		if not Scanner.TokenNeeded(':'):
			raise SyntaxError('":" expected.');
		self.ReturnType.Parse(Scanner);
		if self.ReturnType.Array:
			raise SyntaxError('Arrays cannot be as return type.');
		if not Scanner.TokenNeeded(';'):
			raise SyntaxError('";" expected.');
		if Scanner.TokenExpected('Forward'):
			self.Forward = True;
			Scanner.NextToken();
		else:
			self.Declarations.Parse(Scanner);
			self.Statements.Parse(Scanner);
		if not Scanner.TokenNeeded(';'):
			raise SyntaxError('";" expected.');
		return self;

class Procedure(object):
	def __init__(self):
		self.Name = '';
		self.Forward = False;
		self.Parameters = [];
		self.Statements = Statements();
		self.Declarations = Declarations();

	def Parse(self, Scanner):
		Scanner.NextToken();
		Type, self.Name = Scanner.NextToken();
		if Type != 'Identifier':
			raise SyntaxError('Identifier expected.');
		if Scanner.TokenExpected('('):
			Scanner.NextToken();
			if Scanner.TokensExpected(['Var', 'Identifier']):
				self.Parameters.append(Variable().Parse(Scanner, True));
				while Scanner.TokenExpected(';'):
					Scanner.NextToken();
					self.Parameters.append(Variable().Parse(Scanner, True));
			if not Scanner.TokenNeeded(')'):
				raise SyntaxError('")" expected.');
		if not Scanner.TokenNeeded(';'):
			raise SyntaxError('";" expected.');
		if Scanner.TokenExpected('Forward'):
			self.Forward = True;
			Scanner.NextToken();
		else:
			self.Declarations.Parse(Scanner);
			self.Statements.Parse(Scanner);
		if not Scanner.TokenNeeded(';'):
			raise SyntaxError('";" expected.');
		return self;

class Factor(object):
	def __init__(self):
		self.Type = None;
		self.Value = None;
		self.Prefix = None;

	def Parse(self, Scanner):
		Type, Value = Scanner.PeekToken();
		if Type == '(':
			Scanner.NextToken();
			self.Type = 'Expression';
			self.Value = Expression().Parse(Scanner);
			if not Scanner.TokenNeeded(')'):
				raise SyntaxError('")" expected.');
		elif Type == '+':
			Scanner.NextToken();
			self.Prefix = '+';
			self.Type = 'Factor';
			self.Value = Factor().Parse(Scanner);
		elif Type == '-':
			Scanner.NextToken();
			self.Prefix = '-';
			self.Type = 'Factor';
			self.Value = Factor().Parse(Scanner);
		elif Type == 'Not':
			Scanner.NextToken();
			self.Prefix = 'Not';
			self.Type = 'Factor';
			self.Value = Factor().Parse(Scanner);
		elif Type in ['Char', 'Real', 'String', 'Integer', 'Boolean']:
			Scanner.NextToken();
			self.Type = Type;
			self.Value = Value;
		elif Type in ['Type', 'Identifier']:
			self.Type = Type;
			self.Value = Value;
			if Scanner.TokenExpected('(', 1):
				self.Type = 'Invoking';
				self.Value = Invoking().Parse(Scanner, True);
			elif Scanner.TokenExpected('[', 1):
				Scanner.NextToken();
				Scanner.NextToken();
				self.Type = 'Indexing';
				self.Indexer = [Expression().Parse(Scanner)];
				while Scanner.TokenExpected(','):
					Scanner.NextToken();
					self.Indexer.append(Expression().Parse(Scanner));
				if not Scanner.TokenNeeded(']'):
					raise SyntaxError('"]" expected.');
			else:
				Scanner.NextToken();
		else:
			raise SyntaxError('Unexpected token "%s".' % (Value or Type));
		return self;