docs: def validate(bo, error_level: str = "WARNING") -> Tuple[bool, List[Tuple[str, str]]]:
      """Semantically validate BEL AST
  
      Add errors and warnings to bel_obj.validation_messages
  
      Error Levels are similar to log levels - selecting WARNING includes both
      WARNING and ERROR, selecting ERROR just includes ERROR
  
      Args:
          bo: main BEL language object
          error_level: return ERRORs only or also WARNINGs
  
      Returns:
          Tuple[bool, List[Tuple[str, str]]]: (is_valid, messages)
      """
  
      if bo.ast:
          bo = validate_functions(bo.ast, bo)  # No WARNINGs generated in this function
          if error_level == "WARNING":
              bo = validate_arg_values(bo.ast, bo)  # validates NSArg and StrArg values
  
      else:
          bo.validation_messages.append(("ERROR", "Invalid BEL Statement - cannot parse"))
  
      for msg in bo.validation_messages:
          if msg[0] == "ERROR":
              bo.parse_valid = False
              break
  
      return bo