예제 #1
0
def main() -> None:
    args = parser.parse_args()
    new_parser = args.parser == "new"
    if args.multiline:
        sep = "\n"
    else:
        sep = " "
    program = sep.join(args.program)
    if new_parser:
        tree = _peg_parser.parse_string(program)

        if args.diff:
            a = _peg_parser.parse_string(program, oldparser=True)
            b = tree
            diff = diff_trees(a, b, args.verbose)
            if diff:
                for line in diff:
                    print(line)
            else:
                print("# Trees are the same")
        else:
            print("# Parsed using the new parser")
            print(format_tree(tree, args.verbose))
    else:
        tree = _peg_parser.parse_string(program, oldparser=True)
        print("# Parsed using the old parser")
        print(format_tree(tree, args.verbose))
예제 #2
0
def main() -> None:
    args = parser.parse_args()
    if args.diff and not args.grammar_file:
        parser.error("-d/--diff requires -g/--grammar-file")
    if args.multiline:
        sep = "\n"
    else:
        sep = " "
    program = sep.join(args.program)
    if args.grammar_file:
        tree = _peg_parser.parse_string(program)

        if args.diff:
            a = tree
            b = _peg_parser.parse_string(program, oldparser=True)
            diff = diff_trees(a, b, args.verbose)
            if diff:
                for line in diff:
                    print(line)
            else:
                print("# Trees are the same")
        else:
            print(f"# Parsed using {args.grammar_file}")
            print(format_tree(tree, args.verbose))
    else:
        tree = _peg_parser.parse_string(program, oldparser=True)
        print("# Parse using the old parser")
        print(format_tree(tree, args.verbose))
예제 #3
0
 def test_incorrect_ast_generation_with_specialized_errors(self) -> None:
     for source, error_text in FAIL_SPECIALIZED_MESSAGE_CASES:
         exc = IndentationError if "indent" in error_text else SyntaxError
         with self.assertRaises(exc) as se:
             peg_parser.parse_string(source)
         self.assertTrue(
             error_text in se.exception.msg,
             f"Actual error message does not match expexted for {source}")
예제 #4
0
 def test_correct_ast_generation_without_pos_info(self) -> None:
     for source in GOOD_BUT_FAIL_SOURCES:
         actual_ast = peg_parser.parse_string(source)
         expected_ast = peg_parser.parse_string(source, oldparser=True)
         self.assertEqual(
             ast.dump(actual_ast),
             ast.dump(expected_ast),
             f"Wrong AST generation for source: {source}",
         )
예제 #5
0
 def test_correct_ast_generatrion_eval(self) -> None:
     for source in EXPRESSIONS_TEST_SOURCES:
         actual_ast = peg_parser.parse_string(source, mode='eval')
         expected_ast = peg_parser.parse_string(source, mode='eval', oldparser=True)
         self.assertEqual(
             ast.dump(actual_ast, include_attributes=True),
             ast.dump(expected_ast, include_attributes=True),
             f"Wrong AST generation for source: {source}",
         )
예제 #6
0
 def test_correct_but_known_to_fail_ast_generation_on_source_files(self) -> None:
     for source in GOOD_BUT_FAIL_SOURCES:
         actual_ast = peg_parser.parse_string(source)
         expected_ast = peg_parser.parse_string(source, oldparser=True)
         self.assertEqual(
             ast.dump(actual_ast, include_attributes=True),
             ast.dump(expected_ast, include_attributes=True),
             f"Wrong AST generation for source: {source}",
         )
예제 #7
0
 def test_correct_ast_generation_on_source_files(self) -> None:
     self.maxDiff = None
     for source in TEST_SOURCES:
         actual_ast = peg_parser.parse_string(source)
         expected_ast = peg_parser.parse_string(source, oldparser=True)
         self.assertEqual(
             ast.dump(actual_ast, include_attributes=True),
             ast.dump(expected_ast, include_attributes=True),
             f"Wrong AST generation for source: {source}",
         )
예제 #8
0
def check_nested_expr(nesting_depth: int) -> bool:
    expr = f"{'(' * nesting_depth}0{')' * nesting_depth}"

    try:
        parse_string(expr)
        print(f"Nesting depth of {nesting_depth} is successful")
        return True
    except Exception as err:
        print(f"{FAIL}(Failed with nesting depth of {nesting_depth}{ENDC}")
        print(f"{FAIL}\t{err}{ENDC}")
        return False
예제 #9
0
def compare_trees(
    actual_tree: ast.AST, file: str, verbose: bool, include_attributes: bool = False,
) -> int:
    with open(file) as f:
        expected_tree = _peg_parser.parse_string(f.read(), oldparser=True)

    expected_text = ast_dump(expected_tree, include_attributes=include_attributes)
    actual_text = ast_dump(actual_tree, include_attributes=include_attributes)
    if actual_text == expected_text:
        if verbose:
            print("Tree for {file}:")
            print(show_parse.format_tree(actual_tree, include_attributes))
        return 0

    print(f"Diffing ASTs for {file} ...")

    expected = show_parse.format_tree(expected_tree, include_attributes)
    actual = show_parse.format_tree(actual_tree, include_attributes)

    if verbose:
        print("Expected for {file}:")
        print(expected)
        print("Actual for {file}:")
        print(actual)
        print(f"Diff for {file}:")

    diff = show_parse.diff_trees(expected_tree, actual_tree, include_attributes)
    for line in diff:
        print(line)

    return 1
예제 #10
0
def parse_file(source: str, file: str, mode: int, oldparser: bool) -> Tuple[Any, float]:
    t0 = time.time()
    if mode == COMPILE:
        result = _peg_parser.compile_string(
            source,
            filename=file,
            oldparser=oldparser,
        )
    else:
        result = _peg_parser.parse_string(
            source,
            filename=file,
            oldparser=oldparser,
            ast=(mode == PARSE),
        )
    t1 = time.time()
    return result, t1 - t0
예제 #11
0
 def test_incorrect_ast_generation_on_source_files(self) -> None:
     for source in FAIL_SOURCES:
         with self.assertRaises(
                 SyntaxError,
                 msg=f"Parsing {source} did not raise an exception"):
             peg_parser.parse_string(source)
예제 #12
0
def is_parsing_failure(source: str) -> bool:
    try:
        _peg_parser.parse_string(source, mode="exec", oldparser=True)
    except SyntaxError:
        return False
    return True
예제 #13
0
def parse_directory(
    directory: str,
    grammar_file: str,
    tokens_file: str,
    verbose: bool,
    excluded_files: List[str],
    skip_actions: bool,
    tree_arg: int,
    short: bool,
    mode: int,
    parser: str,
) -> int:
    if parser == "cpython" and (tree_arg or mode == 0):
        print("Cannot specify tree argument or mode=0 with the cpython parser.", file=sys.stderr)
        return 1

    if not directory:
        print("You must specify a directory of files to test.", file=sys.stderr)
        return 1

    if grammar_file and tokens_file:
        if not os.path.exists(grammar_file):
            print(f"The specified grammar file, {grammar_file}, does not exist.", file=sys.stderr)
            return 1
    else:
        print(
            "A grammar file or a tokens file was not provided - attempting to use existing parser from stdlib...\n"
        )

    if tree_arg:
        assert mode == 1, "Mode should be 1 (parse), when comparing the generated trees"

    # For a given directory, traverse files and attempt to parse each one
    # - Output success/failure for each file
    errors = 0
    files = []
    trees = {}  # Trees to compare (after everything else is done)
    total_seconds = 0

    for file in sorted(glob(f"{directory}/**/*.py", recursive=True)):
        # Only attempt to parse Python files and files that are not excluded
        should_exclude_file = False
        for pattern in excluded_files:
            if PurePath(file).match(pattern):
                should_exclude_file = True
                break

        if not should_exclude_file:
            with tokenize.open(file) as f:
                source = f.read()
            try:
                t0 = time.time()
                if mode == 2:
                    result = _peg_parser.compile_string(
                        source,
                        filename=file,
                        oldparser=parser == "cpython",
                    )
                else:
                    result = _peg_parser.parse_string(
                        source,
                        filename=file,
                        oldparser=parser == "cpython"
                    )
                t1 = time.time()
                total_seconds += (t1 - t0)
                if tree_arg:
                    trees[file] = result
                if not short:
                    report_status(succeeded=True, file=file, verbose=verbose)
            except Exception as error:
                try:
                    _peg_parser.parse_string(source, mode="exec", oldparser=True)
                except Exception:
                    if not short:
                        print(f"File {file} cannot be parsed by either pegen or the ast module.")
                else:
                    report_status(
                        succeeded=False, file=file, verbose=verbose, error=error, short=short
                    )
                    errors += 1
            files.append(file)
    t1 = time.time()

    total_files = len(files)

    total_bytes = 0
    total_lines = 0
    for file in files:
        # Count lines and bytes separately
        with open(file, "rb") as f:
            total_lines += sum(1 for _ in f)
            total_bytes += f.tell()

    print(
        f"Checked {total_files:,} files, {total_lines:,} lines,",
        f"{total_bytes:,} bytes in {total_seconds:,.3f} seconds.",
    )
    if total_seconds > 0:
        print(
            f"That's {total_lines / total_seconds :,.0f} lines/sec,",
            f"or {total_bytes / total_seconds :,.0f} bytes/sec.",
        )

    if short:
        print_memstats()

    if errors:
        print(f"Encountered {errors} failures.", file=sys.stderr)

    # Compare trees (the dict is empty unless -t is given)
    compare_trees_errors = 0
    for file, tree in trees.items():
        if not short:
            print("Comparing ASTs for", file)
        if compare_trees(tree, file, verbose, tree_arg >= 2) == 1:
            compare_trees_errors += 1

    if errors or compare_trees_errors:
        return 1

    return 0
예제 #14
0
def time_notree(source, parser):
    if parser == "old":
        return _peg_parser.parse_string(source, oldparser=True, ast=False)
    else:
        return _peg_parser.parse_string(source, ast=False)
예제 #15
0
def time_parse(source, parser):
    if parser == "old":
        return _peg_parser.parse_string(source, oldparser=True)
    else:
        return _peg_parser.parse_string(source)
예제 #16
0
 def test_fstring_parse_error_tracebacks(self) -> None:
     for source, error_text in FSTRINGS_TRACEBACKS.values():
         with self.assertRaises(SyntaxError) as se:
             peg_parser.parse_string(dedent(source))
         self.assertEqual(error_text, se.exception.text)
예제 #17
0
 def test_tokenizer_errors_are_propagated(self) -> None:
     n = 201
     with self.assertRaisesRegex(SyntaxError,
                                 "too many nested parentheses"):
         peg_parser.parse_string(n * '(' + ')' * n)
예제 #18
0
def show_parse(source: str, verbose: bool = False) -> str:
    tree = _peg_parser.parse_string(source, oldparser=True)
    return format_tree(tree, verbose).rstrip("\n")