docs: def get_equation_components(equation_str):
      """
      Breaks down a string representing only the equation part of a model element.
      Recognizes the various types of model elements that may exist, and identifies them.
  
      Parameters
      ----------
      equation_str : basestring
          the first section in each model element - the full equation.
  
      Returns
      -------
      Returns a dictionary containing the following:
  
      real_name: basestring
          The name of the element as given in the original vensim file
  
      subs: list of strings
          list of subscripts or subscript elements
  
      expr: basestring
  
      kind: basestring
          What type of equation have we found?
          - *component* - normal model expression or constant
          - *lookup* - a lookup table
          - *subdef* - a subscript definition
  
      Examples
      --------
      >>> get_equation_components(r'constant = 25')
      {'expr': '25', 'kind': 'component', 'subs': [], 'real_name': 'constant'}
  
      Notes
      -----
      in this function we dont create python identifiers, we use real names.
      This is so that when everything comes back together, we can manage
      any potential namespace conflicts properly
      """
  
      component_structure_grammar = _include_common_grammar(r"""
      entry = component / subscript_definition / lookup_definition
      component = name _ subscriptlist? _ "=" _ expression
      subscript_definition = name _ ":" _ subscript _ ("," _ subscript)*
      lookup_definition = name _ &"(" _ expression  # uses lookahead assertion to capture whole group
  
      name = basic_id / escape_group
      subscriptlist = '[' _ subscript _ ("," _ subscript)* _ ']'
      expression = ~r".*"  # expression could be anything, at this point.
  
      subscript = basic_id / escape_group
      """)
  
      # replace any amount of whitespace  with a single space
      equation_str = equation_str.replace('\\t', ' ')
      equation_str = re.sub(r"\s+", ' ', equation_str)
  
      parser = parsimonious.Grammar(component_structure_grammar)
      tree = parser.parse(equation_str)
  
      class ComponentParser(parsimonious.NodeVisitor):
          def __init__(self, ast):
              self.subscripts = []
              self.real_name = None
              self.expression = None
              self.kind = None
              self.visit(ast)
  
          def visit_subscript_definition(self, n, vc):
              self.kind = 'subdef'
  
          def visit_lookup_definition(self, n, vc):
              self.kind = 'lookup'
  
          def visit_component(self, n, vc):
              self.kind = 'component'
  
          def visit_name(self, n, vc):
              (name,) = vc
              self.real_name = name.strip()
  
          def visit_subscript(self, n, vc):
              (subscript,) = vc
              self.subscripts.append(subscript.strip())
  
          def visit_expression(self, n, vc):
              self.expression = n.text.strip()
  
          def generic_visit(self, n, vc):
              return ''.join(filter(None, vc)) or n.text
  
          def visit__(self, n, vc):
              return ' '
  
      parse_object = ComponentParser(tree)
  
      return {'real_name': parse_object.real_name,
              'subs': parse_object.subscripts,
              'expr': parse_object.expression,
              'kind': parse_object.kind}