17db96d56Sopenharmony_ciimport importlib.util
27db96d56Sopenharmony_ciimport io
37db96d56Sopenharmony_ciimport os
47db96d56Sopenharmony_ciimport pathlib
57db96d56Sopenharmony_ciimport sys
67db96d56Sopenharmony_ciimport textwrap
77db96d56Sopenharmony_ciimport token
87db96d56Sopenharmony_ciimport tokenize
97db96d56Sopenharmony_cifrom typing import IO, Any, Dict, Final, Optional, Type, cast
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_cifrom pegen.build import compile_c_extension
127db96d56Sopenharmony_cifrom pegen.c_generator import CParserGenerator
137db96d56Sopenharmony_cifrom pegen.grammar import Grammar
147db96d56Sopenharmony_cifrom pegen.grammar_parser import GeneratedParser as GrammarParser
157db96d56Sopenharmony_cifrom pegen.parser import Parser
167db96d56Sopenharmony_cifrom pegen.python_generator import PythonParserGenerator
177db96d56Sopenharmony_cifrom pegen.tokenizer import Tokenizer
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ciALL_TOKENS = token.tok_name
207db96d56Sopenharmony_ciEXACT_TOKENS = token.EXACT_TOKEN_TYPES
217db96d56Sopenharmony_ciNON_EXACT_TOKENS = {
227db96d56Sopenharmony_ci    name for index, name in token.tok_name.items() if index not in EXACT_TOKENS.values()
237db96d56Sopenharmony_ci}
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_cidef generate_parser(grammar: Grammar) -> Type[Parser]:
277db96d56Sopenharmony_ci    # Generate a parser.
287db96d56Sopenharmony_ci    out = io.StringIO()
297db96d56Sopenharmony_ci    genr = PythonParserGenerator(grammar, out)
307db96d56Sopenharmony_ci    genr.generate("<string>")
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci    # Load the generated parser class.
337db96d56Sopenharmony_ci    ns: Dict[str, Any] = {}
347db96d56Sopenharmony_ci    exec(out.getvalue(), ns)
357db96d56Sopenharmony_ci    return ns["GeneratedParser"]
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_cidef run_parser(file: IO[bytes], parser_class: Type[Parser], *, verbose: bool = False) -> Any:
397db96d56Sopenharmony_ci    # Run a parser on a file (stream).
407db96d56Sopenharmony_ci    tokenizer = Tokenizer(tokenize.generate_tokens(file.readline))  # type: ignore # typeshed issue #3515
417db96d56Sopenharmony_ci    parser = parser_class(tokenizer, verbose=verbose)
427db96d56Sopenharmony_ci    result = parser.start()
437db96d56Sopenharmony_ci    if result is None:
447db96d56Sopenharmony_ci        raise parser.make_syntax_error("invalid syntax")
457db96d56Sopenharmony_ci    return result
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_cidef parse_string(
497db96d56Sopenharmony_ci    source: str, parser_class: Type[Parser], *, dedent: bool = True, verbose: bool = False
507db96d56Sopenharmony_ci) -> Any:
517db96d56Sopenharmony_ci    # Run the parser on a string.
527db96d56Sopenharmony_ci    if dedent:
537db96d56Sopenharmony_ci        source = textwrap.dedent(source)
547db96d56Sopenharmony_ci    file = io.StringIO(source)
557db96d56Sopenharmony_ci    return run_parser(file, parser_class, verbose=verbose)  # type: ignore # typeshed issue #3515
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_cidef make_parser(source: str) -> Type[Parser]:
597db96d56Sopenharmony_ci    # Combine parse_string() and generate_parser().
607db96d56Sopenharmony_ci    grammar = parse_string(source, GrammarParser)
617db96d56Sopenharmony_ci    return generate_parser(grammar)
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_cidef import_file(full_name: str, path: str) -> Any:
657db96d56Sopenharmony_ci    """Import a python module from a path"""
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci    spec = importlib.util.spec_from_file_location(full_name, path)
687db96d56Sopenharmony_ci    assert spec is not None
697db96d56Sopenharmony_ci    mod = importlib.util.module_from_spec(spec)
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_ci    # We assume this is not None and has an exec_module() method.
727db96d56Sopenharmony_ci    # See https://docs.python.org/3/reference/import.html?highlight=exec_module#loading
737db96d56Sopenharmony_ci    loader = cast(Any, spec.loader)
747db96d56Sopenharmony_ci    loader.exec_module(mod)
757db96d56Sopenharmony_ci    return mod
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ci
787db96d56Sopenharmony_cidef generate_c_parser_source(grammar: Grammar) -> str:
797db96d56Sopenharmony_ci    out = io.StringIO()
807db96d56Sopenharmony_ci    genr = CParserGenerator(grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, out)
817db96d56Sopenharmony_ci    genr.generate("<string>")
827db96d56Sopenharmony_ci    return out.getvalue()
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_cidef generate_parser_c_extension(
867db96d56Sopenharmony_ci    grammar: Grammar, path: pathlib.PurePath, debug: bool = False,
877db96d56Sopenharmony_ci    library_dir: Optional[str] = None,
887db96d56Sopenharmony_ci) -> Any:
897db96d56Sopenharmony_ci    """Generate a parser c extension for the given grammar in the given path
907db96d56Sopenharmony_ci
917db96d56Sopenharmony_ci    Returns a module object with a parse_string() method.
927db96d56Sopenharmony_ci    TODO: express that using a Protocol.
937db96d56Sopenharmony_ci    """
947db96d56Sopenharmony_ci    # Make sure that the working directory is empty: reusing non-empty temporary
957db96d56Sopenharmony_ci    # directories when generating extensions can lead to segmentation faults.
967db96d56Sopenharmony_ci    # Check issue #95 (https://github.com/gvanrossum/pegen/issues/95) for more
977db96d56Sopenharmony_ci    # context.
987db96d56Sopenharmony_ci    assert not os.listdir(path)
997db96d56Sopenharmony_ci    source = path / "parse.c"
1007db96d56Sopenharmony_ci    with open(source, "w", encoding="utf-8") as file:
1017db96d56Sopenharmony_ci        genr = CParserGenerator(
1027db96d56Sopenharmony_ci            grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, file, debug=debug
1037db96d56Sopenharmony_ci        )
1047db96d56Sopenharmony_ci        genr.generate("parse.c")
1057db96d56Sopenharmony_ci    compile_c_extension(
1067db96d56Sopenharmony_ci        str(source),
1077db96d56Sopenharmony_ci        build_dir=str(path),
1087db96d56Sopenharmony_ci        # Significant test_peg_generator speedups
1097db96d56Sopenharmony_ci        disable_optimization=True,
1107db96d56Sopenharmony_ci        library_dir=library_dir,
1117db96d56Sopenharmony_ci    )
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_cidef print_memstats() -> bool:
1157db96d56Sopenharmony_ci    MiB: Final = 2 ** 20
1167db96d56Sopenharmony_ci    try:
1177db96d56Sopenharmony_ci        import psutil  # type: ignore
1187db96d56Sopenharmony_ci    except ImportError:
1197db96d56Sopenharmony_ci        return False
1207db96d56Sopenharmony_ci    print("Memory stats:")
1217db96d56Sopenharmony_ci    process = psutil.Process()
1227db96d56Sopenharmony_ci    meminfo = process.memory_info()
1237db96d56Sopenharmony_ci    res = {}
1247db96d56Sopenharmony_ci    res["rss"] = meminfo.rss / MiB
1257db96d56Sopenharmony_ci    res["vms"] = meminfo.vms / MiB
1267db96d56Sopenharmony_ci    if sys.platform == "win32":
1277db96d56Sopenharmony_ci        res["maxrss"] = meminfo.peak_wset / MiB
1287db96d56Sopenharmony_ci    else:
1297db96d56Sopenharmony_ci        # See https://stackoverflow.com/questions/938733/total-memory-used-by-python-process
1307db96d56Sopenharmony_ci        import resource  # Since it doesn't exist on Windows.
1317db96d56Sopenharmony_ci
1327db96d56Sopenharmony_ci        rusage = resource.getrusage(resource.RUSAGE_SELF)
1337db96d56Sopenharmony_ci        if sys.platform == "darwin":
1347db96d56Sopenharmony_ci            factor = 1
1357db96d56Sopenharmony_ci        else:
1367db96d56Sopenharmony_ci            factor = 1024  # Linux
1377db96d56Sopenharmony_ci        res["maxrss"] = rusage.ru_maxrss * factor / MiB
1387db96d56Sopenharmony_ci    for key, value in res.items():
1397db96d56Sopenharmony_ci        print(f"  {key:12.12s}: {value:10.0f} MiB")
1407db96d56Sopenharmony_ci    return True
141