# GP_EVAL.PY
# Dominic Ford
# 12/04/2006

# Evaluates expressions using user variables and functions

import sys
import re
import traceback
from math import *

# GP_EVAL(): Evaluates an expression, substituting user defined variables and functions
def gp_eval(expression, vars, funcs):
  try:
    for func,fexp in funcs.iteritems():
      funcname = re.match(r"(.*)(\(.*\))",func)
      if (funcname == None):
        print "Internal Error: Bad function definition %s"%func
        return 0
      while 1:
        test = re.match(r"^(.*)%s\s*\((.*)$"%funcname.group(1),expression)
        if (test == None): break
        bracketmatch = gp_bracketmatch(expression, test.start(2)-1)
        if (len(bracketmatch) == 0):
          print "Mismatched brackets for function %s"%func
          return 0
        bracketmatch_funcname = gp_bracketmatch(funcname.group(2),0)
        func_scope = vars.copy()
        start_varval = test.start(2)
        start_varnam = 1
        for i in range(len(bracketmatch_funcname)):
          varnam = funcname.group(2)[start_varnam:bracketmatch_funcname[i]]
          if (i < len(bracketmatch)):
            varval = expression[start_varval:bracketmatch[i]]
            start_varval = bracketmatch         [i]+1
            func_scope[varnam.strip()] = gp_eval(varval,vars,funcs)
          else:
            func_scope[varnam.strip()] = 0.0
          start_varnam   = bracketmatch_funcname[i]+1
        expression = test.group(1) + str(gp_eval(fexp,func_scope,funcs)) + expression[bracketmatch[len(bracketmatch)-1]+1:]
        
    return float(eval(expression, globals(), vars))
  except:
    print "Error evaluating expression %s"%expression
    print "Error:" , sys.exc_info()[1], "(" , sys.exc_info()[0] , ")"
#    traceback.print_tb( sys.exc_info()[2] )    # Use for debugging only
    raise

# GP_BRACKETMATCH(): Find a matching closing bracket for an opening bracket

def gp_bracketmatch(expression, i):
  # Returns a list of comma positions in expression
  # Final item on list is closing )

  bracket_level = 0
  max           = len(expression)
  list          = []

  while( i < max-1 ):
    i = i+1 # Start searching after bracket at position i
    if (expression[i]==')'):
      if (bracket_level == 0):
        list.append(i)
        return list
      else:
        bracket_level = bracket_level - 1
    elif (expression[i]=='('):
        bracket_level = bracket_level + 1
    elif (expression[i]==','):
      if (bracket_level == 0):
        list.append(i)
        continue

# GP_SPLIT(): An intelligent string splitter, which doesn't grab split characters embedded in () or ""

def gp_split(expression, splitchar):
  bracket_level = 0
  quote_level   = 0
  apostro_level = 0
  max           = len(expression)
  list          = []
  i             = -1
  word          = ""

  while( i < max-1 ):
    i = i+1
    if   (expression[i]==')'): bracket_level = bracket_level - 1
    elif (expression[i]=='('): bracket_level = bracket_level + 1 
    elif ((expression[i]=='"') and (apostro_level == 0)): quote_level   = 1 - quote_level   # Hyphens allowed in quotes and vice-versa
    elif ((expression[i]=="'") and (quote_level   == 0)): apostro_level = 1 - apostro_level
    elif (expression[i]==splitchar):
      if ((bracket_level == 0) and (quote_level == 0) and (apostro_level == 0)):
        list.append(word)
        word=""
        continue
    word = word + expression[i]
  list.append(word)
  return list

# We've hit the end of string without brackets being closed
  return ()
