docs: def get_file_sections(file_str):
      """
      This is where we separate out the macros from the rest of the model file.
      Working based upon documentation at: https://www.vensim.com/documentation/index.html?macros.htm
  
      Macros will probably wind up in their own python modules eventually.
  
      Parameters
      ----------
      file_str
  
      Returns
      -------
      entries: list of dictionaries
          Each dictionary represents a different section of the model file, either a macro,
          or the main body of the model file. The dictionaries contain various elements:
          - returns: list of strings
              represents what is returned from a macro (for macros) or empty for main model
          - params: list of strings
              represents what is passed into a macro (for macros) or empty for main model
          - name: string
              the name of the macro, or 'main' for main body of model
          - string: string
              string representing the model section
      Examples
      --------
      >>> get_file_sections(r'a~b~c| d~e~f| g~h~i|')
      [{'returns': [], 'params': [], 'name': 'main', 'string': 'a~b~c| d~e~f| g~h~i|'}]
  
      """
  
      # the leading 'r' for 'raw' in this string is important for handling backslashes properly
      file_structure_grammar = _include_common_grammar(r"""
      file = encoding? (macro / main)+
      macro = ":MACRO:" _ name _ "(" _ (name _ ","? _)+ _ ":"? _ (name _ ","? _)* _ ")" ~r".+?(?=:END OF MACRO:)" ":END OF MACRO:"
      main = !":MACRO:" ~r".+(?!:MACRO:)"
      encoding = ~r"\{[^\}]*\}"
      """)
  
      parser = parsimonious.Grammar(file_structure_grammar)
      tree = parser.parse(file_str)
  
      class FileParser(parsimonious.NodeVisitor):
          def __init__(self, ast):
              self.entries = []
              self.visit(ast)
  
          def visit_main(self, n, vc):
              self.entries.append({'name': '_main_',
                                   'params': [],
                                   'returns': [],
                                   'string': n.text.strip()})
  
          def visit_macro(self, n, vc):
              name = vc[2]
              params = vc[6]
              returns = vc[10]
              text = vc[13]
              self.entries.append({'name': name,
                                   'params': [x.strip() for x in params.split(',')] if params else [],
                                   'returns': [x.strip() for x in
                                               returns.split(',')] if returns else [],
                                   'string': text.strip()})
  
          def generic_visit(self, n, vc):
              return ''.join(filter(None, vc)) or n.text or ''
  
      return FileParser(tree).entries