parameters: (text)
return_statement: return "".join(output_chars)
docstring: Migrates text which may contain equals style expressions, e.g. "Hi =UPPER(contact)", converting them to new style
      expressions, e.g. "Hi @(UPPER(contact))"
docstring_summary: Migrates text which may contain equals style expressions, e.g. "Hi =UPPER(contact)", converting them to new style
      expressions, e.g. "Hi
docstring_tokens: Migrates text which may contain equals style expressions e . g . Hi = UPPER ( contact ) converting them to new style expressions e . g . Hi
function: def _replace_equals_style(text):
      """
      Migrates text which may contain equals style expressions, e.g. "Hi =UPPER(contact)", converting them to new style
      expressions, e.g. "Hi @(UPPER(contact))"
      """
      STATE_BODY = 0  # not in a expression
      STATE_PREFIX = 1  # '=' prefix that denotes the start of an expression
      STATE_IDENTIFIER = 2  # the identifier part, e.g. 'SUM' in '=SUM(1, 2)' or 'contact.age' in '=contact.age'
      STATE_BALANCED = 3  # the balanced parentheses delimited part, e.g. '(1, 2)' in 'SUM(1, 2)'
      STATE_STRING_LITERAL = 4  # a string literal
      input_chars = list(text)
      output_chars = []
      state = STATE_BODY
      current_expression_chars = []
      current_expression_terminated = False
      parentheses_level = 0
  
      def replace_expression(expression):
          expression_body = expression[1:]
  
          # if expression doesn't end with ) then check it's an allowed top level context reference
          if not expression_body.endswith(")"):
              top_level = expression_body.split(".")[0].lower()
              if top_level not in ALLOWED_TOP_LEVELS:
                  return expression
  
          return "@" + _convert_equals_style(expression_body)
  
      # determines whether the given character is a word character, i.e. \w in a regex
      def is_word_char(c):
          return c and (c.isalnum() or c == "_")
  
      for pos, ch in enumerate(input_chars):
          # in order to determine if the b in a.b terminates an identifier, we have to peek two characters ahead as it
          # could be a.b. (b terminates) or a.b.c (b doesn't terminate)
          next_ch = input_chars[pos + 1] if (pos < (len(input_chars) - 1)) else None
          next_next_ch = input_chars[pos + 2] if (pos < (len(input_chars) - 2)) else None
  
          if state == STATE_BODY:
              if ch == "=" and (is_word_char(next_ch) or next_ch == "("):
                  state = STATE_PREFIX
                  current_expression_chars = [ch]
              else:
                  output_chars.append(ch)
  
          elif state == STATE_PREFIX:
              if is_word_char(ch):
                  # we're parsing an expression like =XXX or =YYY()
                  state = STATE_IDENTIFIER
              elif ch == "(":
                  # we're parsing an expression like =(1 + 2)
                  state = STATE_BALANCED
                  parentheses_level += 1
  
              current_expression_chars.append(ch)
  
          elif state == STATE_IDENTIFIER:
              if ch == "(":
                  state = STATE_BALANCED
                  parentheses_level += 1
  
              current_expression_chars.append(ch)
  
          elif state == STATE_BALANCED:
              if ch == "(":
                  parentheses_level += 1
              elif ch == ")":
                  parentheses_level -= 1
              elif ch == '"':
                  state = STATE_STRING_LITERAL
  
              current_expression_chars.append(ch)
  
              # expression terminates if parentheses balance
              if parentheses_level == 0:
                  current_expression_terminated = True
  
          elif state == STATE_STRING_LITERAL:
              if ch == '"':
                  state = STATE_BALANCED
              current_expression_chars.append(ch)
  
          # identifier can terminate expression in 3 ways:
          #  1. next char is null (i.e. end of the input)
          #  2. next char is not a word character or period or left parentheses
          #  3. next char is a period, but it's not followed by a word character
          if state == STATE_IDENTIFIER:
              if (
                  not next_ch
                  or (not is_word_char(next_ch) and not next_ch == "." and not next_ch == "(")
                  or (next_ch == "." and not is_word_char(next_next_ch))
              ):
                  current_expression_terminated = True
  
          if current_expression_terminated:
              output_chars.append(replace_expression("".join(current_expression_chars)))
              current_expression_chars = []
              current_expression_terminated = False
              state = STATE_BODY
  
      return "".join(output_chars)
function_tokens: def _replace_equals_style ( text ) : STATE_BODY = 0 # not in a expression STATE_PREFIX = 1 # '=' prefix that denotes the start of an expression STATE_IDENTIFIER = 2 # the identifier part, e.g. 'SUM' in '=SUM(1, 2)' or 'contact.age' in '=contact.age' STATE_BALANCED = 3 # the balanced parentheses delimited part, e.g. '(1, 2)' in 'SUM(1, 2)' STATE_STRING_LITERAL = 4 # a string literal input_chars = list ( text ) output_chars = [ ] state = STATE_BODY current_expression_chars = [ ] current_expression_terminated = False parentheses_level = 0 def replace_expression ( expression ) : expression_body = expression [ 1 : ] # if expression doesn't end with ) then check it's an allowed top level context reference if not expression_body . endswith ( ")" ) : top_level = expression_body . split ( "." ) [ 0 ] . lower ( ) if top_level not in ALLOWED_TOP_LEVELS : return expression return "@" + _convert_equals_style ( expression_body ) # determines whether the given character is a word character, i.e. \w in a regex def is_word_char ( c ) : return c and ( c . isalnum ( ) or c == "_" ) for pos , ch in enumerate ( input_chars ) : # in order to determine if the b in a.b terminates an identifier, we have to peek two characters ahead as it # could be a.b. (b terminates) or a.b.c (b doesn't terminate) next_ch = input_chars [ pos + 1 ] if ( pos < ( len ( input_chars ) - 1 ) ) else None next_next_ch = input_chars [ pos + 2 ] if ( pos < ( len ( input_chars ) - 2 ) ) else None if state == STATE_BODY : if ch == "=" and ( is_word_char ( next_ch ) or next_ch == "(" ) : state = STATE_PREFIX current_expression_chars = [ ch ] else : output_chars . append ( ch ) elif state == STATE_PREFIX : if is_word_char ( ch ) : # we're parsing an expression like =XXX or =YYY() state = STATE_IDENTIFIER elif ch == "(" : # we're parsing an expression like =(1 + 2) state = STATE_BALANCED parentheses_level += 1 current_expression_chars . append ( ch ) elif state == STATE_IDENTIFIER : if ch == "(" : state = STATE_BALANCED parentheses_level += 1 current_expression_chars . append ( ch ) elif state == STATE_BALANCED : if ch == "(" : parentheses_level += 1 elif ch == ")" : parentheses_level -= 1 elif ch == '"' : state = STATE_STRING_LITERAL current_expression_chars . append ( ch ) # expression terminates if parentheses balance if parentheses_level == 0 : current_expression_terminated = True elif state == STATE_STRING_LITERAL : if ch == '"' : state = STATE_BALANCED current_expression_chars . append ( ch ) # identifier can terminate expression in 3 ways: #  1. next char is null (i.e. end of the input) #  2. next char is not a word character or period or left parentheses #  3. next char is a period, but it's not followed by a word character if state == STATE_IDENTIFIER : if ( not next_ch or ( not is_word_char ( next_ch ) and not next_ch == "." and not next_ch == "(" ) or ( next_ch == "." and not is_word_char ( next_next_ch ) ) ) : current_expression_terminated = True if current_expression_terminated : output_chars . append ( replace_expression ( "" . join ( current_expression_chars ) ) ) current_expression_chars = [ ] current_expression_terminated = False state = STATE_BODY return "" . join ( output_chars )