"""	Pythonica.pyThis module does input and output parsing.  All the real work is doneby PythonicaCore."""import stringimport regexfrom pythonicacore import *gEntryNum = 1gSymToName = {}gNameToSym = {}gNameToPrec = {}gNonchainable = ('ReplaceAll','Rule','Power','Minus')# note: instead of just "nonchainable", we should note whether# each function groups left, groups right, or chains# (compare -> and /. in Mathematica; right and left, resp.)#----------------------------------------------------------------------def buildOperTables():	global gSymToName, gNameToPrec	ops = ( '/.',	'ReplaceAll',			'->',	'Rule',			'==',	'Equal',			'=',	'Set',			'=.',	'Unset',			'+',	'Plus',			'-',	'Minus',			'*',	'Times',			'/',	'Divide',			'^',	'Power')	for i in range(0,len(ops)/2):		gSymToName[ops[i*2]] = ops[i*2+1]		gNameToSym[ops[i*2+1]] = ops[i*2]		gNameToPrec[ops[i*2+1]] = ibuildOperTables()#----------------------------------------------------------------------def endOfExpr(s):	# return the position at which this token ends	# it may be delimited by a delimiter, or the end of the string#	print "endOfExpr(" + s + ")"	p = 0	maxp = len(s)	while 1:		# check for end of line		if p == maxp: return p		# check for one-char opers		if s[p] == ',' or s[p] in gSymToName.keys():			if p>0: return p		# check for two-char opers		if p < maxp-1 and s[p:p+2] in gSymToName.keys(): return p		# check for groupings...		if s[p] == '[':					# if we start a '[...]',			nest = 1			while nest:					# ...finish it				p = p + 1				if s[p] == '[': nest = nest + 1				if s[p] == ']': nest = nest - 1		if s[p] == '(':					# if we start a '(...)',			nest = 1			while nest:					# ...finish it				p = p + 1				if s[p] == '(': nest = nest + 1				if s[p] == ')': nest = nest - 1		p = p + 1	#----------------------------------------------------------------------def splitExprs(s):	# split on delimiters, ignoring those nested in '[...]' or '(...)'	outexpr = []	outdelim = []	while s:		end = endOfExpr(s)		outexpr.append(s[:end])		if end >= len(s)-1: outdelim.append('')		elif end < len(s)-1 and \				s[end:end+2] in gSymToName.keys():			outdelim.append(s[end:end+2])		else:			outdelim.append(s[end])		s = s[end+len(outdelim[-1]):]	return outexpr,outdelim#----------------------------------------------------------------------def subPercent(s):	"""subPercent(s): replace one '%' with Out[-1],	'%%' with the Out[-2], etc., and %n (where n is	an integer) with the Out[n]."""	while 1:		l = len(s)		pos = string.find(s,'%')		if pos < 0: return s		cnt = 1		while pos+cnt < l and s[pos+cnt] in '%0123456789':			cnt = cnt+1		substr = s[pos:pos+cnt]		if len(substr) > 1 and substr[1] != '%':			rep = 'Out[' + substr[1:] + ']'		else:			rep = 'Out[-' + str(len(substr)) + ']'		s = s[:pos] + rep + s[pos+cnt:]#----------------------------------------------------------------------def stripSpaces(s):	"""stripSpaces(s): strip all spaces from the input string,	except that if a space occurs between any combination of	numbers and symbols, convert it to '*'."""		out = ''	hadsym = 0		# flag: did we just have a symbol or number?	hadspace = 1	# flag: did we just have some whitespace?	symchars = string.letters + string.digits	for c in s:		if c != ' ':			if hadspace:				# we just had a space...				if c in symchars:					# and now we see a symbol					# if we had a symbol before that, throw in a '*'					if hadsym: out = out + '*'			hadsym = (c in symchars)			out = out + c			hadspace = 0		else:			hadspace = 1	return out#----------------------------------------------------------------------def parseOneExpr(s):	"""parseOneExpr(s): build one token from the string s.	This string should immediately be a token, and contain no	paretheses, operators, etc., except within arguments."""		wip = []	# get first token	delim = regex.search('[\[, ]', s)	if string.find(s,']') < delim:		delim = string.find(s,']')	# why can't we do this w/ regex?	if delim < 0:		token = s		delimchar = '\n'	else:		token = s[:delim]		delimchar = s[delim]	expr = None	# do we have a FullForm expression (e.g., Head[args])?	if delimchar == '[':		# check for a built-in symbol; otherwise, use ExprSymbol		try: expr = eval('Expr' + token + '()' )		except: expr = Expr([],token)		# ...find all the elements for this expression		estr = s[:endOfExpr(s)+1]		estr = estr[delim+1:-1]		estrlist,delimlist = splitExprs(estr)#		print "args:", estrlist		expr.data = map(lambda x:parse(x), estrlist)	# if not, then do we have a number?	elif token[0] in '0123456789' or (len(token)>1 and \		 token[0] == '-' and token[1] in '0123456789'):		# a Number...		expr = ExprNumber(string.atof(token))	# if neither of those, then it must be a symbol	else:		# a Symbol...		expr = ExprSymbol(token)	return expr#----------------------------------------------------------------------def parse(s):	global gSymToName, gNameToPrec, gNonchainable	# split into tokens...	estrlist,delimlist = splitExprs(s)	# crawl through tokens, combining where operators are found	wip = []			# stack of expressions#	print "estrlist:", estrlist#	print "delimlist:", delimlist		# evaluate each token, considering its following delimiter	for i in range(0,len(estrlist)):		if estrlist[i]:			if estrlist[i][0] == '(':				# if we have parentheses, parse their contents!				expr = parse(estrlist[i][1:-1])			else:				# otherwise, load a single expression				expr = parseOneExpr(estrlist[i])		else: expr = None		if delimlist[i] in gSymToName.keys():			# we have an operator...			opername = gSymToName[delimlist[i]]			operclass = eval('Expr' + opername)			if not operclass: raise "Bug", "Undefined operator " + opername			if not wip:				# first expression we've seen; throw it on the stack				wip.append(operclass(expr))			elif not wip[-1].Head():				# current expression is headless -- make it us				wip[-1].head = opername				if expr: wip[-1].data.append(expr)			elif wip[-1].Head() == opername and opername not in gNonchainable:				# same operator; append to previous data				wip[-1].data.append(expr)			else:				# figure out where to stuff token,				# according to precedence rules				myPrecedence = gNameToPrec[opername]				curPrecedence = gNameToPrec[wip[-1].Head()]				if curPrecedence < myPrecedence:					# current head has lower precedence... start a new level#					print opername, "higher than", wip[-1].Head()					wip.append(operclass(expr))					wip[-2].data.append(wip[-1])				else:					# current head has higher precedence...					# add expr to current, then move it within us#					print opername, "lower than", wip[-1].Head()					if expr: wip[-1].data.append(expr)					which = -1					while which != -len(wip):						if gNameToPrec[ wip[which].Head() ] > myPrecedence:#							print "...lower than", wip[which].Head()							pass						else:#							print "...but not lower than", wip[which].Head()							break						which = which-1#					print "Stuffing around wip", which					if wip[which].Head() != opername or \							opername in gNonchainable:						if which == -len(wip):							wip[0] = operclass(wip[0])							if len(wip) > 1: del wip[1:]						else:							wip[which-1].data[-1] = operclass(wip[which-1].data[-1])							wip[-1] = wip[which-1].data[-1]		elif delimlist[i] == '':			if not wip:				wip.append(expr)			else:				wip[-1].data.append(expr)#		print "wip:", tostr(wip)	return wip[0]#----------------------------------------------------------------------def unparse(expr, leftprec=-1, rightprec=-1):	# is this expression something we have an operator for?	head = expr.Head()	if head not in gNameToPrec.keys():		# nope, no operator... just print normally		out = str(expr)		return out	# yep, we have an operator... get it, and its precedence	prec = gNameToPrec[head]	# (for readability, we'll code a couple of formatting hacks)	op = gNameToSym[head]	if op == '*': op = ' '	elif op != '^': op = ' ' + op + ' '	# combine data using operator -- pass precedence info	out = ''	# leftmost argument gets has our left, and us for the right	if len(expr.data) > 0:		if len(expr.data) == 1: r = rightprec		else: r = prec		out = unparse(expr.data[0],leftprec,r)	# middle arguments have us on both left and right	if len(expr.data) > 2:		out = out + op + \		  string.join(map(lambda x,p=prec:unparse(x,p,p),expr.data[1:-1]), \		 		 op)	# last argument has our right, and us for the left	if len(expr.data) > 1:		out = out + op + \				unparse(expr.data[-1],prec,rightprec)	# use parenthesis if we're lower precedence than our neighbors	if prec < leftprec or prec < rightprec:		out = '(' + out + ')'	return out#----------------------------------------------------------------------def handleInput(s, surpressOutput=0):	"""handleInput(s, surpressOutput=0): handle the given input,	generating output if the second parameter is 0.  Return a	tuple of (input-expr, output-expr)."""		s = stripSpaces(subPercent(s))	if s:		inexp = parse(s)		eval = inexp.Eval()		if surpressOutput:			simp = eval.Simplify()			while simp != eval:				eval = simp				simp = eval.Simplify()		else:			print			print "in FullForm:", inexp			print "evaluates to:", eval			# now simplify			simp = eval.Simplify()			while simp != eval:				print "simplifies to:", simp				eval = simp				simp = eval.Simplify()	return inexp,simp#----------------------------------------------------------------------def mainLoop():	global gEntryNum	s = 'foo'	while s:		numstr = '[' + str(gEntryNum) + ']'		s = raw_input("In" + numstr + " :=   ")		# break on ';' and surpress output for all but the last		commands = string.split(s,";")		for c in commands[:-1]:			inexp,outexp = handleInput(c,1)		# on the last one, don't surpress output		if commands[-1]:			inexp,outexp = handleInput(commands[-1])			print			print "Out" + numstr, "=  ", unparse(outexp)		print		# store the in/out data for future reference		StoreInOut( inexp,outexp )		gEntryNum = gEntryNum + 1#----------------------------------------------------------------------mainLoop()raw_input("Press Return.")