import tokenize
import StringIO
import frame
import decimal

class Token(object):
	def __init__(self, type, value):
		self.type = type
		self.value = value

	def __repr__(self):
		return "Token(%s, %r)" % (self.type, self.value)

class Lexer(object):
	remap = {(tokenize.NL, '\n') : 'NEWLINE',
		 (tokenize.OP, ':')  : 'COLON',
		 (tokenize.OP, '+')  : 'PLUS',
		 (tokenize.OP, '-')  : 'MINUS',
		 (tokenize.OP, '*')  : 'TIMES',
		 (tokenize.OP, '/')  : 'DIVIDE',
		 (tokenize.OP, '(')  : 'LPAREN',
		 (tokenize.OP, ')')  : 'RPAREN',
		 (tokenize.OP, '=')  : 'EQUALS',
		 (tokenize.OP, ',')  : 'COMMA',
		 (tokenize.OP, '.')  : 'DOT',
		 (tokenize.OP, '%')  : 'PERCENT',
		 (tokenize.OP, '[')  : 'LSBRACE',
		 (tokenize.OP, ']')  : 'RSBRACE',
		 (tokenize.OP, '==')  : 'EQUAL_TO',
		 (tokenize.OP, '!=')  : 'NOT_EQUAL_TO',
		 (tokenize.OP, '<')  : 'LT',
		 (tokenize.OP, '>')  : 'GT',
		 (tokenize.OP, '~')  : 'TILDE',
		 (tokenize.NAME, 'let') : 'LET',
		 (tokenize.NAME, 'as') : 'AS',
		 (tokenize.NAME, 'in') : 'IN',
		 (tokenize.NAME, 'return') : 'RETURN',
		 (tokenize.NAME, 'if') : 'IF',
		 (tokenize.NAME, 'else') : 'ELSE',
		 (tokenize.NAME, 'while') : 'WHILE',
		 }

	def __init__(self):
		self.tokenstream = None
		
	def input(self, data):
		self.tokenstream = tokenize.generate_tokens(StringIO.StringIO(data).readline)
		
	def __iter__(self):
		tok = self.token()
		while tok:
			yield tok
			tok = self.token()
		
	def token(self):
		try:
			tok = list(self.tokenstream.next())
			if tok[0] == tokenize.ENDMARKER: 
				return None
			tok[0] = self.remap.get((tok[0], tok[1]), tokenize.tok_name[tok[0]])
			#print Token(tok[0], tok[1])
			return Token(tok[0], tok[1])
		except StopIteration, e:
			return None
			
tokens = ['NAME', 'NEWLINE', 'INDENT', 'DEDENT', 'COLON', 'NUMBER', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE',
	'LPAREN', 'RPAREN', 'EQUALS', 'COMMA', 'DOT', 'IF', 'ELSE', 'AS', 'RETURN', 'COMMENT', 'STRING',
	'LET', 'EQUAL_TO', 'NOT_EQUAL_TO', 'LT', 'GT', 'WHILE', 'TILDE', 'IN']

def p_code(p):
	"code : lines"
	p[0] = p[1]

def p_subblock(p):
	"subblock : INDENT lines DEDENT"
	p[0] = p[2]
		
def p_lines_start(p):
	"lines : line"
	p[0] = frame.Suite([])
	if p[1]: p[0].append(p[1])
	
def p_lines_rec(p):
	"lines : lines line"
	p[0] = p[1]
	if p[2]: p[0].append(p[2])

def p_line_1(p):
	"line : NEWLINE"
	p[0] = None
			
def p_line_2(p):
	"line : NAME ast_expression NEWLINE subblock"
	p[0] = frame.SuiteDeclaration(p[1], p[2], p[4])
	
def p_line_3(p):
	"line : expression NEWLINE"
	p[0] = frame.ExprStmt(p[1])
	
def p_line_4(p):
	"line : LET NAME EQUALS expression NEWLINE"
	p[0] = frame.Define(p[2], p[4])
	
def p_line_5(p):
	"line : LET atom DOT NAME EQUALS expression NEWLINE"
	p[0] = frame.DefineAttr(p[4], p[2], p[6])
	
def p_line_6(p):
	"line : RETURN expression NEWLINE"	
	p[0] = frame.Return(p[2])
	
def p_line_7(p):
	"line : if"
	p[0] = p[1]

def p_line_8(p):
	"line : while"
	p[0] = p[1]
	
def p_line_9(p):
	"line : COMMENT"
	p[0] = None

def p_if_1(p):
	"if : IF ast_expression NEWLINE subblock"
	p[0] = frame.If(p[2], p[4], None)
	
def p_if_2(p):
	"if : IF ast_expression NEWLINE subblock ELSE NEWLINE subblock"
	p[0] = frame.If(p[2], p[4], p[7])

def p_if_3(p):
	"if : IF ast_expression NEWLINE subblock ELSE if"
	p[0] = frame.If(p[2], p[4], p[6])

def p_while(p):
	"while : WHILE ast_expression NEWLINE subblock"
	p[0] = frame.While(p[2], p[4])

def p_name_1(p):
	"name : NAME"
	p[0] = frame.VarLookup(p[1])

def p_name_2(p):
	"name : TILDE NAME"
	p[0] = frame.SpecialLookup(p[2])

def p_name_3(p):
	"name : dotted_name"
	p[0] = p[1]
	
def p_dotted_name(p):
	"dotted_name : atom DOT NAME"
	p[0] = frame.AttrLookup(p[3], p[1])

precedence = (
    ('nonassoc', 'EQUALS'),
    ('nonassoc', 'AS'),
    ('nonassoc', 'IN'),
    ('nonassoc', 'EQUAL_TO', 'NOT_EQUAL_TO'),
    ('nonassoc', 'LT', 'GT'),
    ('left', 'PLUS', 'MINUS'),
    ('left', 'TIMES', 'DIVIDE'),
    ('nonassoc', 'LPAREN', 'COLON'),
    ('left', 'DOT'),
)
	
def p_atom_1(p):
	"atom : NUMBER"
	p[0] = frame.Const(decimal.Decimal(p[1]))
	
def p_atom_2(p):
	"atom : name"
	p[0] = p[1]
	
def p_atom_3(p):
 	"atom : bracketted_expression"
	p[0] = p[1]

def p_atom_4(p):
	"atom : ast_expression"
	p[0] = p[1]

def p_atom_5(p):
	"atom : STRING"
	p[0] = frame.Const(p[1].strip("\"").replace("\\n", "\n"))
	
def p_atom_6(p):
	"atom : call"
	p[0] = p[1]
	
def p_expression_1(p):
	"""expression : expression PLUS expression 
		      | expression MINUS expression
		      | expression TIMES expression
		      | expression DIVIDE expression
		      | expression EQUAL_TO expression
		      | expression NOT_EQUAL_TO expression
		      | expression LT expression
		      | expression GT expression
		      """
	p[0] = frame.BinaryOperator(p[2], p[1], p[3])

def p_expression_2(p):
	"expression : atom"
	p[0] = p[1]
	
def p_expression_5(p):
	"""expression : as
			| in"""
	p[0] = p[1]

def p_expression_7(p):
	"expression : NAME EQUALS expression"
	p[0] = frame.Set(p[1], p[3])

def p_expression_8(p):
	"expression : atom DOT NAME EQUALS expression"
	p[0] = frame.SetAttr(p[3], p[1], p[5])

def p_bracketted_expression(p):
 	"bracketted_expression : LPAREN expression RPAREN"
	p[0] = p[2]	

def p_ast_expression(p):
	"ast_expression : COLON expression COLON"
	p[0] = frame.Const(p[2])

def p_call_1(p):
	"call : atom LPAREN RPAREN"
	p[0] = frame.Call(p[1], [])

def p_call_2(p):
	"call : atom LPAREN exprlist RPAREN"
	p[0] = frame.Call(p[1], p[3])

def p_as(p):
	"as : expression AS expression"
	p[0] = frame.As(p[1], p[3])

def p_in(p):
	"in : expression IN expression"
	p[0] = frame.In(p[1], p[3])
	
def p_exprlist_start(p):
	"exprlist : expression"
	p[0] = [p[1]]
	
def p_exprlist_rec(p):
	"exprlist : exprlist COMMA expression"
	p[0] = p[1]
	p[0].append(p[3])

def p_error(p):
	print "Error:", p

if __name__ == "__main__":
	import yacc
	import pprint

	p = yacc.yacc(debug=1)

	data = r"""
class :bar(a, b) as fish: 
	let a = 6.2
	let ~outer.z = 1
	
	def :fish(a, b):
		print(a+b)
	
	def :fish2(self, message):
		print(self)
		print(message)
	
	print(a)
print(z)

def :foo(a as int, b) as fish:
	~outer.z = "fish"
	return a+b

let a = 2
let bar.foo = 5

print(bar.fish)
bar.fish("hello", ", goodbye")
bar.foo = 6
a = (7 + 2) as bar
print(foo(a, 10))
print(z)
print(bar)

let b = bar()
b = bar.operator_call()
print(b)
b.fish2("hello")

print(a)
if :7 < a:
	print(1)
else if :0:
	print(2)
else if :1:
	print(3)

print("Hello\n")
	
let x = 5
while :x > 0:
	print(x)
	x = x - 1
	
print(~operator.equal_to)

def :define_for(ast, suite):
	print("For loop")
	print(ast)
	print(suite)
	
let ~definition.for = define_for

for :a in f.range(10):
	print(a)

print.operator_call(1)	
print.operator_call.operator_call(1)
a = :a = 1:
print(a.type())
print(a.render())
print(type(a))
print(:a = 1:.render())
"""

	result = p.parse(input=data, lexer=Lexer(), debug=1)

	code = []
	result.render(code)
	
	run = frame.Frame(code, frame.initial_context(), {}, debug=False)
	frame.call_object(run, (), None)
	print run.valuestack.data
