<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def parse( self, assertion: Union[str, Mapping[str, str]], strict: bool = False, parseinfo: bool = False, rule_name: str = "start", error_level: str = "WARNING", ) -> "BEL": """Parse and semantically validate BEL statement Parses a BEL statement given as a string and returns an AST, Abstract Syntax Tree (defined in ast.py) if the statement is valid, self.parse_valid. Else, the AST attribute is None and there will be validation error messages in self.validation_messages. self.validation_messages will contain WARNINGS if warranted even if the statement parses correctly. Error Levels are similar to log levels - selecting WARNING includes both WARNING and ERROR, selecting ERROR just includes ERROR Args: assertion: BEL statement (if str -> 'S R O', if dict {'subject': S, 'relation': R, 'object': O}) strict: specify to use strict or loose parsing; defaults to loose parseinfo: specify whether or not to include Tatsu parse information in AST rule_name: starting point in parser - defaults to 'start' error_level: return ERRORs only or also WARNINGs Returns: ParseObject: The ParseObject which contain either an AST or error messages. """


        self.ast = None
        self.parse_valid = False
        self.parse_visualize_error = ""
        self.validation_messages = []  # Reset messages when parsing a new BEL Statement

        if isinstance(assertion, dict):
            if assertion.get("relation", False) and assertion.get("object", False):
                statement = f"{assertion['subject']} {assertion['relation']} {assertion['object']}"
            elif assertion.get("subject"):
                statement = f"{assertion['subject']}"
            else:
                statement = ""
        else:
            statement = assertion

        self.original_bel_stmt = statement

        # pre-process to remove extra white space, add space after commas, etc.
        self.bel_stmt = bel_utils.preprocess_bel_stmt(statement)

        # TODO - double check these tests before enabling
        # is_valid, messages = bel_utils.simple_checks(self.bel_stmt)
        # if not is_valid:
        #     self.validation_messages.extend(messages)
        #     return self

        # Check to see if empty string for bel statement
        if len(self.bel_stmt) == 0:
            self.validation_messages.append(
                ("ERROR", "Please include a valid BEL statement - found empty string.")
            )
            return self

        try:
            # see if an AST is returned without any parsing errors

            ast_dict = self.parser.parse(
                self.bel_stmt, rule_name=rule_name, trace=False, parseinfo=parseinfo
            )
            self.ast = lang_ast.ast_dict_to_objects(ast_dict, self)

            self.parse_valid = True

        except FailedParse as e:
            # if an error is returned, send to handle_syntax, error
            error, visualize_error = bel_utils.handle_parser_syntax_error(e)
            self.parse_visualize_error = visualize_error
            if visualize_error:
                self.validation_messages.append(
                    ("ERROR", f"{error}\n{visualize_error}")
                )
            else:
                self.validation_messages.append(
                    ("ERROR", f"{error}\nBEL: {self.bel_stmt}")
                )
            self.ast = None

        except Exception as e:
            log.error("Error {}, error type: {}".format(e, type(e)))
            self.validation_messages.append(
                ("ERROR", "Error {}, error type: {}".format(e, type(e)))
            )

        return self