├── .gitignore ├── tests ├── test_resources │ ├── error_recovery │ │ ├── simple.pytest.expect_fails │ │ └── simple.pytest │ ├── test_config.json │ ├── unit_tests │ │ ├── more_match │ │ │ ├── match1.pytest.expect_fails │ │ │ ├── match12.pytest.expect_fails │ │ │ ├── match6.pytest.expect_fails │ │ │ ├── match8.pytest │ │ │ ├── match9.pytest │ │ │ ├── match7.pytest │ │ │ ├── match12.pytest │ │ │ ├── match11.pytest │ │ │ ├── match10.pytest │ │ │ └── match6.pytest │ │ ├── sandbox.pytest │ │ ├── ast_async_and_await.pytest │ │ ├── ast_variables.pytest │ │ └── ast_statements.pytest │ ├── invalid_syntax_tests │ │ ├── invalid_types.pytest │ │ ├── invalid_match_case.pytest │ │ ├── invalid_identifiers.pytest │ │ ├── invalid_types.pytest_expected_results │ │ └── invalid_match_case.pytest_expected_results │ └── error_recovery_specific_tests │ │ ├── invalid_grammar.pytest │ │ ├── node_dropping.pytest │ │ ├── invalid_grammar.pytest_expected_results │ │ ├── node_dropping.pytest_expected_results │ │ ├── parso_error_recovery_tests.pytest │ │ ├── milestone_tests.pytest │ │ └── parso_error_recovery_tests.pytest_expected_results ├── dune ├── Cargo.toml ├── test_pretty_printer.py ├── test_invalid_syntax.py ├── test_grammar_support.py ├── test_error_recovery.py ├── test_more_match.py ├── test_parser_pre_process.rs ├── test_sandbox.py └── test_parser_post_process.rs ├── vendor ├── ocaml │ └── interop │ │ ├── rust_to_ocaml │ │ ├── test │ │ │ ├── cases │ │ │ │ ├── bool_alias.rs.exp │ │ │ │ ├── option.rs.exp │ │ │ │ ├── tuple.rs.exp │ │ │ │ ├── my_result.rs.exp │ │ │ │ ├── mutual_rec.rs.exp │ │ │ │ ├── inline_tuple.rs.exp │ │ │ │ ├── type_args.rs.exp │ │ │ │ ├── qualified_name.rs.exp │ │ │ │ ├── tuple_structs.rs.exp │ │ │ │ ├── inline_tuple_bad.rs.exp │ │ │ │ ├── .ocamlformat │ │ │ │ ├── lists.rs.exp │ │ │ │ ├── struct.rs.exp │ │ │ │ ├── bool_alias.rs │ │ │ │ ├── type_name_matches_module_name.rs.exp │ │ │ │ ├── pointers.rs.exp │ │ │ │ ├── tuple.rs │ │ │ │ ├── option.rs │ │ │ │ ├── my_result.rs │ │ │ │ ├── int.rs.exp │ │ │ │ ├── name_attribute.rs.exp │ │ │ │ ├── inline_tuple_bad.rs │ │ │ │ ├── type_args.rs │ │ │ │ ├── mutual_rec.rs │ │ │ │ ├── variants.rs.exp │ │ │ │ ├── tuple_structs.rs │ │ │ │ ├── qualified_name.rs │ │ │ │ ├── lists.rs │ │ │ │ ├── inline_tuple.rs │ │ │ │ ├── attrs.rs.exp │ │ │ │ ├── type_name_matches_module_name.rs │ │ │ │ ├── struct.rs │ │ │ │ ├── int.rs │ │ │ │ ├── pointers.rs │ │ │ │ ├── variants.rs │ │ │ │ ├── name_attribute.rs │ │ │ │ ├── attrs.rs │ │ │ │ ├── doc_comment.rs.exp │ │ │ │ ├── doc_comment.rs │ │ │ │ ├── keywords.rs │ │ │ │ └── keywords.rs.exp │ │ │ ├── config.toml │ │ │ └── Cargo.toml │ │ ├── rust_to_ocaml_attr │ │ │ ├── Cargo.toml │ │ │ └── rust_to_ocaml_attr.rs │ │ ├── attr_parser │ │ │ └── Cargo.toml │ │ ├── rust_to_ocaml │ │ │ ├── Cargo.toml │ │ │ ├── macros.rs │ │ │ └── rewrite_module_names.rs │ │ ├── attr │ │ │ └── rust_to_ocaml_attr.rs │ │ └── src │ │ │ ├── macros.rs │ │ │ └── rewrite_module_names.rs │ │ ├── CHANGELOG.md │ │ ├── oss │ │ ├── CHANGELOG.md │ │ ├── .gitignore │ │ ├── .buckconfig │ │ ├── ocaml-setup.sh │ │ ├── LICENSE │ │ ├── CONTRIBUTING.md │ │ ├── README-BUCK.md │ │ └── CODE_OF_CONDUCT.md │ │ ├── ocamlrep_ocamlpool │ │ ├── dune │ │ ├── test │ │ │ ├── Cargo.toml │ │ │ └── ocamlpool_test.ml │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── ocamlpool.h │ │ ├── cargo_test_utils │ │ ├── Cargo.toml │ │ └── cargo_test_utils.rs │ │ ├── ocamlrep │ │ ├── test │ │ │ ├── test_add_root │ │ │ │ └── Cargo.toml │ │ │ ├── test_from_ocamlrep │ │ │ │ └── Cargo.toml │ │ │ ├── test_from_ocamlrep_in │ │ │ │ └── Cargo.toml │ │ │ ├── test_bindings │ │ │ │ └── Cargo.toml │ │ │ └── test_from_ocamlrep_in.rs │ │ ├── Cargo.toml │ │ ├── from.rs │ │ └── cache.rs │ │ ├── .cargo │ │ └── config.toml │ │ ├── macro_test_util │ │ ├── Cargo.toml │ │ └── macro_test_util.rs │ │ ├── ocamlrep_custom │ │ ├── Cargo.toml │ │ └── test │ │ │ ├── Cargo.toml │ │ │ ├── counter_client.ml │ │ │ ├── test_custom.ml │ │ │ ├── test_custom.rs │ │ │ └── counter.rs │ │ ├── signed_source │ │ └── Cargo.toml │ │ ├── ocamlrep_caml_builtins │ │ ├── Cargo.toml │ │ └── lib.rs │ │ ├── ocamlrep_derive │ │ └── Cargo.toml │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── LICENSE │ │ ├── CONTRIBUTING.md │ │ └── CODE_OF_CONDUCT.md └── tree-sitter-python │ ├── test │ ├── highlight │ │ ├── parameters.py │ │ ├── keywords.py │ │ └── pattern_matching.py │ └── corpus │ │ └── errors.txt │ ├── queries │ ├── tags.scm │ └── highlights.scm │ ├── Cargo.toml │ └── bindings │ └── rust │ ├── build.rs │ ├── README.md │ └── lib.rs ├── rust_to_ocaml_config.toml ├── lib.rs ├── parser.ml ├── ffi_python.pyi ├── .ocamlformat ├── dune ├── dune-project ├── parse_and_print.ml ├── benchmark ├── benchmark_resources │ └── simple.pytest └── benchmark_errpy.py ├── errpy.opam ├── LICENSE ├── Cargo.toml ├── .github └── workflows │ └── cargo_vendor.yml ├── print_ast.rs ├── CONTRIBUTING.md ├── print_cst.rs ├── README.md ├── errors.rs ├── ffi_ocaml.rs ├── ffi_python.rs ├── string_helpers.rs ├── printers └── ast_pretty_print_helper.rs ├── CODE_OF_CONDUCT.md └── parser_post_process.rs /.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery/simple.pytest.expect_fails: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names test_ffi_ocaml) 3 | (libraries ounit2 errpy)) 4 | -------------------------------------------------------------------------------- /tests/test_resources/test_config.json: -------------------------------------------------------------------------------- 1 | { "TEST_ERRPY_RESULTS_NEWFILE" : true } 2 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/bool_alias.rs.exp: -------------------------------------------------------------------------------- 1 | type a = bool 2 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.alpha (December, 2022) 2 | Creation of the repository 3 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.alpha (December, 2022) 2 | Creation of the repository 3 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/option.rs.exp: -------------------------------------------------------------------------------- 1 | type 'a my_option = 2 | | None 3 | | Some of 'a 4 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/tuple.rs.exp: -------------------------------------------------------------------------------- 1 | type tuple_a = a * b 2 | 3 | type tuple_b = a * (b * c) 4 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/my_result.rs.exp: -------------------------------------------------------------------------------- 1 | type ('t, 'e) result = 2 | | Ok of 't 3 | | Err of 'e 4 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/mutual_rec.rs.exp: -------------------------------------------------------------------------------- 1 | type foo = bar * bar 2 | 3 | and bar = foo option * foo option 4 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match1.pytest.expect_fails: -------------------------------------------------------------------------------- 1 | This sample tests basic parsing of match statements as described in PEP 634. 2 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match12.pytest.expect_fails: -------------------------------------------------------------------------------- 1 | This sample tests narrowing of subject subexpressions in match statements. 2 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/inline_tuple.rs.exp: -------------------------------------------------------------------------------- 1 | type e = 2 | | Foo of (a * b) 3 | | Bar of (a * b) 4 | | Baz of a * b 5 | | Qux of a * b 6 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/type_args.rs.exp: -------------------------------------------------------------------------------- 1 | type maybe_a = a option 2 | 3 | type my_result = (b, c) result 4 | 5 | type bar = (d, e, f) Foo.bar 6 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/test/highlight/parameters.py: -------------------------------------------------------------------------------- 1 | def g(h, i, /, j, *, k=100, **kwarg): 2 | # ^ operator 3 | # ^ operator 4 | pass 5 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match6.pytest.expect_fails: -------------------------------------------------------------------------------- 1 | This sample tests type checking for match statements (as described in PEP 634) that contain literal patterns. 2 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/qualified_name.rs.exp: -------------------------------------------------------------------------------- 1 | type a = X.foo 2 | 3 | type b = Y.Z.foo 4 | 5 | type c = My_module.Some_submodule.foo 6 | 7 | type d = x IMap.t 8 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/tuple_structs.rs.exp: -------------------------------------------------------------------------------- 1 | type a = unit 2 | 3 | type b = unit 4 | 5 | type c = unit 6 | 7 | type d = x 8 | 9 | type e = y * z 10 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/inline_tuple_bad.rs.exp: -------------------------------------------------------------------------------- 1 | Error: Failed to convert type E 2 | 3 | Caused by: 4 | Variant Foo must have a single argument which is a tuple 5 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_ocamlpool/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name ocamlpool) 3 | (package errpy) 4 | (wrapped false) 5 | (modules) 6 | (foreign_stubs 7 | (language c) 8 | (names ocamlpool))) 9 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/.ocamlformat: -------------------------------------------------------------------------------- 1 | # -*- conf -*- 2 | 3 | doc-comments = before 4 | field-space = tight-decl 5 | module-item-spacing = sparse 6 | space-around-lists = false 7 | type-decl = sparse 8 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/lists.rs.exp: -------------------------------------------------------------------------------- 1 | type my_vec = x list 2 | 3 | type boxed_slice = x list 4 | 5 | type slice = x list 6 | 7 | type std_vec = x list 8 | 9 | type std_boxed_slice = x list 10 | -------------------------------------------------------------------------------- /rust_to_ocaml_config.toml: -------------------------------------------------------------------------------- 1 | [types] 2 | transparent = [ 3 | "Box", "std::boxed::Box", 4 | "Rc", "std::rc::Rc", 5 | "Arc", "std::sync::Arc", 6 | ] 7 | rename = [ 8 | ["Vec", "list"], 9 | ["std::vec::Vec", "list"], 10 | ["f64", "float"], 11 | ] 12 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | cargo-out 3 | Cargo.lock 4 | buck-out 5 | shim/third-party/rust/BUCK 6 | shim/third-party/rust/vendor 7 | shim/third-party/ocaml/opam 8 | shim/third-party/ocaml/standard_library 9 | **/.DS_Store 10 | **/.cargo 11 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/struct.rs.exp: -------------------------------------------------------------------------------- 1 | type my_struct = { 2 | foo: int; 3 | bar: int; 4 | } 5 | 6 | type struct_a = { 7 | a_foo: int; 8 | a_bar: int; 9 | } 10 | 11 | type struct_b = { 12 | b_foo: int; 13 | b_bar: int; 14 | } 15 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/bool_alias.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type A = bool; 7 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/type_name_matches_module_name.rs.exp: -------------------------------------------------------------------------------- 1 | type a = Pos.t 2 | 3 | type b = Relative_path.t 4 | 5 | type c = Collections.SSet.t 6 | 7 | type t = d 8 | 9 | module Foo = struct 10 | type t = e 11 | 12 | type maybe = t option 13 | end 14 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/pointers.rs.exp: -------------------------------------------------------------------------------- 1 | type box_a = a 2 | 3 | type rc_a = a 4 | 5 | type arc_a = a 6 | 7 | type rc_oc_a = a 8 | 9 | type std_box_a = a 10 | 11 | type std_rc_a = a 12 | 13 | type std_arc_a = a 14 | 15 | type ocamlrep_rc_oc_a = a 16 | 17 | type boxed_tuple = a * b 18 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/tuple.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | type TupleA = (A, B); 7 | 8 | type TupleB = (A, (B, C)); 9 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/option.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub enum MyOption { 7 | None, 8 | Some(A), 9 | } 10 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/my_result.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub enum Result { 7 | Ok(T), 8 | Err(E), 9 | } 10 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/int.rs.exp: -------------------------------------------------------------------------------- 1 | type a = int 2 | 3 | type b = int 4 | 5 | type c = int 6 | 7 | type d = int 8 | 9 | type e = int 10 | 11 | type f = int 12 | 13 | type g = int 14 | 15 | type h = int 16 | 17 | type i = int 18 | 19 | type j = int 20 | 21 | type k = int 22 | 23 | type l = int 24 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/name_attribute.rs.exp: -------------------------------------------------------------------------------- 1 | type a = x 2 | 3 | type b = { 4 | bb_x: x; 5 | bb_y: y; 6 | } 7 | 8 | type c = 9 | | C_foo 10 | | Bar of { 11 | bar_x: x; 12 | bar_y: y; 13 | } 14 | | CBaz 15 | 16 | type a_alias = a 17 | 18 | type b_alias = b 19 | 20 | type c_alias = c 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/inline_tuple_bad.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub enum E { 7 | #[rust_to_ocaml(inline_tuple)] 8 | Foo(Box), 9 | } 10 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/queries/tags.scm: -------------------------------------------------------------------------------- 1 | (class_definition 2 | name: (identifier) @name) @definition.class 3 | 4 | (function_definition 5 | name: (identifier) @name) @definition.function 6 | 7 | (call 8 | function: [ 9 | (identifier) @name 10 | (attribute 11 | attribute: (identifier) @name) 12 | ]) @reference.call 13 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/sandbox.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Tests for playing around async with [problematic?] syntax, debugging etc 7 | 8 | # Expected to be committed empty 9 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/cargo_test_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/cargo_test_utils:cargo_test_utils 2 | 3 | [package] 4 | name = "cargo_test_utils" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "cargo_test_utils.rs" 12 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/type_args.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type MaybeA = Option; 7 | 8 | pub type MyResult = Result; 9 | 10 | pub type Bar = foo::Bar; 11 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/mutual_rec.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub struct Foo(pub Bar, pub Bar); 7 | 8 | #[rust_to_ocaml(and)] 9 | pub struct Bar(pub Option, pub Option); 10 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/variants.rs.exp: -------------------------------------------------------------------------------- 1 | type a = 2 | | I 3 | | J of int 4 | | K of int * int 5 | | L of (int * int) 6 | | M of { 7 | x: int; 8 | y: int; 9 | } 10 | 11 | type prefixed = 12 | | PI 13 | | PJ of int 14 | | PK of int * int 15 | | PL of (int * int) 16 | | PM of { 17 | m_x: int; 18 | m_y: int; 19 | } 20 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/tuple_structs.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub struct A; 7 | 8 | pub struct B(); 9 | 10 | pub struct C(()); 11 | 12 | pub struct D(pub X); 13 | 14 | pub struct E(pub Y, pub Z); 15 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/qualified_name.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type A = x::Foo; 7 | 8 | pub type B = y::z::Foo; 9 | 10 | pub type C = my_module::some_submodule::Foo; 11 | 12 | pub type D = i_map::IMap; 13 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/config.toml: -------------------------------------------------------------------------------- 1 | [modules] 2 | rename = [ 3 | ["i_map", "IMap"], 4 | ["i_set", "ISet"], 5 | ["s_map", "SMap"], 6 | ["s_set", "SSet"], 7 | ] 8 | 9 | [types] 10 | transparent = [ 11 | "Box", "std::boxed::Box", 12 | "Rc", "std::rc::Rc", 13 | "Arc", "std::sync::Arc", 14 | "RcOc", "ocamlrep::rc::RcOc", 15 | ] 16 | rename = [ 17 | ["Vec", "list"], 18 | ["std::vec::Vec", "list"], 19 | ] 20 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/rust_to_ocaml_attr/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/rust_to_ocaml:rust_to_ocaml_attr 2 | 3 | [package] 4 | name = "rust_to_ocaml_attr" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "rust_to_ocaml_attr.rs" 12 | test = false 13 | doctest = false 14 | proc-macro = true 15 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/lists.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type MyVec = Vec; 7 | pub type BoxedSlice = Box<[X]>; 8 | pub type Slice<'a> = &'a [X]; 9 | 10 | pub type StdVec = std::vec::Vec; 11 | pub type StdBoxedSlice = std::boxed::Box<[X]>; 12 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/inline_tuple.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub enum E { 7 | Foo((A, B)), 8 | Bar(Box<(A, B)>), 9 | #[rust_to_ocaml(inline_tuple)] 10 | Baz((A, B)), 11 | #[rust_to_ocaml(inline_tuple)] 12 | Qux(Box<(A, B)>), 13 | } 14 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/test/test_add_root/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep/test:test_add_root 2 | 3 | [package] 4 | name = "test_add_root" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "../test_add_root.rs" 12 | doctest = false 13 | crate-type = ["lib", "staticlib"] 14 | 15 | [dev-dependencies] 16 | ocamlrep = { path = "../.." } 17 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under both the MIT license found in the 4 | # LICENSE-MIT file in the root directory of this source tree and the Apache 5 | # License, Version 2.0 found in the LICENSE-APACHE file in the root directory 6 | # of this source tree. 7 | 8 | [build] 9 | rustflags = [ 10 | "-Drust-2018-idioms", 11 | "-Dwarnings", 12 | "-Dunused-crate-dependencies" 13 | ] 14 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/macro_test_util/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/macro_test_util:macro_test_util 2 | 3 | [package] 4 | name = "macro_test_util" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "macro_test_util.rs" 12 | test = false 13 | doctest = false 14 | 15 | [dependencies] 16 | proc-macro2 = { version = "1.0.70", features = ["span-locations"] } 17 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_custom/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep_custom:ocamlrep_custom 2 | 3 | [package] 4 | name = "ocamlrep_custom" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "lib.rs" 12 | doctest = false 13 | 14 | [dependencies] 15 | ocamlrep = { path = "../ocamlrep" } 16 | ocamlrep_ocamlpool = { path = "../ocamlrep_ocamlpool" } 17 | -------------------------------------------------------------------------------- /lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #[macro_use] 9 | extern crate rust_to_ocaml_attr; 10 | 11 | pub mod ast; 12 | pub mod constants; 13 | pub mod cst_to_ast; 14 | pub mod errors; 15 | pub mod node_wrapper; 16 | pub mod parser_post_process; 17 | pub mod parser_pre_process; 18 | pub mod sitter; 19 | pub mod string_helpers; 20 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/test/test_from_ocamlrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep/test:test_from_ocamlrep 2 | 3 | [package] 4 | name = "test_from_ocamlrep" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "../test_from_ocamlrep.rs" 12 | doctest = false 13 | crate-type = ["lib", "staticlib"] 14 | 15 | [dev-dependencies] 16 | ocamlrep = { path = "../.." } 17 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/.buckconfig: -------------------------------------------------------------------------------- 1 | [repositories] 2 | root = . 3 | prelude = prelude 4 | shim = shim 5 | 6 | [repository_aliases] 7 | config = prelude 8 | ovr_config = prelude 9 | toolchains = shim 10 | fbcode = shim 11 | fbcode_macros = shim 12 | fbsource = shim 13 | buck = shim 14 | 15 | [parser] 16 | target_platform_detector_spec = target:root//...->prelude//platforms:default target:shim//...->prelude//platforms:default 17 | 18 | # Enable this in the fullness of time. 19 | # [buildfile] 20 | # name=TARGETS 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/attr_parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/rust_to_ocaml:attr_parser 2 | 3 | [package] 4 | name = "attr_parser" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "attr_parser.rs" 12 | test = false 13 | doctest = false 14 | 15 | [dependencies] 16 | syn = { version = "1.0.109", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } 17 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/test/highlight/keywords.py: -------------------------------------------------------------------------------- 1 | if foo(): 2 | # <- keyword 3 | pass 4 | # <- keyword 5 | elif bar(): 6 | # <- keyword 7 | pass 8 | else: 9 | # <- keyword 10 | foo 11 | 12 | return 13 | # ^ keyword 14 | raise e 15 | # ^ keyword 16 | 17 | for i in foo(): 18 | # <- keyword 19 | # ^ variable 20 | # ^ operator 21 | # ^ function 22 | continue 23 | # <- keyword 24 | break 25 | # <- keyword 26 | 27 | a and b or c 28 | # ^ operator 29 | # ^ variable 30 | # ^ operator 31 | -------------------------------------------------------------------------------- /tests/test_resources/invalid_syntax_tests/invalid_types.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | ## cannot mix bytes and nonbytes literals 7 | message = b"something " \ 8 | "another {a} line" 9 | message = "something " \ 10 | b"another {a} line" 11 | 12 | ## invalid string prefix 13 | something = a"a string" 14 | something = asdasd"a string" 15 | something = ru"a string" 16 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/tree-sitter-python:tree-sitter-python 2 | 3 | [package] 4 | name = "tree_sitter_python" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | build = "bindings/rust/build.rs" 10 | include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"] 11 | 12 | [lib] 13 | path = "bindings/rust/lib.rs" 14 | 15 | [dependencies] 16 | tree-sitter = "0.20.8" 17 | 18 | [build-dependencies] 19 | cc = "1.0" 20 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/attrs.rs.exp: -------------------------------------------------------------------------------- 1 | (** type X *) 2 | type x = a [@@deriving show] 3 | 4 | (** type Y *) 5 | type y = { 6 | y_foo: a; [@opaque] [@visitors.opaque] (** foo *) 7 | y_bar: b; (** bar *) 8 | } 9 | [@@deriving visitors { variety = "iter"; ancestors = ["iter_ab"] }] 10 | 11 | (** type Visibility *) 12 | type visibility = 13 | | VPrivate [@visitors.name "visibility_VPrivate"] (** Private *) 14 | | VPublic [@visitors.name "visibility_VPublic"] (** Public *) 15 | [@@deriving eq, ord, show { with_path = false }] 16 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/type_name_matches_module_name.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type A = pos::Pos; 7 | 8 | pub type B = crate::relative_path::RelativePath; 9 | 10 | pub type C = collections::s_set::SSet; 11 | 12 | pub type TypeNameMatchesModuleName = D; 13 | 14 | pub mod foo { 15 | pub type Foo = E; 16 | pub type Maybe = Option; 17 | } 18 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/signed_source/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/signed_source:signed_source 2 | 3 | [package] 4 | name = "signed_source" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "signed_source.rs" 12 | doctest = false 13 | 14 | [dependencies] 15 | bstr = { version = "1.10.0", features = ["serde", "std", "unicode"] } 16 | hex = "0.4.3" 17 | md-5 = "0.10" 18 | once_cell = "1.12" 19 | regex = "1.9.2" 20 | thiserror = "2" 21 | -------------------------------------------------------------------------------- /parser.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | *) 7 | 8 | (** [parse_module input] takes the string [input] and parse it as a Python module, represented by a 9 | list of {!type: Ast.mod_}. 10 | 11 | In the future we can add support for type comments here *) 12 | external parse_module 13 | : string -> 14 | (Ast.mod_ * Ast.recoverableerrorwithlocation list, string) result 15 | = "parse_module" 16 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/test/test_from_ocamlrep_in/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep/test:test_from_ocamlrep_in 2 | 3 | [package] 4 | name = "test_from_ocamlrep_in" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "../test_from_ocamlrep_in.rs" 12 | doctest = false 13 | crate-type = ["lib", "staticlib"] 14 | 15 | [dev-dependencies] 16 | bumpalo = { version = "3.14.0", features = ["collections"] } 17 | ocamlrep = { path = "../.." } 18 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/struct.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub struct MyStruct { 7 | pub foo: isize, 8 | pub bar: isize, 9 | } 10 | 11 | #[rust_to_ocaml(prefix = "a_")] 12 | pub struct StructA { 13 | pub foo: isize, 14 | pub bar: isize, 15 | } 16 | 17 | #[rust_to_ocaml(prefix = "b_")] 18 | pub struct StructB { 19 | pub foo: isize, 20 | pub bar: isize, 21 | } 22 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/int.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type A = i8; 7 | 8 | pub type B = u8; 9 | 10 | pub type C = i16; 11 | 12 | pub type D = u16; 13 | 14 | pub type E = i32; 15 | 16 | pub type F = u32; 17 | 18 | pub type G = i64; 19 | 20 | pub type H = u64; 21 | 22 | pub type I = i128; 23 | 24 | pub type J = u128; 25 | 26 | pub type K = isize; 27 | 28 | pub type L = usize; 29 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/tests:[test_parser_post_process,test_parser_pre_process,test_string_helpers] 2 | 3 | [package] 4 | name = "python_errpy_tests" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [[test]] 11 | name = "test_parser_post_process" 12 | path = "test_parser_post_process.rs" 13 | 14 | [[test]] 15 | name = "test_parser_pre_process" 16 | path = "test_parser_pre_process.rs" 17 | 18 | [[test]] 19 | name = "test_string_helpers" 20 | path = "test_string_helpers.rs" 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_caml_builtins/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep_caml_builtins:ocamlrep_caml_builtins 2 | 3 | [package] 4 | name = "ocamlrep_caml_builtins" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "lib.rs" 12 | test = false 13 | doctest = false 14 | 15 | [dependencies] 16 | ocamlrep = { path = "../ocamlrep" } 17 | ocamlrep_custom = { path = "../ocamlrep_custom" } 18 | serde = { version = "1.0.185", features = ["derive", "rc"] } 19 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/pointers.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub type BoxA = Box; 7 | pub type RcA = Rc; 8 | pub type ArcA = Arc; 9 | pub type RcOcA = RcOc; 10 | 11 | pub type StdBoxA = std::boxed::Box; 12 | pub type StdRcA = std::rc::Rc; 13 | pub type StdArcA = std::sync::Arc; 14 | pub type OcamlrepRcOcA = ocamlrep::rc::RcOc; 15 | 16 | pub type BoxedTuple = Box<(Box, Box)>; 17 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_ocamlpool/test/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep_ocamlpool/test:ocamlpool_test 2 | 3 | [package] 4 | name = "ocamlpool_test" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "ocamlpool_test.rs" 12 | doctest = false 13 | crate-type = ["lib", "staticlib"] 14 | 15 | [dependencies] 16 | anyhow = "1.0.95" 17 | cargo_test_utils = { path = "../../cargo_test_utils" } 18 | ocamlrep_ocamlpool = { path = ".." } 19 | once_cell = "1.12" 20 | tempfile = "3.8" 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_custom/test/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep_custom/test:counter 2 | 3 | [package] 4 | name = "counter" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "counter.rs" 12 | doctest = false 13 | crate-type = ["lib", "staticlib"] 14 | 15 | [dependencies] 16 | anyhow = "1.0.95" 17 | cargo_test_utils = { path = "../../cargo_test_utils" } 18 | ocamlrep_custom = { path = ".." } 19 | ocamlrep_ocamlpool = { path = "../../ocamlrep_ocamlpool" } 20 | tempfile = "3.8" 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/rust_to_ocaml/test:test_rust_to_ocaml 2 | 3 | [package] 4 | name = "test_rust_to_ocaml" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [[bin]] 11 | name = "test_rust_to_ocaml" 12 | path = "test_rust_to_ocaml.rs" 13 | 14 | [dependencies] 15 | anyhow = "1.0.95" 16 | clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } 17 | similar = { version = "2.2.0", features = ["inline"] } 18 | walkdir = "2.3" 19 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_ocamlpool/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep_ocamlpool:ocamlrep_ocamlpool 2 | 3 | [package] 4 | name = "ocamlrep_ocamlpool" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | build = "build.rs" 10 | 11 | [lib] 12 | path = "lib.rs" 13 | test = false 14 | doctest = false 15 | crate-type = ["lib", "staticlib"] 16 | 17 | [dependencies] 18 | bumpalo = { version = "3.14.0", features = ["collections"] } 19 | ocamlrep = { path = "../ocamlrep" } 20 | 21 | [build-dependencies] 22 | cc = "1.0.78" 23 | -------------------------------------------------------------------------------- /ffi_python.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # pyre-unsafe 7 | 8 | def py_parse_module_print_ast(input_code: str) -> tuple[str, str]: ... 9 | def py_parse_module_print_ast_and_pretty_print(input_code: str) -> tuple[str, str]: ... 10 | def py_parse_module_print_ast_pretty_print_only(input_code: str) -> tuple[str, str]: ... 11 | def py_parse_module_print_ast_pretty_and_errors( 12 | input_code: str, 13 | ) -> tuple[str, str, str]: ... 14 | def py_parse_module(input_code: str) -> None: ... 15 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/variants.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub enum A { 7 | I, 8 | J(isize), 9 | K(isize, isize), 10 | L((isize, isize)), 11 | M { x: isize, y: isize }, 12 | } 13 | 14 | #[rust_to_ocaml(prefix = "P")] 15 | pub enum Prefixed { 16 | I, 17 | J(isize), 18 | K(isize, isize), 19 | L((isize, isize)), 20 | #[rust_to_ocaml(prefix = "m_")] 21 | M { 22 | x: isize, 23 | y: isize, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/invalid_grammar.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Examples of invalid Python grammar that we are able to partially accept 7 | # but for which we may wish to consider outputting a notification for that 8 | # shows the input as being partially syntatically imperfect 9 | 10 | ## Confusion with dot operator and def 11 | a.b def something(): 12 | doith() 13 | 14 | ## lhs assignment is incorrect 15 | a.b.c.3 = 9 16 | 17 | ## Used to blow up the parser 18 | a=3\ng=7 19 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/node_dropping.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # This is a series of tests which try focusing on error recovery which look 7 | # at the specific way ERRPY acts in cases where there are recoverable errors 8 | # in input on the likes of block statements, sub-expressions, tuples, 9 | # arguments etc 10 | 11 | 12 | ## Avoid panic on function artuments having incorrect expression syntax 13 | def testcase(): 14 | f = 9 15 | foo(1, 2, a.3, 4, identifier1 identifier2, 6, 7, identifier 8, 9, 10) 16 | d=9 17 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep:ocamlrep 2 | 3 | [package] 4 | name = "ocamlrep" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "lib.rs" 12 | doctest = false 13 | 14 | [dependencies] 15 | bstr = { version = "1.10.0", features = ["serde", "std", "unicode"] } 16 | bumpalo = { version = "3.14.0", features = ["collections"] } 17 | indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } 18 | ocamlrep_derive = { path = "../ocamlrep_derive" } 19 | rustc-hash = "2.1.0" 20 | serde = { version = "1.0.185", features = ["derive", "rc"] } 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/test/test_bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep/test:test_bindings 2 | 3 | [package] 4 | name = "test_bindings" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "../test_bindings.rs" 12 | doctest = false 13 | crate-type = ["lib", "staticlib"] 14 | 15 | [dependencies] 16 | anyhow = "1.0.95" 17 | cargo_test_utils = { path = "../../../cargo_test_utils" } 18 | ocamlrep = { path = "../.." } 19 | ocamlrep_caml_builtins = { path = "../../../ocamlrep_caml_builtins" } 20 | ocamlrep_ocamlpool = { path = "../../../ocamlrep_ocamlpool" } 21 | tempfile = "3.8" 22 | -------------------------------------------------------------------------------- /tests/test_resources/invalid_syntax_tests/invalid_match_case.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | ## cannot use general expression in place of a case pattern 7 | match xx: 8 | case x>2: 9 | pass 10 | 11 | ## various types of invalid complex numbers 12 | match x: 13 | case 3j + 5j: # invalud, first part must be real 14 | pass 15 | case -3j + 5j: # invalud, first part must be real 16 | pass 17 | case 3 + 5: #inlvauid, second part must be imaginary 18 | pass 19 | case 3 + -5j: # invalid imaginary number cannot be negative 20 | pass 21 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/ocamlrep_derive:ocamlrep_derive 2 | 3 | [package] 4 | name = "ocamlrep_derive" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "lib.rs" 12 | test = false 13 | doctest = false 14 | proc-macro = true 15 | 16 | [dependencies] 17 | proc-macro2 = { version = "1.0.70", features = ["span-locations"] } 18 | quote = "1.0.29" 19 | syn = { version = "1.0.109", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } 20 | synstructure = "0.12" 21 | 22 | [dev-dependencies] 23 | anyhow = "1.0.95" 24 | macro_test_util = { path = "../macro_test_util" } 25 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | 4 | let mut c_config = cc::Build::new(); 5 | c_config.include(src_dir); 6 | c_config 7 | .flag_if_supported("-Wno-unused-parameter") 8 | .flag_if_supported("-Wno-unused-but-set-variable") 9 | .flag_if_supported("-Wno-trigraphs"); 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | 13 | let scanner_path = src_dir.join("scanner.c"); 14 | c_config.file(&scanner_path); 15 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 16 | 17 | c_config.compile("parser"); 18 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 19 | } 20 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_custom/test/counter_client.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | *) 8 | 9 | type counter 10 | 11 | external counter_new : unit -> counter = "counter_new" 12 | 13 | external counter_inc : counter -> unit = "counter_inc" 14 | 15 | external counter_read : counter -> int = "counter_read" 16 | 17 | let () = begin 18 | print_endline "[counter_test][info]: start"; 19 | let counter = counter_new () in 20 | assert (counter_read counter == 0); 21 | counter_inc counter; 22 | assert (counter_read counter == 1); 23 | print_endline "[counter_test][info]: finish"; 24 | end 25 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | exclude = [ 4 | # This directory relates to the buck build, not the cargo build. 5 | "shim/third-party/rust", 6 | ] 7 | 8 | members = [ 9 | "macro_test_util", 10 | "ocamlrep", 11 | "ocamlrep_caml_builtins", 12 | "ocamlrep_custom", 13 | "ocamlrep_derive", 14 | "ocamlrep_ocamlpool", 15 | "rust_to_ocaml/attr_parser", 16 | "rust_to_ocaml/rust_to_ocaml", 17 | "rust_to_ocaml/rust_to_ocaml_attr", 18 | "signed_source", 19 | 20 | "cargo_test_utils", 21 | "ocamlrep_custom/test", 22 | "ocamlrep_ocamlpool/test", 23 | "ocamlrep/test/test_bindings", 24 | "ocamlrep/test/test_from_ocamlrep", 25 | "ocamlrep/test/test_from_ocamlrep_in", 26 | "ocamlrep/test/test_add_root", 27 | ] 28 | -------------------------------------------------------------------------------- /tests/test_pretty_printer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree 6 | 7 | # pyre-strict 8 | 9 | import unittest 10 | 11 | from python.errpy.tests.utils.test_common import ASTTestCommon, PRETTY_PRINTER_TESTS_DIR 12 | 13 | 14 | class AllGrammarTests(ASTTestCommon): 15 | """These tests focus on individual types of bugs found in errpy which have been fixed""" 16 | 17 | def test_pretty_printer_bugs(self) -> None: 18 | self.check_many_cases_in_file( 19 | "pretty_printer_bugs.pytest", flavour=PRETTY_PRINTER_TESTS_DIR 20 | ) 21 | 22 | 23 | if __name__ == "__main__": 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/test/corpus/errors.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | An error before a string literal 3 | ================================================================================ 4 | 5 | def a(b): 6 | c. 7 | 8 | """ 9 | d 10 | """ 11 | 12 | e 13 | 14 | -------------------------------------------------------------------------------- 15 | 16 | (module 17 | (function_definition 18 | (identifier) 19 | (parameters 20 | (identifier)) 21 | (ERROR 22 | (identifier)) 23 | (block 24 | (expression_statement 25 | (string 26 | (string_start) 27 | (string_content) 28 | (string_end))) 29 | (expression_statement 30 | (identifier))))) 31 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | assignment-operator=end-line 2 | break-cases=all 3 | break-fun-decl=fit-or-vertical 4 | break-infix=fit-or-vertical 5 | break-infix-before-func 6 | break-separators=after 7 | break-sequences 8 | dock-collection-brackets=true 9 | doc-comments=before 10 | exp-grouping=preserve 11 | field-space=tight-decl 12 | if-then-else=fit-or-vertical 13 | indicate-nested-or-patterns=unsafe-no 14 | let-and=sparse 15 | let-binding-spacing=sparse 16 | margin=100 17 | ocp-indent-compat 18 | parens-tuple=multi-line-only 19 | parens-tuple-patterns=multi-line-only 20 | sequence-style=terminator 21 | sequence-blank-line=preserve-one 22 | single-case=sparse 23 | space-around-lists=false 24 | space-around-records 25 | space-around-variants 26 | type-decl=sparse 27 | wrap-comments 28 | wrap-fun-args=false 29 | parse-docstrings 30 | -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (env 2 | (dev 3 | (flags 4 | (:standard -w A-30)) 5 | (ocamlopt_flags 6 | (-g -Oclassic))) 7 | (release 8 | (flags 9 | (:standard -w A-30)) 10 | (ocamlopt_flags (-O3)))) 11 | 12 | (rule 13 | (targets libffi_ocaml.a) 14 | (locks /cargo) 15 | (deps (source_tree .)) 16 | (action 17 | (no-infer 18 | (progn 19 | (run cargo build --release) 20 | (copy target/release/libffi_ocaml.a libffi_ocaml.a))))) 21 | 22 | (library 23 | (name errpy) 24 | (modules parser ast) 25 | (public_name errpy) 26 | (flags -linkall) 27 | (preprocess 28 | (pps ppx_deriving.show)) 29 | (libraries ocamlpool) 30 | (c_library_flags -lstdc++ -lpthread) 31 | (no_dynlink) 32 | (foreign_archives ffi_ocaml)) 33 | 34 | (executable 35 | (name parse_and_print) 36 | (modules parse_and_print) 37 | (libraries errpy)) 38 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 3.4) 2 | 3 | (name errpy) 4 | 5 | (version 0.0.10) 6 | 7 | (generate_opam_files true) 8 | 9 | (maintainers "pyre@meta.com") 10 | 11 | (authors "pyre@meta.com") 12 | 13 | (bug_reports "https://github.com/facebook/errpy/issues") 14 | 15 | (homepage "https://github.com/facebook/errpy") 16 | 17 | (source (github "facebook/errpy" ) ) 18 | 19 | (formatting 20 | (enabled_for dune) 21 | ) 22 | 23 | (license MIT) 24 | 25 | (package 26 | (name errpy) 27 | (synopsis "Errpy: An Error Recovering Python Parser implemented in Rust") 28 | (description "This is a new project to provide an error-recovering Python parser, implemented in Rust based on tree-sitter. Our short-term goal is to use it in the pyre type checker, in order to provide better IDE features.") 29 | (depends ocaml dune ppx_deriving (conf-rust-2021 ( and :build)) (ounit2 (and :with-test))) 30 | ) 31 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/invalid_grammar.pytest_expected_results: -------------------------------------------------------------------------------- 1 | @generated 2 | 3 | ##Confusion with dot operator and def 4 | def something(): 5 | doith() 6 | 7 | 1 Recoverable error detected: 8 | SyntaxError: invalid syntax at [1:1 - 1:4] 9 | CST Stack: 10 | {Node ERROR (0, 0) - (0, 3)} 11 | {Node module (0, 0) - (2, 0)} 12 | 13 | ##lhs assignment is incorrect 14 | 9 15 | 1 Recoverable error detected: 16 | SyntaxError: invalid syntax at [1:1 - 1:10] 17 | CST Stack: 18 | {Node ERROR (0, 0) - (0, 9)} 19 | {Node module (0, 0) - (1, 0)} 20 | 21 | ##Used to blow up the parser 22 | a = g = 7 23 | 1 Recoverable error detected: 24 | SyntaxError: invalid syntax at [1:3 - 1:6] 25 | CST Stack: 26 | {Node ERROR (0, 2) - (0, 5)} 27 | {Node assignment (0, 0) - (0, 8)} 28 | {Node expression_statement (0, 0) - (0, 8)} 29 | {Node module (0, 0) - (1, 0)} 30 | 31 | -------------------------------------------------------------------------------- /parse_and_print.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | *) 7 | 8 | print_endline ("Running on OCaml version: " ^ Sys.ocaml_version) 9 | 10 | module Errpyast = Errpy.Ast 11 | module Errpyparser = Errpy.Parser 12 | 13 | let () = 14 | let print_recoverable_errors recoverable_errors = 15 | recoverable_errors 16 | |> List.map Errpyast.show_recoverableerrorwithlocation 17 | |> String.concat ", " 18 | |> Format.asprintf "[%s]" 19 | in 20 | match Errpyparser.parse_module Sys.argv.(1) with 21 | | Ok (mod_, recoverable_errors) -> 22 | Printf.printf 23 | "\nParser produced AST:\n%s\n\nRecoverable Errors:\n%s\n" 24 | (Errpyast.show_mod_ mod_) 25 | (print_recoverable_errors recoverable_errors) 26 | | Error err -> Printf.eprintf "Parser error: %s\n" err 27 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/name_attribute.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #[rust_to_ocaml(name = "aa")] // ignored 7 | type A = X; 8 | 9 | #[rust_to_ocaml(name = "bb")] // ignored 10 | struct B { 11 | #[rust_to_ocaml(name = "bb_x")] 12 | foo: x, 13 | #[rust_to_ocaml(name = "bb_y")] 14 | bar: y, 15 | } 16 | 17 | #[rust_to_ocaml(name = "cc")] // ignored 18 | #[rust_to_ocaml(prefix = "C")] 19 | enum C { 20 | #[rust_to_ocaml(name = "C_foo")] 21 | Foo, 22 | #[rust_to_ocaml(name = "Bar")] 23 | Bar { 24 | #[rust_to_ocaml(name = "bar_x")] 25 | foo: x, 26 | #[rust_to_ocaml(name = "bar_y")] 27 | bar: y, 28 | }, 29 | Baz, 30 | } 31 | 32 | type a_alias = a; 33 | 34 | type b_alias = b; 35 | 36 | type c_alias = c; 37 | -------------------------------------------------------------------------------- /benchmark/benchmark_resources/simple.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # a simple program using a subset of python grammar, functions, expressions and some control flow 7 | 8 | def fib(a: int) -> int: 9 | # just does some nonsense assignment 10 | tmp = a, a 11 | ba, bb = tmp 12 | inset = a in {0,1} 13 | if a <= 2 or a ==1: 14 | return 1 15 | elif inset: 16 | return 1 17 | else: 18 | return fib(a-1) + fib(a-2) 19 | 20 | def somethingelse(): 21 | pass 22 | 23 | def format_a_string(astring: str) -> str: 24 | return ','.join([x for x in astring]) 25 | 26 | def main() -> int: 27 | """DOC_STRING""" 28 | somethingelse() 29 | fib(12) 30 | astring = format_a_string() 31 | return 0 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match8.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests keyword-only class pattern matching for dataclasses. 10 | 11 | from dataclasses import dataclass, field 12 | 13 | @dataclass 14 | class Point: 15 | optional: int | None = field(default=None, kw_only=True) 16 | x: int 17 | y: int 18 | 19 | 20 | obj = Point(1, 2) 21 | match obj: 22 | case Point(x, y, optional=opt): 23 | reveal_type(x, expected_text="int") 24 | reveal_type(y, expected_text="int") 25 | reveal_type(opt, expected_text="int | None") 26 | distance = (x ** 2 + y ** 2) ** 0.5 27 | -------------------------------------------------------------------------------- /tests/test_resources/invalid_syntax_tests/invalid_identifiers.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Ensure correct rejection of syntatically incorrect input given grammar defined under: https://docs.python.org/3/library/ast.html :: 3.10.7 grammar 7 | 8 | 9 | ## keyword cant be identifer 10 | and 11 | 12 | ## empty space cannot be identifier 13 | a[] # reject empty space identifer 14 | 15 | ## async and await are keywords 16 | x = xx.async # keyword 17 | x = xx.await # keyword 18 | 19 | 20 | ## keywords cant be imported 21 | import a.def 22 | import def 23 | import a.async 24 | import async 25 | from and import a 26 | from a import and 27 | from a import f as and 28 | from ...and.bar import a as b, c 29 | 30 | def and(): 31 | pass 32 | 33 | class and(): 34 | pass 35 | 36 | def foo(and): 37 | pass 38 | 39 | foo(and) 40 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/rust_to_ocaml/Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy/vendor/ocaml/interop/rust_to_ocaml:rust_to_ocaml 2 | 3 | [package] 4 | name = "rust_to_ocaml" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [[bin]] 11 | name = "rust_to_ocaml" 12 | path = "rust_to_ocaml.rs" 13 | test = false 14 | 15 | [dependencies] 16 | anyhow = "1.0.95" 17 | attr_parser = { path = "../attr_parser" } 18 | clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } 19 | convert_case = "0.6" 20 | derive_more = { version = "1.0.0", features = ["full"] } 21 | indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } 22 | serde = { version = "1.0.185", features = ["derive", "rc"] } 23 | signed_source = { path = "../../signed_source" } 24 | syn = { version = "1.0.109", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } 25 | toml = "0.8.4" 26 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery/simple.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | ## Simple broad case 7 | 8 | # a simple program using a subset of python grammar, functions, expressions and some control flow 9 | 10 | def fib(a: int) -> int: 11 | # just does some nonsense assignment 12 | tmp = a, a 13 | ba, bb = tmp 14 | inset = a in {0,1} 15 | if a <= 2 or a ==1: 16 | return 1 17 | elif inset: 18 | return 1 19 | else: 20 | return fib(a-1) + fib(a-2) 21 | 22 | def somethingelse(): 23 | pass 24 | 25 | def format_a_string(astring: str) -> str: 26 | return ','.join([x for x in astring]) 27 | 28 | def main() -> int: 29 | """DOC_STRING""" 30 | somethingelse() 31 | fib(12) 32 | astring = format_a_string() 33 | return 0 34 | 35 | if __name__ == "__main__": 36 | main() 37 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/attr/rust_to_ocaml_attr.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use proc_macro::TokenStream; 7 | 8 | /// This attribute macro is intended to be consumed by the rust_to_ocaml codegen 9 | /// tool, so this proc macro doesn't need to do anything other than return the 10 | /// item (with the rust_to_ocaml attribute stripped by rustc). 11 | /// 12 | /// Use of the rust_to_ocaml attribute in positions other than items (like field 13 | /// definitions) are stripped by ocamlrep_derive macros (which is simpler than 14 | /// filtering them from the `item` in this crate). 15 | /// 16 | /// We may want to add validation later so that incorrect use of the attribute 17 | /// emits errors at compile time, but stripping is good enough for now. 18 | #[proc_macro_attribute] 19 | pub fn rust_to_ocaml(_attr: TokenStream, item: TokenStream) -> TokenStream { 20 | item 21 | } 22 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/ocaml-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Meta Platforms, Inc. and affiliates. 4 | # 5 | # This source code is licensed under both the MIT license found in the 6 | # LICENSE-MIT file in the root directory of this source tree and the Apache 7 | # License, Version 2.0 found in the LICENSE-APACHE file in the root directory 8 | # of this source tree. 9 | 10 | set -euxo pipefail 11 | 12 | if ! command -v opam &> /dev/null 13 | then 14 | echo "opam is not installed, which is a dependency for building targets in ocaml." 15 | exit 16 | fi 17 | 18 | set +u 19 | if [ -z "$OPAM_SWITCH_PREFIX" ]; then 20 | echo "OPAM_SWITCH_PREFIX is undefined. First execute \`eval (\$opam env)\` and then try running $0 again." 21 | exit 22 | fi 23 | set -u 24 | 25 | # Link 'third-party/ocaml/opam'. 26 | if [ ! -L shim/third-party/ocaml/opam ]; then 27 | (cd shim/third-party/ocaml && ln -s "$OPAM_SWITCH_PREFIX" opam) 28 | else 29 | echo "Link 'shim/third-party/ocaml/opam' exists. To overwrite it, first remove it and run $0 again" 30 | fi 31 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/rust_to_ocaml_attr/rust_to_ocaml_attr.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use proc_macro::TokenStream; 7 | 8 | /// This attribute macro is intended to be consumed by the rust_to_ocaml codegen 9 | /// tool, so this proc macro doesn't need to do anything other than return the 10 | /// item (with the rust_to_ocaml attribute stripped by rustc). 11 | /// 12 | /// Use of the rust_to_ocaml attribute in positions other than items (like field 13 | /// definitions) are stripped by ocamlrep_derive macros (which is simpler than 14 | /// filtering them from the `item` in this crate). 15 | /// 16 | /// We may want to add validation later so that incorrect use of the attribute 17 | /// emits errors at compile time, but stripping is good enough for now. 18 | #[proc_macro_attribute] 19 | pub fn rust_to_ocaml(_attr: TokenStream, item: TokenStream) -> TokenStream { 20 | item 21 | } 22 | -------------------------------------------------------------------------------- /errpy.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | version: "0.0.10" 4 | synopsis: "Errpy: An Error Recovering Python Parser implemented in Rust" 5 | description: 6 | "This is a new project to provide an error-recovering Python parser, implemented in Rust based on tree-sitter. Our short-term goal is to use it in the pyre type checker, in order to provide better IDE features." 7 | maintainer: ["pyre@meta.com"] 8 | authors: ["pyre@meta.com"] 9 | license: "MIT" 10 | homepage: "https://github.com/facebook/errpy" 11 | bug-reports: "https://github.com/facebook/errpy/issues" 12 | depends: [ 13 | "ocaml" 14 | "dune" {>= "3.4"} 15 | "ppx_deriving" 16 | "conf-rust-2021" {build} 17 | "ounit2" {with-test} 18 | "odoc" {with-doc} 19 | ] 20 | build: [ 21 | ["dune" "subst"] {dev} 22 | [ 23 | "dune" 24 | "build" 25 | "-p" 26 | name 27 | "-j" 28 | jobs 29 | "@install" 30 | "@runtest" {with-test} 31 | "@doc" {with-doc} 32 | ] 33 | ] 34 | dev-repo: "git+https://github.com/facebook/errpy.git" 35 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/attrs.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | /// type X 7 | #[rust_to_ocaml(attr = "deriving show")] 8 | pub type X = A; 9 | 10 | /// type Y 11 | #[rust_to_ocaml( 12 | prefix = "y_", 13 | attr = r#"deriving visitors { 14 | variety = "iter"; 15 | ancestors = ["iter_ab"]; 16 | }"# 17 | )] 18 | pub struct Y { 19 | /// foo 20 | #[rust_to_ocaml(attr = "opaque")] 21 | #[rust_to_ocaml(attr = "visitors.opaque")] 22 | pub foo: A, 23 | /// bar 24 | pub bar: B, 25 | } 26 | 27 | /// type Visibility 28 | #[rust_to_ocaml(prefix = "V", attr = "deriving eq, ord, show { with_path = false }")] 29 | enum Visibility { 30 | /// Private 31 | #[rust_to_ocaml(attr = "visitors.name \"visibility_VPrivate\"")] 32 | Private, 33 | /// Public 34 | #[rust_to_ocaml(attr = r#"visitors.name "visibility_VPublic""#)] 35 | Public, 36 | } 37 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/README.md: -------------------------------------------------------------------------------- 1 | # ocamlrep: Interop OCaml and Rust code [![facebook](https://circleci.com/gh/facebook/ocamlrep.svg?style=svg)](https://app.circleci.com/pipelines/github/facebook/ocamlrep) 2 | 3 | The goal of this project is to make OCaml and Rust code interoperable. While in its early stage of development, this library is actively used by the [HHVM](https://github.com/facebook/hhvm) project, mostly in the Hack type checker, to enable OCaml code to rely on Rust. 4 | 5 | ## Requirements 6 | This project is stand-alone and requires or works with: 7 | - OCaml 4.14.0; 8 | - A rust nightly toolchain. 9 | 10 | ## Building ocamlrep 11 | 12 | There are two build methods. One way is to use [Cargo](https://doc.rust-lang.org/cargo/guide/cargo-home.html) and the other is to use [Buck2](https://buck2.build/). 13 | 14 | - Cargo: Install OPAM then `cargo build`. 15 | - Buck2: See this guide for [Building ocamlrep with Buck2](README-BUCK.md). 16 | 17 | ## Contributing 18 | See the [CONTRIBUTING](CONTRIBUTING.md) file for how to help out. 19 | 20 | ## License 21 | ocamlrep has an MIT license. See the LICENSE file included in this distribution. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Meta Platforms, Inc. and affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Meta Platforms, Inc. and affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/ast_async_and_await.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Ensure correct AST creation given units defined under: https://docs.python.org/3/library/ast.html :: 3.10.7 grammar 7 | # Note: the guide referenced above is not complete, so we've extended it here where appropriate 8 | 9 | 10 | ## AsyncFunctionDef(name, args, body, decorator_list, returns, type_comment) 11 | 12 | ## Await(value) 13 | async def f(): 14 | await other_func() 15 | 16 | ## AsyncFor(target, iter, body, orelse, type_comment) 17 | async for a in y: 18 | somethign() 19 | 20 | async for a in y: 21 | somethign() 22 | else: 23 | ysss() 24 | 25 | ## AsyncWith(items, body, type_comment) 26 | async with a: 27 | something(a) 28 | 29 | async with a, c as d: 30 | something(b, c, d) 31 | 32 | async with a as b, c as d: 33 | something(b, d) 34 | 35 | async with open('file_path', 'w') as file: 36 | file.write('hello world !') 37 | 38 | async with A() as a, B() as b, C() as c: 39 | doSomething(a,b,c) 40 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Meta Platforms, Inc. and affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_ocamlpool/test/ocamlpool_test.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | *) 8 | 9 | external test : unit -> unit = "test" 10 | external test_call_ocaml_from_rust : unit -> unit = "test_call_ocaml_from_rust" 11 | 12 | let f_unit_to_unit (): unit = () 13 | let f_one_arg_to_unit (x: int) = assert (x = 3) 14 | let f_sum_tuple ((x,y):int*int): int = x + y 15 | 16 | (* Although the test is entirely written in rust, 17 | * we need to build the rust code with the ocaml runtime dependencies 18 | * in order to allocate memory for ocaml. Calling rust from ocaml 19 | * is a good way of ensuring this dependecy is built. 20 | *) 21 | let () = begin 22 | print_endline "[ocamlpool_test][info]: start"; 23 | 24 | Callback.register "f_unit_to_unit" f_unit_to_unit; 25 | Callback.register "f_one_arg_to_unit" f_one_arg_to_unit; 26 | Callback.register "f_sum_tuple" f_sum_tuple; 27 | 28 | test (); 29 | 30 | test_call_ocaml_from_rust (); 31 | 32 | print_endline "[ocamlpool_test][info]: finish" 33 | end 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # @generated by autocargo from //python/errpy:[ffi_ocaml,print_ast,print_cst] 2 | 3 | [package] 4 | name = "ffi_ocaml" 5 | version = "0.0.0" 6 | edition = "2021" 7 | repository = "https://github.com/facebook/errpy" 8 | license = "MIT" 9 | 10 | [lib] 11 | path = "ffi_ocaml.rs" 12 | test = false 13 | doctest = false 14 | crate-type = ["lib", "staticlib"] 15 | 16 | [[bin]] 17 | name = "print_ast" 18 | path = "print_ast.rs" 19 | 20 | [[bin]] 21 | name = "print_cst" 22 | path = "print_cst.rs" 23 | 24 | [dependencies] 25 | bstr = { version = "1.10.0", features = ["serde", "std", "unicode"] } 26 | clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } 27 | itertools = "0.14.0" 28 | num_enum = "0.5" 29 | ocamlrep = { path = "vendor/ocaml/interop/ocamlrep" } 30 | ocamlrep_ocamlpool = { path = "vendor/ocaml/interop/ocamlrep_ocamlpool" } 31 | phf = { version = "0.11", features = ["macros"] } 32 | regex = "1.9.2" 33 | rust_to_ocaml_attr = { path = "vendor/ocaml/interop/rust_to_ocaml/rust_to_ocaml_attr" } 34 | serde = { version = "1.0.185", features = ["derive", "rc"] } 35 | thiserror = "2" 36 | tree-sitter = "0.20.8" 37 | tree_sitter_python = { path = "vendor/tree-sitter-python" } 38 | -------------------------------------------------------------------------------- /tests/test_invalid_syntax.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree 6 | 7 | # pyre-strict 8 | 9 | import unittest 10 | 11 | from python.errpy.tests.utils.error_recovery_common import ErrorRecoveryCommon 12 | from python.errpy.tests.utils.test_common import INVALID_SYNTAX_TESTS_DIR 13 | 14 | 15 | class ExpectedFailureTests(ErrorRecoveryCommon): 16 | """These tests focus aspects of invalid syntax we wish to explicitly fail on""" 17 | 18 | def test_invalid_keywords(self) -> None: 19 | self.compare_recovered_ast_many( 20 | "invalid_identifiers.pytest", test_dir=INVALID_SYNTAX_TESTS_DIR 21 | ) 22 | 23 | def test_invalid_types(self) -> None: 24 | self.compare_recovered_ast_many( 25 | "invalid_types.pytest", test_dir=INVALID_SYNTAX_TESTS_DIR 26 | ) 27 | 28 | def test_invalid_match_case(self) -> None: 29 | self.compare_recovered_ast_many( 30 | "invalid_match_case.pytest", test_dir=INVALID_SYNTAX_TESTS_DIR 31 | ) 32 | 33 | 34 | if __name__ == "__main__": 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /.github/workflows/cargo_vendor.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: cargo vendor and commit 6 | run-name: cargo vendor 🚚 on - ${{ github.event.head_commit.message }} 7 | jobs: 8 | cargo_vendor: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Delete release branch 12 | uses: dawidd6/action-delete-branch@v3 13 | with: 14 | soft_fail: true # set to true so that if the branch doesn't exist action doesnt fail 15 | branches: release 16 | - uses: actions/checkout@v2 17 | - uses: dtolnay/rust-toolchain@stable 18 | - run: cargo vendor vendor_external 19 | - name: Create .cargo dir 20 | run: | 21 | mkdir -p .cargo 22 | - name: Create cargo config 23 | shell: bash 24 | run: | 25 | cat << EOF > .cargo/config.toml 26 | [source.crates-io] 27 | replace-with = "vendored-sources" 28 | 29 | [source.vendored-sources] 30 | directory = "vendor_external" 31 | EOF 32 | - name: commit to 'new' release branch 33 | uses: EndBug/add-and-commit@v9.1.1 34 | with: 35 | new_branch: release 36 | author_name: GitHub Action 37 | author_email: action@github.com 38 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/node_dropping.pytest_expected_results: -------------------------------------------------------------------------------- 1 | @generated 2 | 3 | ##Avoid panic on function artuments having incorrect expression syntax 4 | def testcase(): 5 | f = 9 6 | foo(1, 2, a, 4, identifier1, 6, 7, identifier, 9, 10) 7 | d = 9 8 | 9 | 3 Recoverable errors detected: 10 | SyntaxError: invalid syntax at [3:16 - 3:18] 11 | CST Stack: 12 | {Node ERROR (2, 15) - (2, 17)} 13 | {Node argument_list (2, 7) - (2, 73)} 14 | {Node call (2, 4) - (2, 73)} 15 | {Node expression_statement (2, 4) - (2, 73)} 16 | {Node block (1, 4) - (3, 7)} 17 | {Node function_definition (0, 0) - (3, 7)} 18 | {Node module (0, 0) - (4, 0)} 19 | SyntaxError: invalid syntax at [3:35 - 3:46] 20 | CST Stack: 21 | {Node ERROR (2, 34) - (2, 45)} 22 | {Node argument_list (2, 7) - (2, 73)} 23 | {Node call (2, 4) - (2, 73)} 24 | {Node expression_statement (2, 4) - (2, 73)} 25 | {Node block (1, 4) - (3, 7)} 26 | {Node function_definition (0, 0) - (3, 7)} 27 | {Node module (0, 0) - (4, 0)} 28 | SyntaxError: invalid syntax at [3:65 - 3:66] 29 | CST Stack: 30 | {Node ERROR (2, 64) - (2, 65)} 31 | {Node argument_list (2, 7) - (2, 73)} 32 | {Node call (2, 4) - (2, 73)} 33 | {Node expression_statement (2, 4) - (2, 73)} 34 | {Node block (1, 4) - (3, 7)} 35 | {Node function_definition (0, 0) - (3, 7)} 36 | {Node module (0, 0) - (4, 0)} 37 | 38 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/bindings/rust/README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-python 2 | 3 | This crate provides a Python grammar for the [tree-sitter][] parsing library. 4 | To use this crate, add it to the `[dependencies]` section of your `Cargo.toml` 5 | file. (Note that you will probably also need to depend on the 6 | [`tree-sitter`][tree-sitter crate] crate to use the parsed result in any useful 7 | way.) 8 | 9 | ``` toml 10 | [dependencies] 11 | tree-sitter = "0.17" 12 | tree-sitter-python = "0.17" 13 | ``` 14 | 15 | Typically, you will use the [language][language func] function to add this 16 | grammar to a tree-sitter [Parser][], and then use the parser to parse some code: 17 | 18 | ``` rust 19 | let code = r#" 20 | def double(x): 21 | return x * 2 22 | "#; 23 | let mut parser = Parser::new(); 24 | parser.set_language(tree_sitter_python::language()).expect("Error loading Python grammar"); 25 | let parsed = parser.parse(code, None); 26 | ``` 27 | 28 | If you have any questions, please reach out to us in the [tree-sitter 29 | discussions] page. 30 | 31 | [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 32 | [language func]: https://docs.rs/tree-sitter-python/*/tree_sitter_python/fn.language.html 33 | [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 34 | [tree-sitter]: https://tree-sitter.github.io/ 35 | [tree-sitter crate]: https://crates.io/crates/tree-sitter 36 | [tree-sitter discussions]: https://github.com/tree-sitter/tree-sitter/discussions 37 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/ast_variables.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Ensure correct AST creation given units defined under: https://docs.python.org/3/library/ast.html :: 3.10.7 grammar 7 | # Note: the guide referenced above is not complete, so we've extended it here where appropriate 8 | 9 | 10 | ## Name(id, ctx) 11 | a 12 | 13 | ## Load 14 | a 15 | 16 | ## Store 17 | a = 1 # lhs has store applied, rhs is load 18 | 19 | ## Del 20 | del a 21 | del a, b, c 22 | 23 | ## Starred(value, ctx) 24 | a, *b = it 25 | 26 | ## simple assignments 27 | a=9 28 | b=a 29 | 30 | ## simple assignments 2 31 | a=9 32 | b=a 33 | 34 | ## Assign(targets, value, type_comment) 35 | a = b = 1 36 | a,b = c 37 | 38 | b = 99 39 | a = 88 40 | c = d = 6 41 | 42 | ## AnnAssign(target, annotation, value, simple) 43 | c: int 44 | aa: int = 99 45 | (a): int = 1 46 | a.b: int 47 | a[1]: int 48 | 49 | ## AugAssign(target, op, value) 50 | x += 2 51 | 52 | ## tuple and list assignments rhs and lhs 53 | (a,b) = xxx 54 | a, b = xxx 55 | [a,b] = xxx 56 | 57 | xx = [1,2] 58 | xx = (1,2) 59 | xx = 1,2 60 | 61 | xx = {1,2} 62 | 63 | ## yield on rhs of assignment 64 | ff = yield 65 | ff = yield yy 66 | ff = yield from yy 67 | 68 | ## bugfix 1 item tuple, should resolve to just name unless ended with comma 69 | b = (a) 70 | b = (a,) 71 | 72 | (a) = 3 73 | (a, ) = 3 74 | -------------------------------------------------------------------------------- /print_ast.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #[macro_use] 9 | extern crate rust_to_ocaml_attr; 10 | 11 | use std::path::PathBuf; 12 | 13 | use crate::printers::parse_module_print_ast_code; 14 | use crate::printers::read_stdin_or_file; 15 | use crate::printers::PrintingMode; 16 | 17 | pub mod ast; 18 | pub mod constants; 19 | pub mod cst_to_ast; 20 | pub mod errors; 21 | pub mod node_wrapper; 22 | pub mod parser_post_process; 23 | pub mod parser_pre_process; 24 | pub mod printers; 25 | pub mod sitter; 26 | pub mod string_helpers; 27 | 28 | /// Python Parser which will output AST pretty printed. 29 | /// Usage: `print_ast file.py` or `echo "print('hello')" | print_ast`. 30 | /// With a build system such as buck, `buck run //path/to/errpy:print_ast -- file.py` and `echo "print('test')" | buck run //path/to/errpy:print_ast -` 31 | #[derive(clap::Parser)] 32 | struct Args { 33 | /// Python file to generate AST for 34 | input_file: PathBuf, 35 | } 36 | 37 | fn main() { 38 | let args = ::parse(); 39 | 40 | let input_file = read_stdin_or_file(args.input_file); 41 | let (ast, errors) = parse_module_print_ast_code(input_file, PrintingMode::ASTAndPrettyPrintAST); 42 | if errors.is_empty() { 43 | println!("{}", ast); 44 | } else { 45 | print!("{}\n{}", ast, errors); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/doc_comment.rs.exp: -------------------------------------------------------------------------------- 1 | (** Type A *) 2 | type a = x 3 | 4 | (** Type B 5 | * is int 6 | *) 7 | type b = x 8 | 9 | (** Type C has a fenced code block: 10 | * 11 | * ``` 12 | * function f(): int { 13 | * return 0; 14 | * } 15 | * ``` 16 | * 17 | * And an unfenced code block: 18 | * 19 | * function g(): int { 20 | * return 0; 21 | * } 22 | * 23 | * It should stay indented. 24 | *) 25 | type c = x 26 | 27 | (** Type D has a multiline delimited comment: 28 | 29 | ``` 30 | function f(): int { 31 | return 0; 32 | } 33 | ``` 34 | 35 | And an indented code block: 36 | 37 | ``` 38 | function g(): int { 39 | return 0; 40 | } 41 | ``` 42 | *) 43 | type d = x 44 | 45 | (** Records can have comments on the fields. *) 46 | type record = { 47 | foo: x; (** The comments come after the field declaration in OCaml. *) 48 | bar: x; (** bar comment *) 49 | } 50 | 51 | (** Variant types can have comments on each variant. *) 52 | type variant = 53 | | Foo 54 | (** Again, the comments come after the variant declaration. 55 | * Multiline comments are understood. *) 56 | | Bar 57 | (** Bar has a multiline delimited comment, even though it's 58 | unusual in Rust source. *) 59 | | Baz of { 60 | a: x; 61 | b: x; 62 | } (** Baz comment *) 63 | | Qux of { 64 | q_a: x; 65 | q_b: x; 66 | } 67 | (** Qux is a struct-like variant with a long comment spanning 68 | * multiple lines. *) 69 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Errpy 2 | 3 | ## Issues 4 | 5 | We use GitHub issues to track public bugs. Please ensure your description is 6 | clear and has sufficient instructions to be able to reproduce the issue. 7 | We want to make contributing to this project as easy and transparent as 8 | possible. 9 | 10 | ## Contributor License Agreement ("CLA") 11 | 12 | In order to accept your pull request, we need you to submit a CLA. You only need 13 | to do this once to work on any of Facebook's open source projects. 14 | 15 | Complete your CLA here: . If you have any questions, 16 | please drop us a line at cla@fb.com. 17 | 18 | You are also expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md), 19 | so please read that if you are a new contributor. 20 | 21 | ## Developing Errpy 22 | 23 | Unfortunately at the moment it is difficult to build Errpy because we 24 | are still in the process of open-sourcing a dependency, 25 | [ocamlrep](https://github.com/facebook/ocamlrep). It is possible to build, 26 | but requires cloning `ocamlrep` and setting paths appropriately. 27 | 28 | If you are interested in working on Errpy, please file an issue and we'll 29 | work on the docs for this, in the meantime we are focused on getting 30 | `ocamlrep` into the Rust repository in crates.io so that the build will be 31 | come standard. 32 | 33 | ## License 34 | 35 | By contributing to Pyre, you agree that your contributions will be licensed 36 | under the LICENSE file in the root directory of this source tree. 37 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ocamlrep 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Our Development Process 6 | The project is in its early steps and the process is not yet finalized. 7 | 8 | ## Pull Requests 9 | We actively welcome your pull requests. 10 | 11 | 1. Fork the repo and create your branch from `main`. 12 | 2. Code shall pass our test-suite. 13 | 3. If you've changed APIs, update the documentation. 14 | 4. If you haven't already, complete the Contributor License Agreement ("CLA"). 15 | 16 | ## Contributor License Agreement ("CLA") 17 | In order to accept your pull request, we need you to submit a CLA. You only need 18 | to do this once to work on any of Facebook's open source projects. 19 | 20 | Complete your CLA here: 21 | 22 | ## Issues 23 | We use GitHub issues to track public bugs. Please ensure your description is 24 | clear and has sufficient instructions to be able to reproduce the issue. 25 | 26 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 27 | disclosure of security bugs. In those cases, please go through the process 28 | outlined on that page and do not file a public issue. 29 | 30 | ## Coding Style 31 | * 2 spaces for indentation rather than tabs 32 | * 80 character line length if possible 33 | 34 | ## License 35 | By contributing to ocamlrep, you agree that your contributions will be licensed 36 | under the LICENSE file in the root directory of this source tree. 37 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ocamlrep 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Our Development Process 6 | The project is in its early steps and the process is not yet finalized. 7 | 8 | ## Pull Requests 9 | We actively welcome your pull requests. 10 | 11 | 1. Fork the repo and create your branch from `main`. 12 | 2. Code shall pass our test-suite. 13 | 3. If you've changed APIs, update the documentation. 14 | 4. If you haven't already, complete the Contributor License Agreement ("CLA"). 15 | 16 | ## Contributor License Agreement ("CLA") 17 | In order to accept your pull request, we need you to submit a CLA. You only need 18 | to do this once to work on any of Facebook's open source projects. 19 | 20 | Complete your CLA here: 21 | 22 | ## Issues 23 | We use GitHub issues to track public bugs. Please ensure your description is 24 | clear and has sufficient instructions to be able to reproduce the issue. 25 | 26 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 27 | disclosure of security bugs. In those cases, please go through the process 28 | outlined on that page and do not file a public issue. 29 | 30 | ## Coding Style 31 | * 2 spaces for indentation rather than tabs 32 | * 80 character line length if possible 33 | 34 | ## License 35 | By contributing to ocamlrep, you agree that your contributions will be licensed 36 | under the LICENSE file in the root directory of this source tree. 37 | -------------------------------------------------------------------------------- /tests/test_grammar_support.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree 6 | 7 | # pyre-strict 8 | 9 | import unittest 10 | 11 | from python.errpy.tests.utils.test_common import ASTTestCommon 12 | 13 | 14 | class AllGrammarTests(ASTTestCommon): 15 | """These tests focus on individual aspects of Python syntax""" 16 | 17 | def test_ast_expressions(self) -> None: 18 | self.check_many_cases_in_file("ast_expressions.pytest") 19 | 20 | def test_ast_async_and_await(self) -> None: 21 | self.check_many_cases_in_file("ast_async_and_await.pytest") 22 | 23 | def test_ast_control_flow(self) -> None: 24 | self.check_many_cases_in_file("ast_control_flow.pytest") 25 | 26 | def test_ast_variables(self) -> None: 27 | self.check_many_cases_in_file("ast_variables.pytest") 28 | 29 | def test_ast_functions_and_classes(self) -> None: 30 | self.check_many_cases_in_file("ast_functions_and_classes.pytest") 31 | 32 | def test_ast_literals(self) -> None: 33 | self.check_many_cases_in_file("ast_literals.pytest") 34 | 35 | def test_ast_pattern_matching(self) -> None: 36 | self.check_many_cases_in_file("ast_pattern_matching.pytest") 37 | 38 | def test_ast_statements(self) -> None: 39 | self.check_many_cases_in_file("ast_statements.pytest") 40 | 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/cargo_test_utils/cargo_test_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | #![feature(exit_status_error)] 6 | 7 | use std::process::Command; 8 | use std::process::ExitStatusError; 9 | 10 | pub fn cmd(prog: &str, args: &[&str], dir: Option<&std::path::Path>) -> Command { 11 | let mut prog_cmd = Command::new(prog); 12 | if let Some(path) = dir { 13 | prog_cmd.current_dir(path); 14 | } 15 | prog_cmd.args(args); 16 | prog_cmd 17 | } 18 | 19 | pub fn workspace_dir(ds: &[&str]) -> std::path::PathBuf { 20 | let mut cargo_cmd = cmd( 21 | "cargo", 22 | &["locate-project", "--workspace", "--message-format=plain"], 23 | None, 24 | ); 25 | let output = cargo_cmd.output().unwrap().stdout; 26 | let root_cargo_toml = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()); 27 | let mut p = root_cargo_toml.parent().unwrap().to_path_buf(); 28 | for d in ds { 29 | p.push(d); 30 | } 31 | p 32 | } 33 | 34 | pub fn run(mut cmd: Command) -> Result<(), ExitStatusError> { 35 | cmd.spawn().unwrap().wait().ok().unwrap().exit_ok() 36 | } 37 | 38 | pub fn fmt_exit_status_err(err: ExitStatusError) -> String { 39 | format!("error status: {err}") 40 | } 41 | 42 | pub fn build_flavor() -> &'static str { 43 | if cfg!(debug_assertions) { 44 | "debug" 45 | } else { 46 | "release" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/test/highlight/pattern_matching.py: -------------------------------------------------------------------------------- 1 | match command.split(): 2 | # ^ keyword 3 | case ["quit"]: 4 | # ^ keyword 5 | print("Goodbye!") 6 | quit_game() 7 | case ["look"]: 8 | # ^ keyword 9 | current_room.describe() 10 | case ["get", obj]: 11 | # ^ keyword 12 | character.get(obj, current_room) 13 | case ["go", direction]: 14 | # ^ keyword 15 | current_room = current_room.neighbor(direction) 16 | # The rest of your commands go here 17 | 18 | match command.split(): 19 | # ^ keyword 20 | case ["drop", *objects]: 21 | # ^ keyword 22 | for obj in objects: 23 | character.drop(obj, current_room) 24 | 25 | match command.split(): 26 | # ^ keyword 27 | case ["quit"]: ... # Code omitted for brevity 28 | case ["go", direction]: pass 29 | case ["drop", *objects]: pass 30 | case _: 31 | print(f"Sorry, I couldn't understand {command!r}") 32 | 33 | match command.split(): 34 | # ^ keyword 35 | case ["north"] | ["go", "north"]: 36 | # ^ keyword 37 | current_room = current_room.neighbor("north") 38 | case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]: 39 | # ^ keyword 40 | pass 41 | 42 | match = 2 43 | # ^ variable 44 | match, a = 2, 3 45 | # ^ variable 46 | match: int = secret 47 | # ^ variable 48 | x, match: str = 2, "hey, what's up?" 49 | # <- variable 50 | # ^ variable 51 | 52 | if match := re.fullmatch(r"(-)?(\d+:)?\d?\d:\d\d(\.\d*)?", time, flags=re.ASCII): 53 | # ^ variable 54 | return match 55 | -------------------------------------------------------------------------------- /print_cst.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #[macro_use] 9 | extern crate rust_to_ocaml_attr; 10 | 11 | use std::path::PathBuf; 12 | 13 | use clap::ArgAction; 14 | use clap::Parser as ClapParser; 15 | 16 | use crate::printers::read_stdin_or_file; 17 | use crate::printers::CSTPrinter; 18 | 19 | pub mod ast; 20 | pub mod constants; 21 | pub mod cst_to_ast; 22 | pub mod errors; 23 | pub mod node_wrapper; 24 | pub mod parser_post_process; 25 | pub mod parser_pre_process; 26 | pub mod printers; 27 | pub mod sitter; 28 | pub mod string_helpers; 29 | 30 | /// Python Parser which will output CST pretty printed. 31 | /// Usage: `print_cst file.py` or `echo "print('hello')" | print_cst`. 32 | /// With a build system such as buck, `buck run //path/to/errpy:print_cst -- file.py` and `echo "print('test')" | buck run //path/to/errpy:print_cst -` 33 | #[derive(ClapParser)] 34 | struct Args { 35 | /// Python file to generate CST for 36 | input_file: PathBuf, 37 | 38 | /// If the error nodes should be filtered in the output CST 39 | /// (default is to include all nodes) 40 | #[clap(long, short, action=ArgAction::SetTrue)] 41 | filter_errors: bool, 42 | } 43 | 44 | fn main() { 45 | let args = ::parse(); 46 | 47 | let input_file = read_stdin_or_file(args.input_file); 48 | 49 | let filter_errors = args.filter_errors; 50 | 51 | CSTPrinter::new(input_file).print_cst(filter_errors); 52 | } 53 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match9.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests class-based pattern matching when the class is marked final and can be discriminated based on the argument patterns. 10 | 11 | from typing import final 12 | 13 | 14 | class A: 15 | title: str 16 | 17 | class B: 18 | name: str 19 | 20 | class C: 21 | name: str 22 | 23 | def func1(r: A | B | C): 24 | match r: 25 | case object(title=_): 26 | reveal_type(r, expected_text='A | B | C') 27 | 28 | case object(name=_): 29 | reveal_type(r, expected_text='A | B | C') 30 | 31 | case _: 32 | reveal_type(r, expected_text='A | B | C') 33 | 34 | @final 35 | class AFinal: 36 | title: str 37 | 38 | @final 39 | class BFinal: 40 | name: str 41 | 42 | @final 43 | class CFinal: 44 | name: str 45 | 46 | @final 47 | class DFinal: 48 | nothing: str 49 | 50 | 51 | def func2(r: AFinal | BFinal | CFinal | DFinal): 52 | match r: 53 | case object(title=_): 54 | reveal_type(r, expected_text='AFinal') 55 | 56 | case object(name=_): 57 | reveal_type(r, expected_text='BFinal | CFinal') 58 | 59 | case _: 60 | reveal_type(r, expected_text='DFinal') 61 | -------------------------------------------------------------------------------- /tests/test_resources/invalid_syntax_tests/invalid_types.pytest_expected_results: -------------------------------------------------------------------------------- 1 | @generated 2 | 3 | ##cannot mix bytes and nonbytes literals 4 | message = 'something another {a} line' 5 | message = b'something another {a} line' 6 | 2 Recoverable errors detected: 7 | SyntaxError: cannot mix bytes and nonbytes literals at [1:11 - 2:29] 8 | CST Stack: 9 | {Node concatenated_string (0, 10) - (1, 28)} 10 | {Node assignment (0, 0) - (1, 28)} 11 | {Node expression_statement (0, 0) - (1, 28)} 12 | {Node module (0, 0) - (4, 0)} 13 | SyntaxError: cannot mix bytes and nonbytes literals at [3:11 - 4:30] 14 | CST Stack: 15 | {Node concatenated_string (2, 10) - (3, 29)} 16 | {Node assignment (2, 0) - (3, 29)} 17 | {Node expression_statement (2, 0) - (3, 29)} 18 | {Node module (0, 0) - (4, 0)} 19 | 20 | ##invalid string prefix 21 | something = 'a string' 22 | something = 'a string' 23 | something = 'u"a string' 24 | 3 Recoverable errors detected: 25 | SyntaxError: invalid syntax at [1:13 - 1:14] 26 | CST Stack: 27 | {Node ERROR (0, 12) - (0, 13)} 28 | {Node assignment (0, 0) - (0, 23)} 29 | {Node expression_statement (0, 0) - (0, 23)} 30 | {Node module (0, 0) - (3, 0)} 31 | SyntaxError: invalid syntax at [2:13 - 2:19] 32 | CST Stack: 33 | {Node ERROR (1, 12) - (1, 18)} 34 | {Node assignment (1, 0) - (1, 28)} 35 | {Node expression_statement (1, 0) - (1, 28)} 36 | {Node module (0, 0) - (3, 0)} 37 | UnexpectedExpression: Invalid string prefix: "ru" at [3:13 - 3:25] 38 | CST Stack: 39 | {Node string (2, 12) - (2, 24)} 40 | {Node assignment (2, 0) - (2, 24)} 41 | {Node expression_statement (2, 0) - (2, 24)} 42 | {Node module (0, 0) - (3, 0)} 43 | 44 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/parso_error_recovery_tests.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Collection of tests from https://github.com/davidhalter/parso/blob/master/test/test_error_recovery.py 7 | # Parso is able to handle these instances, at the very least ERRPY should be 8 | # able to recover a reasonable AST for these instances 9 | 10 | ## test_with_stmt 11 | code = before 12 | 13 | with x: f. 14 | a 15 | 16 | code = after 17 | 18 | ## test_one_line_function 19 | code = before 20 | 21 | def x(): f. 22 | 23 | code = after 24 | 25 | ## test_one_line_function 2 26 | code = before 27 | 28 | def x(a: 29 | 30 | code = after 31 | 32 | ## test_if_else 33 | code = before 34 | 35 | if x: 36 | f. 37 | else: 38 | g( 39 | 40 | code = after 41 | 42 | ## test_if_stmt 43 | code = before 44 | 45 | if x: f. 46 | else: g( 47 | 48 | code = after 49 | 50 | ## test_invalid_token 51 | code = before 52 | 53 | a + ? + b 54 | 55 | code = after 56 | 57 | ## test_invalid_token_in_fstr 58 | code = before 59 | 60 | f"{a + ? + b}" 61 | 62 | code = after 63 | 64 | ## test_dedent_issues1 65 | code = before 66 | 67 | class C: 68 | @property 69 | f 70 | g 71 | end 72 | 73 | code = after 74 | 75 | ## test_dedent_issues2 76 | code = before 77 | 78 | class C: 79 | @property 80 | if 1: 81 | g 82 | else: 83 | h 84 | end 85 | 86 | code = after 87 | 88 | ## test_dedent_issues3 89 | code = before 90 | 91 | class C: 92 | f 93 | g 94 | 95 | code = after 96 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_ocamlpool/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // Assume an opam environment (`eval "$(opam env --switch=default 4 | // --set-switch)"`) then to find the prevailing standard library caml 5 | // headers, `OCAMLLIB=$(ocamlopt.opt -config | grep standard_library: 6 | // | awk '{ print $2 }')`. 7 | fn ocamllib_dir() -> std::path::PathBuf { 8 | let mut sh = std::process::Command::new("sh"); 9 | sh.args([ 10 | "-c", 11 | "ocamlopt.opt -config | grep standard_library: | awk '{ print $2 }'", 12 | ]); 13 | let output = sh.output().unwrap().stdout; 14 | let proposed_path = std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()); 15 | // A supercaml 'ocamlopt.opt' can report standard library paths that don't 16 | // exist. 17 | if proposed_path.exists() { 18 | proposed_path.to_path_buf() 19 | } else { 20 | // Fallback to guessing the location given knowledge of where 21 | // 'ocamlopt.opt' itself it. 22 | let mut sh = std::process::Command::new("sh"); 23 | sh.args(["-c", "which ocamlopt.opt"]); 24 | let output = sh.output().unwrap().stdout; 25 | std::path::Path::new(std::str::from_utf8(&output).unwrap().trim()) 26 | .ancestors() 27 | .nth(2) 28 | .unwrap() 29 | .join("lib/ocaml") 30 | } 31 | } 32 | 33 | fn main() { 34 | // Tell Cargo that if the given file changes, to rerun this build script. 35 | println!("cargo:rerun-if-changed=ocamlpool.c"); 36 | cc::Build::new() 37 | .include(ocamllib_dir().as_path().to_str().unwrap()) 38 | .file("ocamlpool.c") 39 | .compile("ocamlpool"); 40 | } 41 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/doc_comment.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | /// Type A 7 | pub type A = X; 8 | 9 | /// Type B 10 | /// is int 11 | pub type B = X; 12 | 13 | /// Type C has a fenced code block: 14 | /// 15 | /// ``` 16 | /// function f(): int { 17 | /// return 0; 18 | /// } 19 | /// ``` 20 | /// 21 | /// And an unfenced code block: 22 | /// 23 | /// function g(): int { 24 | /// return 0; 25 | /// } 26 | /// 27 | /// It should stay indented. 28 | pub type C = X; 29 | 30 | /** Type D has a multiline delimited comment: 31 | 32 | ``` 33 | function f(): int { 34 | return 0; 35 | } 36 | ``` 37 | 38 | And an indented code block: 39 | 40 | ``` 41 | function g(): int { 42 | return 0; 43 | } 44 | ``` 45 | */ 46 | pub type D = X; 47 | 48 | /// Records can have comments on the fields. 49 | pub struct Record { 50 | /// The comments come after the field declaration in OCaml. 51 | pub foo: X, 52 | /// bar comment 53 | pub bar: X, 54 | } 55 | 56 | /// Variant types can have comments on each variant. 57 | pub enum Variant { 58 | /// Again, the comments come after the variant declaration. 59 | /// Multiline comments are understood. 60 | Foo, 61 | /** Bar has a multiline delimited comment, even though it's 62 | unusual in Rust source. */ 63 | Bar, 64 | /// Baz comment 65 | Baz { a: X, b: X }, 66 | /// Qux is a struct-like variant with a long comment spanning 67 | /// multiple lines. 68 | #[rust_to_ocaml(prefix = "q_")] 69 | Qux { a: X, b: X }, 70 | } 71 | -------------------------------------------------------------------------------- /tests/test_error_recovery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree 6 | 7 | # pyre-strict 8 | 9 | import unittest 10 | 11 | from python.errpy.tests.utils.error_recovery_common import ErrorRecoveryCommon 12 | 13 | 14 | class AllLargeInputTests(ErrorRecoveryCommon): 15 | """These tests focus on ERRPY error recovery across a large input 16 | with simulated input source errors""" 17 | 18 | def test_simple_er_char_by_char(self) -> None: 19 | self.check_error_recovery_char_by_char("simple.pytest") 20 | 21 | def test_simple_er_insert_whitespace(self) -> None: 22 | self.check_error_recovery_insert_whitespace("simple.pytest") 23 | 24 | def test_simple_er_nth_removed(self) -> None: 25 | self.check_error_recovery_nth_removed("simple.pytest") 26 | 27 | def test_simple_er_insert_keyword(self) -> None: 28 | self.check_error_recovery_insert_keyword("simple.pytest") 29 | 30 | def test_simple_er_insert_garbage(self) -> None: 31 | self.check_error_recovery_insert_garbage("simple.pytest") 32 | 33 | 34 | class TestSpecificInputs(ErrorRecoveryCommon): 35 | def test_invalid_grammar(self) -> None: 36 | self.compare_recovered_ast_many("invalid_grammar.pytest") 37 | 38 | def test_milestone_tests(self) -> None: 39 | self.compare_recovered_ast_many("milestone_tests.pytest") 40 | 41 | def test_node_dropping_tests(self) -> None: 42 | self.compare_recovered_ast_many("node_dropping.pytest") 43 | 44 | def test_parso_tests(self) -> None: 45 | self.compare_recovered_ast_many("parso_error_recovery_tests.pytest") 46 | 47 | 48 | if __name__ == "__main__": 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /tests/test_more_match.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree 6 | 7 | # pyre-strict 8 | 9 | import unittest 10 | 11 | from python.errpy.tests.utils.test_common import ASTTestCommon 12 | 13 | 14 | class AllGrammarTests(ASTTestCommon): 15 | """These tests focus on individual aspects of Python syntax""" 16 | 17 | def more_match_test(self, to_test: str) -> None: 18 | self.check_many_cases_in_file(f"more_match/{to_test}") 19 | 20 | def test_more_match_1(self) -> None: 21 | self.more_match_test("match1.pytest") 22 | 23 | def test_more_match_2(self) -> None: 24 | self.more_match_test("match2.pytest") 25 | 26 | def test_more_match_3(self) -> None: 27 | self.more_match_test("match3.pytest") 28 | 29 | def test_more_match_4(self) -> None: 30 | self.more_match_test("match4.pytest") 31 | 32 | def test_more_match_5(self) -> None: 33 | self.more_match_test("match5.pytest") 34 | 35 | def test_more_match_6(self) -> None: 36 | self.more_match_test("match6.pytest") 37 | 38 | def test_more_match_7(self) -> None: 39 | self.more_match_test("match7.pytest") 40 | 41 | def test_more_match_8(self) -> None: 42 | self.more_match_test("match8.pytest") 43 | 44 | def test_more_match_9(self) -> None: 45 | self.more_match_test("match9.pytest") 46 | 47 | def test_more_match_10(self) -> None: 48 | self.more_match_test("match10.pytest") 49 | 50 | def test_more_match_11(self) -> None: 51 | self.more_match_test("match11.pytest") 52 | 53 | def test_more_match_12(self) -> None: 54 | self.more_match_test("match12.pytest") 55 | 56 | 57 | if __name__ == "__main__": 58 | unittest.main() 59 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | /// Provide impls of `Serialize` and `Deserialize` which delegate to the impls 7 | /// of `std::fmt::Display` and `std::str::FromStr` respectively. 8 | macro_rules! serde_from_display { 9 | ($name:ident, $expecting:expr) => { 10 | impl ::serde::Serialize for $name { 11 | fn serialize(&self, serializer: S) -> Result { 12 | serializer.serialize_str(&self.to_string()) 13 | } 14 | } 15 | impl<'de> ::serde::Deserialize<'de> for $name { 16 | fn deserialize(deserializer: D) -> Result 17 | where 18 | D: ::serde::Deserializer<'de>, 19 | { 20 | struct Visitor; 21 | impl<'de> ::serde::de::Visitor<'de> for Visitor { 22 | type Value = $name; 23 | fn expecting( 24 | &self, 25 | formatter: &mut ::std::fmt::Formatter<'_>, 26 | ) -> ::std::fmt::Result { 27 | formatter.write_str($expecting) 28 | } 29 | fn visit_str(self, value: &str) -> Result 30 | where 31 | E: ::serde::de::Error, 32 | { 33 | value.parse().map_err(|e| { 34 | E::invalid_value(::serde::de::Unexpected::Other(&format!("{e}")), &self) 35 | }) 36 | } 37 | } 38 | deserializer.deserialize_str(Visitor) 39 | } 40 | } 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/rust_to_ocaml/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | /// Provide impls of `Serialize` and `Deserialize` which delegate to the impls 7 | /// of `std::fmt::Display` and `std::str::FromStr` respectively. 8 | macro_rules! serde_from_display { 9 | ($name:ident, $expecting:expr) => { 10 | impl ::serde::Serialize for $name { 11 | fn serialize(&self, serializer: S) -> Result { 12 | serializer.serialize_str(&self.to_string()) 13 | } 14 | } 15 | impl<'de> ::serde::Deserialize<'de> for $name { 16 | fn deserialize(deserializer: D) -> Result 17 | where 18 | D: ::serde::Deserializer<'de>, 19 | { 20 | struct Visitor; 21 | impl<'de> ::serde::de::Visitor<'de> for Visitor { 22 | type Value = $name; 23 | fn expecting( 24 | &self, 25 | formatter: &mut ::std::fmt::Formatter<'_>, 26 | ) -> ::std::fmt::Result { 27 | formatter.write_str($expecting) 28 | } 29 | fn visit_str(self, value: &str) -> Result 30 | where 31 | E: ::serde::de::Error, 32 | { 33 | value.parse().map_err(|e| { 34 | E::invalid_value(::serde::de::Unexpected::Other(&format!("{e}")), &self) 35 | }) 36 | } 37 | } 38 | deserializer.deserialize_str(Visitor) 39 | } 40 | } 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 2 | 3 | # Errpy: An Error Recovering Rust Python Parser 4 | 5 | This is a new project to provide an error-recovering Python parser, implemented 6 | in Rust based on tree-sitter. Our short-term goal is to use it in the [pyre 7 | type checker](https://github.com/facebook/pyre-check), in order to provide 8 | better IDE features. 9 | 10 | ## License 11 | 12 | Errpy is licensed under the MIT license. 13 | 14 | ## Building 15 | Errpy depends upon the following: 16 | * Ocaml 17 | * Dune 18 | * Rust 19 | * Cargo 20 | 21 | ### Platform 22 | ERRPY is verified as being buildable on Linux on OCaml 4.14.0 23 | 24 | ### Installing Ocaml and Dune 25 | Install `opam` and run `opam switch install "4.14.0"` 26 | 27 | ## Usage 28 | It is recommended to use dune as your build system. To use errpy in your dune project, you can add errpy to the libraries stanza in your dune file. For example, 29 | 30 | ``` 31 | (library 32 | (name mylib) 33 | (libraries errpy)) 34 | ``` 35 | 36 | ### For local development 37 | If you want to test your changes to errpy locally and use them in another OCaml project (.e.g. Pyre) you can try the following, 38 | 39 | First clone the repo with: `$ git clone https://github.com/facebook/errpy.git` 40 | 41 | Or use `$ git clone --branch release https://github.com/facebook/errpy.git` if you're operating within an `offline` rust enviroment. 42 | 43 | ``` 44 | ... make the required changed ... 45 | $ cd errpy 46 | $ dune build @install # Build the errpy library 47 | $ dune test # Run tests 48 | $ opam pin add errpy . -n # pin opam to errpy 49 | $ opam install errpy --verbose # install local build as errpy in opam 50 | ``` 51 | 52 | ## Formatting 53 | When making Ocaml changes you can run `ocamlformat` in order to ensure code is 54 | formatted in a conistent manner. This is invoked as follows: 55 | ``` 56 | $ dune build @fmt 57 | $ dune promote 58 | ``` 59 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_caml_builtins/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use ocamlrep::*; 7 | use ocamlrep_custom::CustomOperations; 8 | use serde::Deserialize; 9 | use serde::Serialize; 10 | 11 | /// Communicates that when the wrapped i64 is converted to an OCaml 12 | /// representation, the type `Int64.t` (representing a boxed 64-bit integer) 13 | /// should be used rather than the type `int` (which, on 64-bit architectures, 14 | /// is an unboxed 63-bit integer). 15 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 16 | #[derive(Serialize, Deserialize)] 17 | pub struct Int64(pub i64); 18 | 19 | extern "C" { 20 | static mut caml_int64_ops: CustomOperations; 21 | } 22 | 23 | impl From for Int64 { 24 | fn from(x: i64) -> Self { 25 | Self(x) 26 | } 27 | } 28 | 29 | impl From for i64 { 30 | fn from(x: Int64) -> i64 { 31 | x.0 32 | } 33 | } 34 | 35 | impl ToOcamlRep for Int64 { 36 | fn to_ocamlrep<'a, A: Allocator>(&'a self, alloc: &'a A) -> Value<'a> { 37 | let mut block = alloc.block_with_size_and_tag(2, CUSTOM_TAG); 38 | alloc.set_field(&mut block, 0, unsafe { 39 | Value::from_bits(std::ptr::addr_of!(caml_int64_ops) as *const CustomOperations as usize) 40 | }); 41 | alloc.set_field(&mut block, 1, unsafe { Value::from_bits(self.0 as usize) }); 42 | block.build() 43 | } 44 | } 45 | 46 | impl FromOcamlRep for Int64 { 47 | fn from_ocamlrep(value: Value<'_>) -> Result { 48 | let block = from::expect_block_with_size_and_tag(value, 2, CUSTOM_TAG)?; 49 | Ok(Self(block[1].to_bits() as i64)) 50 | } 51 | } 52 | 53 | impl<'a> FromOcamlRepIn<'a> for Int64 { 54 | fn from_ocamlrep_in(value: Value<'_>, _alloc: &'a ocamlrep::Bump) -> Result { 55 | Self::from_ocamlrep(value) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /errors.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #[derive(Debug, thiserror::Error)] 9 | pub enum ParserError { 10 | #[error("failed to set tree_sitter language: {0}")] 11 | Language(#[from] tree_sitter::LanguageError), 12 | #[error("parser timed out or was cancelled")] 13 | DidNotComplete, 14 | } 15 | 16 | #[derive(Debug, thiserror::Error)] 17 | pub enum RecoverableError { 18 | #[error("encountered unexpected expression")] 19 | UnexpectedExpression(String), 20 | #[error("encountered unimplemented expression")] 21 | UnimplementedStatement(String), 22 | #[error("expected a node, but it was missing")] 23 | MissingChild, // TODO: add String parameter to state which child is missing 24 | #[error("expected comparison to have lhs node, but it was missing")] 25 | MissingLhs, 26 | #[error("expected BinaryOperator node, but got unexpected node kind: {0}")] 27 | MissingOperator(String), 28 | #[error("Syntax error resulted in ERROR node")] 29 | SyntaxError(String), 30 | } 31 | 32 | pub fn recoverable_error_to_string(recoverable_error: &RecoverableError) -> String { 33 | match recoverable_error { 34 | RecoverableError::UnexpectedExpression(expression_name) => { 35 | format!("UnexpectedExpression: {}", expression_name) 36 | } 37 | RecoverableError::UnimplementedStatement(statement_name) => { 38 | format!("UnimplementedStatement: {}", statement_name) 39 | } 40 | RecoverableError::MissingChild => "MissingChild".to_string(), 41 | RecoverableError::MissingLhs => "MissingLhs".to_string(), 42 | RecoverableError::MissingOperator(operator) => { 43 | format!("MissingOperator: {}", operator) 44 | } 45 | RecoverableError::SyntaxError(invalid_syntax) => { 46 | format!("SyntaxError: {}", invalid_syntax) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/keywords.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | pub struct Foo { 7 | and: A, 8 | assert: A, 9 | asr: A, 10 | begin: A, 11 | class: A, 12 | constraint: A, 13 | done: A, 14 | downto: A, 15 | end: A, 16 | exception: A, 17 | external: A, 18 | fun: A, 19 | function: A, 20 | functor: A, 21 | include: A, 22 | inherit: A, 23 | initializer: A, 24 | land: A, 25 | lazy: A, 26 | lor: A, 27 | lsl: A, 28 | lsr: A, 29 | lxor: A, 30 | method: A, 31 | module: A, 32 | mutable: A, 33 | new: A, 34 | nonrec: A, 35 | object: A, 36 | of: A, 37 | open: A, 38 | or: A, 39 | private: A, 40 | rec: A, 41 | sig: A, 42 | then: A, 43 | to: A, 44 | val: A, 45 | when: A, 46 | with: A, 47 | } 48 | 49 | type A = And; 50 | type A = As; 51 | type A = Assert; 52 | type A = Asr; 53 | type A = Begin; 54 | type A = Class; 55 | type A = Constraint; 56 | type A = Do; 57 | type A = Done; 58 | type A = Downto; 59 | type A = Else; 60 | type A = End; 61 | type A = Exception; 62 | type A = External; 63 | type A = False; 64 | type A = For; 65 | type A = Fun; 66 | type A = Function; 67 | type A = Functor; 68 | type A = If; 69 | type A = In; 70 | type A = Include; 71 | type A = Inherit; 72 | type A = Initializer; 73 | type A = Land; 74 | type A = Lazy; 75 | type A = Let; 76 | type A = Lor; 77 | type A = Lsl; 78 | type A = Lsr; 79 | type A = Lxor; 80 | type A = Match; 81 | type A = Method; 82 | type A = Mod; 83 | type A = Module; 84 | type A = Mutable; 85 | type A = New; 86 | type A = Nonrec; 87 | type A = Object; 88 | type A = Of; 89 | type A = Open; 90 | type A = Or; 91 | type A = Private; 92 | type A = Rec; 93 | type A = Sig; 94 | type A = Struct; 95 | type A = Then; 96 | type A = To; 97 | type A = True; 98 | type A = Try; 99 | type A = Type; 100 | type A = Val; 101 | type A = Virtual; 102 | type A = When; 103 | type A = While; 104 | type A = With; 105 | -------------------------------------------------------------------------------- /ffi_ocaml.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #[macro_use] 9 | extern crate rust_to_ocaml_attr; 10 | 11 | use cst_to_ast::Parser as CSTToASTParser; 12 | use parser_pre_process::remove_comments; 13 | 14 | use crate::errors::recoverable_error_to_string; 15 | 16 | pub mod ast; 17 | pub mod constants; 18 | pub mod cst_to_ast; 19 | pub mod errors; 20 | pub mod node_wrapper; 21 | pub mod parser_post_process; 22 | pub mod parser_pre_process; 23 | pub mod sitter; 24 | pub mod string_helpers; 25 | 26 | ocamlrep_ocamlpool::ocaml_ffi! { 27 | fn parse_module(_source_text: bstr::BString) -> Result< (ast::Mod_, Vec), String> { 28 | let input_code_as_rust_string = format!("{}", _source_text); 29 | let input_without_comments = remove_comments(input_code_as_rust_string); 30 | 31 | let mut cst_to_ast = CSTToASTParser::new(input_without_comments); 32 | 33 | match cst_to_ast.parse() { 34 | Ok(_) => { 35 | let ast_and_metadata = cst_to_ast.ast_and_metadata; 36 | let ast = ast_and_metadata.ast.unwrap(); 37 | let recoverable_errors = ast_and_metadata.recoverable_errors; 38 | let mut errors: Vec = vec![]; 39 | for recoverable_error_with_location in recoverable_errors{ 40 | let error_message = recoverable_error_to_string(&recoverable_error_with_location.parser_error); 41 | let location = &recoverable_error_with_location.location; 42 | 43 | errors.push(ast::RecoverableErrorWithLocation{ 44 | error: error_message, 45 | lineno: location.lineno, 46 | col_offset: location.col_offset, 47 | end_lineno: location.end_lineno, 48 | end_col_offset: location.end_col_offset 49 | }); 50 | } 51 | Ok((ast, errors)) 52 | } 53 | Err(e) => Err(format!("Failure parsing input\n{:?}\n", e)) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match7.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests type narrowing of subject expressions for match statements. 10 | 11 | 12 | def func1(subj: int | dict[str, str] | tuple[int] | str, cond: bool): 13 | match subj: 14 | case (3 | "hi"): 15 | reveal_type(subj, expected_text="Literal[3, 'hi']") 16 | return 17 | 18 | case int(y) if cond: 19 | reveal_type(subj, expected_text="int") 20 | return 21 | 22 | case int(y): 23 | reveal_type(subj, expected_text="int") 24 | return 25 | 26 | case int(): 27 | reveal_type(subj, expected_text="Never") 28 | return 29 | 30 | case str(z): 31 | reveal_type(subj, expected_text="str") 32 | return 33 | 34 | reveal_type(subj, expected_text="dict[str, str] | tuple[int]") 35 | return subj 36 | 37 | 38 | # This should generate an error because there is the potential 39 | # for fall-through if the subject expression is a str. 40 | def func2(subj: int | str) -> str: 41 | match subj: 42 | case int(): 43 | return "int" 44 | 45 | reveal_type(subj, expected_text='str') 46 | 47 | 48 | # This should generate an error because there is the potential 49 | # for fall-through if the guard expressions are false. 50 | def func3(subj: int | str) -> str: 51 | match subj: 52 | case str() if len(subj) > 0: 53 | return "str" 54 | 55 | case int() if subj < 0: 56 | return "int" 57 | 58 | reveal_type(subj, expected_text='int | str') 59 | 60 | 61 | def func4(subj: int | str) -> str: 62 | match subj: 63 | case int(): 64 | return "int" 65 | 66 | case str(): 67 | return "str" 68 | 69 | case _: 70 | # This should be ignored because the pattern has already 71 | # been exhaustively matched. 72 | pass 73 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match12.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests narrowing of subject subexpressions in match statements. 10 | 11 | from typing import Literal, TypedDict 12 | 13 | 14 | class TD1(TypedDict): 15 | name: Literal["a"] 16 | extra_value: int 17 | 18 | 19 | class TD2(TypedDict): 20 | name: Literal["b"] 21 | other_extra_value: int 22 | 23 | 24 | def func1(item: TD1 | TD2): 25 | match item["name"]: 26 | case "c": 27 | reveal_type(item, expected_text="Never") 28 | case "a": 29 | reveal_type(item, expected_text="TD1") 30 | case "b": 31 | reveal_type(item, expected_text="TD2") 32 | 33 | 34 | T1 = tuple[Literal[0], int] 35 | T2 = tuple[Literal[1], str] 36 | 37 | 38 | def func2(item: T1 | T2): 39 | match item[0]: 40 | case 0: 41 | reveal_type(item, expected_text="tuple[Literal[0], int]") 42 | case 1: 43 | reveal_type(item, expected_text="tuple[Literal[1], str]") 44 | 45 | 46 | def func3(a: object, b: int) -> None: 47 | match a, b: 48 | case (complex(), 3): 49 | reveal_type(a, expected_text="complex") 50 | reveal_type(b, expected_text="Literal[3]") 51 | 52 | 53 | Token = ( 54 | str 55 | | tuple[Literal["define"], str, str] 56 | | tuple[Literal["include"], str] 57 | | tuple[Literal["use"], str, int, int] 58 | ) 59 | 60 | 61 | def func4(token: Token): 62 | match token: 63 | case str(x): 64 | reveal_type(token, expected_text="str") 65 | case "define", _, _: 66 | reveal_type(token, expected_text="tuple[Literal['define'], str, str]") 67 | case "include", _: 68 | reveal_type(token, expected_text="tuple[Literal['include'], str]") 69 | case "use", _, _, _: 70 | reveal_type(token, expected_text="tuple[Literal['use'], str, int, int]") 71 | case _: 72 | reveal_type(token, expected_text="Never") 73 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/test/cases/keywords.rs.exp: -------------------------------------------------------------------------------- 1 | type foo = { 2 | and_: a; 3 | assert_: a; 4 | asr_: a; 5 | begin_: a; 6 | class_: a; 7 | constraint_: a; 8 | done_: a; 9 | downto_: a; 10 | end_: a; 11 | exception_: a; 12 | external_: a; 13 | fun_: a; 14 | function_: a; 15 | functor_: a; 16 | include_: a; 17 | inherit_: a; 18 | initializer_: a; 19 | land_: a; 20 | lazy_: a; 21 | lor_: a; 22 | lsl_: a; 23 | lsr_: a; 24 | lxor_: a; 25 | method_: a; 26 | module_: a; 27 | mutable_: a; 28 | new_: a; 29 | nonrec_: a; 30 | object_: a; 31 | of_: a; 32 | open_: a; 33 | or_: a; 34 | private_: a; 35 | rec_: a; 36 | sig_: a; 37 | then_: a; 38 | to_: a; 39 | val_: a; 40 | when_: a; 41 | with_: a; 42 | } 43 | 44 | type a = and_ 45 | 46 | type a = as_ 47 | 48 | type a = assert_ 49 | 50 | type a = asr_ 51 | 52 | type a = begin_ 53 | 54 | type a = class_ 55 | 56 | type a = constraint_ 57 | 58 | type a = do_ 59 | 60 | type a = done_ 61 | 62 | type a = downto_ 63 | 64 | type a = else_ 65 | 66 | type a = end_ 67 | 68 | type a = exception_ 69 | 70 | type a = external_ 71 | 72 | type a = false_ 73 | 74 | type a = for_ 75 | 76 | type a = fun_ 77 | 78 | type a = function_ 79 | 80 | type a = functor_ 81 | 82 | type a = if_ 83 | 84 | type a = in_ 85 | 86 | type a = include_ 87 | 88 | type a = inherit_ 89 | 90 | type a = initializer_ 91 | 92 | type a = land_ 93 | 94 | type a = lazy_ 95 | 96 | type a = let_ 97 | 98 | type a = lor_ 99 | 100 | type a = lsl_ 101 | 102 | type a = lsr_ 103 | 104 | type a = lxor_ 105 | 106 | type a = match_ 107 | 108 | type a = method_ 109 | 110 | type a = mod_ 111 | 112 | type a = module_ 113 | 114 | type a = mutable_ 115 | 116 | type a = new_ 117 | 118 | type a = nonrec_ 119 | 120 | type a = object_ 121 | 122 | type a = of_ 123 | 124 | type a = open_ 125 | 126 | type a = or_ 127 | 128 | type a = private_ 129 | 130 | type a = rec_ 131 | 132 | type a = sig_ 133 | 134 | type a = struct_ 135 | 136 | type a = then_ 137 | 138 | type a = to_ 139 | 140 | type a = true_ 141 | 142 | type a = try_ 143 | 144 | type a = type_ 145 | 146 | type a = val_ 147 | 148 | type a = virtual_ 149 | 150 | type a = when_ 151 | 152 | type a = while_ 153 | 154 | type a = with_ 155 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/test/test_from_ocamlrep_in.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #![cfg(test)] 7 | 8 | use std::fmt::Debug; 9 | 10 | use bumpalo::Bump; 11 | use ocamlrep::FromOcamlRepIn; 12 | use ocamlrep::ToOcamlRep; 13 | 14 | fn test_round_trip<'a, T>(bump: &'a Bump, rust_value: T) 15 | where 16 | T: FromOcamlRepIn<'a> + ToOcamlRep + Debug + PartialEq, 17 | { 18 | let arena = ocamlrep::Arena::new(); 19 | let ocaml_value = arena.add(&rust_value); 20 | assert_eq!(T::from_ocamlrep_in(ocaml_value, bump), Ok(rust_value)); 21 | } 22 | 23 | #[test] 24 | fn convert_primitives() { 25 | let bump = &Bump::new(); 26 | 27 | test_round_trip(bump, ()); 28 | test_round_trip(bump, 1isize); 29 | test_round_trip(bump, 2usize); 30 | test_round_trip(bump, 3i64); 31 | test_round_trip(bump, 4u64); 32 | test_round_trip(bump, 5i32); 33 | test_round_trip(bump, 6u32); 34 | test_round_trip(bump, true); 35 | test_round_trip(bump, false); 36 | test_round_trip(bump, 'a'); 37 | test_round_trip(bump, 7.7f64); 38 | } 39 | 40 | #[test] 41 | fn convert_std_types() { 42 | let bump = &Bump::new(); 43 | 44 | test_round_trip(bump, None::); 45 | test_round_trip(bump, Some(&*bump.alloc(5usize))); 46 | test_round_trip(bump, Ok::<&str, &str>("okay")); 47 | test_round_trip(bump, Err::<&str, &str>("error")); 48 | } 49 | 50 | #[derive(Debug, FromOcamlRepIn, ToOcamlRep, PartialEq)] 51 | struct Foo<'a> { 52 | bar: &'a usize, 53 | baz: usize, 54 | } 55 | 56 | #[test] 57 | fn convert_struct_with_ref() { 58 | let bump = &Bump::new(); 59 | test_round_trip( 60 | bump, 61 | Foo { 62 | bar: bump.alloc(3), 63 | baz: 4, 64 | }, 65 | ); 66 | } 67 | 68 | #[derive(Debug, FromOcamlRepIn, ToOcamlRep, PartialEq)] 69 | enum Fruit<'a> { 70 | Apple, 71 | Orange(&'a str), 72 | Pear { is_tasty: bool }, 73 | Kiwi, 74 | Peach(&'a (isize, bool)), 75 | } 76 | 77 | #[test] 78 | fn convert_str_variant() { 79 | test_round_trip(&Bump::new(), Fruit::Orange("mandarin")); 80 | } 81 | 82 | #[test] 83 | fn convert_boxed_tuple_variant() { 84 | let bump = &Bump::new(); 85 | test_round_trip(bump, Fruit::Peach(bump.alloc((42, true)))); 86 | } 87 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match11.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests the reportUnnecessaryComparison check when applied to match statements. 10 | 11 | from typing import Literal, Mapping, Sequence 12 | 13 | Letters = Literal["A", "B", "C"] 14 | 15 | 16 | def func1(subj: Letters): 17 | match subj: 18 | # This should generate an error if reportUnnecessaryComparison is enabled. 19 | case "A" | "B" | "D": 20 | pass 21 | case str(): 22 | pass 23 | # This should generate an error if reportUnnecessaryComparison is enabled. 24 | case "C": 25 | pass 26 | # This should generate an error if reportUnnecessaryComparison is enabled. 27 | case x: 28 | print(x) 29 | 30 | 31 | def func2(subj: int | dict[str, str]): 32 | match subj: 33 | # This should generate an error if reportUnnecessaryComparison is enabled. 34 | case str() if subj > 4: 35 | pass 36 | case int() if subj > 4: 37 | pass 38 | case int(): 39 | pass 40 | # This should generate an error if reportUnnecessaryComparison is enabled. 41 | case int(): 42 | pass 43 | # This should generate an error if reportUnnecessaryComparison is enabled. 44 | case (a, b): 45 | print(a, b) 46 | case {"": d}: 47 | print(d) 48 | case dict(): 49 | pass 50 | # This should generate an error if reportUnnecessaryComparison is enabled. 51 | case x: 52 | print(x) 53 | 54 | 55 | JsonValue = ( 56 | None | bool | int | float | str | Sequence["JsonValue"] | Mapping[str, "JsonValue"] 57 | ) 58 | JsonObject = Mapping[str, JsonValue] 59 | 60 | 61 | def func3(json_object: JsonObject) -> None: 62 | match json_object: 63 | case { 64 | "a": { 65 | "b": [ 66 | { 67 | "c": "d", 68 | } 69 | ], 70 | } 71 | }: 72 | pass 73 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_custom/test/test_custom.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | *) 8 | 9 | type drop_test (* abstract type for the Rust DropTest type *) 10 | 11 | type ref_cell (* abstract type for the Rust Rc> type *) 12 | 13 | external drop_test_new : unit -> drop_test = "test_custom_drop_test_new" 14 | 15 | external drop_test_custom_ref_count : drop_test -> int 16 | = "test_custom_drop_test_custom_ref_count" 17 | 18 | external drop_test_get_cell : drop_test -> ref_cell 19 | = "test_custom_drop_test_get_cell" 20 | 21 | external drop_test_cell_is_dropped : ref_cell -> bool 22 | = "test_custom_drop_test_cell_is_dropped" 23 | 24 | let test_is_dropped_on_gc () = 25 | let drop_test = ref (Some (drop_test_new ())) in 26 | let cell = drop_test_get_cell (Option.get !drop_test) in 27 | 28 | assert (Int.equal (drop_test_custom_ref_count (Option.get !drop_test)) 1); 29 | assert (not @@ drop_test_cell_is_dropped cell); 30 | 31 | Gc.full_major (); 32 | assert (Int.equal (drop_test_custom_ref_count (Option.get !drop_test)) 1); 33 | assert (not @@ drop_test_cell_is_dropped cell); 34 | 35 | drop_test := None; 36 | Gc.full_major (); 37 | assert (drop_test_cell_is_dropped cell); 38 | 39 | () 40 | 41 | type boxed_int (* abstract tyep for Rust BoxedInt type *) 42 | 43 | external boxed_int_register : unit -> unit = "test_custom_boxed_int_register" 44 | 45 | external boxed_int_new : int -> boxed_int = "test_custom_boxed_int_new" 46 | 47 | external boxed_int_equal : boxed_int -> boxed_int -> bool 48 | = "test_custom_boxed_int_equal" 49 | 50 | let test_boxed_int_serialize () = 51 | boxed_int_register (); 52 | 53 | let x = boxed_int_new 1 in 54 | assert (not @@ boxed_int_equal x (boxed_int_new 2)); 55 | assert (boxed_int_equal x (boxed_int_new 1)); 56 | 57 | let x_serialized = Marshal.to_bytes x [] in 58 | let x_deserialized = Marshal.from_bytes x_serialized 0 in 59 | Gc.full_major (); 60 | 61 | assert (boxed_int_equal x x_deserialized); 62 | assert (not @@ boxed_int_equal x_deserialized (boxed_int_new 2)); 63 | Gc.full_major (); 64 | 65 | () 66 | 67 | let test_cases = [test_is_dropped_on_gc; test_boxed_int_serialize] 68 | 69 | let main () = List.iter (fun test -> test ()) test_cases 70 | 71 | let () = main () 72 | -------------------------------------------------------------------------------- /tests/test_parser_pre_process.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree 5 | 6 | use parser_pre_process::remove_comments; 7 | 8 | fn test_harness(input_code: &str, expected: &str) { 9 | let actual = remove_comments(input_code.to_string()); 10 | assert_eq!(expected.to_string(), actual); 11 | } 12 | 13 | #[test] 14 | fn test_simple() { 15 | test_harness("a = 8", "a = 8\n"); 16 | test_harness("a = 8 # a comment", "a = 8 \n"); 17 | } 18 | 19 | #[test] 20 | fn test_strings_simple() { 21 | test_harness("'#a'", "'#a'\n"); 22 | test_harness("\"#a\"", "\"#a\"\n"); 23 | test_harness("\"a\" #a", "\"a\" \n"); 24 | test_harness("'a' #a", "'a' \n"); 25 | } 26 | 27 | #[test] 28 | fn test_strings_complex() { 29 | test_harness("'#a' #b", "'#a' \n"); 30 | test_harness("'''\n#a\n'''", "'''\n#a\n'''\n"); 31 | test_harness("\"\"\"\n#a\n\"\"\"", "\"\"\"\n#a\n\"\"\"\n"); 32 | } 33 | 34 | #[test] 35 | fn test_strings_escaping() { 36 | test_harness("'#a\\' #b'", "'#a\\' #b'\n"); 37 | test_harness("\"#a\\\" #b\"", "\"#a\\\" #b\"\n"); 38 | test_harness("'#a\\\n#b'", "'#a\\\n#b'\n"); 39 | test_harness("\"#a\\\n#b\"", "\"#a\\\n#b\"\n"); 40 | } 41 | 42 | #[test] 43 | fn test_pre_comments() { 44 | // we expect just newlines in place of comments 45 | // to preserve line and column information 46 | test_harness( 47 | "# comment before 48 | # the main body of code 49 | a = 8", 50 | "\n\na = 8\n", 51 | ); 52 | } 53 | 54 | #[test] 55 | fn test_remove_comments_middle_of_def() { 56 | // comments can appear in weird places, like this which are stripped out... 57 | // such as in a tuple definition 58 | test_harness( 59 | "a = (# comment in strange place 60 | 1 , 2, 3)", 61 | "a = (\n1 , 2, 3)\n", 62 | ); 63 | 64 | // or in a function definition 65 | test_harness( 66 | "def foo( 67 | a=34, 68 | # a comment here 69 | skip_loads=True, 70 | ): 71 | pass 72 | ", 73 | "def foo(\n a=34,\n \n skip_loads=True,\n):\n pass\n\n", 74 | ); 75 | } 76 | 77 | #[test] 78 | fn test_remove_post_plus() { 79 | test_harness( 80 | "verification_report += '\\n #' + '#' 81 | if not self.partition_fbig_col_wipeout_map: 82 | pass", 83 | "verification_report += '\\n #' + '#'\nif not self.partition_fbig_col_wipeout_map:\n pass\n", 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/milestone_tests.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # This is a series of tests which try focusing on error recovery which try to capture the 7 | # minimum functionality required of ERRPY in order to be considered useable for 8 | # Pyre. 9 | 10 | # Note: we require Class support before the performance of error recovery for these instances can be improved 11 | 12 | ## Milestone 1 - Error correction for powering navigation on unsaved files 13 | # we should be able to recover at least the surrounding functions from within a class 14 | # which has a syntax error within one of its functions 15 | class Thing: 16 | def ok1(self, slice_elements: MyObject) -> str: 17 | thing = slice_elements.thing() 18 | return thing 19 | 20 | def broken(self): 21 | # localized error in broken 22 | this_is_ok = [a for a in [1,2,3]] 23 | thing = [a for a in in [1,2,3]] 24 | this_part_is_ok = 34 + 8 25 | 26 | def ok2(self, slice_elements: MyObject, b) -> None: 27 | thing = slice_elements.something(b) 28 | 29 | 30 | ## M2 - Autocomplete on trailing `.` 31 | # standard trailing dot 32 | def myfunc(): 33 | thing = slice_elements. 34 | 35 | 36 | ## M2 - autocomplete common case 37 | # where user adds a dot in between existing lhs and dot 38 | 39 | def myfunc(): 40 | thing = slice_elements. .pop() # for dot completion 41 | return thing 42 | 43 | 44 | ## Milestone 3 - Signature help for Function and Method Signatures 45 | class Thing: 46 | def ok1(self, slice_elements: MyObject) -> str: 47 | thing = slice_elements.thing() 48 | return thing 49 | 50 | def broken(self): 51 | # here is the open param without close... 52 | self.call_something( 53 | 54 | def ok2(self, slice_elements: MyObject, b) -> None: 55 | # should still be ok 56 | thing = slice_elements.something(b) 57 | 58 | 59 | ## M3 - we must gracefully handle this... 60 | apple = 55 61 | acid = “citric” 62 | a = foo(x: int = 63 | 64 | 65 | ## M3 - this must also be gracefully handled 66 | apple = 55 67 | acid = “citric” 68 | a = foo(x: int = 42, a 69 | 70 | ## M3 - we must gracefully handle this... 71 | apple = 55 72 | acid = “citric” 73 | a = foo( 74 | a| 75 | 76 | 77 | ## M3 - this must also be gracefully handled 78 | apple = 55 79 | acid = “citric” 80 | a = foo( 81 | x: int = 42, 82 | a| 83 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ; Identifier naming conventions 2 | 3 | ((identifier) @constructor 4 | (#match? @constructor "^[A-Z]")) 5 | 6 | ((identifier) @constant 7 | (#match? @constant "^[A-Z][A-Z_]*$")) 8 | 9 | ; Builtin functions 10 | 11 | ((call 12 | function: (identifier) @function.builtin) 13 | (#match? 14 | @function.builtin 15 | "^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$")) 16 | 17 | ; Function calls 18 | 19 | (decorator) @function 20 | 21 | (call 22 | function: (attribute attribute: (identifier) @function.method)) 23 | (call 24 | function: (identifier) @function) 25 | 26 | ; Function definitions 27 | 28 | (function_definition 29 | name: (identifier) @function) 30 | 31 | (identifier) @variable 32 | (attribute attribute: (identifier) @property) 33 | (type (identifier) @type) 34 | 35 | ; Literals 36 | 37 | [ 38 | (none) 39 | (true) 40 | (false) 41 | ] @constant.builtin 42 | 43 | [ 44 | (integer) 45 | (float) 46 | ] @number 47 | 48 | (comment) @comment 49 | (string) @string 50 | (escape_sequence) @escape 51 | 52 | (interpolation 53 | "{" @punctuation.special 54 | "}" @punctuation.special) @embedded 55 | 56 | [ 57 | "-" 58 | "-=" 59 | "!=" 60 | "*" 61 | "**" 62 | "**=" 63 | "*=" 64 | "/" 65 | "//" 66 | "//=" 67 | "/=" 68 | "&" 69 | "%" 70 | "%=" 71 | "^" 72 | "+" 73 | "->" 74 | "+=" 75 | "<" 76 | "<<" 77 | "<=" 78 | "<>" 79 | "=" 80 | ":=" 81 | "==" 82 | ">" 83 | ">=" 84 | ">>" 85 | "|" 86 | "~" 87 | "and" 88 | "in" 89 | "is" 90 | "not" 91 | "or" 92 | ] @operator 93 | 94 | [ 95 | "as" 96 | "assert" 97 | "async" 98 | "await" 99 | "break" 100 | "class" 101 | "continue" 102 | "def" 103 | "del" 104 | "elif" 105 | "else" 106 | "except" 107 | "exec" 108 | "finally" 109 | "for" 110 | "from" 111 | "global" 112 | "if" 113 | "import" 114 | "lambda" 115 | "nonlocal" 116 | "pass" 117 | "print" 118 | "raise" 119 | "return" 120 | "try" 121 | "while" 122 | "with" 123 | "yield" 124 | "match" 125 | "case" 126 | ] @keyword 127 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_custom/test/test_custom.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::cell::RefCell; 7 | use std::io::Write; 8 | use std::rc::Rc; 9 | 10 | use ocamlrep_custom::caml_serialize_default_impls; 11 | use ocamlrep_custom::CamlSerialize; 12 | use ocamlrep_custom::Custom; 13 | use ocamlrep_ocamlpool::ocaml_ffi; 14 | 15 | struct DropTest(Rc>); 16 | 17 | impl CamlSerialize for DropTest { 18 | caml_serialize_default_impls!(); 19 | } 20 | 21 | struct DropTestCell(Rc>); 22 | 23 | impl CamlSerialize for DropTestCell { 24 | caml_serialize_default_impls!(); 25 | } 26 | 27 | impl DropTest { 28 | pub fn new() -> Self { 29 | Self(Rc::new(RefCell::new(false))) 30 | } 31 | 32 | pub fn cell(&self) -> Rc> { 33 | self.0.clone() 34 | } 35 | } 36 | 37 | impl Drop for DropTest { 38 | fn drop(&mut self) { 39 | *self.0.borrow_mut() = true; 40 | } 41 | } 42 | 43 | ocaml_ffi! { 44 | fn test_custom_drop_test_new() -> Custom { 45 | Custom::from(DropTest::new()) 46 | } 47 | 48 | fn test_custom_drop_test_custom_ref_count(x: Custom) -> usize { 49 | let w = Rc::downgrade(x.inner()); 50 | drop(x); 51 | w.strong_count() 52 | } 53 | 54 | fn test_custom_drop_test_get_cell(x: Custom) -> Custom { 55 | Custom::from(DropTestCell(x.cell())) 56 | } 57 | 58 | fn test_custom_drop_test_cell_is_dropped(x: Custom) -> bool { 59 | *x.0.borrow() 60 | } 61 | } 62 | 63 | struct BoxedInt(isize); 64 | 65 | impl CamlSerialize for BoxedInt { 66 | caml_serialize_default_impls!(); 67 | 68 | fn serialize(&self) -> Vec { 69 | let mut buffer = Vec::new(); 70 | buffer.write_all(&self.0.to_be_bytes()).unwrap(); 71 | buffer 72 | } 73 | 74 | fn deserialize(buffer: &[u8]) -> Self { 75 | let i = isize::from_be_bytes(buffer[0..std::mem::size_of::()].try_into().unwrap()); 76 | BoxedInt(i) 77 | } 78 | } 79 | 80 | ocaml_ffi! { 81 | fn test_custom_boxed_int_register() { 82 | // Safety: called from OCaml in a single-threaded context. 83 | unsafe { 84 | BoxedInt::register(); 85 | } 86 | } 87 | 88 | fn test_custom_boxed_int_new(x: isize) -> Custom { 89 | Custom::from(BoxedInt(x)) 90 | } 91 | 92 | fn test_custom_boxed_int_equal(x: Custom, y: Custom) -> bool { 93 | x.0 == y.0 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /vendor/tree-sitter-python/bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- coding: utf-8 -*- 2 | // ------------------------------------------------------------------------------------------------ 3 | // Copyright © 2020, tree-sitter-python authors. 4 | // See the LICENSE file in this repo for license details. 5 | // ------------------------------------------------------------------------------------------------ 6 | 7 | //! This crate provides a Python grammar for the [tree-sitter][] parsing library. 8 | //! 9 | //! Typically, you will use the [language][language func] function to add this grammar to a 10 | //! tree-sitter [Parser][], and then use the parser to parse some code: 11 | //! 12 | //! ``` 13 | //! use tree_sitter::Parser; 14 | //! 15 | //! let code = r#" 16 | //! def double(x): 17 | //! return x * 2 18 | //! "#; 19 | //! let mut parser = Parser::new(); 20 | //! parser.set_language(tree_sitter_python::language()).expect("Error loading Python grammar"); 21 | //! let parsed = parser.parse(code, None); 22 | //! # let parsed = parsed.unwrap(); 23 | //! # let root = parsed.root_node(); 24 | //! # assert!(!root.has_error()); 25 | //! ``` 26 | //! 27 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 28 | //! [language func]: fn.language.html 29 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 30 | //! [tree-sitter]: https://tree-sitter.github.io/ 31 | 32 | use tree_sitter::Language; 33 | 34 | extern "C" { 35 | fn tree_sitter_python() -> Language; 36 | } 37 | 38 | /// Returns the tree-sitter [Language][] for this grammar. 39 | /// 40 | /// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 41 | pub fn language() -> Language { 42 | unsafe { tree_sitter_python() } 43 | } 44 | 45 | /// The source of the Python tree-sitter grammar description. 46 | pub const GRAMMAR: &'static str = include_str!("../../grammar.js"); 47 | 48 | /// The syntax highlighting query for this language. 49 | pub const HIGHLIGHT_QUERY: &'static str = include_str!("../../queries/highlights.scm"); 50 | 51 | /// The content of the [`node-types.json`][] file for this grammar. 52 | /// 53 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 54 | pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); 55 | 56 | /// The symbol tagging query for this language. 57 | pub const TAGGING_QUERY: &'static str = include_str!("../../queries/tags.scm"); 58 | 59 | #[cfg(test)] 60 | mod tests { 61 | #[test] 62 | fn can_load_grammar() { 63 | let mut parser = tree_sitter::Parser::new(); 64 | parser 65 | .set_language(super::language()) 66 | .expect("Error loading Python grammar"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/README-BUCK.md: -------------------------------------------------------------------------------- 1 | # Building ocamlrep with Buck2 2 | 3 | ## Setup 4 | 5 | These are things that need to be done once to get going. 6 | 7 | ### Install Buck2 8 | 9 | Buck2 provides prebuilt binaries. For example the following commands will install a prebuilt Buck2 and symlink it into `/usr/local/bin`. 10 | ```bash 11 | wget https://github.com/facebook/buck2/releases/download/latest/buck2-"$PLAT".zst 12 | zstd -d buck2-"$PLAT".zst -o buck2 13 | chmod +x buck2 14 | sudo ln -s "$(pwd)"/buck2 /usr/local/bin/buck2 15 | ``` 16 | Valid values for `$PLAT` are `x86_64-unknown-linux-gnu` on Linux, `x86_64-apple-darwin` on x86 macOS and `aarch64-apple-darwin` on ARM macOS. 17 | 18 | It's also possible to install Buck2 from source into `~/.cargo/bin` like this. 19 | ```bash 20 | cargo +nightly-2023-12-11 install --git https://github.com/facebook/buck2.git buck2 21 | ``` 22 | *Note: If on aarch64-apple-darwin then be sure install to `brew install protobuf` and for the now it's necessary to add 23 | ```bash 24 | export BUCK2_BUILD_PROTOC=/opt/homebrew/opt/protobuf/bin/protoc 25 | export BUCK2_BUILD_PROTOC_INCLUDE=/opt/homebrew/opt/protobuf/include 26 | ``` 27 | to the build environment.* 28 | 29 | ### Install Reindeer 30 | 31 | Install the `reindeer` binary from source into '~/.cargo/bin' like this. 32 | ```bash 33 | cargo +nightly-2023-12-11 install --git https://github.com/facebookincubator/reindeer.git reindeer 34 | ``` 35 | 36 | *Note: Make sure after installing Buck2 and Reindeer to configure your `PATH` environment variable if necessary so they can be found.* 37 | 38 | ### Install the OCaml package Manager 39 | 40 | If you haven't already, install [opam](https://opam.ocaml.org/). 41 | 42 | When opam has been installed execute `~/.ocaml-setup.sh` from the root of the distribution. The effect of `ocaml-setup.sh` is to create symlinks in `shim/third/party/ocaml` that point into the local opam installation. 43 | 44 | *Note: The script assumes that [`OPAM_SWITCH_PREFIX`](https://opam.ocaml.org/doc/Manual.html#Switches) is set.* 45 | 46 | ## Vendor sources & generate buck rules for ocamlrep's Rust dependencies 47 | 48 | [Reindeer](https://github.com/facebookincubator/reindeer) is a a tool that imports Rust crates from crates.io and generates Buck2 build rules for them. Run it from the root of the ocamlrep repository like this. 49 | ```bash 50 | reindeer --third-party-dir shim/third-party/rust buckify 51 | ``` 52 | 53 | That's it, you're all set. 54 | 55 | ## Profit! 56 | 57 | Run this command from the root of the repository to build all the targets you can. 58 | ``` 59 | buck2 build root//... 60 | ``` 61 | 62 | More examples and more detail about building with Buck2 are available on the [Buck2 website](https://buck2.build/)! 63 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/macro_test_util/macro_test_util.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::fmt::Display; 7 | 8 | use proc_macro2::TokenStream; 9 | use proc_macro2::TokenTree; 10 | 11 | fn mismatch(ta: Option, tb: Option, ax: &TokenStream, bx: &TokenStream) -> ! { 12 | panic!( 13 | "Mismatch!\nLeft: {}\nRight: {}\nwhen comparing\nLeft: {}\nRight: {}\n", 14 | ta.map_or("None".into(), |t| t.to_string()), 15 | tb.map_or("None".into(), |t| t.to_string()), 16 | ax, 17 | bx 18 | ); 19 | } 20 | 21 | pub fn assert_pat_eq(a: Result, b: TokenStream) { 22 | let a = match a { 23 | Err(err) => { 24 | panic!("Unexpected error '{}'", err); 25 | } 26 | Ok(ok) => ok, 27 | }; 28 | 29 | fn inner_cmp(a: TokenStream, b: TokenStream, ax: &TokenStream, bx: &TokenStream) { 30 | let mut ia = a.into_iter(); 31 | let mut ib = b.into_iter(); 32 | 33 | loop { 34 | let t_a = ia.next(); 35 | let t_b = ib.next(); 36 | if t_a.is_none() && t_b.is_none() { 37 | break; 38 | } 39 | if t_a.is_none() || t_b.is_none() { 40 | mismatch(t_a, t_b, ax, bx); 41 | } 42 | let t_a = t_a.unwrap(); 43 | let t_b = t_b.unwrap(); 44 | 45 | match (&t_a, &t_b) { 46 | (TokenTree::Ident(a), TokenTree::Ident(b)) if a == b => {} 47 | (TokenTree::Literal(a), TokenTree::Literal(b)) 48 | if a.to_string() == b.to_string() => {} 49 | (TokenTree::Punct(a), TokenTree::Punct(b)) if a.to_string() == b.to_string() => {} 50 | (TokenTree::Group(ga), TokenTree::Group(gb)) 51 | if ga.delimiter() == gb.delimiter() => 52 | { 53 | inner_cmp(ga.stream(), gb.stream(), ax, bx); 54 | } 55 | (TokenTree::Group(_), _) 56 | | (TokenTree::Ident(_), _) 57 | | (TokenTree::Punct(_), _) 58 | | (TokenTree::Literal(_), _) => mismatch(Some(t_a), Some(t_b), ax, bx), 59 | } 60 | } 61 | } 62 | 63 | inner_cmp(a.clone(), b.clone(), &a, &b); 64 | } 65 | 66 | pub fn assert_error(a: Result, b: &str) { 67 | match a { 68 | Ok(a) => panic!("Expected error but got:\n{}", a), 69 | Err(e) => { 70 | let a = format!("{}", e); 71 | assert_eq!(a, b, "Incorrect error") 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/test_sandbox.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree 6 | 7 | # pyre-strict 8 | 9 | import unittest 10 | from difflib import unified_diff 11 | from typing import Optional 12 | 13 | import python.errpy.tests.utils.ast_utils as ast_utils 14 | from python.errpy.tests.utils.test_common import read_code 15 | 16 | 17 | class TestSandbox(unittest.TestCase): 18 | """The sandbox area can be used in order to test ERRPY syntax and compare 19 | the output AST with that produced by CPython.""" 20 | 21 | maxDiff: Optional[int] = ( 22 | None # this is to display large diffs which we want for this tool 23 | ) 24 | 25 | def check_ast_file(self, fname: str, pretty_print: bool, flat_ast: bool) -> None: 26 | code = read_code(fname) 27 | expected = ast_utils.get_cpython_ast(code, pretty_print=pretty_print).strip() 28 | 29 | if pretty_print: 30 | (got_ast, errors), _ = ast_utils.run_errpy( 31 | code, 32 | ) 33 | else: 34 | (got_ast, errors), _ = ast_utils.run_errpy_ast_only( 35 | code, 36 | ) 37 | 38 | if errors: 39 | got_ast += errors 40 | 41 | got_ast = got_ast.strip() 42 | 43 | if got_ast != expected: 44 | if not flat_ast: 45 | got_ast = ast_utils.format_ast_with_indentation(got_ast) 46 | expected = ast_utils.format_ast_with_indentation(expected) 47 | 48 | print("\n\ntest fail\n") 49 | print("Result:\n" + got_ast) 50 | print("\nExpect:\n" + expected) 51 | 52 | print( 53 | "\nDiff:\n" 54 | + "".join( 55 | unified_diff( 56 | expected.splitlines(keepends=True), 57 | got_ast.splitlines(keepends=True), 58 | ) 59 | ) 60 | + "\n" 61 | ) 62 | 63 | self.assertEqual(got_ast, expected) 64 | 65 | def test_sandbox_ast_and_pretty(self) -> None: 66 | self.check_ast_file("sandbox.pytest", pretty_print=True, flat_ast=False) 67 | 68 | def test_sandbox_just_ast(self) -> None: 69 | self.check_ast_file("sandbox.pytest", pretty_print=False, flat_ast=False) 70 | 71 | def test_sandbox_flat_ast(self) -> None: 72 | self.check_ast_file("sandbox.pytest", pretty_print=False, flat_ast=True) 73 | 74 | def test_sandbox_flat_ast_and_pretty(self) -> None: 75 | self.check_ast_file("sandbox.pytest", pretty_print=True, flat_ast=True) 76 | 77 | 78 | if __name__ == "__main__": 79 | unittest.main() 80 | -------------------------------------------------------------------------------- /ffi_python.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | use pyo3::prelude::*; 9 | 10 | #[macro_use] 11 | extern crate rust_to_ocaml_attr; 12 | 13 | use cst_to_ast::Parser as CSTToASTParser; 14 | 15 | use crate::printers::parse_module_print_ast_code; 16 | use crate::printers::parse_module_print_ast_pretty_and_errors; 17 | use crate::printers::PrintingMode; 18 | 19 | pub mod ast; 20 | pub mod constants; 21 | pub mod cst_to_ast; 22 | pub mod errors; 23 | pub mod node_wrapper; 24 | pub mod parser_post_process; 25 | pub mod parser_pre_process; 26 | pub mod printers; 27 | pub mod sitter; 28 | pub mod string_helpers; 29 | 30 | #[pyfunction] 31 | fn py_parse_module_print_ast(input_code: String) -> PyResult<(String, String)> { 32 | Ok(parse_module_print_ast_code( 33 | input_code, 34 | PrintingMode::ASTOnly, 35 | )) 36 | } 37 | 38 | #[pyfunction] 39 | fn py_parse_module_print_ast_and_pretty_print(input_code: String) -> PyResult<(String, String)> { 40 | Ok(parse_module_print_ast_code( 41 | input_code, 42 | PrintingMode::ASTAndPrettyPrintAST, 43 | )) 44 | } 45 | 46 | #[pyfunction] 47 | fn py_parse_module_print_ast_pretty_print_only(input_code: String) -> PyResult<(String, String)> { 48 | Ok(parse_module_print_ast_code( 49 | input_code, 50 | PrintingMode::PrettyPrintASTOnly, 51 | )) 52 | } 53 | 54 | #[pyfunction] 55 | fn py_parse_module_print_ast_pretty_and_errors( 56 | input_code: String, 57 | ) -> PyResult<(String, String, String)> { 58 | Ok(parse_module_print_ast_pretty_and_errors(input_code)) 59 | } 60 | 61 | /// 62 | /// run errpy but ignore result - useful for benchmarking 63 | #[pyfunction] 64 | fn py_parse_module(input_code: String) -> PyResult<()> { 65 | let mut cst_to_ast = CSTToASTParser::new(input_code); 66 | _ = cst_to_ast.parse(); 67 | 68 | Ok(()) 69 | } 70 | 71 | /// A Python module implemented in Rust. The name of this function must match 72 | /// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to 73 | /// import the module. 74 | #[pymodule] 75 | fn ffi_python(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 76 | m.add_function(wrap_pyfunction!(py_parse_module_print_ast, m)?)?; 77 | m.add_function(wrap_pyfunction!( 78 | py_parse_module_print_ast_and_pretty_print, 79 | m 80 | )?)?; 81 | m.add_function(wrap_pyfunction!( 82 | py_parse_module_print_ast_pretty_print_only, 83 | m 84 | )?)?; 85 | m.add_function(wrap_pyfunction!( 86 | py_parse_module_print_ast_pretty_and_errors, 87 | m 88 | )?)?; 89 | m.add_function(wrap_pyfunction!(py_parse_module, m)?)?; 90 | Ok(()) 91 | } 92 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/src/rewrite_module_names.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use crate::ir; 7 | use crate::Config; 8 | 9 | pub fn rewrite_file(config: &'static Config, file: &mut ir::File) { 10 | let rewriter = Rewriter { config }; 11 | rewriter.rewrite_module(&mut file.root) 12 | } 13 | 14 | struct Rewriter { 15 | config: &'static Config, 16 | } 17 | 18 | impl Rewriter { 19 | fn rewrite_module(&self, module: &mut ir::Module) { 20 | module.defs.iter_mut().for_each(|def| self.rewrite_def(def)) 21 | } 22 | 23 | fn rewrite_def(&self, def: &mut ir::Def) { 24 | match def { 25 | ir::Def::Module(module) => self.rewrite_module(module), 26 | ir::Def::Alias { ty, .. } => self.rewrite_type(ty), 27 | ir::Def::Record { fields, .. } => fields.iter_mut().for_each(|f| self.rewrite_field(f)), 28 | ir::Def::Variant { variants, .. } => { 29 | variants.iter_mut().for_each(|v| self.rewrite_variant(v)) 30 | } 31 | } 32 | } 33 | 34 | fn rewrite_field(&self, field: &mut ir::Field) { 35 | self.rewrite_type(&mut field.ty) 36 | } 37 | 38 | fn rewrite_variant(&self, variant: &mut ir::Variant) { 39 | variant 40 | .fields 41 | .iter_mut() 42 | .for_each(|f| self.rewrite_variant_fields(f)) 43 | } 44 | 45 | fn rewrite_variant_fields(&self, fields: &mut ir::VariantFields) { 46 | match fields { 47 | ir::VariantFields::Unnamed(tys) => tys.iter_mut().for_each(|ty| self.rewrite_type(ty)), 48 | ir::VariantFields::Named(fields) => { 49 | fields.iter_mut().for_each(|f| self.rewrite_field(f)) 50 | } 51 | } 52 | } 53 | 54 | fn rewrite_type(&self, ty: &mut ir::Type) { 55 | match ty { 56 | ir::Type::Path(path) => self.rewrite_type_path(path), 57 | ir::Type::Tuple(tuple) => self.rewrite_type_tuple(tuple), 58 | } 59 | } 60 | 61 | fn rewrite_type_path(&self, path: &mut ir::TypePath) { 62 | match path.modules.get(0).map(ir::ModuleName::as_str) { 63 | Some("crate" | "super") => { 64 | path.modules.remove(0); 65 | } 66 | _ => {} 67 | } 68 | path.modules.iter_mut().for_each(|m| { 69 | if let Some(name) = self.config.get_renamed_module(m) { 70 | *m = name; 71 | } 72 | }); 73 | path.targs.iter_mut().for_each(|ty| self.rewrite_type(ty)) 74 | } 75 | 76 | fn rewrite_type_tuple(&self, tuple: &mut ir::TypeTuple) { 77 | tuple.elems.iter_mut().for_each(|ty| self.rewrite_type(ty)) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/rust_to_ocaml/rust_to_ocaml/rewrite_module_names.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use crate::ir; 7 | use crate::Config; 8 | 9 | pub fn rewrite_file(config: &'static Config, file: &mut ir::File) { 10 | let rewriter = Rewriter { config }; 11 | rewriter.rewrite_module(&mut file.root) 12 | } 13 | 14 | struct Rewriter { 15 | config: &'static Config, 16 | } 17 | 18 | impl Rewriter { 19 | fn rewrite_module(&self, module: &mut ir::Module) { 20 | module.defs.iter_mut().for_each(|def| self.rewrite_def(def)) 21 | } 22 | 23 | fn rewrite_def(&self, def: &mut ir::Def) { 24 | match def { 25 | ir::Def::Module(module) => self.rewrite_module(module), 26 | ir::Def::Alias { ty, .. } => self.rewrite_type(ty), 27 | ir::Def::Record { fields, .. } => fields.iter_mut().for_each(|f| self.rewrite_field(f)), 28 | ir::Def::Variant { variants, .. } => { 29 | variants.iter_mut().for_each(|v| self.rewrite_variant(v)) 30 | } 31 | } 32 | } 33 | 34 | fn rewrite_field(&self, field: &mut ir::Field) { 35 | self.rewrite_type(&mut field.ty) 36 | } 37 | 38 | fn rewrite_variant(&self, variant: &mut ir::Variant) { 39 | variant 40 | .fields 41 | .iter_mut() 42 | .for_each(|f| self.rewrite_variant_fields(f)) 43 | } 44 | 45 | fn rewrite_variant_fields(&self, fields: &mut ir::VariantFields) { 46 | match fields { 47 | ir::VariantFields::Unnamed(tys) => tys.iter_mut().for_each(|ty| self.rewrite_type(ty)), 48 | ir::VariantFields::Named(fields) => { 49 | fields.iter_mut().for_each(|f| self.rewrite_field(f)) 50 | } 51 | } 52 | } 53 | 54 | fn rewrite_type(&self, ty: &mut ir::Type) { 55 | match ty { 56 | ir::Type::Path(path) => self.rewrite_type_path(path), 57 | ir::Type::Tuple(tuple) => self.rewrite_type_tuple(tuple), 58 | } 59 | } 60 | 61 | fn rewrite_type_path(&self, path: &mut ir::TypePath) { 62 | match path.modules.get(0).map(ir::ModuleName::as_str) { 63 | Some("crate" | "super") => { 64 | path.modules.remove(0); 65 | } 66 | _ => {} 67 | } 68 | path.modules.iter_mut().for_each(|m| { 69 | if let Some(name) = self.config.get_renamed_module(m) { 70 | *m = name; 71 | } 72 | }); 73 | path.targs.iter_mut().for_each(|ty| self.rewrite_type(ty)) 74 | } 75 | 76 | fn rewrite_type_tuple(&self, tuple: &mut ir::TypeTuple) { 77 | tuple.elems.iter_mut().for_each(|ty| self.rewrite_type(ty)) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/test_parser_post_process.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree 5 | 6 | /// 7 | /// Tests the process_trailing_dots method of the `parser_post_process` module. 8 | 9 | #[cfg(test)] 10 | mod process_trailing_dots_tests { 11 | use std::collections::HashSet; 12 | 13 | use parser_post_process::ParserPostprocessor; 14 | use parser_post_process::AUTOCOMPLETE_TOKEN; 15 | 16 | fn test_harness(input_code: &str, expected: &str, error_segments: Option>) { 17 | let parser_post_processor = ParserPostprocessor::new(); 18 | // Use specified error_segments or default to all lines 19 | let err_lines = error_segments.unwrap_or(HashSet::from_iter(0..input_code.lines().count())); 20 | let actual = parser_post_processor.postprocess(input_code, err_lines); 21 | println!("INPUT:\n{}", input_code.to_string().replace('\n', "\\n")); 22 | println!("EXPECTED:\n{}", expected.to_string().replace('\n', "\\n")); 23 | println!("ACTUAL:\n{}\n\n", actual.replace('\n', "\\n")); 24 | assert_eq!(expected.to_string(), actual); 25 | } 26 | 27 | #[test] 28 | fn test_simple() { 29 | test_harness("x = a.\n", &format!("x = a.{}\n", AUTOCOMPLETE_TOKEN), None); 30 | test_harness("x = a\n", "x = a\n", None); 31 | } 32 | 33 | #[test] 34 | fn test_dots_complex() { 35 | test_harness( 36 | "x = a.b.\n", 37 | &format!("x = a.b.{}\n", AUTOCOMPLETE_TOKEN), 38 | None, 39 | ); 40 | test_harness("x = .\n", "x = .\n", None); 41 | test_harness("x = . ", "x = .", None); 42 | test_harness("x '\n.'", "x '\n.'", None); 43 | } 44 | 45 | #[test] 46 | fn test_lambda_ellipsis() { 47 | test_harness( 48 | "lambda x,y: ...\nx = lambda x,y: ...", 49 | "lambda x,y: ...\nx = lambda x,y: ...", 50 | None, 51 | ); 52 | } 53 | 54 | #[test] 55 | fn test_long_floats() { 56 | test_harness( 57 | "timestamp3 = 9223372036854775810.\n", 58 | "timestamp3 = 9223372036854775810.\n", 59 | None, 60 | ); 61 | test_harness( 62 | "not_scientific = 1111111111111111.\n", 63 | "not_scientific = 1111111111111111.\n", 64 | None, 65 | ); 66 | } 67 | 68 | #[test] 69 | fn test_alphanumeral_vars() { 70 | test_harness("a123.\n", &format!("a123.{}\n", AUTOCOMPLETE_TOKEN), None); 71 | } 72 | 73 | #[test] 74 | fn test_internal_trailing_dot() { 75 | test_harness( 76 | "x. = 123\n", 77 | &format!("x.{}= 123\n", AUTOCOMPLETE_TOKEN), 78 | None, 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/from.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | //! Helpers for implementing `FromOcamlRep::from_ocamlrep` or 7 | //! `FromOcamlRepIn::from_ocamlrep_in`. 8 | 9 | use bumpalo::Bump; 10 | 11 | use crate::Block; 12 | use crate::FromError; 13 | use crate::FromOcamlRep; 14 | use crate::FromOcamlRepIn; 15 | use crate::Value; 16 | 17 | pub fn expect_int(value: Value<'_>) -> Result { 18 | match value.as_int() { 19 | Some(value) => Ok(value), 20 | None => Err(FromError::ExpectedInt(value.to_bits())), 21 | } 22 | } 23 | 24 | pub fn expect_nullary_variant(value: Value<'_>, max: usize) -> Result { 25 | let value = expect_int(value)?; 26 | let max_as_isize: isize = max.try_into().unwrap(); 27 | if 0 <= value && value <= max_as_isize { 28 | Ok(value) 29 | } else { 30 | Err(FromError::NullaryVariantTagOutOfRange { max, actual: value }) 31 | } 32 | } 33 | 34 | pub fn expect_block(value: Value<'_>) -> Result, FromError> { 35 | match value.as_block() { 36 | Some(block) => Ok(block), 37 | None => Err(FromError::ExpectedBlock(value.as_int().unwrap())), 38 | } 39 | } 40 | 41 | pub fn expect_block_size(block: Block<'_>, size: usize) -> Result<(), FromError> { 42 | if block.size() != size { 43 | return Err(FromError::WrongBlockSize { 44 | expected: size, 45 | actual: block.size(), 46 | }); 47 | } 48 | Ok(()) 49 | } 50 | 51 | pub fn expect_block_tag(block: Block<'_>, tag: u8) -> Result<(), FromError> { 52 | if block.tag() != tag { 53 | return Err(FromError::ExpectedBlockTag { 54 | expected: tag, 55 | actual: block.tag(), 56 | }); 57 | } 58 | Ok(()) 59 | } 60 | 61 | pub fn expect_block_with_size_and_tag( 62 | value: Value<'_>, 63 | size: usize, 64 | tag: u8, 65 | ) -> Result, FromError> { 66 | let block = expect_block(value)?; 67 | expect_block_size(block, size)?; 68 | expect_block_tag(block, tag)?; 69 | Ok(block) 70 | } 71 | 72 | pub fn expect_tuple(value: Value<'_>, size: usize) -> Result, FromError> { 73 | let block = expect_block(value)?; 74 | expect_block_size(block, size)?; 75 | if block.tag() != 0 { 76 | return Err(FromError::ExpectedZeroTag(block.tag())); 77 | } 78 | Ok(block) 79 | } 80 | 81 | pub fn field(block: Block<'_>, field: usize) -> Result { 82 | T::from_ocamlrep(block[field]).map_err(|e| FromError::ErrorInField(field, Box::new(e))) 83 | } 84 | 85 | pub fn field_in<'a, T: FromOcamlRepIn<'a>>( 86 | block: Block<'_>, 87 | field: usize, 88 | alloc: &'a Bump, 89 | ) -> Result { 90 | T::from_ocamlrep_in(block[field], alloc) 91 | .map_err(|e| FromError::ErrorInField(field, Box::new(e))) 92 | } 93 | -------------------------------------------------------------------------------- /tests/test_resources/invalid_syntax_tests/invalid_match_case.pytest_expected_results: -------------------------------------------------------------------------------- 1 | @generated 2 | 3 | ##cannot use general expression in place of a case pattern 4 | match xx: 5 | case x: 6 | pass 7 | 8 | 1 Recoverable error detected: 9 | SyntaxError: invalid syntax at [2:11 - 2:13] 10 | CST Stack: 11 | {Node ERROR (1, 10) - (1, 12)} 12 | {Node case_clause (1, 4) - (2, 12)} 13 | {Node match_statement (0, 0) - (2, 12)} 14 | {Node module (0, 0) - (3, 0)} 15 | 16 | ##various types of invalid complex numbers 17 | match x: 18 | case 3j + 5j: 19 | pass 20 | case -3j + 5j: 21 | pass 22 | case 3 + 5: 23 | pass 24 | case 3 + -5j: 25 | pass 26 | 27 | 4 Recoverable errors detected: 28 | SyntaxError: first part of complex number must be real at [2:11 - 2:13] 29 | CST Stack: 30 | {Node case_literal_integer_or_float (1, 10) - (1, 12)} 31 | {Node case_literal_pattern_complex_number (1, 10) - (1, 17)} 32 | {Node case_literal_pattern (1, 10) - (1, 17)} 33 | {Node case_closed_pattern (1, 10) - (1, 17)} 34 | {Node case_or_pattern (1, 10) - (1, 17)} 35 | {Node case_pattern (1, 10) - (1, 17)} 36 | {Node case_clause (1, 4) - (2, 12)} 37 | {Node match_statement (0, 0) - (8, 12)} 38 | {Node module (0, 0) - (9, 0)} 39 | SyntaxError: first part of complex number must be real at [4:11 - 4:14] 40 | CST Stack: 41 | {Node case_literal_integer_or_float (3, 10) - (3, 13)} 42 | {Node case_literal_pattern_complex_number (3, 10) - (3, 18)} 43 | {Node case_literal_pattern (3, 10) - (3, 18)} 44 | {Node case_closed_pattern (3, 10) - (3, 18)} 45 | {Node case_or_pattern (3, 10) - (3, 18)} 46 | {Node case_pattern (3, 10) - (3, 18)} 47 | {Node case_clause (3, 4) - (4, 12)} 48 | {Node match_statement (0, 0) - (8, 12)} 49 | {Node module (0, 0) - (9, 0)} 50 | SyntaxError: second part of complex number must be a imaginary at [6:15 - 6:16] 51 | CST Stack: 52 | {Node case_literal_integer_or_float (5, 14) - (5, 15)} 53 | {Node case_literal_pattern_complex_number (5, 10) - (5, 15)} 54 | {Node case_literal_pattern (5, 10) - (5, 15)} 55 | {Node case_closed_pattern (5, 10) - (5, 15)} 56 | {Node case_or_pattern (5, 10) - (5, 15)} 57 | {Node case_pattern (5, 10) - (5, 15)} 58 | {Node case_clause (5, 4) - (6, 12)} 59 | {Node match_statement (0, 0) - (8, 12)} 60 | {Node module (0, 0) - (9, 0)} 61 | SyntaxError: second part of complex number must be a positive at [8:15 - 8:18] 62 | CST Stack: 63 | {Node case_literal_integer_or_float (7, 14) - (7, 17)} 64 | {Node case_literal_pattern_complex_number (7, 10) - (7, 17)} 65 | {Node case_literal_pattern (7, 10) - (7, 17)} 66 | {Node case_closed_pattern (7, 10) - (7, 17)} 67 | {Node case_or_pattern (7, 10) - (7, 17)} 68 | {Node case_pattern (7, 10) - (7, 17)} 69 | {Node case_clause (7, 4) - (8, 12)} 70 | {Node match_statement (0, 0) - (8, 12)} 71 | {Node module (0, 0) - (9, 0)} 72 | Output AST is not valid according to CPython: real number required in complex literal (, line 2) 73 | 74 | -------------------------------------------------------------------------------- /tests/test_resources/error_recovery_specific_tests/parso_error_recovery_tests.pytest_expected_results: -------------------------------------------------------------------------------- 1 | @generated 2 | 3 | ##test_with_stmt 4 | code = before 5 | with x: 6 | f.a 7 | code = after 8 | 9 | ##test_one_line_function 10 | code = before 11 | 12 | def x(): 13 | f.code = after 14 | 15 | ##test_one_line_function 2 16 | code = before 17 | 1 Recoverable error detected: 18 | SyntaxError: invalid syntax at [3:1 - 5:13] 19 | CST Stack: 20 | {Node ERROR (2, 0) - (4, 12)} 21 | {Node module (0, 0) - (5, 0)} 22 | 23 | ##test_if_else 24 | code = before 25 | if x: 26 | f. 27 | 2 Recoverable errors detected: 28 | SyntaxError: invalid syntax at [4:5 - 4:7] 29 | CST Stack: 30 | {Node ERROR (3, 4) - (3, 6)} 31 | {Node if_statement (2, 0) - (7, 12)} 32 | {Node module (0, 0) - (8, 0)} 33 | SyntaxError: invalid syntax at [6:5 - 8:13] 34 | CST Stack: 35 | {Node ERROR (5, 4) - (7, 12)} 36 | {Node else_clause (4, 0) - (7, 12)} 37 | {Node if_statement (2, 0) - (7, 12)} 38 | {Node module (0, 0) - (8, 0)} 39 | Output AST is not valid according to CPython: invalid syntax (, line 3) 40 | 41 | ##test_if_stmt 42 | code = before 43 | if x: 44 | f. 45 | 1 Recoverable error detected: 46 | SyntaxError: invalid syntax at [3:1 - 6:13] 47 | CST Stack: 48 | {Node ERROR (2, 0) - (5, 12)} 49 | {Node module (0, 0) - (6, 0)} 50 | Output AST is not valid according to CPython: invalid syntax (, line 3) 51 | 52 | ##test_invalid_token 53 | code = before 54 | a + +b 55 | code = after 56 | 1 Recoverable error detected: 57 | SyntaxError: invalid syntax at [3:5 - 3:6] 58 | CST Stack: 59 | {Node ERROR (2, 4) - (2, 5)} 60 | {Node binary_operator (2, 0) - (2, 9)} 61 | {Node expression_statement (2, 0) - (2, 9)} 62 | {Node module (0, 0) - (5, 0)} 63 | 64 | ##test_invalid_token_in_fstr 65 | code = before 66 | f'{a + +b}' 67 | code = after 68 | 1 Recoverable error detected: 69 | SyntaxError: invalid syntax at [3:8 - 3:9] 70 | CST Stack: 71 | {Node ERROR (2, 7) - (2, 8)} 72 | {Node binary_operator (2, 3) - (2, 12)} 73 | {Node interpolation (2, 2) - (2, 13)} 74 | {Node string (2, 0) - (2, 14)} 75 | {Node expression_statement (2, 0) - (2, 14)} 76 | {Node module (0, 0) - (5, 0)} 77 | 78 | ##test_dedent_issues1 79 | code = before 80 | 81 | class C: 82 | f 83 | g 84 | end 85 | code = after 86 | 1 Recoverable error detected: 87 | SyntaxError: invalid syntax at [4:5 - 4:14] 88 | CST Stack: 89 | {Node ERROR (3, 4) - (3, 13)} 90 | {Node class_definition (2, 0) - (6, 7)} 91 | {Node module (0, 0) - (9, 0)} 92 | 93 | ##test_dedent_issues2 94 | code = before 95 | 96 | class C: 97 | if 1: 98 | g 99 | else: 100 | h 101 | end 102 | code = after 103 | 1 Recoverable error detected: 104 | SyntaxError: invalid syntax at [4:5 - 4:14] 105 | CST Stack: 106 | {Node ERROR (3, 4) - (3, 13)} 107 | {Node class_definition (2, 0) - (8, 7)} 108 | {Node module (0, 0) - (11, 0)} 109 | 110 | ##test_dedent_issues3 111 | code = before 112 | 113 | class C: 114 | f 115 | g 116 | code = after 117 | 118 | -------------------------------------------------------------------------------- /string_helpers.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #[derive(Debug)] 9 | #[derive(PartialEq)] 10 | #[derive(Default)] 11 | pub struct StringCategory { 12 | pub is_byte: bool, 13 | pub is_raw: bool, 14 | pub is_format: bool, 15 | pub is_unicode: bool, 16 | } 17 | 18 | #[derive(Debug)] 19 | #[derive(PartialEq)] 20 | pub struct InvalidStringCategoryError { 21 | pub invalid_prefix: String, 22 | } 23 | 24 | /// Return a string containing all characters prior to the first ' or " 25 | pub fn string_prefix(string_contents: &str) -> String { 26 | let mut prefix: String = String::from(""); 27 | for ch in string_contents.chars() { 28 | if ch == '\'' || ch == '"' { 29 | break; 30 | } 31 | prefix.push(ch); 32 | } 33 | prefix 34 | } 35 | 36 | /// categorize_string will return a StringCategory 37 | /// containing the type of string which has been passed to the function 38 | /// i.e. a byte, format, unicode or raw string as signified by the prefix 39 | /// of the string, e.g. `b"a byte string"` is a byte string etc. 40 | /// If an invalid combination of prefix is provided then this is returned 41 | /// as a InvalidStringCategoryError for processing by the caller 42 | pub fn categorize_string( 43 | to_categorize: &str, 44 | ) -> Result { 45 | // Only valid prefixes are: 46 | // "r" | "u" | "f" | "b" 47 | // "fr" | "rf" 48 | // "br"| "rb" 49 | let mut prefix = string_prefix(to_categorize); 50 | 51 | prefix = prefix.to_lowercase(); 52 | match prefix.len() { 53 | 1 => match prefix.as_str() { 54 | "r" => Ok(StringCategory { 55 | is_raw: true, 56 | ..Default::default() 57 | }), 58 | "u" => Ok(StringCategory { 59 | is_unicode: true, 60 | ..Default::default() 61 | }), 62 | "f" => Ok(StringCategory { 63 | is_format: true, 64 | ..Default::default() 65 | }), 66 | "b" => Ok(StringCategory { 67 | is_byte: true, 68 | ..Default::default() 69 | }), 70 | _ => Err(InvalidStringCategoryError { 71 | invalid_prefix: prefix, 72 | }), 73 | }, 74 | 2 => match prefix.as_str() { 75 | "fr" | "rf" => Ok(StringCategory { 76 | is_format: true, 77 | is_raw: true, 78 | ..Default::default() 79 | }), 80 | "br" | "rb" => Ok(StringCategory { 81 | is_byte: true, 82 | is_raw: true, 83 | ..Default::default() 84 | }), 85 | _ => Err(InvalidStringCategoryError { 86 | invalid_prefix: prefix, 87 | }), 88 | }, 89 | 0 => Ok(StringCategory { 90 | ..Default::default() 91 | }), 92 | _ => Err(InvalidStringCategoryError { 93 | invalid_prefix: prefix, 94 | }), 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match10.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests the reportMatchNotExhaustive diagnostic check. 10 | 11 | from types import NoneType 12 | from typing import Literal 13 | from enum import Enum 14 | 15 | 16 | def func1(subj: Literal["a", "b"], cond: bool): 17 | # This should generate an error if reportMatchNotExhaustive is enabled. 18 | match subj: 19 | case "a": 20 | pass 21 | 22 | case "b" if cond: 23 | pass 24 | 25 | 26 | def func2(subj: object): 27 | # This should generate an error if reportMatchNotExhaustive is enabled. 28 | match subj: 29 | case int(): 30 | pass 31 | 32 | 33 | def func3(subj: object): 34 | match subj: 35 | case object(): 36 | pass 37 | 38 | 39 | def func4(subj: tuple[str] | tuple[int]): 40 | match subj[0]: 41 | case str(): 42 | pass 43 | 44 | case int(): 45 | pass 46 | 47 | 48 | def func5(subj: Literal[1, 2, 3]): 49 | # This should generate an error if reportMatchNotExhaustive is enabled. 50 | match subj: 51 | case 1 | 2: 52 | pass 53 | 54 | 55 | class Color(Enum): 56 | red = 0 57 | green = 1 58 | blue = 2 59 | 60 | 61 | def func6(subj: Color): 62 | # This should generate an error if reportMatchNotExhaustive is enabled. 63 | match subj: 64 | case Color.red: 65 | pass 66 | 67 | case Color.green: 68 | pass 69 | 70 | 71 | class ClassA: 72 | def method1(self) -> str: 73 | match self: 74 | case ClassA(): 75 | return "" 76 | 77 | 78 | def func7() -> int: 79 | match [10]: 80 | case [*values]: 81 | return values[0] 82 | 83 | 84 | class SingleColor(Enum): 85 | red = 0 86 | 87 | 88 | def func8(subj: SingleColor) -> int: 89 | match subj: 90 | case SingleColor.red: 91 | return 1 92 | 93 | 94 | def func9(subj: int | None): 95 | match subj: 96 | case NoneType(): 97 | return 1 98 | case int(): 99 | return 2 100 | 101 | 102 | def func10(subj: Color | None = None) -> list[str]: 103 | results = [""] 104 | for x in [""]: 105 | match subj: 106 | case None: 107 | results.append(x) 108 | case Color.red: 109 | pass 110 | case Color.green: 111 | pass 112 | case Color.blue: 113 | pass 114 | return results 115 | 116 | 117 | def func11(subj: int | float | None): 118 | match subj: 119 | case float(): 120 | reveal_type(subj, expected_text="int | float") 121 | case int(): 122 | reveal_type(subj, expected_text="int") 123 | case NoneType(): 124 | reveal_type(subj, expected_text="None") 125 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_custom/test/counter.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #![feature(exit_status_error)] 7 | 8 | use std::cell::Cell; 9 | 10 | use ocamlrep_custom::caml_serialize_default_impls; 11 | use ocamlrep_custom::CamlSerialize; 12 | use ocamlrep_custom::Custom; 13 | use ocamlrep_ocamlpool::ocaml_ffi; 14 | 15 | pub struct Counter(Cell); 16 | 17 | impl CamlSerialize for Counter { 18 | caml_serialize_default_impls!(); 19 | } 20 | 21 | ocaml_ffi! { 22 | fn counter_new() -> Custom { 23 | Custom::from(Counter(Cell::new(0))) 24 | } 25 | 26 | fn counter_inc(counter: Custom) -> Custom { 27 | counter.0.set(counter.0.get() + 1); 28 | counter 29 | } 30 | 31 | fn counter_read(counter: Custom) -> isize { 32 | counter.0.get() 33 | } 34 | } 35 | 36 | // Hack! Trick buck into believing that these libraries are used. See [Note: 37 | // Test blocks for Cargo] in `ocamlrep_ocamlpool/test/ocamlpool_test.rs`. 38 | const _: () = { 39 | #[allow(unused_imports)] 40 | use anyhow; 41 | #[allow(unused_imports)] 42 | use cargo_test_utils; 43 | #[allow(unused_imports)] 44 | use tempfile; 45 | }; 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use anyhow::Result; 50 | use cargo_test_utils::*; 51 | use tempfile::TempDir; 52 | 53 | #[test] 54 | fn counter_test() -> Result<()> { 55 | let tmp_dir = TempDir::with_prefix("ocamlrep_custom_test.")?; 56 | std::fs::copy( 57 | "counter_client.ml", 58 | tmp_dir.path().join("counter_client.ml"), 59 | )?; 60 | let compile_cmd = cmd( 61 | "ocamlopt.opt", 62 | &[ 63 | "-verbose", 64 | "-c", 65 | "counter_client.ml", 66 | "-o", 67 | "counter_client_ml.cmx", 68 | ], 69 | Some(tmp_dir.path()), 70 | ); 71 | assert_eq!(run(compile_cmd).map_err(fmt_exit_status_err), Ok(())); 72 | let link_cmd = cmd( 73 | "ocamlopt.opt", 74 | &[ 75 | "-verbose", 76 | "-o", 77 | "counter_test", 78 | "counter_client_ml.cmx", 79 | "-ccopt", 80 | &("-L".to_owned() + workspace_dir(&["target", build_flavor()]).to_str().unwrap()), 81 | "-cclib", 82 | "-lcounter", 83 | "-cclib", 84 | "-locamlrep_ocamlpool", 85 | ], 86 | Some(tmp_dir.path()), 87 | ); 88 | assert_eq!(run(link_cmd).map_err(fmt_exit_status_err), Ok(())); 89 | let counter_test_cmd = cmd( 90 | tmp_dir 91 | .path() 92 | .join("counter_test") 93 | .as_path() 94 | .to_str() 95 | .unwrap(), 96 | &[], 97 | None, 98 | ); 99 | assert_eq!(run(counter_test_cmd).map_err(fmt_exit_status_err), Ok(())); 100 | tmp_dir.close()?; 101 | Ok(()) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /printers/ast_pretty_print_helper.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | use std::cmp::min; 9 | 10 | pub struct PrintHelper<'a> { 11 | pub pprint_output: &'a mut String, 12 | ident_amount: usize, 13 | ident_current: usize, 14 | ident_str: String, 15 | ignore_next_n_chars: usize, 16 | } 17 | 18 | impl PrintHelper<'_> { 19 | pub fn new(pprint_output: &'_ mut String, ident_amount: usize) -> PrintHelper { 20 | PrintHelper { 21 | pprint_output, 22 | ident_amount, 23 | ident_current: 0, 24 | ident_str: "".to_string(), 25 | ignore_next_n_chars: 0, 26 | } 27 | } 28 | 29 | pub fn ignore_next_n_chars(&mut self, ignore_next_n_chars: usize) { 30 | self.ignore_next_n_chars += ignore_next_n_chars; 31 | } 32 | 33 | pub fn push_str(&mut self, to_push: &str) { 34 | if self.ignore_next_n_chars > 0 { 35 | // cut up to ignore_next_n_chars chars from input, if not enough to remove in one go 36 | // cut remainder from next call to push_str 37 | let to_cut = min(to_push.len(), self.ignore_next_n_chars); 38 | self.pprint_output.push_str(&to_push[to_cut..]); 39 | self.ignore_next_n_chars -= to_cut; 40 | } else { 41 | self.pprint_output.push_str(to_push); 42 | } 43 | } 44 | 45 | pub fn push_ident(&mut self) { 46 | if self.ident_current > 0 { 47 | self.pprint_output.push_str(&self.ident_str); 48 | } 49 | } 50 | 51 | pub fn pop(&mut self) { 52 | self.pprint_output.pop(); 53 | } 54 | 55 | pub fn pop_many(&mut self, topop: usize) { 56 | match topop { 57 | 1 => { 58 | self.pprint_output.pop(); 59 | } 60 | _ => { 61 | self.pprint_output 62 | .truncate(self.pprint_output.len() - topop); 63 | } 64 | }; 65 | } 66 | 67 | pub fn inc_ident(&mut self) { 68 | self.ident_current += self.ident_amount; 69 | self.inc_calc(); 70 | } 71 | 72 | pub fn dec_ident(&mut self) { 73 | self.ident_current -= self.ident_amount; 74 | self.inc_calc(); 75 | } 76 | 77 | fn inc_calc(&mut self) { 78 | self.ident_str = " ".repeat(self.ident_current); 79 | } 80 | 81 | pub fn current_length(&mut self) -> usize { 82 | self.pprint_output.len() 83 | } 84 | 85 | pub fn insert_at(&mut self, at_point: usize, what: &str) { 86 | self.pprint_output.insert_str(at_point, what); 87 | } 88 | 89 | pub fn substring_contains(&mut self, at_point: usize, what: &str) -> bool { 90 | self.pprint_output[at_point..].contains(what) 91 | } 92 | 93 | pub fn strip_all_but_one_trailing_newline(&mut self) { 94 | let mut offset_from_end = 0; 95 | for last_char in self.pprint_output.chars().rev() { 96 | if last_char != '\n' { 97 | break; 98 | } 99 | 100 | offset_from_end += 1; 101 | } 102 | 103 | if offset_from_end > 1 { 104 | self.pprint_output 105 | .truncate(self.pprint_output.len() - offset_from_end + 1); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/more_match/match6.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # These match tests are inspired by those defined for pyright under: 7 | # https://github.com/microsoft/pyright/tree/main/packages/pyright-internal/src/tests/samples 8 | 9 | ## This sample tests type checking for match statements (as described in PEP 634) that contain literal patterns. 10 | 11 | 12 | def test_unknown(value_to_match): 13 | match value_to_match: 14 | case 3 as a1, -3 as a2: 15 | reveal_type(a1, expected_text="Literal[3]") 16 | reveal_type(a2, expected_text="Literal[-3]") 17 | reveal_type(value_to_match, expected_text="Unknown") 18 | 19 | case 3j as b1, -3 + 5j as b2: 20 | reveal_type(b1, expected_text="complex") 21 | reveal_type(b2, expected_text="complex") 22 | reveal_type(value_to_match, expected_text="Unknown") 23 | 24 | case "hi" as c1, None as c2: 25 | reveal_type(c1, expected_text="Literal['hi']") 26 | reveal_type(c2, expected_text="None") 27 | reveal_type(value_to_match, expected_text="Unknown") 28 | 29 | case True as d1, False as d2: 30 | reveal_type(d1, expected_text="Literal[True]") 31 | reveal_type(d2, expected_text="Literal[False]") 32 | reveal_type(value_to_match, expected_text="Unknown") 33 | 34 | 35 | def test_tuple(value_to_match: tuple[int | float | str | complex, ...]): 36 | match value_to_match: 37 | case (3, -3) as a1: 38 | reveal_type(a1, expected_text="tuple[Literal[3], Literal[-3]]") 39 | reveal_type(value_to_match, expected_text="tuple[Literal[3], Literal[-3]]") 40 | 41 | case (3j, -3 + 5j) as b1: 42 | reveal_type(b1, expected_text="tuple[complex, complex]") 43 | reveal_type(value_to_match, expected_text="tuple[complex, complex]") 44 | 45 | 46 | def test_union(value_to_match: int | float | str | complex | bool | None): 47 | match value_to_match: 48 | case (3 | -3j) as a1: 49 | reveal_type(a1, expected_text="bool | complex | Literal[3]") 50 | reveal_type(value_to_match, expected_text="bool | complex | Literal[3]") 51 | 52 | case (True | False | 3.4 | -3 + 3j | None) as b1: 53 | reveal_type(b1, expected_text="float | complex | bool | None") 54 | reveal_type(value_to_match, expected_text="float | complex | bool | None") 55 | 56 | case ("hi" | 3.4) as c1: 57 | reveal_type(c1, expected_text="float | Literal['hi']") 58 | reveal_type(value_to_match, expected_text="float | Literal['hi']") 59 | 60 | case ((True | "True") as d1) | ((False | "False") as d1): 61 | reveal_type(d1, expected_text="bool | Literal['True', 'False']") 62 | reveal_type(value_to_match, expected_text="bool | Literal['True', 'False']") 63 | 64 | 65 | def test_none(value_to_match: int | None): 66 | match value_to_match: 67 | case None as a1: 68 | reveal_type(a1, expected_text="None") 69 | 70 | case a2: 71 | reveal_type(a2, expected_text="int") 72 | 73 | 74 | class A(str): 75 | ... 76 | 77 | 78 | def test_subclass(a: A): 79 | match a: 80 | case "TEST" as m: 81 | reveal_type(m, expected_text="A") 82 | case x: 83 | reveal_type(x, expected_text="A") 84 | -------------------------------------------------------------------------------- /tests/test_resources/unit_tests/ast_statements.pytest: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree 5 | 6 | # Ensure correct AST creation given units defined under: https://docs.python.org/3/library/ast.html :: 3.10.7 grammar 7 | # Note: the guide referenced above is not complete, so we've extended it here where appropriate 8 | 9 | 10 | ## Raise(exc, cause) 11 | raise 12 | raise gg 13 | raise x from y 14 | 15 | ## Assert(test, msg) 16 | assert x 17 | assert x,y 18 | 19 | 20 | ## Delete(targets) 21 | del x,y,z 22 | 23 | ## Pass 24 | def thing(): 25 | pass 26 | 27 | ## Import(names) 28 | import x,y,z 29 | 30 | ## ImportFrom(module, names, level) 31 | from y import x,y,z 32 | 33 | ## import from with params 34 | from y import (x,y,z) 35 | 36 | ## alias(name, asname) 37 | from ..foo.bar import a as b, c 38 | from .foo.bar import a as b, c 39 | from ...foo.bar import a as b, c 40 | 41 | ## future import from (yes, we use this in fbcode) 42 | from __future__ import annotations 43 | from __future__ import absolute_import, division, print_function, unicode_literals 44 | 45 | ## comments in places other than on their own lines or after statements 46 | # comment - this is here because at first I forgot to add a newline for 47 | # comments without anything preceeding them which messed up the AST 48 | # line numbers 49 | 50 | def foo( 51 | a=34, 52 | # a comment here 53 | skip_loads=True, 54 | ): 55 | pass 56 | 57 | 58 | a = (# comment in strange place 59 | 1 , 2, 3) 60 | 61 | "some # comment (hash) character in a string" 62 | 'another # comment (hash) character in a string' 63 | 64 | """ 65 | some multiline string # this is not a comment 66 | """ 67 | 68 | ## Tuple unpacking inside list comprehension 69 | a = [f"{i}{j}" for (i, j) in [("alpha", "beta"), ("one", "two")]] 70 | 71 | ## multiline format string with format on first line 72 | format_string = f"""{a} 73 | """ 74 | 75 | format_string = f"""{a 76 | } 77 | """ 78 | 79 | ## multiline format string with format on second line 80 | format_string = f""" 81 | {b} 82 | """ 83 | 84 | format_string = f""" 85 | {b} {c} 86 | """ 87 | 88 | format_string = f'''{sin(a+b)} is 89 | {cos(b+c):.3} plus remainder 90 | {tan(a):.3} plus remainder''' 91 | 92 | ## multiline format string with format on second line continuation 93 | 94 | format_string = f""" 95 | {b} {c} \ 96 | """ 97 | 98 | format_string = f" a \ 99 | b {c} \ 100 | d {e}" 101 | 102 | 103 | ## formatted value test 104 | format_string = f"{v=!r:->8s}" 105 | format_string = f"{v=:->8s}" 106 | format_string = f"{v=!r}" 107 | format_string = f"{v=}" 108 | format_string = f"{v!r:->8s}" 109 | format_string = f"{v:->8s}" 110 | format_string = f"{v!r}" 111 | format_string = f"{v}" 112 | 113 | format_string = f"sometext {v=!r:->8s}" 114 | format_string = f"sometext {v=:->8s}" 115 | format_string = f"sometext {v=!r}" 116 | format_string = f"sometext {v=}" 117 | format_string = f"sometext {v!r:->8s}" 118 | format_string = f"sometext {v:->8s}" 119 | format_string = f"sometext {v!r}" 120 | format_string = f"sometext {v}" 121 | 122 | ##PEP 448 – Additional Unpacking Generalizations 123 | print(*[1], *[2], 3) 124 | x=dict(**{'x': 1}, y=2, **{'z': 3}) 125 | x=*range(4), 4 126 | x=[*range(4), 4] 127 | x={*range(4), 4} 128 | x={'x': 1, **{'y': 2}} 129 | x={**{'x': 2}, 'x': 1} 130 | ranges = [range(i) for i in range(5)] 131 | -------------------------------------------------------------------------------- /benchmark/benchmark_errpy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fbpython 2 | 3 | # pyre-strict 4 | 5 | # Copyright (c) Meta Platforms, Inc. and affiliates. 6 | # 7 | # This source code is licensed under the MIT license found in the 8 | # LICENSE file in the root directory of this source tree 9 | 10 | import ast 11 | import time 12 | 13 | from python.errpy import ffi_python 14 | 15 | RESOURCES_DIR = "errpy/benchmark/benchmark_resources/" 16 | NANO_TO_MS = 1e-6 17 | 18 | 19 | class BenchmarkRunner: 20 | """For each benchmark return the time in nanoseconds to invoke the parser 21 | on 'iterations' number of times with 'warm_up_cycles' iterations as 22 | warm ups. Note that for ERRPY 'warm_up_cycles' is less relevant as 23 | its not JIT compiled (but then CPython isnt currently either)""" 24 | 25 | def __init__( 26 | self, source: str, warm_up_cycles: int = 1000, iterations: int = 100000 27 | ) -> None: 28 | self.source = source 29 | self.warm_up_cycles = warm_up_cycles 30 | self.iterations = iterations 31 | 32 | def run_cpython(self) -> int: 33 | source = self.source 34 | for _ in range(self.warm_up_cycles): 35 | ast.parse(source) 36 | 37 | iters = range(self.iterations) 38 | # WARNING: be careful about code put between tick and toc as this can 39 | # affect the benchmarking 40 | tick = time.time_ns() 41 | for _ in iters: 42 | ast.parse(source) 43 | toc = time.time_ns() 44 | return toc - tick 45 | 46 | def run_errpy(self) -> int: 47 | source = self.source 48 | for _ in range(self.warm_up_cycles): 49 | ffi_python.py_parse_module(source) 50 | 51 | iters = range(self.iterations) 52 | # WARNING: be careful about code put between tick and toc as this can 53 | # affect the benchmarking 54 | tick = time.time_ns() 55 | for _ in iters: 56 | ffi_python.py_parse_module(source) 57 | toc = time.time_ns() 58 | return toc - tick 59 | 60 | 61 | def main() -> int: 62 | """DOC_STRING""" 63 | source_filename = RESOURCES_DIR + "simple.pytest" 64 | 65 | with open(source_filename) as fobj: 66 | source = fobj.read() 67 | 68 | runner = BenchmarkRunner(source) 69 | ns_cpython = runner.run_cpython() 70 | ns_errpy = runner.run_errpy() 71 | 72 | warm_up = runner.warm_up_cycles 73 | iterations = runner.iterations 74 | 75 | print( 76 | "Cpython vs Errpy benchmark complete! ({} warm ups, {} iterations each)".format( 77 | warm_up, iterations 78 | ) 79 | ) 80 | print( 81 | "============================================================================\n" 82 | ) 83 | print( 84 | "CPython: {} ns || {:.2f} ms per run of sample input".format( 85 | ns_cpython, ns_cpython / iterations * NANO_TO_MS 86 | ) 87 | ) 88 | print( 89 | "Errpy: {} ns || {:.2f} ms per run of sample input\n".format( 90 | ns_errpy, ns_errpy / iterations * NANO_TO_MS 91 | ) 92 | ) 93 | if ns_errpy > ns_cpython: 94 | pct = (ns_errpy - ns_cpython) / ns_cpython * 100.0 95 | print("Errpy is slower than CPython by: {:.2f} % \n".format(pct)) 96 | else: 97 | pct = (ns_cpython - ns_errpy) / ns_errpy * 100.0 98 | print("CPython is slower than Errpy by: {:.2f} % \n".format(pct)) 99 | 100 | return 0 101 | 102 | 103 | if __name__ == "__main__": 104 | main() 105 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep/cache.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | //! Provides `MemoizationCache`, a simple cache designed to aid implementation 7 | //! of the `Allocator` trait. 8 | 9 | use std::cell::RefCell; 10 | 11 | type HashMap = rustc_hash::FxHashMap; 12 | 13 | /// A simple scoped cache for memoizing conversions from one pointer-sized value 14 | /// to another. Useful for memoizing conversions between OCaml values and Rust 15 | /// references. 16 | pub struct MemoizationCache { 17 | /// Maps from input (address,size_in_bytes) -> output 18 | cache: RefCell>>, 19 | } 20 | 21 | impl Default for MemoizationCache { 22 | #[inline(always)] 23 | fn default() -> Self { 24 | MemoizationCache::new() 25 | } 26 | } 27 | 28 | impl MemoizationCache { 29 | #[inline(always)] 30 | pub fn new() -> Self { 31 | Self { 32 | cache: RefCell::new(None), 33 | } 34 | } 35 | 36 | #[inline(always)] 37 | pub fn with_cache(&self, f: impl FnOnce() -> T) -> T { 38 | // The `replace` below should not panic because the only borrows of 39 | // `self.cache` are in this function and in `memoized`. In both 40 | // functions, we do not hold a `Ref` or `RefMut` while calling into code 41 | // which might attempt to re-enter `memoized` or `with_cache`. 42 | let prev_value = self.cache.replace(Some(Default::default())); 43 | if prev_value.is_some() { 44 | panic!( 45 | "Attempted to re-enter MemoizationCache::with_cache \ 46 | (probably via ocamlrep::Allocator::add_root, which is not re-entrant)" 47 | ); 48 | } 49 | let result = f(); 50 | // As above, this `replace` should not panic. 51 | self.cache.replace(None); 52 | result 53 | } 54 | 55 | #[inline(always)] 56 | pub fn memoized(&self, input: usize, size_in_bytes: usize, f: impl FnOnce() -> usize) -> usize { 57 | if size_in_bytes == 0 { 58 | return f(); 59 | } 60 | let memoized_output = match (self.cache.borrow().as_ref()) 61 | .map(|cache| cache.get(&(input, size_in_bytes)).copied()) 62 | { 63 | None => return f(), 64 | Some(output) => output, 65 | }; 66 | match memoized_output { 67 | Some(output) => output, 68 | None => { 69 | let output = f(); 70 | // The `borrow_mut` below should not panic because the only 71 | // borrows of `self.cache` are in this function and in 72 | // `with_cache`. In this function, we do not hold a `Ref` or 73 | // `RefMut` while calling into `f` (or any other function which 74 | // might attempt to re-enter this function or `with_cache`). 75 | let mut cache = self.cache.borrow_mut(); 76 | // The `unwrap` below should not panic. We know `self.cache` was 77 | // not None upon entering this function because we looked up 78 | // `memoized_output`. The only function which can replace the 79 | // cache with None is `with_cache`, which would have panicked if 80 | // `f` attempted to re-enter it. 81 | (cache.as_mut().unwrap()).insert((input, size_in_bytes), output); 82 | output 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/ocamlrep_ocamlpool/ocamlpool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | #ifndef OCAMLPOOL_H 8 | #define OCAMLPOOL_H 9 | 10 | #include 11 | #include 12 | 13 | /* OCamlpool sections 14 | * =========================================================================== 15 | * 16 | * Inside the section, the OCaml heap will be in an invalid state. 17 | * OCaml runtime functions should not be called. 18 | * 19 | * Since the GC will never run while in an OCaml pool section, 20 | * it is safe to keep references to OCaml values as long as these does not 21 | * outlive the section. 22 | */ 23 | 24 | void ocamlpool_enter(void); 25 | void ocamlpool_leave(void); 26 | 27 | /* OCaml value allocations 28 | * =========================================================================== 29 | * 30 | * Reserve OCaml memory when inside ocamlpool section. 31 | */ 32 | 33 | #if OCAML_VERSION < 50000 34 | value ocamlpool_reserve_block(int tag, size_t words); 35 | #else 36 | value ocamlpool_reserve_block(tag_t tag, mlsize_t words); 37 | #endif 38 | 39 | #if OCAML_VERSION < 50000 40 | 41 | /* 42 | * FIXME: The current system always maintain the heap in a well formed state, 43 | * making the current pool look like a string to the OCaml GC and 44 | * fragmenting it during allocation. 45 | * This is not necessary, it should be correct to just keep a pointer 46 | * and the size of the unallocated area while in the section and make 47 | * it look like a string when leaving the section. 48 | * FIXME: The current chunking system might be incorrect if the incremental 49 | * scan stops in the middle of the unallocated chunk. 50 | * To prevent that, this chunk is marked as non-scannable (a string), 51 | * but I should double check the behavior of Obj.truncate. 52 | * FIXME: For now, the chunk is just strongly referenced during used and 53 | * unreferenced when released. 54 | * Improvements: 55 | * - make it weak so that OCaml GC can grab it under memory pressure 56 | * - add it to freelist on release, so that memory can be reclaimed 57 | * before next GC. 58 | */ 59 | 60 | /* Memory chunking 61 | * =========================================================================== 62 | * 63 | * Pool memory is allocated by large chunks. 64 | * The default settings should work well, though it is possible to tweak 65 | * and monitor these parameters. 66 | * 67 | */ 68 | 69 | /* Number of chunks allocated by ocamlpool since beginning of execution */ 70 | int ocamlpool_allocated_chunks(void); 71 | 72 | /* Controlling the size of allocated chunks. 73 | * >= 512, preferably >= 2^20 74 | * 75 | * NOTE: When changing this value, change the magic number in ocamlpool_test.rs 76 | */ 77 | #define OCAMLPOOL_DEFAULT_SIZE (1024 * 1024) 78 | size_t ocamlpool_get_next_chunk_size(void); 79 | void ocamlpool_set_next_chunk_size(size_t sz); 80 | 81 | /* Return the current chunk to OCaml memory system */ 82 | void ocamlpool_chunk_release(void); 83 | 84 | value ocamlpool_reserve_string(size_t bytes); 85 | 86 | extern color_t ocamlpool_color; 87 | extern value *ocamlpool_limit, *ocamlpool_cursor, *ocamlpool_bound; 88 | 89 | #endif /* OCAML_VERSION < 50000 */ 90 | 91 | // Defined for now in the `OCAML_VERSION >= 50000` case too for compatibility 92 | // with the expectations of `ocamlrep_ocamlpool`. 93 | extern uintnat ocamlpool_generation; 94 | 95 | #endif /* !defined(OCAMLPOOL_H) */ 96 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when there is a 56 | reasonable belief that an individual's behavior may have a negative impact on 57 | the project or its community. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported by contacting the project team at . All 63 | complaints will be reviewed and investigated and will result in a response that 64 | is deemed necessary and appropriate to the circumstances. The project team is 65 | obligated to maintain confidentiality with regard to the reporter of an incident. 66 | Further details of specific enforcement policies may be posted separately. 67 | 68 | Project maintainers who do not follow or enforce the Code of Conduct in good 69 | faith may face temporary or permanent repercussions as determined by other 70 | members of the project's leadership. 71 | 72 | ## Attribution 73 | 74 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 75 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | 77 | [homepage]: https://www.contributor-covenant.org 78 | 79 | For answers to common questions about this code of conduct, see 80 | https://www.contributor-covenant.org/faq 81 | -------------------------------------------------------------------------------- /vendor/ocaml/interop/oss/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when there is a 56 | reasonable belief that an individual's behavior may have a negative impact on 57 | the project or its community. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported by contacting the project team at . All 63 | complaints will be reviewed and investigated and will result in a response that 64 | is deemed necessary and appropriate to the circumstances. The project team is 65 | obligated to maintain confidentiality with regard to the reporter of an incident. 66 | Further details of specific enforcement policies may be posted separately. 67 | 68 | Project maintainers who do not follow or enforce the Code of Conduct in good 69 | faith may face temporary or permanent repercussions as determined by other 70 | members of the project's leadership. 71 | 72 | ## Attribution 73 | 74 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 75 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | 77 | [homepage]: https://www.contributor-covenant.org 78 | 79 | For answers to common questions about this code of conduct, see 80 | https://www.contributor-covenant.org/faq 81 | -------------------------------------------------------------------------------- /parser_post_process.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | use std::collections::HashSet; 9 | 10 | use regex::Regex; 11 | 12 | // TODO: Refactor this into the constants.rs file once necessary updates are made to the TARGETS file 13 | pub static AUTOCOMPLETE_TOKEN: &str = "AUTOCOMPLETE"; 14 | 15 | #[derive(Debug)] 16 | pub struct ParserPostprocessor {} 17 | 18 | #[derive(Debug)] 19 | pub struct ErrorSegment { 20 | pub start_lineno: isize, // inclusive 21 | pub end_lineno: isize, // exclusive 22 | } 23 | 24 | impl ParserPostprocessor { 25 | pub fn new() -> Self { 26 | ParserPostprocessor {} 27 | } 28 | 29 | /// 30 | /// Returns if the series of characters form a valid Python identifier or 31 | /// a valid combinations that can precede a "." 32 | /// 33 | /// (e.g., "foo.bar" is valid, "'foo'.split" is also valid, but 34 | /// "!.bar" is not). 35 | /// 36 | /// A valid identifier has the following properties: 37 | /// * It contains only ASCII alphanumeric characters and underscores. 38 | /// * It begins with a letter or an underscore. 39 | /// * It is not followed by any punctuation. 40 | /// * It is not a reserved keyword. 41 | fn valid_tokens_preceding_dot(&self, line: &str) -> bool { 42 | let preceding_tokens = match line.rsplit_once([' ', '.', ')', '\'', '}', ']', '\"']) { 43 | Some((_, identifier_and_trailing_dot)) => identifier_and_trailing_dot, 44 | None => line, 45 | }; 46 | 47 | !preceding_tokens.is_empty() 48 | && preceding_tokens 49 | .chars() 50 | .next() 51 | .map_or(false, |c| c.is_ascii_alphabetic() || c == '_') // First char must be letter or an underscore. 52 | && preceding_tokens 53 | .chars() 54 | .all(|c| c.is_ascii_alphanumeric() || c == '_') // Only ASCII alphanumeric characters and underscores. 55 | } 56 | 57 | /// 58 | /// Injects a `AUTOCOMPLETE_TOKEN` to the input code if a valid identifier 59 | /// followed by a trailing dot is found. 60 | fn process_trailing_dots(&self, line: &str) -> String { 61 | let re = Regex::new(r"\.\s").expect("Invalid Regex"); 62 | let mut result = Vec::new(); 63 | let mut curr = 0; 64 | 65 | let segments = re.split(line).collect::>(); 66 | let num_segments = segments.len(); 67 | 68 | for substring in segments { 69 | if curr < num_segments - 1 { 70 | if self.valid_tokens_preceding_dot(substring) { 71 | result.push(format!("{}.{}", substring, AUTOCOMPLETE_TOKEN)); 72 | } else { 73 | result.push(format!("{}.", substring)); 74 | } 75 | curr += 1; 76 | } else { 77 | // Skip last segment 78 | result.push(substring.trim_end().to_string()); 79 | if line.ends_with('\n') { 80 | result.push("\n".to_string()); 81 | } 82 | break; 83 | } 84 | } 85 | result.join("") 86 | } 87 | 88 | pub fn postprocess(self, input_code: &str, error_lines: HashSet) -> String { 89 | input_code 90 | .split_inclusive('\n') 91 | .enumerate() 92 | .map(|(line_number, line)| { 93 | if error_lines.contains(&line_number) { 94 | self.process_trailing_dots(line) 95 | } else { 96 | String::from(line) 97 | } 98 | }) 99 | .collect() 100 | } 101 | } 102 | --------------------------------------------------------------------------------