Parse the text in infile and return the resulting schema
    :param input_: text or input stream to parse
    :param default_base_: base URI for relative URI's in schema
    :return: ShExJ Schema object.  None if error.

def parse(input_: Union[str, FileStream], default_base: Optional[str]=None) -> Optional[Schema]:
    """
    Parse the text in infile and return the resulting schema
    :param input_: text or input stream to parse
    :param default_base_: base URI for relative URI's in schema
    :return: ShExJ Schema object.  None if error.
    """

    # Step 1: Tokenize the input stream
    error_listener = ParseErrorListener()
    if not isinstance(input_, FileStream):
        input_ = InputStream(input_)
    lexer = ShExDocLexer(input_)
    lexer.addErrorListener(error_listener)
    tokens = CommonTokenStream(lexer)
    tokens.fill()
    if error_listener.n_errors:         # Lexer prints errors directly
        return None

    # Step 2: Generate the parse tree
    parser = ShExDocParser(tokens)
    parser.addErrorListener(error_listener)
    parse_tree = parser.shExDoc()
    if error_listener.n_errors:
        print('\n'.join(error_listener.errors), file=sys.stderr)
        return None

    # Step 3: Transform the results the results
    parser = ShexDocParser(default_base=default_base)
    parser.visit(parse_tree)

    return parser.context.schema