nodes: 0, module; 1, function_definition; 2, function_name:validate_functions; 3, parameters; 4, block; 5, typed_parameter; 6, identifier:bo; 7, expression_statement; 8, if_statement; 9, comment:# Recursively process every NSArg by processing BELAst and Functions; 10, if_statement; 11, return_statement; 12, identifier:ast; 13, type; 14, comment:"""Recursively validate function signatures
  
      Determine if function matches one of the available signatures. Also,
  
      1. Add entity types to AST NSArg, e.g. Abundance, ...
      2. Add optional to  AST Arg (optional means it is not a
          fixed, required argument and needs to be sorted for
          canonicalization, e.g. reactants(A, B, C) )
  
      Args:
          bo: bel object
  
      Returns:
          bel object
      """; 15, call; 16, block; 17, call; 18, block; 19, identifier:bo; 20, identifier:BELAst; 21, identifier:isinstance; 22, argument_list; 23, expression_statement; 24, expression_statement; 25, expression_statement; 26, expression_statement; 27, if_statement; 28, identifier:hasattr; 29, argument_list; 30, for_statement; 31, identifier:ast; 32, identifier:Function; 33, call; 34, assignment; 35, assignment; 36, assignment; 37, not_operator; 38, block; 39, identifier:ast; 40, string:"args"; 41, identifier:arg; 42, attribute; 43, block; 44, attribute; 45, argument_list; 46, identifier:function_signatures; 47, subscript; 48, identifier:function_name; 49, attribute; 50, tuple_pattern; 51, call; 52, identifier:valid_function; 53, expression_statement; 54, expression_statement; 55, expression_statement; 56, identifier:ast; 57, identifier:args; 58, expression_statement; 59, identifier:log; 60, identifier:debug; 61, string:f"Validating: {ast.name}, {ast.function_type}, {ast.args}"; 62, subscript; 63, string:"signatures"; 64, identifier:ast; 65, identifier:name; 66, identifier:valid_function; 67, identifier:messages; 68, identifier:check_function_args; 69, argument_list; 70, assignment; 71, call; 72, assignment; 73, call; 74, subscript; 75, attribute; 76, attribute; 77, identifier:function_signatures; 78, identifier:function_name; 79, identifier:message; 80, call; 81, attribute; 82, argument_list; 83, attribute; 84, False; 85, identifier:validate_functions; 86, argument_list; 87, subscript; 88, string:"signatures"; 89, identifier:ast; 90, identifier:name; 91, identifier:ast; 92, identifier:args; 93, attribute; 94, argument_list; 95, attribute; 96, identifier:append; 97, tuple; 98, identifier:bo; 99, identifier:parse_valid; 100, identifier:arg; 101, identifier:bo; 102, attribute; 103, string:"functions"; 104, string:", "; 105, identifier:join; 106, identifier:messages; 107, identifier:bo; 108, identifier:validation_messages; 109, string:"ERROR"; 110, call; 111, identifier:bo; 112, identifier:spec; 113, attribute; 114, argument_list; 115, string:"Invalid BEL Statement function {} - problem with function signatures: {}"; 116, identifier:format; 117, call; 118, identifier:message; 119, attribute; 120, argument_list; 121, identifier:ast; 122, identifier:to_string
code: def validate_functions(ast: BELAst, bo):
      """Recursively validate function signatures
  
      Determine if function matches one of the available signatures. Also,
  
      1. Add entity types to AST NSArg, e.g. Abundance, ...
      2. Add optional to  AST Arg (optional means it is not a
          fixed, required argument and needs to be sorted for
          canonicalization, e.g. reactants(A, B, C) )
  
      Args:
          bo: bel object
  
      Returns:
          bel object
      """
  
      if isinstance(ast, Function):
          log.debug(f"Validating: {ast.name}, {ast.function_type}, {ast.args}")
          function_signatures = bo.spec["functions"]["signatures"][ast.name]["signatures"]
  
          function_name = ast.name
          (valid_function, messages) = check_function_args(
              ast.args, function_signatures, function_name
          )
          if not valid_function:
              message = ", ".join(messages)
              bo.validation_messages.append(
                  (
                      "ERROR",
                      "Invalid BEL Statement function {} - problem with function signatures: {}".format(
                          ast.to_string(), message
                      ),
                  )
              )
              bo.parse_valid = False
  
      # Recursively process every NSArg by processing BELAst and Functions
      if hasattr(ast, "args"):
          for arg in ast.args:
              validate_functions(arg, bo)
  
      return bo