├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── free_form.md ├── pull_request_template.md └── workflows │ ├── audit.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .prettierrc.js ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── logo.png └── logo.svg ├── benchmark ├── Cargo.toml └── benches │ ├── files │ ├── capt_groups.pomsky │ ├── classes.pomsky │ ├── email.pomsky │ ├── groups.pomsky │ ├── modes.pomsky │ ├── properties.pomsky │ ├── repetitions.pomsky │ ├── special.pomsky │ ├── strings.pomsky │ ├── version.melody │ └── version.pomsky │ └── main.rs ├── completions ├── pomsky.bash ├── pomsky.fish └── pomsky.zsh ├── helptext ├── Cargo.toml ├── README.md ├── docs │ ├── long_help.png │ └── short_help.png └── src │ ├── help.rs │ ├── lib.rs │ ├── macros.rs │ └── style.rs ├── justfile ├── pomsky-bin ├── Cargo.toml ├── README.md ├── src │ ├── args │ │ ├── engines.rs │ │ ├── errors.rs │ │ ├── features.rs │ │ ├── flavors.rs │ │ ├── help.rs │ │ ├── input.rs │ │ ├── mod.rs │ │ ├── parse.rs │ │ └── warnings.rs │ ├── format.rs │ ├── lib.rs │ ├── main.rs │ ├── result │ │ ├── mod.rs │ │ └── serde_code.rs │ ├── test_runner.rs │ └── testing.rs └── tests │ └── e2e.rs ├── pomsky-lib ├── Cargo.toml ├── afl-fuzz │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── ignored_errors.txt │ ├── in │ │ └── 1.txt │ ├── justfile │ └── src │ │ └── main.rs ├── fuzz │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── fuzz_targets │ │ └── parse_and_compile.rs ├── src │ ├── capturing_groups.rs │ ├── compile.rs │ ├── defer.rs │ ├── diagnose │ │ ├── compile_error.rs │ │ ├── diagnostic_code.rs │ │ ├── diagnostic_kind.rs │ │ ├── diagnostics.rs │ │ ├── feature.rs │ │ ├── help.rs │ │ └── mod.rs │ ├── error │ │ └── mod.rs │ ├── exprs │ │ ├── alternation.rs │ │ ├── boundary.rs │ │ ├── char_class │ │ │ ├── char_set_item.rs │ │ │ └── mod.rs │ │ ├── codepoint.rs │ │ ├── dot.rs │ │ ├── grapheme.rs │ │ ├── group.rs │ │ ├── intersection.rs │ │ ├── literal.rs │ │ ├── lookaround.rs │ │ ├── mod.rs │ │ ├── range.rs │ │ ├── recursion.rs │ │ ├── reference.rs │ │ ├── regex.rs │ │ ├── repetition.rs │ │ ├── rule.rs │ │ ├── stmt.rs │ │ └── var.rs │ ├── features.rs │ ├── lib.rs │ ├── options.rs │ ├── regex │ │ ├── mod.rs │ │ └── optimize.rs │ ├── unicode_set.rs │ ├── validation.rs │ └── visitor.rs └── tests │ ├── it │ ├── args.rs │ ├── color.rs │ ├── diff.rs │ ├── files.rs │ ├── fuzzer.rs │ └── main.rs │ └── testcases │ ├── ascii │ ├── ascii_dotnet.txt │ ├── ascii_java.txt │ ├── ascii_js.txt │ ├── ascii_pcre.txt │ ├── ascii_python.txt │ ├── ascii_re2.txt │ ├── ascii_ruby.txt │ ├── ascii_rust.txt │ ├── nested_ascii.txt │ ├── nested_ascii_variables.txt │ ├── variables_1.txt │ └── variables_2.txt │ ├── basics │ ├── alt.txt │ ├── cp.txt │ ├── dot.txt │ ├── explicit_crlf.txt │ ├── grapheme_var.txt │ ├── invalid_tokens_unic.txt │ ├── line_comments.txt │ ├── parens.txt │ ├── strings.txt │ ├── unic_string.txt │ └── windows_crlf.txt │ ├── boundaries │ ├── boundary_neg.txt │ ├── caret_dollar.txt │ ├── js_ascii_word_start_end.txt │ ├── ruby_word_boundary_in_lookbehind.txt │ ├── ruby_word_start_in_lookahead.txt │ ├── ruby_word_start_in_lookbehind.txt │ ├── unsupported_word_start_end.txt │ ├── word_boundary.txt │ ├── word_start_end_pcre.txt │ └── word_start_end_rust.txt │ ├── classes │ ├── ascii_shorthands.txt │ ├── char_set.txt │ ├── char_set_codepoints.txt │ ├── char_set_codepoints_rust.txt │ ├── escapes.txt │ ├── escapes_js.txt │ ├── escapes_pcre.txt │ ├── escapes_single.txt │ ├── neg_word.txt │ ├── not_nothing_1.txt │ ├── not_nothing_2.txt │ ├── nothing_1.txt │ ├── nothing_2.txt │ ├── nothing_3.txt │ ├── nothing_4.txt │ ├── nothing_5.txt │ ├── nothing_6.txt │ ├── nothing_7.txt │ ├── nothing_8.txt │ ├── perl_class_like.txt │ ├── shorthands.txt │ ├── shorthands_dep_fixed.txt │ ├── shorthands_deprecated.txt │ ├── shorthands_js_polyfills.txt │ ├── shorthands_neg_union.txt │ ├── shorthands_re2_polyfills.txt │ ├── shorthands_union_verbose.txt │ ├── special_chars_neg.txt │ ├── special_chars_union.txt │ ├── ws_shorthands.txt │ └── ws_shorthands_rust.txt │ ├── codepoints │ ├── e_missing_plus.txt │ ├── e_surrogate.txt │ ├── e_too_large.txt │ ├── e_too_long.txt │ ├── java.txt │ ├── js.txt │ ├── pcre.txt │ ├── py.txt │ ├── ruby.txt │ └── rust.txt │ ├── errors │ ├── atomic_capturing.txt │ ├── caret_in_set.txt │ ├── code_point_invalid.txt │ ├── empty_set.txt │ ├── empty_set_negated.txt │ ├── empty_string_in_set_range.txt │ ├── excess_closing_bracket.txt │ ├── forbidden_neg_in_set.txt │ ├── grapheme_within_set.txt │ ├── huge_reference.txt │ ├── keyword_after_let.txt │ ├── keyword_in_set.txt │ ├── missing_closing_paren.txt │ ├── negated_shorthand_ascii.txt │ ├── negated_shorthand_twice.txt │ ├── number_range_decreasing.txt │ ├── range_with_multichar_string_1.txt │ ├── range_with_multichar_string_2.txt │ ├── range_with_multichar_string_3.txt │ ├── range_with_multichar_string_4.txt │ ├── recursion_limit_1.txt │ ├── recursion_limit_2.txt │ ├── relative_ref_zero_1.txt │ ├── relative_ref_zero_2.txt │ ├── repetition_descending.txt │ ├── set_range_descending.txt │ ├── star.txt │ ├── unclosed_brace_1.txt │ ├── unclosed_brace_2.txt │ ├── unclosed_brace_3.txt │ ├── unclosed_bracket.txt │ ├── unclosed_range_in_set.txt │ ├── unclosed_string.txt │ ├── unexpected_braces.txt │ ├── unicode_prop_ascii_mode.txt │ ├── unknown_token.txt │ ├── unsupported │ │ ├── atomic.txt │ │ ├── backref.txt │ │ ├── forward_ref.txt │ │ ├── grapheme.txt │ │ ├── lookahead.txt │ │ ├── neg_word.txt │ │ ├── relative_ref.txt │ │ ├── unicode_block.txt │ │ ├── unicode_prop.txt │ │ └── unicode_prop_specific.txt │ ├── word_boundary_js.txt │ └── wrong_type_of_bracket.txt │ ├── groups │ ├── atomic_groups.txt │ ├── capturing_groups.txt │ ├── huge_group_name.txt │ ├── invalid_group_name.txt │ ├── keyword_after_colon.txt │ ├── mixed_backrefs.txt │ ├── mixed_backrefs_net.txt │ ├── mixed_forward_refs.txt │ ├── mixed_forward_refs_net.txt │ ├── name_used_twice.txt │ ├── named_backrefs_net.txt │ ├── named_capturing_groups.txt │ ├── named_capturing_groups_js.txt │ ├── named_forward_refs_net.txt │ ├── named_refs_net.txt │ ├── num_refs_net.txt │ ├── num_to_named_refs_net.txt │ ├── opt_in_lookaround.txt │ ├── repeated_groups.txt │ ├── ruby_group_mix_error.txt │ └── ruby_stress.txt │ ├── intersections │ ├── e_distinct_strings.txt │ ├── e_multiple.txt │ ├── e_string_empty.txt │ ├── e_string_too_long.txt │ ├── e_with_assertions.txt │ ├── optimized.txt │ ├── ranges_java.txt │ ├── ranges_rs.txt │ ├── simple_py_error.txt │ ├── simple_rs.txt │ ├── with_groups.txt │ └── with_variables.txt │ ├── lookaround │ ├── ruby_lookahead_after_lookbehind.txt │ ├── ruby_lookahead_in_lookbehind_1.txt │ └── ruby_lookahead_in_lookbehind_2.txt │ ├── negation │ ├── char_neg.txt │ ├── double_neg_expected_expression.txt │ ├── double_neg_lookbehind.txt │ ├── double_neg_set.txt │ ├── double_neg_word_boundary.txt │ ├── grapheme_var_neg_error.txt │ ├── large_codepoint.txt │ ├── large_codepoint_in_class.txt │ ├── large_codepoint_in_class_net.txt │ ├── large_codepoint_net.txt │ ├── lookahead_neg.txt │ ├── lookbehind_neg.txt │ ├── neg_and_repeat_precedence_1.txt │ ├── neg_and_repeat_precedence_2.txt │ ├── number_range_neg.txt │ ├── number_range_neg_error.txt │ ├── set_in_parens_neg.txt │ ├── set_neg.txt │ ├── start_neg_error.txt │ ├── start_var_neg_error.txt │ ├── string_neg_error_1.txt │ ├── string_neg_error_2.txt │ └── var_neg.txt │ ├── opt │ └── fold_repetitions.txt │ ├── pipes │ ├── leading.txt │ ├── leading_in_group.txt │ ├── leading_modified.txt │ ├── normal.txt │ ├── normal_in_group.txt │ ├── normal_modified.txt │ ├── sole_pipe1.txt │ ├── sole_pipe2.txt │ ├── sole_pipe3.txt │ ├── sole_pipe4.txt │ ├── sole_pipe5.txt │ └── sole_pipe6.txt │ ├── prefixes │ ├── blk_dotnet.txt │ ├── blk_ruby.txt │ ├── error_unexpected_prefix.txt │ ├── error_wrong_prefix.txt │ ├── gc_dotnet.txt │ ├── gc_js.txt │ ├── gc_ruby.txt │ ├── script_java.txt │ ├── script_js.txt │ ├── script_pcre.txt │ ├── script_rs.txt │ ├── scx_java.txt │ ├── scx_pcre.txt │ └── scx_rs.txt │ ├── props │ ├── blocks_dotnet.txt │ ├── blocks_java.txt │ ├── blocks_ruby.txt │ ├── categories.txt │ ├── categories_neg.txt │ ├── category_double_neg.txt │ ├── other_js.txt │ ├── scripts_dotnet.txt │ ├── scripts_java.txt │ ├── scripts_js.txt │ ├── scripts_pcre.txt │ ├── scripts_python.txt │ ├── scripts_rs.txt │ └── scripts_ruby.txt │ ├── ranges │ ├── 00018_to_49180.txt │ ├── 000_to_471.txt │ ├── 03918_to_49180.txt │ ├── 0_to_255.txt │ ├── 0_to_471.txt │ ├── 0_to_57.txt │ ├── 0_to_741.txt │ ├── 0_to_75.txt │ ├── 0_to_9.txt │ ├── 0_to_99.txt │ ├── 3900_to_491999.txt │ ├── 3918_to_4918.txt │ ├── 3918_to_49180.txt │ ├── 5_to_70.txt │ ├── 70_to_500.txt │ ├── base16.txt │ ├── e_empty_string.txt │ ├── e_expected_number.txt │ ├── e_expected_string.txt │ ├── e_leading_0_variable.txt │ └── too_big_error.txt │ ├── recursion │ ├── balanced_parens.txt │ ├── never_terminates.txt │ └── unsupported.txt │ ├── references │ ├── one.txt │ ├── one_named.txt │ └── zero_invalid.txt │ ├── regex-diagnostics │ ├── b_0.txt │ ├── b_1.txt │ ├── b_8.txt │ ├── b_A.txt │ ├── b_B_upper.txt │ ├── b_G_upper.txt │ ├── b_N_upper.txt │ ├── b_R_upper.txt │ ├── b_W_upper.txt │ ├── b_X_upper.txt │ ├── b_Z_upper.txt │ ├── b_b.txt │ ├── b_g_0_recursion.txt │ ├── b_g_1.txt │ ├── b_g_1_relative.txt │ ├── b_g_braces_relative.txt │ ├── b_g_quotes.txt │ ├── b_k_angle.txt │ ├── b_k_braces.txt │ ├── b_n.txt │ ├── b_p_category.txt │ ├── b_p_category_double_neg.txt │ ├── b_p_category_neg.txt │ ├── b_p_category_neg2.txt │ ├── b_p_category_single.txt │ ├── b_p_category_single_neg.txt │ ├── b_s.txt │ ├── b_w.txt │ ├── b_z.txt │ ├── caret_in_group.txt │ ├── comment.txt │ ├── conditional.txt │ ├── conditional2.txt │ ├── dot.txt │ ├── lokkbehind.txt │ ├── lokkbehind_neg.txt │ ├── lookahead.txt │ ├── lookahead_neg.txt │ ├── non_capturing.txt │ ├── quantifier_lazy.txt │ ├── unicode_2.txt │ ├── unicode_4.txt │ └── unicode_braces.txt │ ├── regex │ ├── error_in_set.txt │ ├── error_negated.txt │ ├── in_alt.txt │ ├── in_alt_2.txt │ ├── in_group.txt │ ├── in_group_2.txt │ ├── in_group_3.txt │ ├── in_repetition.txt │ ├── invalid.txt │ └── literal.txt │ ├── repetitions │ ├── boundaries.txt │ ├── boundaries_1_ruby.txt │ ├── boundaries_2_ruby.txt │ ├── boundaries_3_ruby.txt │ ├── boundaries_4_ruby.txt │ ├── braces_1.txt │ ├── braces_2.txt │ ├── braces_3.txt │ ├── braces_4.txt │ ├── braces_chained.txt │ ├── different_exprs.txt │ ├── double_plus_error.txt │ ├── double_qm_error.txt │ ├── empty_parens_qm_removed.txt │ ├── greedy_and_lazy.txt │ ├── greedy_and_lazy_variables.txt │ ├── huge.txt │ ├── leading_zero.txt │ ├── leading_zeroes.txt │ ├── lookaround_dotnet.txt │ ├── lookaround_java.txt │ ├── lookaround_pcre.txt │ ├── lookaround_py.txt │ ├── lookaround_ruby.txt │ ├── lookaround_ruby_2.txt │ ├── plus_after_lazy.txt │ ├── plus_and_star.txt │ ├── qm.txt │ ├── qm_chained_with_parens.txt │ ├── star_and_braces_chained.txt │ ├── star_and_plus.txt │ ├── too_huge.txt │ └── too_huge_re2.txt │ ├── strings │ ├── escapes.txt │ ├── escapes_in_set_string_1.txt │ ├── escapes_in_set_string_2.txt │ ├── invalid_escape.txt │ ├── special_chars_in_strings.txt │ └── unclosed_string_bc_escaped_quote.txt │ ├── tests │ ├── captures.txt │ ├── empty.txt │ ├── empty_captures.txt │ ├── errors │ │ ├── missing_comma_1.txt │ │ ├── missing_comma_2.txt │ │ ├── multiple_strings.txt │ │ ├── nested_test_1.txt │ │ ├── nested_test_2.txt │ │ └── reject_captures.txt │ ├── match.txt │ ├── no_substrings.txt │ ├── quotes.txt │ ├── reject.txt │ ├── reject_substring.txt │ ├── substrings.txt │ └── trailing_comma.txt │ └── variables │ ├── builtins.txt │ ├── capturing.txt │ ├── capturing_unused.txt │ ├── directly_recursive.txt │ ├── empty_var.txt │ ├── let_stmt.txt │ ├── million_hellos.txt │ ├── missing_equals.txt │ ├── missing_keyword.txt │ ├── missing_semicolon.txt │ ├── missing_var_name.txt │ ├── mutually_recursive_1.txt │ ├── mutually_recursive_2.txt │ ├── nested_scopes.txt │ ├── nested_scopes_shadowing.txt │ ├── reference_in_let_stmt.txt │ ├── scoping_of_modes.txt │ ├── unused_recursive_var.txt │ ├── unused_var.txt │ ├── var_already_defined_in_scope.txt │ ├── var_does_not_exist.txt │ ├── var_not_in_scope.txt │ ├── var_used_once.txt │ └── var_used_twice.txt ├── pomsky-macro ├── Cargo.toml ├── README.md ├── src │ ├── diagnostic.rs │ └── lib.rs └── tests │ └── test_macro.rs ├── pomsky-syntax ├── Blocks.txt ├── Cargo.toml ├── DotNetSupportedBlocks.txt ├── JavaSupportedProps.txt ├── PropertyValueAliases.txt ├── SupportedBooleanProps.txt ├── build.rs └── src │ ├── diagnose.rs │ ├── error.rs │ ├── exprs │ ├── alternation.rs │ ├── arbitrary.rs │ ├── boundary.rs │ ├── char_class │ │ ├── ascii.rs │ │ ├── char_group.rs │ │ ├── mod.rs │ │ └── unicode.rs │ ├── group.rs │ ├── intersection.rs │ ├── literal.rs │ ├── lookaround.rs │ ├── mod.rs │ ├── negation.rs │ ├── range.rs │ ├── recursion.rs │ ├── reference.rs │ ├── regex.rs │ ├── repetition.rs │ ├── rule.rs │ ├── stmt.rs │ ├── test.rs │ └── var.rs │ ├── lexer │ ├── diagnostics.rs │ ├── error.rs │ ├── micro_regex.rs │ ├── mod.rs │ ├── token.rs │ └── tokenize.rs │ ├── lib.rs │ ├── parse │ ├── helper.rs │ ├── mod.rs │ ├── parser.rs │ └── parser_impl.rs │ ├── pretty_print.rs │ ├── span.rs │ ├── util.rs │ └── warning.rs ├── pomsky-wasm ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT ├── README-node.md ├── README-web.md ├── js │ └── mod.js ├── justfile ├── pkg │ └── package.json ├── src │ ├── lib.rs │ └── utils.rs └── tests │ └── web.rs ├── regex-test ├── .gitignore ├── Cargo.toml ├── dotnet │ ├── Tester.cs │ └── TesterAsync.cs ├── java │ ├── Tester.java │ └── TesterAsync.java ├── js │ ├── tester-deno-async.js │ ├── tester-deno.js │ ├── tester-node-async.js │ └── tester-node.js ├── python │ ├── tester.py │ └── tester_async.py └── src │ ├── count.rs │ ├── lib.rs │ ├── main.rs │ ├── native.rs │ └── sync │ ├── mod.rs │ └── process.rs └── rustfmt.toml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Aloso] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | 15 | 16 | ### Describe the bug 17 | 18 | A clear and concise description of what the bug is. 19 | 20 | ### To Reproduce 21 | 22 | The pomsky expression, the command, or the source code for reproducing the bug. 23 | 24 | ### Expected behavior 25 | 26 | A clear and concise description of what you expected to happen. 27 | 28 | ### Additional context 29 | 30 | How do you use Pomsky? E.g. via playground, the CLI, the Rust macro, NodeJS bindings, ... 31 | 32 | If you use the CLI or a library, which version? 33 | 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Is your feature request related to a problem? Please describe. 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | ### Describe the solution you'd like 14 | A clear and concise description of what you want to happen. 15 | 16 | ### Describe alternatives you've considered 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | ### Additional context 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/free_form.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Free form 3 | about: Create a custom issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Description 8 | 9 | 14 | 15 | Fixes # (issue) 16 | 17 | 25 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | push: 4 | paths: 5 | - '**/Cargo.toml' 6 | - '**/Cargo.lock' 7 | jobs: 8 | security_audit: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions-rs/audit-check@v1 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /vendor 3 | core.* 4 | /pomsky-wasm2 5 | 6 | # coverage info 7 | cov.zip 8 | lcov.info -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'es5', 3 | tabWidth: 2, 4 | semi: false, 5 | singleQuote: true, 6 | printWidth: 100, 7 | } 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "./pomsky-lib", 4 | "./pomsky-syntax", 5 | "./pomsky-bin", 6 | "./pomsky-macro", 7 | "./helptext", 8 | "./regex-test", 9 | "./benchmark", 10 | ] 11 | default-members = ["./pomsky-bin", "./pomsky-lib", "./pomsky-syntax"] 12 | resolver = "2" 13 | package.edition = "2021" 14 | 15 | [profile.release] 16 | lto = "thin" 17 | opt-level = 2 18 | incremental = true 19 | 20 | [profile.dist] 21 | inherits = "release" 22 | lto = "fat" 23 | opt-level = "s" 24 | codegen-units = 1 25 | strip = true 26 | incremental = false 27 | 28 | [patch.crates-io.onig_sys] 29 | git = "https://github.com/rust-onig/rust-onig" 30 | revision = "fa90c0e97e90a056af89f183b23cd417b59ee6a2" 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Ludwig Stecher 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomsky-lang/pomsky/3113ceb185f3a4baa355e90775db3c6f57037c6e/assets/logo.png -------------------------------------------------------------------------------- /benchmark/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benchmark" 3 | version = "0.1.3" 4 | edition.workspace = true 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | pomsky = { version = "0.11.0", path = "../pomsky-lib" } 11 | divan = "0.1.11" 12 | melody_compiler = "0.20.0" 13 | 14 | [[bench]] 15 | # run benchmarks with `cargo bench -p benchmark` 16 | name = "main" 17 | harness = false 18 | -------------------------------------------------------------------------------- /benchmark/benches/files/capt_groups.pomsky: -------------------------------------------------------------------------------- 1 | :tests(:foo(:thisisareallylongcapturinggroup() :bar() :() :baz('test' :(:(:()):())):())) -------------------------------------------------------------------------------- /benchmark/benches/files/classes.pomsky: -------------------------------------------------------------------------------- 1 | ['a' '0'-'9' 'a'-'z' Greek !InBasic_Latin U+09-U+0D U+10FFFF '"' "'"] -------------------------------------------------------------------------------- /benchmark/benches/files/email.pomsky: -------------------------------------------------------------------------------- 1 | let before_at = ['a'-'z' '0'-'9' "!#$%&'*+/=?^_`{|}~-"]; 2 | let escaped = '\' [U+01-U+09 U+0b U+0c U+0e-U+7f]; 3 | let quoted_before_at = [U+01-U+08 U+0b U+0c U+0e-U+1f U+21 U+23-U+5b U+5d-U+7f]; 4 | let port_digit = [U+01-U+08 U+0b U+0c U+0e-U+1f U+21-U+5a U+53-U+7f]; 5 | 6 | let lower_digit = ['a'-'z' '0'-'9']; 7 | let lower_digit_dash = ['a'-'z' '0'-'9' '-']; 8 | 9 | let domain_label = lower_digit (lower_digit_dash* lower_digit)?; 10 | 11 | ( 12 | | before_at+ ('.' before_at+)* 13 | | '"' (quoted_before_at | escaped)* '"' 14 | ) 15 | '@' 16 | ( 17 | | (domain_label '.')+ domain_label 18 | | '[' 19 | (:(range '0'-'255') '.'){3} 20 | ( 21 | | :(range '0'-'255') 22 | | lower_digit_dash* lower_digit ':' (port_digit | escaped)+ 23 | ) 24 | ']' 25 | ) -------------------------------------------------------------------------------- /benchmark/benches/files/groups.pomsky: -------------------------------------------------------------------------------- 1 | ( ( ( ( ( ( ( () ) ) ( ( ( ( ( ( ('hello') ) ) ) ) ) ) ) ) ) ( ( ( ( ( () ) ) 'world') ) ) ) ) -------------------------------------------------------------------------------- /benchmark/benches/files/modes.pomsky: -------------------------------------------------------------------------------- 1 | enable lazy; disable lazy; enable lazy; disable lazy; enable lazy; 2 | (disable lazy; enable lazy; 'w'+) -------------------------------------------------------------------------------- /benchmark/benches/files/properties.pomsky: -------------------------------------------------------------------------------- 1 | [Adlam] [Adlm] [InGreek_Extended] ![InBasic_Latin] 2 | [Tibetan] ![Tibt] [!Uppercase_Letter] [Z] ![!Zl] ![cntrl] 3 | [ascii_digit] ![n] [w] 4 | -------------------------------------------------------------------------------- /benchmark/benches/files/repetitions.pomsky: -------------------------------------------------------------------------------- 1 | (((([w]{4}){3}){7,}){1,2})* [w]+ greedy ([w]* lazy){500} -------------------------------------------------------------------------------- /benchmark/benches/files/special.pomsky: -------------------------------------------------------------------------------- 1 | % !% ^ $ Grapheme !>> 'this is' | 'a test' -------------------------------------------------------------------------------- /benchmark/benches/files/strings.pomsky: -------------------------------------------------------------------------------- 1 | 'hello' "world" 'this is great!' "I absolutely love it!" '"' -------------------------------------------------------------------------------- /benchmark/benches/files/version.melody: -------------------------------------------------------------------------------- 1 | ; 2 | 3 | option of "v"; 4 | 5 | capture major { 6 | some of ; 7 | } 8 | 9 | "."; 10 | 11 | capture minor { 12 | some of ; 13 | } 14 | 15 | "."; 16 | 17 | capture patch { 18 | some of ; 19 | } 20 | 21 | ; 22 | 23 | // v1.0.0 -------------------------------------------------------------------------------- /benchmark/benches/files/version.pomsky: -------------------------------------------------------------------------------- 1 | ^ 2 | 'v'? 3 | :major([ascii_digit]+) 4 | '.' 5 | :minor([ascii_digit]+) 6 | '.' 7 | :patch([ascii_digit]+) 8 | $ 9 | 10 | # v1.0.0 -------------------------------------------------------------------------------- /completions/pomsky.fish: -------------------------------------------------------------------------------- 1 | set -l features \ 2 | 'ascii-mode atomic-groups boundaries dot grapheme lazy-mode lookahead lookbehind named-groups numbered-groups ranges recursion references regexes variables' 3 | set -l flavors \ 4 | 'pcre PCRE flavor 5 | python Python re flavor 6 | java Java flavor 7 | js JavaScript (ECMAScript) flavor 8 | dotnet C# (.NET) flavor 9 | ruby Ruby (Oniguruma) flavor 10 | rust Rust regex flavor' 11 | set -l warnings \ 12 | '0 Disable all warnings 13 | compat=0 Disable compatibility warnings 14 | deprecated=0 Disable deprecation warnings' 15 | 16 | complete -c pomsky -l allowed-features -d 'Allowed features, comma-separated' -xa "(__fish_append , $features)" 17 | complete -c pomsky -s f -l flavor -d 'Regex flavor' -xa "(echo \"$flavors\")" 18 | complete -c pomsky -s h -l help -d 'Show help information' 19 | complete -c pomsky -l list -d 'List shorthands' -xa "shorthands" 20 | complete -c pomsky -s n -l no-new-line -d "Don't print line break after the output" 21 | complete -c pomsky -s p -l path -d 'File to compile' -kxa "(__fish_complete_suffix .pom)" 22 | complete -c pomsky -l test -d 'Run unit tests' -xa 'pcre2' 23 | complete -c pomsky -s V -l version -d 'Print version information' 24 | complete -c pomsky -s W -l warnings -d 'Disable some or all warnings' -xa "(echo \"$warnings\")" 25 | complete -c pomsky -s d -l debug -d 'Show debug information' 26 | complete -c pomsky -l json -d 'Return output as JSON' -------------------------------------------------------------------------------- /helptext/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "helptext" 3 | description = "Create beautiful help messages" 4 | version = "0.1.1" 5 | edition.workspace = true 6 | authors = ["Pomsky developers "] 7 | repository = "https://github.com/pomsky-lang/pomsky/tree/main/helptext" 8 | readme = "README.md" 9 | keywords = ["help", "colors"] 10 | categories = ["command-line-utilities"] 11 | license = "MIT OR Apache-2.0" 12 | exclude = ["./docs/**"] 13 | documentation = "https://docs.rs/helptext" 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | -------------------------------------------------------------------------------- /helptext/README.md: -------------------------------------------------------------------------------- 1 | # helptext 2 | 3 | [Documentation](https://docs.rs/helptext) 4 | 5 | A Rust macro to print beautiful help messages. 6 | 7 | ![Short help](https://raw.githubusercontent.com/pomsky-lang/pomsky/main/helptext/docs/short_help.png) 8 | 9 | ![Long help](https://raw.githubusercontent.com/pomsky-lang/pomsky/main/helptext/docs/long_help.png) 10 | -------------------------------------------------------------------------------- /helptext/docs/long_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomsky-lang/pomsky/3113ceb185f3a4baa355e90775db3c6f57037c6e/helptext/docs/long_help.png -------------------------------------------------------------------------------- /helptext/docs/short_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pomsky-lang/pomsky/3113ceb185f3a4baa355e90775db3c6f57037c6e/helptext/docs/short_help.png -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | set positional-arguments 2 | 3 | coverage_flags := '-Zprofile -Ccodegen-units=1 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 4 | 5 | # run Pomsky (debug mode) 6 | run S *args: 7 | cargo run -- "$@" 8 | 9 | # install Pomsky 10 | install: 11 | cargo install --path=pomsky-bin 12 | 13 | # benchmark Pomsky 14 | bench *flags: 15 | cargo bench -p benchmark -- {{flags}} 16 | 17 | # benchmark Pomsky with the plotters backend 18 | bench-plotters *flags: 19 | cargo bench -p benchmark -- --plotting-backend plotters {{flags}} 20 | 21 | coverage: 22 | cargo clean 23 | RUSTFLAGS="{{ coverage_flags }}" RUSTDOCFLAGS="{{ coverage_flags }}" CARGO_INCREMENTAL=0 cargo +nightly test 24 | zip -0 cov.zip $(find . -name "pomsky*.gc*" -print) 25 | grcov cov.zip -s . -t lcov --llvm --ignore-not-existing --ignore "/*" -o lcov.info 26 | 27 | # test Pomsky 28 | test *args: 29 | cargo test "$@" 30 | 31 | test-it *args: 32 | cargo test --test it --all-features -- "$@" 33 | 34 | # fuzz Pomsky ranges 35 | fuzz-ranges *flags: 36 | cargo test --test it -- --fuzz-ranges {{flags}} 37 | 38 | bump-versions: 39 | cargo set-version --bump minor -p pomsky-syntax 40 | cargo set-version --bump minor -p pomsky-lib 41 | cargo set-version --bump minor -p pomsky-bin 42 | cargo set-version --bump minor -p pomsky-macro 43 | 44 | publish *args: 45 | # only run this once versions are bumped and the changelog is up to date! 46 | 47 | cargo publish --manifest-path helptext/Cargo.toml --token $CARGO_TOKEN "$@" 48 | cargo publish --manifest-path pomsky-syntax/Cargo.toml --token $CARGO_TOKEN "$@" 49 | cargo publish --manifest-path pomsky-lib/Cargo.toml --token $CARGO_TOKEN "$@" 50 | cargo publish --manifest-path pomsky-macro/Cargo.toml --token $CARGO_TOKEN "$@" 51 | cargo publish --manifest-path pomsky-bin/Cargo.toml --token $CARGO_TOKEN "$@" 52 | echo \n"next steps:"\n" publish WASM"\n" publish git tag"\n" update GitHub release assets"\n" write blog post"\n"" 53 | -------------------------------------------------------------------------------- /pomsky-bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pomsky-bin" 3 | description = "Compile pomsky expressions, a new regular expression language" 4 | version = "0.11.0" 5 | edition.workspace = true 6 | authors = ["Pomsky developers "] 7 | license = "MIT OR Apache-2.0" 8 | homepage = "https://pomsky-lang.org/" 9 | repository = "https://github.com/pomsky-lang/pomsky" 10 | readme = "README.md" 11 | keywords = ["regexp", "regex", "pomsky"] 12 | categories = ["text-processing", "command-line-utilities"] 13 | 14 | [package.metadata.wix] 15 | upgrade-guid = "05169652-50F7-4B36-B5E5-677EFD6885FB" 16 | path-guid = "87E01AEC-EB08-449F-A80E-F4286E3B1EDC" 17 | license = false 18 | eula = false 19 | 20 | [package.metadata.dist] 21 | npm-scope = "@pomsky-lang" 22 | npm-package = "cli" 23 | 24 | [[bin]] 25 | name = "pomsky" 26 | path = "src/main.rs" 27 | 28 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 29 | 30 | [features] 31 | default = ["test"] 32 | test = ["dep:pcre2", "dep:regex", "dep:ignore"] 33 | 34 | [dependencies] 35 | lexopt = "0.3.0" 36 | supports-color = "3.0.2" 37 | helptext = { version = "0.1.0", path = "../helptext" } 38 | serde_json = "1.0.91" 39 | serde = { version = "1.0.152", features = ["derive"] } 40 | pcre2 = { version = "0.2.5", optional = true } 41 | regex = { version = "1.11.1", optional = true } 42 | ignore = { version = "0.4.23", optional = true } 43 | 44 | [dependencies.miette] 45 | version = "7.4.0" 46 | default-features = false 47 | features = ["fancy-no-backtrace"] 48 | 49 | [dependencies.pomsky] 50 | version = "0.11.0" 51 | path = "../pomsky-lib" 52 | features = ["dbg", "miette", "suggestions"] 53 | 54 | [dev-dependencies] 55 | assert_cmd = "2.0.12" 56 | assert_fs = "1.0.13" 57 | predicates = "2.1.5" 58 | -------------------------------------------------------------------------------- /pomsky-bin/README.md: -------------------------------------------------------------------------------- 1 | # Pomsky CLI 2 | 3 | This CLI allows you to compile [pomsky expressions](https://pomsky-lang.org/) to regexes in the 4 | command line. 5 | 6 | ## Use pre-built binaries 7 | 8 | Binaries are available for Windows, Linux and macOS. Download them from the 9 | [releases page](https://github.com/pomsky-lang/pomsky/releases). 10 | 11 | ## Install from source 12 | 13 | This requires that a recent Rust toolchain is installed. Instructions for how to install Rust can be 14 | found [here](https://www.rust-lang.org/tools/install). 15 | 16 | Install the CLI with 17 | 18 | ```sh 19 | cargo install pomsky-bin 20 | ``` 21 | 22 | ## Usage 23 | 24 | Then you can compile pomsky expressions to a regex flavor of your choice; the default is PCRE. 25 | 26 | Run `pomsky --help` for more information. 27 | 28 | Pomsky provides nice error messages: 29 | 30 | ```sh 31 | $ pomsky "'Hello world'* \X+" 32 | Error: 33 | × Backslash escapes are not supported 34 | ╭──── 35 | 1 │ 'Hello world'* \X+ 36 | · ─┬ 37 | · ╰── error occurred here 38 | ╰──── 39 | help: Replace `\X` with `Grapheme` 40 | ``` 41 | 42 | ## License 43 | 44 | Dual-licensed under the [MIT license](https://opensource.org/licenses/MIT) or the 45 | [Apache 2.0 license](https://opensource.org/licenses/Apache-2.0). 46 | -------------------------------------------------------------------------------- /pomsky-bin/src/args/engines.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | 3 | use super::ParseArgsError; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 6 | pub(crate) enum RegexEngine { 7 | Pcre2, 8 | Rust, 9 | } 10 | 11 | impl RegexEngine { 12 | pub(crate) fn parse(value: OsString) -> Result { 13 | let lower = value.to_string_lossy().to_ascii_lowercase(); 14 | Ok(match lower.as_str() { 15 | "pcre2" => RegexEngine::Pcre2, 16 | "rust" => RegexEngine::Rust, 17 | _ => return Err(ParseArgsError::UnknownEngine(lower)), 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pomsky-bin/src/args/features.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | 3 | use pomsky::features::PomskyFeatures; 4 | 5 | use crate::format::Logger; 6 | 7 | use super::ParseArgsError; 8 | 9 | pub(super) fn parse_features( 10 | logger: &Logger, 11 | value: OsString, 12 | ) -> Result { 13 | let lower = value.to_string_lossy().to_ascii_lowercase(); 14 | 15 | let mut features = PomskyFeatures::new(); 16 | for part in lower.split(',') { 17 | let part = part.trim(); 18 | if !part.is_empty() { 19 | match part { 20 | "grapheme" => features.grapheme(true), 21 | "numbered-groups" => features.numbered_groups(true), 22 | "named-groups" => features.named_groups(true), 23 | "atomic-groups" => features.atomic_groups(true), 24 | "references" => features.references(true), 25 | "lazy-mode" => features.lazy_mode(true), 26 | "ascii-mode" => features.ascii_mode(true), 27 | "ranges" => features.ranges(true), 28 | "variables" => features.variables(true), 29 | "lookahead" => features.lookahead(true), 30 | "lookbehind" => features.lookbehind(true), 31 | "boundaries" => features.boundaries(true), 32 | "regexes" => features.regexes(true), 33 | "dot" => features.dot(true), 34 | "recursion" => features.recursion(true), 35 | "intersection" => features.intersection(true), 36 | s => { 37 | logger.warn().println(format_args!("unknown feature `{s}`")); 38 | features 39 | } 40 | }; 41 | } 42 | } 43 | 44 | Ok(features) 45 | } 46 | -------------------------------------------------------------------------------- /pomsky-bin/src/args/flavors.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | 3 | use pomsky::options::RegexFlavor; 4 | 5 | use super::ParseArgsError; 6 | 7 | pub(super) fn parse_flavor(value: OsString) -> Result { 8 | let lower = value.to_string_lossy().to_ascii_lowercase(); 9 | Ok(match lower.as_str() { 10 | "pcre" => RegexFlavor::Pcre, 11 | "python" => RegexFlavor::Python, 12 | "java" => RegexFlavor::Java, 13 | "js" | "javascript" => RegexFlavor::JavaScript, 14 | "dotnet" | ".net" => RegexFlavor::DotNet, 15 | "ruby" => RegexFlavor::Ruby, 16 | "rust" => RegexFlavor::Rust, 17 | "re2" => RegexFlavor::RE2, 18 | _ => return Err(ParseArgsError::UnknownFlavor(lower)), 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /pomsky-bin/src/args/input.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{IsTerminal, Read}, 3 | path::PathBuf, 4 | }; 5 | 6 | use super::ParseArgsError; 7 | 8 | /// Compile a Pomsky expression to a regex 9 | #[derive(Debug, PartialEq)] 10 | pub(crate) enum Input { 11 | Value(String), 12 | File(PathBuf), 13 | } 14 | 15 | impl Input { 16 | pub(crate) fn read_stdin() -> Result { 17 | let mut stdin = std::io::stdin(); 18 | if !stdin.is_terminal() { 19 | let mut buf = Vec::new(); 20 | stdin.read_to_end(&mut buf).unwrap(); 21 | 22 | match String::from_utf8(buf) { 23 | Ok(input) => Ok(Input::Value(input)), 24 | Err(e) => Err(ParseArgsError::StdinUtf8(e)), 25 | } 26 | } else { 27 | Err(ParseArgsError::NoInput) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pomsky-bin/src/main.rs: -------------------------------------------------------------------------------- 1 | pub fn main() { 2 | pomsky_bin::main(); 3 | } 4 | -------------------------------------------------------------------------------- /pomsky-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pomsky" 3 | description = "A new regular expression language" 4 | version = "0.11.0" 5 | edition.workspace = true 6 | authors = ["Pomsky developers "] 7 | license = "MIT OR Apache-2.0" 8 | homepage = "https://pomsky-lang.org" 9 | repository = "https://github.com/pomsky-lang/pomsky" 10 | documentation = "https://docs.rs/pomsky" 11 | readme = "../README.md" 12 | keywords = ["regexp", "regex", "syntax", "parser", "pomsky"] 13 | categories = ["text-processing", "parser-implementations"] 14 | exclude = ["tests/**", "fuzz/**", "afl-fuzz/**"] 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [features] 19 | default = [] 20 | dbg = ["pomsky-syntax/dbg"] 21 | suggestions = ["pomsky-syntax/suggestions"] 22 | arbitrary = ["dep:arbitrary", "pomsky-syntax/arbitrary"] 23 | 24 | [dependencies] 25 | pomsky-syntax = { version = "0.11.0", path = "../pomsky-syntax" } 26 | arbitrary = { version = "1.3.1", features = ["derive"], optional = true } 27 | 28 | [dependencies.miette] 29 | version = "7.4.0" 30 | optional = true 31 | default-features = false 32 | 33 | [dev-dependencies] 34 | lexopt = "0.3.0" 35 | regex = "1.10.2" 36 | regex-test = { path = "../regex-test" } 37 | 38 | [[test]] 39 | name = "it" 40 | path = "./tests/it/main.rs" 41 | harness = false 42 | -------------------------------------------------------------------------------- /pomsky-lib/afl-fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | out 3 | out.txt 4 | errors.txt 5 | log.txt 6 | -------------------------------------------------------------------------------- /pomsky-lib/afl-fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "afl-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | afl = "0.14.3" 12 | arbitrary = "1.3.2" 13 | regex = "1" 14 | regex-test = { path = "../../regex-test" } 15 | pomsky = { path = "..", features = ["dbg", "arbitrary"] } 16 | 17 | # Prevent this from interfering with workspaces 18 | [workspace] 19 | members = ["."] 20 | 21 | [patch.crates-io.onig_sys] 22 | git = "https://github.com/rust-onig/rust-onig" 23 | revision = "fa90c0e97e90a056af89f183b23cd417b59ee6a2" 24 | -------------------------------------------------------------------------------- /pomsky-lib/afl-fuzz/README.md: -------------------------------------------------------------------------------- 1 | # AFL fuzzer 2 | 3 | This fuzzer checks that the Pomsky compiler does not crash for any input, and produces valid regular expressions. 4 | 5 | The latter requirement is tested by compiling the regex with the respective regex engine. This requires the following programs to be installed: 6 | 7 | - deno (for JavaScript) 8 | - javac 9 | - python 10 | - mcs (for .NET) 11 | 12 | ## Usage 13 | 14 | It is recommended to use [just](https://github.com/casey/just). When fuzzing Pomsky for the first time, run 15 | 16 | ```sh 17 | just fuzz_init 18 | just fuzz in 19 | ``` 20 | 21 | When you want to resume a previous fuzzing session, you can just 22 | 23 | ```sh 24 | just fuzz 25 | ``` 26 | 27 | ## Analyze crashes 28 | 29 | When you found a crash, you might find it in `errors.txt`. If it's not in `errors.txt`, that likely means that there was an unexpected panic. To minimize it, run `just tmin `, where `` is the path to a file in the `out/default/crashes` folder. This command minimizes the input for the crash and creates a logfile at `log.txt` that should make it possible to identify the bug. 30 | 31 | ## Report the bug 32 | 33 | Please report the bug [here](https://github.com/pomsky-lang/pomsky/issues). If you think it could be a security vulnerability, please disclose it directly per email: info@pomsky-lang.org. 34 | 35 | ## Latest findings 36 | 37 | ### PCRE 38 | 39 | - Lookbehind cannot contain include unbounded repetitions. 40 | - Bounded repetitions need an upper bound of _at most_ 255. I.e. `(?<=a{4,255})` is ok. 41 | - Nested repetitions reach the limit quicker (TBD) 42 | - Lookbehind cannot contain `\X` 43 | 44 | ### Ruby 45 | 46 | - Lookaround cannot contain capturing groups 47 | 48 | ### Python 49 | 50 | - Lookbehind requires fixed-width pattern 51 | - Cannot refer to open capturing group 52 | -------------------------------------------------------------------------------- /pomsky-lib/afl-fuzz/ignored_errors.txt: -------------------------------------------------------------------------------- 1 | Ruby|Oniguruma error: never ending recursion 2 | # Ruby|Oniguruma error: too big number for repeat range 3 | 4 | Rust|empty character classes are not allowed 5 | Rust|Compiled regex exceeds size limit 6 | 7 | PCRE|branch too long in variable-length lookbehind assertion 8 | PCRE|regular expression is too large 9 | # repetitions can be at most 65535 (2^16) 10 | PCRE|number too big in \{\} quantifier 11 | # lookbehind must not match more than 65535 (2^16) code points 12 | PCRE|lookbehind assertion is too long 13 | 14 | Java|Look-behind group does not have an obvious maximum length -------------------------------------------------------------------------------- /pomsky-lib/afl-fuzz/in/1.txt: -------------------------------------------------------------------------------- 1 | # BACKSLASH_ESC 2 | let dig = [ascii_digit]; 3 | let xdig = [ascii_xdigit]; 4 | 5 | let esc_hex = 'x' xdig{2}; 6 | let esc_octal = '0' ['0'-'7']{2}; 7 | let esc_octal_braced = 'o{' ['0'-'7']{1,8} '}'; 8 | let esc_unicode = 'u' xdig{4}; 9 | let esc_unicode_braced = ['ux'] '{' xdig{1,6} '}'; 10 | let esc_control = 'c' [ascii_alpha]; 11 | 12 | let backref_delim = [ascii_alnum '_-+']+; 13 | let backref_gk = ['gk'] ( '-'? dig dig? 14 | | '{' backref_delim '}' 15 | | '<' backref_delim '>' 16 | | "'" backref_delim "'" 17 | ); 18 | 19 | let category = ['pP'] ( [ascii_alpha] 20 | | '{' '^'? [ascii_alnum '_-+']+ '}' 21 | ); 22 | 23 | Start '\' ( esc_hex 24 | | esc_octal 25 | | esc_octal_braced 26 | | esc_unicode 27 | | esc_unicode_braced 28 | | esc_control 29 | | backref_gk 30 | | category 31 | | C 32 | ) -------------------------------------------------------------------------------- /pomsky-lib/afl-fuzz/justfile: -------------------------------------------------------------------------------- 1 | fuzz_init: 2 | cargo install cargo-afl 3 | cargo afl system-config 4 | 5 | fuzz in='-': 6 | cargo afl build 7 | cargo afl fuzz -i {{in}} -o out target/debug/afl-fuzz 8 | 9 | tmin input: 10 | rm -f log.txt 11 | FUZZ_LOG=1 AFL_DEBUG=1 AFL_MAP_SIZE=100000 cargo afl tmin -i {{input}} -o out.txt -- ./target/debug/afl-fuzz 12 | -------------------------------------------------------------------------------- /pomsky-lib/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | -------------------------------------------------------------------------------- /pomsky-lib/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pomsky-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2021" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | libfuzzer-sys = "0.4" 13 | regex-test = { path = "../../regex-test" } 14 | once_cell = "1.17.0" 15 | 16 | [dependencies.pomsky] 17 | path = ".." 18 | features = ["arbitrary"] 19 | 20 | # Prevent this from interfering with workspaces 21 | [workspace] 22 | members = ["."] 23 | 24 | [[bin]] 25 | name = "parse_and_compile" 26 | path = "fuzz_targets/parse_and_compile.rs" 27 | test = false 28 | doc = false 29 | -------------------------------------------------------------------------------- /pomsky-lib/src/compile.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | 3 | use pomsky_syntax::exprs::Rule; 4 | 5 | use crate::{ 6 | capturing_groups::{CapturingGroupIndex, CapturingGroupsCollector}, 7 | diagnose::{CompileError, Diagnostic}, 8 | regex::Regex, 9 | }; 10 | 11 | pub(crate) type CompileResult = Result; 12 | 13 | #[derive(Clone)] 14 | pub(crate) struct CompileState<'i> { 15 | pub(crate) next_idx: u32, 16 | pub(crate) used_names_vec: Vec>, 17 | pub(crate) used_names: HashMap, 18 | pub(crate) groups_count: u32, 19 | pub(crate) numbered_groups_count: u32, 20 | pub(crate) in_lookbehind: bool, 21 | 22 | pub(crate) variables: Vec<(&'i str, &'i Rule)>, 23 | pub(crate) current_vars: HashSet, 24 | 25 | pub(crate) diagnostics: Vec, 26 | } 27 | 28 | impl<'i> CompileState<'i> { 29 | pub(crate) fn new( 30 | capt_groups: CapturingGroupsCollector, 31 | variables: Vec<(&'i str, &'i Rule)>, 32 | ) -> Self { 33 | let used_names = capt_groups.names; 34 | let groups_count = capt_groups.count_named + capt_groups.count_numbered; 35 | 36 | // needed for Ruby: In Ruby, backreferences to named groups have to be named as 37 | // well 38 | let mut used_names_vec = vec![None; groups_count as usize + 1]; 39 | for (name, index) in &used_names { 40 | used_names_vec[index.absolute as usize] = Some(name.clone()); 41 | } 42 | 43 | CompileState { 44 | next_idx: 1, 45 | used_names_vec, 46 | used_names, 47 | groups_count, 48 | numbered_groups_count: capt_groups.count_numbered, 49 | in_lookbehind: false, 50 | 51 | variables, 52 | current_vars: Default::default(), 53 | 54 | diagnostics: vec![], 55 | } 56 | } 57 | 58 | pub(crate) fn has_named_groups(&self) -> bool { 59 | self.numbered_groups_count < self.groups_count 60 | } 61 | 62 | pub(crate) fn has_numbered_groups(&self) -> bool { 63 | self.numbered_groups_count > 0 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pomsky-lib/src/defer.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | pub(crate) struct Deferred<'a, S, F: FnMut(&mut S)> { 4 | state: &'a mut S, 5 | mutate: F, 6 | } 7 | 8 | impl<'a, S, F: FnMut(&mut S)> Deferred<'a, S, F> { 9 | pub(crate) fn new(state: &'a mut S, mutate: F) -> Self { 10 | Deferred { state, mutate } 11 | } 12 | } 13 | 14 | impl Drop for Deferred<'_, S, F> { 15 | fn drop(&mut self) { 16 | let mutator = &mut self.mutate; 17 | mutator(self.state); 18 | } 19 | } 20 | 21 | impl Deref for Deferred<'_, S, F> { 22 | type Target = S; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | self.state 26 | } 27 | } 28 | 29 | impl DerefMut for Deferred<'_, S, F> { 30 | fn deref_mut(&mut self) -> &mut Self::Target { 31 | self.state 32 | } 33 | } 34 | 35 | #[doc(hidden)] 36 | pub(crate) trait Mutable { 37 | fn mutable(&mut self) -> &mut Self; 38 | } 39 | 40 | impl Mutable for T { 41 | fn mutable(&mut self) -> &mut Self { 42 | self 43 | } 44 | } 45 | 46 | macro_rules! revert_on_drop { 47 | ($state:ident . $($id:tt).*) => { 48 | let __prev = $state.$($id).*; 49 | let mut $state = { 50 | use $crate::defer::Mutable as _; 51 | $crate::defer::Deferred::new($state.mutable(), move |$state| { 52 | $state.$($id).* = __prev; 53 | }) 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /pomsky-lib/src/diagnose/mod.rs: -------------------------------------------------------------------------------- 1 | //! Crate containing diagnostics, i.e. errors and warnings 2 | 3 | pub(crate) use compile_error::{ 4 | CompileError, CompileErrorKind, IllegalNegationKind, UnsupportedError, 5 | }; 6 | 7 | pub use diagnostic_code::DiagnosticCode; 8 | pub use diagnostic_kind::DiagnosticKind; 9 | pub use diagnostics::{Diagnostic, Severity}; 10 | pub use feature::Feature; 11 | 12 | mod compile_error; 13 | mod diagnostic_code; 14 | mod diagnostic_kind; 15 | mod diagnostics; 16 | mod feature; 17 | mod help; 18 | -------------------------------------------------------------------------------- /pomsky-lib/src/error/mod.rs: -------------------------------------------------------------------------------- 1 | //! Contains different kinds of errors emitted by Pomsky. 2 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/alternation.rs: -------------------------------------------------------------------------------- 1 | //! Implements [alternation](https://www.regular-expressions.info/alternation.html): 2 | //! `('alt1' | 'alt2' | 'alt3')`. 3 | 4 | use crate::{ 5 | compile::{CompileResult, CompileState}, 6 | options::{CompileOptions, RegexFlavor}, 7 | regex::Regex, 8 | }; 9 | 10 | use super::{Alternation, Compile}; 11 | 12 | impl Compile for Alternation { 13 | fn compile<'c>( 14 | &'c self, 15 | options: CompileOptions, 16 | state: &mut CompileState<'c>, 17 | ) -> CompileResult { 18 | Ok(Regex::Alternation(RegexAlternation { 19 | parts: self 20 | .rules 21 | .iter() 22 | .map(|rule| rule.compile(options, state)) 23 | .collect::>()?, 24 | })) 25 | } 26 | } 27 | 28 | #[cfg_attr(feature = "dbg", derive(Debug))] 29 | pub(crate) struct RegexAlternation { 30 | pub(crate) parts: Vec, 31 | } 32 | 33 | impl RegexAlternation { 34 | pub(crate) fn new(parts: Vec) -> Self { 35 | Self { parts } 36 | } 37 | 38 | pub(crate) fn codegen(&self, buf: &mut String, flavor: RegexFlavor) { 39 | for rule in &self.parts { 40 | rule.codegen(buf, flavor); 41 | buf.push('|'); 42 | } 43 | if !self.parts.is_empty() { 44 | let _ = buf.pop(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/codepoint.rs: -------------------------------------------------------------------------------- 1 | //! Contains the [`Grapheme`] type, which matches a 2 | //! [Unicode grapheme](https://www.regular-expressions.info/unicode.html#grapheme). 3 | 4 | use crate::{ 5 | compile::CompileResult, 6 | options::CompileOptions, 7 | regex::{Regex, RegexShorthand}, 8 | unicode_set::UnicodeSet, 9 | }; 10 | 11 | use super::char_class::{RegexCharSet, RegexCharSetItem}; 12 | 13 | /// The `Codepoint` expression, matching an arbitrary Unicode code point. 14 | #[derive(Clone, Copy, PartialEq, Eq)] 15 | #[cfg_attr(feature = "dbg", derive(Debug))] 16 | pub(crate) struct Codepoint {} 17 | 18 | impl Codepoint { 19 | pub(crate) fn compile(&self, _options: CompileOptions) -> CompileResult { 20 | let mut set = UnicodeSet::new(); 21 | set.add_prop(RegexCharSetItem::Shorthand(RegexShorthand::Space)); 22 | set.add_prop(RegexCharSetItem::Shorthand(RegexShorthand::NotSpace)); 23 | Ok(Regex::CharSet(RegexCharSet::new(set))) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/dot.rs: -------------------------------------------------------------------------------- 1 | //! Contains the [`Grapheme`] type, which matches a 2 | //! [Unicode grapheme](https://www.regular-expressions.info/unicode.html#grapheme). 3 | 4 | use crate::{compile::CompileResult, options::CompileOptions, regex::Regex}; 5 | 6 | /// The dot, matching anything except line breaks 7 | #[derive(Clone, Copy, PartialEq, Eq)] 8 | #[cfg_attr(feature = "dbg", derive(Debug))] 9 | pub(crate) struct Dot {} 10 | 11 | impl Dot { 12 | pub(crate) fn compile(&self, _: CompileOptions) -> CompileResult { 13 | Ok(Regex::Dot) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/grapheme.rs: -------------------------------------------------------------------------------- 1 | //! Contains the [`Grapheme`] type, which matches a 2 | //! [Unicode grapheme](https://www.regular-expressions.info/unicode.html#grapheme). 3 | 4 | use pomsky_syntax::Span; 5 | 6 | use crate::{ 7 | compile::CompileResult, 8 | diagnose::{CompileErrorKind, Feature}, 9 | options::{CompileOptions, RegexFlavor}, 10 | regex::Regex, 11 | }; 12 | 13 | /// The `Grapheme` expression, matching a 14 | /// [Unicode grapheme](https://www.regular-expressions.info/unicode.html#grapheme). 15 | #[derive(Clone, Copy, PartialEq, Eq)] 16 | #[cfg_attr(feature = "dbg", derive(Debug))] 17 | pub(crate) struct Grapheme {} 18 | 19 | impl Grapheme { 20 | pub(crate) fn compile(&self, options: CompileOptions) -> CompileResult { 21 | if matches!(options.flavor, RegexFlavor::Pcre | RegexFlavor::Java | RegexFlavor::Ruby) { 22 | Ok(Regex::Grapheme) 23 | } else { 24 | Err(CompileErrorKind::Unsupported(Feature::Grapheme, options.flavor).at(Span::empty())) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/recursion.rs: -------------------------------------------------------------------------------- 1 | use pomsky_syntax::exprs::Recursion; 2 | 3 | use crate::{ 4 | compile::{CompileResult, CompileState}, 5 | options::{CompileOptions, RegexFlavor}, 6 | regex::Regex, 7 | }; 8 | 9 | use super::Compile; 10 | 11 | impl Compile for Recursion { 12 | fn compile(&self, _options: CompileOptions, _: &mut CompileState<'_>) -> CompileResult { 13 | Ok(Regex::Recursion) 14 | } 15 | } 16 | 17 | pub(crate) fn codegen(buf: &mut String, _flavor: RegexFlavor) { 18 | buf.push_str("\\g<0>") 19 | } 20 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/regex.rs: -------------------------------------------------------------------------------- 1 | use pomsky_syntax::exprs::Regex as RegexLiteral; 2 | 3 | use crate::{ 4 | compile::{CompileResult, CompileState}, 5 | options::CompileOptions, 6 | regex::Regex, 7 | }; 8 | 9 | use super::Compile; 10 | 11 | impl Compile for RegexLiteral { 12 | fn compile(&self, _: CompileOptions, _: &mut CompileState<'_>) -> CompileResult { 13 | Ok(Regex::Unescaped(self.content.clone())) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/rule.rs: -------------------------------------------------------------------------------- 1 | use pomsky_syntax::exprs::Rule; 2 | 3 | use crate::{ 4 | compile::{CompileResult, CompileState}, 5 | options::CompileOptions, 6 | regex::Regex, 7 | }; 8 | 9 | use super::{ 10 | char_class::check_char_class_empty, codepoint::Codepoint, dot::Dot, grapheme::Grapheme, Compile, 11 | }; 12 | 13 | impl Compile for Rule { 14 | fn compile<'c>( 15 | &'c self, 16 | options: CompileOptions, 17 | state: &mut CompileState<'c>, 18 | ) -> CompileResult { 19 | match self { 20 | Rule::Literal(l) => l.compile(options, state), 21 | Rule::CharClass(c) => c.compile(options, state), 22 | Rule::Group(g) => g.compile(options, state), 23 | Rule::Grapheme => Grapheme {}.compile(options), 24 | Rule::Codepoint => Codepoint {}.compile(options), 25 | Rule::Dot => Dot {}.compile(options), 26 | Rule::Alternation(a) => a.compile(options, state), 27 | Rule::Intersection(a) => a.compile(options, state), 28 | Rule::Repetition(r) => r.compile(options, state), 29 | Rule::Boundary(b) => b.compile(options, state), 30 | Rule::Lookaround(l) => l.compile(options, state), 31 | Rule::Variable(v) => v.compile(options, state).map_err(|mut e| { 32 | e.set_missing_span(v.span); 33 | e 34 | }), 35 | Rule::Reference(r) => r.compile(options, state), 36 | Rule::Range(r) => r.compile(options, state), 37 | Rule::Regex(r) => r.compile(options, state), 38 | Rule::StmtExpr(m) => m.compile(options, state), 39 | Rule::Recursion(r) => r.compile(options, state), 40 | Rule::Negation(n) => { 41 | let span = n.rule.span(); 42 | let regex = n 43 | .rule 44 | .compile(options, state) 45 | .and_then(|r| r.negate(n.not_span, options.flavor))?; 46 | if let Regex::CharSet(char_set) = ®ex { 47 | check_char_class_empty(char_set, span)?; 48 | } 49 | Ok(regex) 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/stmt.rs: -------------------------------------------------------------------------------- 1 | use pomsky_syntax::exprs::{Stmt, StmtExpr}; 2 | 3 | use crate::{ 4 | compile::{CompileResult, CompileState}, 5 | options::CompileOptions, 6 | }; 7 | 8 | use super::Compile; 9 | 10 | impl Compile for StmtExpr { 11 | fn compile<'c>( 12 | &'c self, 13 | options: CompileOptions, 14 | state: &mut CompileState<'c>, 15 | ) -> CompileResult { 16 | match &self.stmt { 17 | Stmt::Enable(..) | Stmt::Disable(..) => self.rule.compile(options, state), 18 | Stmt::Let(r#let) => { 19 | state.variables.push((&r#let.name, &r#let.rule)); 20 | let res = self.rule.compile(options, state)?; 21 | state.variables.pop(); 22 | Ok(res) 23 | } 24 | Stmt::Test(_) => self.rule.compile(options, state), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pomsky-lib/src/exprs/var.rs: -------------------------------------------------------------------------------- 1 | use pomsky_syntax::exprs::{Rule, Variable}; 2 | 3 | use crate::{ 4 | compile::{CompileResult, CompileState}, 5 | diagnose::CompileErrorKind, 6 | features::PomskyFeatures, 7 | options::CompileOptions, 8 | }; 9 | 10 | use super::Compile; 11 | 12 | impl Compile for Variable { 13 | fn compile<'c>( 14 | &'c self, 15 | options: CompileOptions, 16 | state: &mut CompileState<'c>, 17 | ) -> CompileResult { 18 | let rule = state 19 | .variables 20 | .iter() 21 | .enumerate() 22 | .rev() 23 | .find(|&(i, &(name, _))| name == self.name && !state.current_vars.contains(&i)); 24 | 25 | if let Some((i, &(_, rule))) = rule { 26 | match rule { 27 | Rule::Boundary(_) => { 28 | options.allowed_features.require(PomskyFeatures::BOUNDARIES, self.span)?; 29 | } 30 | Rule::Grapheme => { 31 | options.allowed_features.require(PomskyFeatures::GRAPHEME, self.span)?; 32 | } 33 | _ => {} 34 | } 35 | 36 | state.current_vars.insert(i); 37 | let res = rule.compile(options, state)?; 38 | state.current_vars.remove(&i); 39 | Ok(res) 40 | } else { 41 | let recursive_rule = state.variables.iter().rev().find(|&&(name, _)| name == self.name); 42 | if recursive_rule.is_some() { 43 | Err(CompileErrorKind::RecursiveVariable.at(self.span)) 44 | } else { 45 | Err(CompileErrorKind::UnknownVariable { 46 | found: self.name.clone().into(), 47 | #[cfg(feature = "suggestions")] 48 | similar: pomsky_syntax::find_suggestion( 49 | &self.name, 50 | state.variables.iter().map(|&(var, _)| var), 51 | ), 52 | } 53 | .at(self.span)) 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pomsky-lib/src/options.rs: -------------------------------------------------------------------------------- 1 | //! Contains parser and compiler options passed to pomsky. 2 | 3 | use crate::features::PomskyFeatures; 4 | 5 | /// Options passed to the pomsky compiler 6 | #[derive(Debug, Clone, Copy)] 7 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 8 | pub struct CompileOptions { 9 | /// The targeted regex flavor. Pomsky makes sure that the emitted regex is 10 | /// compatible with this flavor. 11 | pub flavor: RegexFlavor, 12 | 13 | /// The maximum number of digits in a `range` expression. Defaults to 6. 14 | /// 15 | /// Note that if you increase this number, the time needed to compile a 16 | /// pomsky expression may increase exponentially. If you parse untrusted 17 | /// input, this can be used for a DoS attack. 18 | pub max_range_size: u8, 19 | 20 | /// Allowed pomsky features. By default, all features are allowed. 21 | pub allowed_features: PomskyFeatures, 22 | } 23 | 24 | impl Default for CompileOptions { 25 | fn default() -> Self { 26 | Self { 27 | flavor: RegexFlavor::default(), 28 | max_range_size: 6, 29 | allowed_features: PomskyFeatures::default(), 30 | } 31 | } 32 | } 33 | 34 | /// A regex flavor is a regex engine or a set of regex engines that are similar 35 | /// enough that they can be treated the same for the purpose of writing regexes. 36 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] 37 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 38 | #[non_exhaustive] 39 | pub enum RegexFlavor { 40 | /// PCRE and PCRE2 41 | #[default] 42 | Pcre, 43 | /// Python's `re` module 44 | Python, 45 | /// The `java.util.regex.Pattern` class 46 | Java, 47 | /// JavaScript (ECMAScript) built-in regular expressions 48 | JavaScript, 49 | /// .NET `Regex` class from the namespace `System.Text.RegularExpressions` 50 | DotNet, 51 | /// Ruby built-in regular expressions 52 | Ruby, 53 | /// The Rust `regex` crate 54 | Rust, 55 | /// The RE2 library 56 | RE2, 57 | } 58 | -------------------------------------------------------------------------------- /pomsky-lib/tests/it/diff.rs: -------------------------------------------------------------------------------- 1 | pub fn simple_diff<'a>(left: &'a str, right: &'a str) -> (&'a str, &'a str, &'a str, &'a str) { 2 | if left == right { 3 | return (left, "", "", ""); 4 | } 5 | 6 | let min = left.len().min(right.len()); 7 | 8 | let prefix_len = left 9 | .char_indices() 10 | .zip(right.chars()) 11 | .find(|&((_, a), b)| a != b) 12 | .map(|((n, _), _)| n) 13 | .unwrap_or(min); 14 | 15 | let suffix_len = left 16 | .char_indices() 17 | .rev() 18 | .zip(right.chars().rev()) 19 | .find(|&((_, a), b)| a != b) 20 | .map(|((n, _), _)| left.len() - n - 1) 21 | .unwrap_or(min); 22 | 23 | let suffix_len = suffix_len.min(left.len() - prefix_len).min(right.len() - prefix_len); 24 | 25 | let prefix = &left[..prefix_len]; 26 | let suffix = &left[left.len() - suffix_len..]; 27 | 28 | let left_diff = &left[prefix_len..left.len() - suffix_len]; 29 | let right_diff = &right[prefix_len..right.len() - suffix_len]; 30 | 31 | (prefix, left_diff, right_diff, suffix) 32 | } 33 | -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_dotnet.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | disable unicode; 3 | 4 | ^ C . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S].[\n\f\r][0-9][0-9A-Z_a-z][\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_java.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Java 2 | disable unicode; 3 | 4 | ^ C G . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S]\X.[\n\f\r][0-9][0-9A-Z_a-z][\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_js.txt: -------------------------------------------------------------------------------- 1 | #! flavor=JavaScript 2 | disable unicode; 3 | 4 | ^ C . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S].[\n\f\r]\d\w[\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_pcre.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | disable unicode; 3 | 4 | ^ C G . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S]\X.[\n\f\r][0-9][0-9A-Z_a-z][\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_python.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Python 2 | disable unicode; 3 | 4 | ^ C . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S].[\n\f\r][0-9][0-9A-Z_a-z][\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_re2.txt: -------------------------------------------------------------------------------- 1 | #! flavor=RE2 2 | disable unicode; 3 | 4 | ^ C . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S].[\n\f\r]\d\w\s\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_ruby.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Ruby 2 | disable unicode; 3 | 4 | ^ C G . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S]\X.[\n\f\r][0-9][0-9A-Z_a-z][\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/ascii_rust.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Rust 2 | disable unicode; 3 | 4 | ^ C . [n r f] [d] [w] [s] % !% 'Hello äöüß' $ 5 | ----- 6 | ^[\s\S].[\n\f\r][0-9][0-9A-Z_a-z][\t-\r ]\b\BHello äöüß$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/nested_ascii.txt: -------------------------------------------------------------------------------- 1 | [d] ( 2 | disable unicode; [d] 3 | ( 4 | enable unicode; [d] 5 | (disable unicode; [d]) 6 | ) 7 | ) 8 | ----- 9 | \d[0-9]\d[0-9] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/nested_ascii_variables.txt: -------------------------------------------------------------------------------- 1 | let digit = [d]; 2 | disable unicode; digit 3 | ----- 4 | \d -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/variables_1.txt: -------------------------------------------------------------------------------- 1 | disable unicode; 2 | let foo = ( 3 | enable unicode; 4 | [d] 5 | ); 6 | 7 | [d] foo 8 | ----- 9 | [0-9]\d -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/ascii/variables_2.txt: -------------------------------------------------------------------------------- 1 | let foo = [d]; 2 | disable unicode; 3 | 4 | [d] foo 5 | ----- 6 | [0-9]\d -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/alt.txt: -------------------------------------------------------------------------------- 1 | 'hello' | 'world' 2 | ----- 3 | hello|world -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/cp.txt: -------------------------------------------------------------------------------- 1 | C Codepoint 2 | ----- 3 | [\s\S][\s\S] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/dot.txt: -------------------------------------------------------------------------------- 1 | . 2 | ----- 3 | . -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/explicit_crlf.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | [r][n] 3 | ----- 4 | \r\n -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/grapheme_var.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | # grapheme cluster 3 | Grapheme 4 | ----- 5 | \X -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/invalid_tokens_unic.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | # this mustn't panic when tokenizing 3 | 4 | C ؾ\ ؾ \u{ؾ} U+ؾ Uؾ' ؾ' 5 | ----- 6 | ERROR: Backslash escapes are not supported 7 | SPAN: 42..44 8 | 9 | ERROR: Backslash escapes are not supported 10 | SPAN: 47..49 11 | 12 | ERROR: Code point contains non-hexadecimal digit 13 | SPAN: 54..58 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/line_comments.txt: -------------------------------------------------------------------------------- 1 | # this is a comment 2 | 'test' # so is this 3 | ----- 4 | test -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/parens.txt: -------------------------------------------------------------------------------- 1 | # unneeded parentheses are eliminated 2 | 3 | 'hello' | ('world') | ((['!?'] '!')) 4 | ----- 5 | hello|world|[!?]! -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/strings.txt: -------------------------------------------------------------------------------- 1 | 'hello' 'world' 2 | ----- 3 | helloworld -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/unic_string.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | # unicode strings 3 | 4 | 'ä Ŧ 🤦🏼‍♂️' 5 | ----- 6 | ä Ŧ \x{1F926}\x{1F3FC}\x{200D}\x{2642}\x{FE0F} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/basics/windows_crlf.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | # this file uses windows line breaks! 3 | 'hello 4 | world 5 | ' 6 | ----- 7 | hello\nworld\n -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/boundary_neg.txt: -------------------------------------------------------------------------------- 1 | !% 2 | ----- 3 | \B -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/caret_dollar.txt: -------------------------------------------------------------------------------- 1 | ^ $ 2 | ----- 3 | ^$ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/js_ascii_word_start_end.txt: -------------------------------------------------------------------------------- 1 | #! flavor=JavaScript 2 | disable unicode; 3 | < 'foo' > 4 | ----- 5 | (?> % 3 | ----- 4 | (?=\b) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/ruby_word_start_in_lookahead.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Ruby 2 | >> < 3 | ----- 4 | (?=(?` word boundaries are not allowed within lookbehind 5 | SPAN: 3..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/unsupported_word_start_end.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | < 'foo' > 3 | ----- 4 | ERROR: In the `JavaScript` flavor, word boundaries may only be used when Unicode is disabled 5 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/word_boundary.txt: -------------------------------------------------------------------------------- 1 | % 2 | ----- 3 | \b -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/word_start_end_pcre.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | < 'foo' > 3 | ----- 4 | [[:<:]]foo[[:>:]] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/boundaries/word_start_end_rust.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Rust 2 | < 'foo' > 3 | ----- 4 | \ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/ascii_shorthands.txt: -------------------------------------------------------------------------------- 1 | ' 1 ' [ascii] [ascii_digit] [ascii_word] [ascii_space] 2 | ' 2 ' [ascii_alpha] [ascii_alnum] [ascii_blank] [ascii_cntrl] 3 | ' 3 ' [ascii_graph] [ascii_lower] [ascii_print] [ascii_punct] 4 | ' 4 ' [ascii_upper] [ascii_xdigit] 5 | ----- 6 | 1 [\x00-\x7F][0-9][0-9A-Z_a-z][\t-\r ] 2 [A-Za-z][0-9A-Za-z][\t ][\x00-\x1F\x7F] 3 [!-~][a-z][ -~][!-/:-@\[-`{-~] 4 [A-Z][0-9A-Fa-f] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/char_set.txt: -------------------------------------------------------------------------------- 1 | ['a'-'f' 'hijkl' !w] 2 | ----- 3 | [\Wa-fh-l] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/char_set_codepoints.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | [U+0-U+16 U+30 U+10FFFF] 3 | ----- 4 | [\x00-\x160\x{10FFFF}] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/char_set_codepoints_rust.txt: -------------------------------------------------------------------------------- 1 | #! flavor=rust 2 | [U+0-U+16 U+30 U+10FFFF] 3 | ----- 4 | [\x00-\x160\u{10FFFF}] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/escapes.txt: -------------------------------------------------------------------------------- 1 | ["dd&& " '||' ':::']['+?*^'] 2 | ----- 3 | [ \&:d\|][*+?^] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/escapes_js.txt: -------------------------------------------------------------------------------- 1 | #! flavor=JavaScript 2 | ["dd&& " '||' ':::']['+?*^'] 3 | ----- 4 | [ &:d|][*+?^] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/escapes_pcre.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | ["dd&& " '||' ':::']['+?*^'] 3 | ----- 4 | [ \&:d\|][*+?^] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/escapes_single.txt: -------------------------------------------------------------------------------- 1 | ['\']['+']['-']['*']['?']['['][']']['{']['}']['a'][' ']['^']['$']['|']['('][')']['.'] 2 | ----- 3 | \\\+-\*\?\[\]\{\}a \^\$\|\(\)\. -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/neg_word.txt: -------------------------------------------------------------------------------- 1 | ['_' !w] !['_' !w] 2 | ----- 3 | [\W_][^\W_] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/not_nothing_1.txt: -------------------------------------------------------------------------------- 1 | ![d !w] 2 | ----- 3 | [^\d\W] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/not_nothing_2.txt: -------------------------------------------------------------------------------- 1 | ![s !w] 2 | ----- 3 | [^\s\W] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ![s !s] 3 | ----- 4 | ERROR: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `space` and `!space`, which together match every code point 6 | SPAN: 1..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ![w !w] 3 | ----- 4 | ERROR: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `word` and `!word`, which together match every code point 6 | SPAN: 1..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_3.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ![White_Space !White_Space] 3 | ----- 4 | ERROR: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `White_Space` and `!White_Space`, which together match every code point 6 | SPAN: 1..27 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_4.txt: -------------------------------------------------------------------------------- 1 | #! ignore, expect=error 2 | ![w !d] 3 | ----- 4 | ERROR: Compile error: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `!digit` and `word`, which together match every code point 6 | SPAN: 1..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_5.txt: -------------------------------------------------------------------------------- 1 | #! ignore, expect=error 2 | ![!d w] 3 | ----- 4 | ERROR: Compile error: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `!digit` and `word`, which together match every code point 6 | SPAN: 1..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_6.txt: -------------------------------------------------------------------------------- 1 | #! ignore, expect=error 2 | ![!s !w] 3 | ----- 4 | ERROR: Compile error: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `!space` and `!word`, which together match every code point 6 | SPAN: 1..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_7.txt: -------------------------------------------------------------------------------- 1 | #! ignore, expect=error 2 | ![!w !s] 3 | ----- 4 | ERROR: Compile error: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `!space` and `!word`, which together match every code point 6 | SPAN: 1..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/nothing_8.txt: -------------------------------------------------------------------------------- 1 | #! ignore, expect=error 2 | ![!d !s] 3 | ----- 4 | ERROR: Compile error: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `!space` and `!digit`, which together match every code point 6 | SPAN: 1..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/perl_class_like.txt: -------------------------------------------------------------------------------- 1 | [":::"] 2 | ----- 3 | : -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands.txt: -------------------------------------------------------------------------------- 1 | [w] [d] [s] ![w] ![d] ![s] 2 | ----- 3 | \w\d\s\W\D\S -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands_dep_fixed.txt: -------------------------------------------------------------------------------- 1 | [U+07-U+0C] 2 | ----- 3 | [\x07-\f] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands_deprecated.txt: -------------------------------------------------------------------------------- 1 | [a-f] 2 | ----- 3 | [\x07-\f] 4 | WARNING: Shorthands in character ranges are deprecated. Use U+07 instead 5 | at 1..2 6 | WARNING: Shorthands in character ranges are deprecated. Use U+0C instead 7 | at 3..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands_js_polyfills.txt: -------------------------------------------------------------------------------- 1 | #! flavor=JavaScript 2 | [w] [d] [s] ' - ' ![w] ![d] ![s] ' - ' [!w] [!d] [!s] ' - ' ![w d s] 3 | ----- 4 | [\p{Alphabetic}\p{M}\p{Nd}\p{Pc}]\p{Nd}\s - [^\p{Alphabetic}\p{M}\p{Nd}\p{Pc}]\P{Nd}\S - [^\p{Alphabetic}\p{M}\p{Nd}\p{Pc}]\P{Nd}\S - [^\p{Alphabetic}\p{M}\p{Nd}\p{Pc}\s] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands_neg_union.txt: -------------------------------------------------------------------------------- 1 | [!w !d !s] 2 | ----- 3 | [\W\D\S] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands_re2_polyfills.txt: -------------------------------------------------------------------------------- 1 | #! flavor=RE2 2 | [d] [s] ' - ' ![d] ![s] ' - ' [!d] [!s] ' - ' ![d s] 3 | ----- 4 | \p{Nd}[\s\x0B\xA0\x{1680}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}\x{FEFF}] - \P{Nd}[^\s\x0B\xA0\x{1680}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}\x{FEFF}] - \P{Nd}[^\s\x0B\xA0\x{1680}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}\x{FEFF}] - [^\p{Nd}\s\x0B\xA0\x{1680}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}\x{FEFF}] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/shorthands_union_verbose.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | [word digit space horiz_space vert_space] 3 | ----- 4 | [\w\d\s\h\v] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/special_chars_neg.txt: -------------------------------------------------------------------------------- 1 | ![n] ![r] ![t] ![a] ![e] ![f] 2 | ----- 3 | [^\n][^\r][^\t][^\x07][^\x1B][^\f] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/special_chars_union.txt: -------------------------------------------------------------------------------- 1 | [n r t a e f] 2 | ----- 3 | [\x07\t\n\f\r\x1B] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/ws_shorthands.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | [h] [v] ![h] ![v] 3 | ----- 4 | \h\v[^\h][^\v] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/classes/ws_shorthands_rust.txt: -------------------------------------------------------------------------------- 1 | [h] [v] ![h] ![v] 2 | ----- 3 | [\p{Zs}\t][\n-\r\x85\u2028\u2029][^\p{Zs}\t][^\n-\r\x85\u2028\u2029] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/e_missing_plus.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | # code points 3 | 4 | U+7 U+FF U7 UFF 5 | ----- 6 | ERROR: Variable `U7` doesn't exist 7 | HELP: Perhaps you meant a code point: `U+7` 8 | SPAN: 24..26 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/e_surrogate.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | U+D801 3 | ----- 4 | ERROR: This code point is outside the allowed range 5 | SPAN: 0..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/e_too_large.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | U+EEEEEE 3 | ----- 4 | ERROR: This code point is outside the allowed range 5 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/e_too_long.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | U+12345678 3 | ----- 4 | ERROR: This code point is outside the allowed range 5 | SPAN: 0..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/java.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Java 2 | # code points 3 | 4 | U+6 U+60 U+600 U+6000 U+F0000 5 | ----- 6 | \x06`\u0600怀\x{F0000} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/js.txt: -------------------------------------------------------------------------------- 1 | #! flavor=JavaScript 2 | # code points 3 | 4 | U+6 U+60 U+600 U+6000 U+F0000 5 | ----- 6 | \x06`\u0600怀\u{F0000} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/pcre.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | # code points 3 | 4 | U+6 U+60 U+600 U+6000 U+F0000 5 | ----- 6 | \x06`\x{600}怀\x{F0000} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/py.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Python 2 | # code points 3 | 4 | U+6 U+60 U+600 U+6000 U+F0000 5 | ----- 6 | \x06`\u0600怀\U000F0000 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/ruby.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Ruby 2 | # code points 3 | 4 | U+6 U+60 U+98 U+600 U+6000 U+F0000 5 | ----- 6 | \x06`\u0098\u0600怀\x{F0000} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/codepoints/rust.txt: -------------------------------------------------------------------------------- 1 | # code points 2 | 3 | U+6 U+60 U+600 U+6000 U+F0000 4 | ----- 5 | \x06`\u0600怀\u{F0000} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/atomic_capturing.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | atomic :('test' '!'*) 3 | ----- 4 | ERROR: Expected `(` 5 | SPAN: 7..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/caret_in_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [^ '4'-'7'] 3 | ----- 4 | ERROR: `^` is not allowed here 5 | HELP: Use `![...]` to negate a character class 6 | SPAN: 1..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/code_point_invalid.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | U123EFG U+123EFG 3 | ----- 4 | ERROR: Code point contains non-hexadecimal digit 5 | SPAN: 8..16 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/empty_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [] 3 | ----- 4 | ERROR: This character class is empty 5 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/empty_set_negated.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ![s !s] 3 | ----- 4 | ERROR: This negated character class matches nothing 5 | HELP: The group is empty because it contains both `space` and `!space`, which together match every code point 6 | SPAN: 1..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/empty_string_in_set_range.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [''-'z'] 3 | ----- 4 | ERROR: Strings used in ranges can't be empty 5 | SPAN: 1..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/excess_closing_bracket.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['abc']] 3 | ----- 4 | ERROR: There are leftover tokens that couldn't be parsed 5 | SPAN: 7..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/forbidden_neg_in_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [!n] 3 | ----- 4 | ERROR: This code point or range can't be negated 5 | SPAN: 1..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/grapheme_within_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [Grapheme] 3 | ----- 4 | ERROR: Unknown character class `Grapheme` 5 | HELP: Perhaps you meant `Grapheme_Base` 6 | SPAN: 1..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/huge_reference.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ::300 3 | ----- 4 | ERROR: Group references this large aren't supported 5 | SPAN: 0..5 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/keyword_after_let.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let let = let; 3 | ----- 4 | ERROR: Unexpected keyword `let` 5 | HELP: Use a different variable name 6 | SPAN: 4..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/keyword_in_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [greedy 'a'] 3 | ----- 4 | ERROR: Unexpected keyword `greedy` 5 | SPAN: 1..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/missing_closing_paren.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ('hello' 3 | ----- 4 | ERROR: Expected `)` or an expression 5 | SPAN: 8..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/negated_shorthand_ascii.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | disable unicode; 3 | [!w d] 4 | ----- 5 | ERROR: Shorthands can't be negated when Unicode is disabled 6 | HELP: Enable Unicode for this expression 7 | SPAN: 19..20 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/negated_shorthand_twice.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [!!d] 3 | ----- 4 | ERROR: A shorthand character class can't be negated more than once 5 | HELP: The number of exclamation marks is even, so you can remove all of them 6 | SPAN: 1..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/number_range_decreasing.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | range '234'-'35' 3 | ----- 4 | ERROR: The first number in a range must be smaller than the second 5 | HELP: Switch the numbers: '35'-'234' 6 | SPAN: 6..16 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/range_with_multichar_string_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['ab'-'c'] 3 | ----- 4 | ERROR: Strings used in ranges can only contain 1 code point 5 | SPAN: 1..5 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/range_with_multichar_string_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['a'-'bc'] 3 | ----- 4 | ERROR: Strings used in ranges can only contain 1 code point 5 | SPAN: 5..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/range_with_multichar_string_3.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['abc' 'de'-'f'] 3 | ----- 4 | ERROR: Strings used in ranges can only contain 1 code point 5 | SPAN: 7..11 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/range_with_multichar_string_4.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['234'-'35'] 3 | ----- 4 | ERROR: Strings used in ranges can only contain 1 code point 5 | HELP: Try a `range` expression instead: 6 | https://pomsky-lang.org/docs/language-tour/ranges/ 7 | SPAN: 1..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/recursion_limit_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (((((((((((((((((((((((((((((((((((((((((((((((((((((( 3 | (((((((((((((((((((((((((((((((((((((((((((((((((((((( 4 | (((((((((((((((((((((((((((((((((((((((((((((((((((((( 5 | ----- 6 | ERROR: Recursion limit reached 7 | HELP: Try a less nested expression. It helps to refactor it using variables: 8 | https://pomsky-lang.org/docs/language-tour/variables/ 9 | SPAN: 130..131 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/relative_ref_zero_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :(::-0) 3 | ----- 4 | ERROR: Relative references can't be 0 5 | HELP: Perhaps you meant `::-1` to refer to the previous or surrounding capturing group 6 | SPAN: 2..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/relative_ref_zero_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :(::+0) 3 | ----- 4 | ERROR: Relative references can't be 0 5 | HELP: Perhaps you meant `::-1` to refer to the previous or surrounding capturing group 6 | SPAN: 2..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/repetition_descending.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'test'{5, 3} 3 | ----- 4 | ERROR: Lower bound can't be greater than the upper bound 5 | SPAN: 7..11 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/set_range_descending.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['z'-'g'] 3 | ----- 4 | ERROR: Character range must be in increasing order, but it is U+007A - U+0067 5 | HELP: Switch the characters: 'g'-'z' 6 | SPAN: 1..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/star.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | * 3 | ----- 4 | ERROR: There are leftover tokens that couldn't be parsed 5 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unclosed_brace_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'{ 3 | ----- 4 | ERROR: Expected number 5 | SPAN: 8..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unclosed_brace_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'{1 3 | ----- 4 | ERROR: Expected `}` 5 | SPAN: 9..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unclosed_brace_3.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'{1, 3 | ----- 4 | ERROR: Expected `}` 5 | SPAN: 10..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unclosed_bracket.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [ 3 | ----- 4 | ERROR: Expected character class, string, code point, Unicode property or `]` 5 | SPAN: 1..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unclosed_range_in_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ['a'-] 3 | ----- 4 | ERROR: Expected code point or character 5 | SPAN: 5..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unclosed_string.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | "test" 'test test 3 | ----- 4 | ERROR: This string literal doesn't have a closing quote 5 | SPAN: 7..17 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unexpected_braces.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | {2}('hello') 3 | ----- 4 | ERROR: There are leftover tokens that couldn't be parsed 5 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unicode_prop_ascii_mode.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | disable unicode; 3 | [Latin] 4 | ----- 5 | ERROR: Unicode properties can't be used when Unicode is disabled 6 | HELP: Enable Unicode for this expression 7 | SPAN: 18..23 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unknown_token.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ~~~~~~~~ 3 | ----- 4 | ERROR: Unknown token 5 | SPAN: 0..1 6 | 7 | ERROR: Unknown token 8 | SPAN: 1..2 9 | 10 | ERROR: Unknown token 11 | SPAN: 2..3 12 | 13 | ERROR: Unknown token 14 | SPAN: 3..4 15 | 16 | ERROR: Unknown token 17 | SPAN: 4..5 18 | 19 | ERROR: Unknown token 20 | SPAN: 5..6 21 | 22 | ERROR: Unknown token 23 | SPAN: 6..7 24 | 25 | ERROR: Unknown token 26 | SPAN: 7..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/atomic.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | atomic ('test' '!'*) 3 | ----- 4 | ERROR: Unsupported feature `atomic groups` in the `JavaScript` regex flavor 5 | SPAN: 0..20 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/backref.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :('test') ::1 3 | ----- 4 | ERROR: Unsupported feature `backreference` in the `Rust` regex flavor 5 | SPAN: 10..13 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/forward_ref.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ::1 :('test') 3 | ----- 4 | ERROR: Unsupported feature `forward reference` in the `Rust` regex flavor 5 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/grapheme.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | Grapheme 3 | ----- 4 | ERROR: Unsupported feature `grapheme cluster matcher (\X)` in the `JavaScript` regex flavor 5 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/lookahead.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | >> 'test' 3 | ----- 4 | ERROR: Unsupported feature `lookahead/behind` in the `Rust` regex flavor 5 | SPAN: 0..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/neg_word.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | ['_' !w] !['_' !w] 3 | ----- 4 | ERROR: In the `JavaScript` flavor, `word` can only be negated in a character class when Unicode is disabled 5 | SPAN: 6..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/relative_ref.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :('test') ::-1 3 | ----- 4 | ERROR: Unsupported feature `backreference` in the `Rust` regex flavor 5 | SPAN: 10..14 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/unicode_block.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | [InBasic_Latin] 3 | ----- 4 | ERROR: Unsupported feature `Unicode blocks (\p{InBlock})` in the `JavaScript` regex flavor 5 | SPAN: 1..14 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/unicode_prop.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Python 2 | [White_Space] 3 | ----- 4 | ERROR: Unsupported feature `Unicode properties (\p{Property})` in the `Python` regex flavor 5 | SPAN: 1..12 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/unsupported/unicode_prop_specific.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | [Bidi_Mirrored] 3 | ----- 4 | ERROR: This Unicode property is not supported in the `Ruby` regex flavor 5 | SPAN: 1..14 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/word_boundary_js.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | 'hello' % 3 | ----- 4 | ERROR: In the `JavaScript` flavor, word boundaries may only be used when Unicode is disabled 5 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/errors/wrong_type_of_bracket.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ('hello'] 3 | ----- 4 | ERROR: Expected `)` or an expression 5 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/atomic_groups.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | atomic ('test' '!'+)* 3 | ----- 4 | (?>test!+)* -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/capturing_groups.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Python 2 | :() :()? :('t')? :('A' | 'B')+ 3 | ----- 4 | ()()?(t)?([AB])+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/huge_group_name.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Pcre 2 | :TestTestTestTestTestTestTestTest1('test') 3 | ----- 4 | ERROR: Group name is too long. It is 33 code points long, but must be at most 32 code points. 5 | SPAN: 1..34 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/invalid_group_name.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :wütend('furious' | 'angry' | 'mad' | 'raging' | 'livid') 3 | ----- 4 | ERROR: Group name contains illegal code point `ü` (U+00FC). Group names must be ASCII only. 5 | SPAN: 2..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/keyword_after_colon.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :let('furious' | 'angry' | 'mad' | 'raging' | 'livid') 3 | ----- 4 | ERROR: Unexpected keyword `let` 5 | SPAN: 1..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/mixed_backrefs.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | :('1') :a('2') :('3') :b('4') ::1 ::2 ::3 ::4 ::a ::b 3 | ----- 4 | (1)(?P2)(3)(?P4)(?:\1)(?:\2)(?:\3)(?:\4)(?:\2)(?:\4) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/mixed_backrefs_net.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=DotNet 2 | :('1') :a('2') :('3') :b('4') ::1 ::2 ::3 ::4 ::a ::b 3 | ----- 4 | ERROR: In the .NET flavor, numeric references are forbidden when there are both named and unnamed capturing groups. This is because .NET counts named and unnamed capturing groups separately, which is inconsistent with other flavors. 5 | HELP: Use a named reference, or don't mix named and unnamed capturing groups 6 | SPAN: 30..33 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/mixed_forward_refs.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | (::1 ::2 ::3 ::4 ::a ::b | :('1') :a('2') :('3') :b('4'))* 3 | ----- 4 | (?:(?:\1)(?:\2)(?:\3)(?:\4)(?:\2)(?:\4)|(1)(?P2)(3)(?P4))* -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/mixed_forward_refs_net.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=DotNet 2 | (::1 ::2 ::3 ::4 ::a ::b | :('1') :a('2') :('3') :b('4'))* 3 | ----- 4 | ERROR: In the .NET flavor, numeric references are forbidden when there are both named and unnamed capturing groups. This is because .NET counts named and unnamed capturing groups separately, which is inconsistent with other flavors. 5 | HELP: Use a named reference, or don't mix named and unnamed capturing groups 6 | SPAN: 1..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/name_used_twice.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | :test1('hello') 'world' :test1('!') 3 | ----- 4 | ERROR: Group name `test1` used multiple times 5 | HELP: Give this group a different name 6 | SPAN: 24..35 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/named_backrefs_net.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | :('1') :a('2') :('3') :b('4') ::a ::b 3 | ----- 4 | (1)(?2)(3)(?4)(?:\3)(?:\4) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/named_capturing_groups.txt: -------------------------------------------------------------------------------- 1 | :n1()? :n2('t')? :n3('A' | 'B')+ 2 | ----- 3 | (?P)?(?Pt)?(?P[AB])+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/named_capturing_groups_js.txt: -------------------------------------------------------------------------------- 1 | #! flavor=JavaScript 2 | :n1()? :n2('t')? :n3('A' | 'B')+ 3 | ----- 4 | (?)?(?t)?(?[AB])+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/named_forward_refs_net.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | (::a ::b | :('1') :a('2') :('3') :b('4'))* 3 | ----- 4 | (?:(?:\3)(?:\4)|(1)(?2)(3)(?4))* -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/named_refs_net.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | :a('1') :b('2') :c('3') :d('4') ::b ::c 3 | ----- 4 | (?1)(?2)(?3)(?4)(?:\2)(?:\3) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/num_refs_net.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | :('1') :('2') :('3') :('4') ::2 ::3 3 | ----- 4 | (1)(2)(3)(4)(?:\2)(?:\3) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/num_to_named_refs_net.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | :a('1') :b('2') :c('3') :d('4') ::2 ::3 3 | ----- 4 | (?1)(?2)(?3)(?4)(?:\2)(?:\3) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/opt_in_lookaround.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Java 2 | (>> 'a' ('b' | 'c')) (>> 'a' (('b'))) 3 | ----- 4 | (?=a[bc])(?=ab) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/repeated_groups.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Python 2 | (C)? ('t')? ('test')? ()? ((()))? ((('t')))? 3 | ----- 4 | [\s\S]?t?(?:test)?t? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/ruby_group_mix_error.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | :test1() ::1 ::test1 :() :test3(:test4() :( ::3 )) ::2 ::3 ::test3 ::4 ::test4 3 | ----- 4 | ERROR: Unsupported feature `references to both named and numbered groups` in the `Ruby` regex flavor 5 | SPAN: 51..54 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/groups/ruby_stress.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Ruby 2 | :test1() ::1 ::test1 :() :test3(:test4() :( ::3 )) ::3 ::test3 ::4 ::test4 3 | ----- 4 | (?)\k\k()(?(?)(\k))\k\k\k\k -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/e_distinct_strings.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'a' & 'b' 3 | ----- 4 | ERROR: Intersection of expressions that do not overlap 5 | SPAN: 0..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/e_multiple.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [w] & [d] [s] 3 | ----- 4 | ERROR: Intersecting these expressions is not supported. Only character sets can be intersected. 5 | HELP: One character sets can be intersected. 6 | Parentheses may be required to clarify the parsing order. 7 | SPAN: 0..13 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/e_string_empty.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'a' & '' 3 | ----- 4 | ERROR: Intersecting these expressions is not supported. Only character sets can be intersected. 5 | HELP: One character sets can be intersected. 6 | Parentheses may be required to clarify the parsing order. 7 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/e_string_too_long.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [w] & 'ab' 3 | ----- 4 | ERROR: Intersecting these expressions is not supported. Only character sets can be intersected. 5 | HELP: One character sets can be intersected. 6 | Parentheses may be required to clarify the parsing order. 7 | SPAN: 0..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/e_with_assertions.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'a' & 'b' $ 3 | ----- 4 | ERROR: Intersecting these expressions is not supported. Only character sets can be intersected. 5 | HELP: One character sets can be intersected. 6 | Parentheses may be required to clarify the parsing order. 7 | SPAN: 0..11 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/optimized.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | ![w] & ![d] 3 | ----- 4 | [^\w\d] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/ranges_java.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Java 2 | ['1'-'7'] & ['5'-'9'] 3 | ----- 4 | [1-7&&5-9] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/ranges_rs.txt: -------------------------------------------------------------------------------- 1 | ['1'-'7'] & ['5'-'9'] 2 | ----- 3 | [1-7&&5-9] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/simple_py_error.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Python 2 | [w] & [d] 3 | ----- 4 | ERROR: Unsupported feature `Character set intersections` in the `Python` regex flavor 5 | SPAN: 0..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/simple_rs.txt: -------------------------------------------------------------------------------- 1 | [w] & [d] 2 | ----- 3 | [\w&&\d] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/with_groups.txt: -------------------------------------------------------------------------------- 1 | ([w]) & ([d]) 2 | ----- 3 | [\w&&\d] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/intersections/with_variables.txt: -------------------------------------------------------------------------------- 1 | let d = [d '_']; 2 | [w] & d 3 | ----- 4 | [\w&&\d_] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/lookaround/ruby_lookahead_after_lookbehind.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Ruby 2 | (<< 'foo') (>> 'bar') 3 | ----- 4 | (?<=foo)(?=bar) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/lookaround/ruby_lookahead_in_lookbehind_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | << 'foo' >> 'bar' 3 | ----- 4 | ERROR: In the Ruby flavor, lookahead is not allowed within lookbehind 5 | SPAN: 9..17 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/lookaround/ruby_lookahead_in_lookbehind_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | let la = >> 'bar'; 3 | << 'foo' la 4 | ----- 5 | ERROR: In the Ruby flavor, lookahead is not allowed within lookbehind 6 | SPAN: 9..17 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/char_neg.txt: -------------------------------------------------------------------------------- 1 | !'a' 2 | ----- 3 | [^a] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/double_neg_expected_expression.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | !! 3 | ----- 4 | ERROR: Expected expression 5 | SPAN: 2..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/double_neg_lookbehind.txt: -------------------------------------------------------------------------------- 1 | #! flavor=PCRE 2 | !!<< 'test' 3 | ----- 4 | (?<=test) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/double_neg_set.txt: -------------------------------------------------------------------------------- 1 | !!['test'] 2 | ----- 3 | [est] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/double_neg_word_boundary.txt: -------------------------------------------------------------------------------- 1 | !!% 2 | ----- 3 | \b -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/grapheme_var_neg_error.txt: -------------------------------------------------------------------------------- 1 | #! flavor=PCRE, expect=error 2 | !Grapheme 3 | ----- 4 | ERROR: A grapheme can't be negated 5 | HELP: Only the following expressions can be negated: 6 | - character sets 7 | - string literals and alternations that match exactly one code point 8 | - lookarounds 9 | - the `%` word boundary 10 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/large_codepoint.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | !U+10330 3 | ----- 4 | [^\x{10330}] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/large_codepoint_in_class.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | ![U+10330] 3 | ----- 4 | [^\x{10330}] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/large_codepoint_in_class_net.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=DotNet 2 | ![U+10330] 3 | ----- 4 | ERROR: Code point '𐌰' (U+10330) can't be negated in the .NET flavor, because it is above U+FFFF, and is therefore incorrectly treated as two code points by .NET. 5 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/large_codepoint_net.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=DotNet 2 | !U+10330 3 | ----- 4 | ERROR: Code point '𐌰' (U+10330) can't be negated in the .NET flavor, because it is above U+FFFF, and is therefore incorrectly treated as two code points by .NET. 5 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/lookahead_neg.txt: -------------------------------------------------------------------------------- 1 | #! flavor=python 2 | !>> 'test' 3 | ----- 4 | (?!test) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/negation/lookbehind_neg.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | !<< 'test' 3 | ----- 4 | (?\)|[0-9]+)(?:[*+\-/]\g<0>)* 26 | MATCH: "3" as { } 27 | MATCH: "-2+4" as { } 28 | MATCH: "(2+4)" as { } 29 | MATCH: "1+(2+4)+5" as { } 30 | MATCH: "(3+(-4)+-(5/4))*2" as { } 31 | REJECT: "" 32 | REJECT: "()" 33 | REJECT: "+4" 34 | REJECT: "1-" 35 | REJECT: "(2+3" 36 | REJECT: "2+3)" 37 | REJECT: "e" 38 | REJECT: "3 + 4" -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/recursion/never_terminates.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Pcre 2 | ("foo" recursion)+ 3 | ----- 4 | ERROR: This recursion never terminates 5 | HELP: A recursive expression must have a branch that doesn't reach the `recursion`, or can repeat 0 times 6 | SPAN: 7..16 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/recursion/unsupported.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | recursion 3 | ----- 4 | ERROR: Unsupported feature `recursion` in the `Rust` regex flavor 5 | SPAN: 0..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/references/one.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | :('test') ::1 3 | ----- 4 | (test)(?:\1) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/references/one_named.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | :foo('foo') ::1 ::foo 3 | ----- 4 | (?Pfoo)(?:\1)(?:\1) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/references/zero_invalid.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ::0 3 | ----- 4 | ERROR: Reference to unknown group. There is no group number 0 5 | HELP: Capturing group numbers start with 1 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_0.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \0 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\0` with `U+00` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \1 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: If this is a backreference, replace it with `::1`. 6 | If this is an octal escape, replace it with `U+01`. 7 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_8.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \8 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\8` with `::8` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_A.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \A 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\A` with `Start` to match the start of the string 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_B_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \B 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\B` with `!%` to match a place without a word boundary 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_G_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \G 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Match attempt anchors are not supported 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_N_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \N 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\N` with `![n]` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_R_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \R 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\R` with `([r] [n] | [v])` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_W_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \W 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\W` with `[!w]` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_X_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \X 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\X` with `Grapheme` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_Z_upper.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \Z 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: \Z is not supported. Use `End` to match the end of the string. 6 | Note, however, that `End` doesn't match the position before the final newline. 7 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_b.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \b 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\b` with `%` to match a word boundary 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_g_0_recursion.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \g<0> 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Recursion is currently not supported 6 | SPAN: 0..5 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_g_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \g1 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\g1` with `::1` 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_g_1_relative.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \g-1 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\g-1` with `::-1` 6 | SPAN: 0..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_g_braces_relative.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \g{-1} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\g{-1}` with `::-1` 6 | SPAN: 0..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_g_quotes.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \g'test' 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\g'test'` with `::test` 6 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_k_angle.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \k 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\k` with `::test` 6 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_k_braces.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \k{test} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\k{test}` with `::test` 6 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_n.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \n 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\n` with `[n]` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_p_category.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \p{Category} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\p{Category}` with `[Category]` 6 | SPAN: 0..12 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_p_category_double_neg.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \P{^Category} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\P{^Category}` with `[Category]` 6 | SPAN: 0..13 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_p_category_neg.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \P{Category} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\P{Category}` with `[!Category]` 6 | SPAN: 0..12 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_p_category_neg2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \p{^Category} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\p{^Category}` with `[!Category]` 6 | SPAN: 0..13 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_p_category_single.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \pL 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\pL` with `[L]` 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_p_category_single_neg.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \PL 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\PL` with `[!L]` 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_s.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [\s] 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\s` with `[s]` 6 | SPAN: 1..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_w.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \w 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\w` with `[w]` 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/b_z.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \z 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Replace `\z` with `End` to match the end of the string 6 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/caret_in_group.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [^test] 3 | ----- 4 | ERROR: `^` is not allowed here 5 | HELP: Use `![...]` to negate a character class 6 | SPAN: 1..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/comment.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?# test!!) 3 | ----- 4 | ERROR: Comments have a different syntax 5 | HELP: Comments start with `#` and go until the end of the line. 6 | SPAN: 0..11 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/conditional.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?(test)) 3 | ----- 4 | ERROR: Conditionals are not supported 5 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/conditional2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?|(test)|(!)) 3 | ----- 4 | ERROR: Branch reset groups are not supported 5 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/dot.txt: -------------------------------------------------------------------------------- 1 | . 2 | ----- 3 | . -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/lokkbehind.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?<=test) 3 | ----- 4 | ERROR: This syntax is not supported 5 | HELP: Lookbehind uses the `<<` syntax. For example, `<< 'bob'` matches if the position is preceded with bob. 6 | SPAN: 0..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/lokkbehind_neg.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?>` syntax. For example, `>> 'bob'` matches if the position is followed by bob. 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/lookahead_neg.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?!=test) 3 | ----- 4 | ERROR: This syntax is not supported 5 | HELP: Negative lookahead uses the `!>>` syntax. For example, `!>> 'bob'` matches if the position is not followed by bob. 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/non_capturing.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | (?:test) 3 | ----- 4 | ERROR: This syntax is not supported 5 | HELP: Non-capturing groups are just parentheses: `(...)`. Capturing groups use the `:(...)` syntax. 6 | SPAN: 0..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/quantifier_lazy.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ('test')?? 3 | ----- 4 | ERROR: Unexpected `?` following a repetition 5 | HELP: If you meant to make the repetition lazy, append the `lazy` keyword instead. 6 | If this is intentional, consider adding parentheses around the inner repetition. 7 | SPAN: 9..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/unicode_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \x17 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Try `U+17` instead 6 | SPAN: 0..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/unicode_4.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \u3033 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Try `U+3033` instead 6 | SPAN: 0..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex-diagnostics/unicode_braces.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | \u{3033} 3 | ----- 4 | ERROR: Backslash escapes are not supported 5 | HELP: Try `U+3033` instead 6 | SPAN: 0..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/error_in_set.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | [regex "]a-n^-"] 3 | ----- 4 | ERROR: Unexpected keyword `regex` 5 | SPAN: 1..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/error_negated.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | !regex "^$_//\\\\{2}\"[^$]" 3 | ----- 4 | ERROR: An inline regex can't be negated 5 | HELP: Only the following expressions can be negated: 6 | - character sets 7 | - string literals and alternations that match exactly one code point 8 | - lookarounds 9 | - the `%` word boundary 10 | SPAN: 0..1 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/in_alt.txt: -------------------------------------------------------------------------------- 1 | regex 'a|b' 'c' | 'd' 2 | ----- 3 | a|bc|d -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/in_alt_2.txt: -------------------------------------------------------------------------------- 1 | 'a' regex 'b?' 'c' | 'd' 2 | ----- 3 | ab?c|d -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/in_group.txt: -------------------------------------------------------------------------------- 1 | :(regex "^$_//\\\\{2}\"[^$]") 2 | ----- 3 | (^$_//\\{2}"[^$]) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/in_group_2.txt: -------------------------------------------------------------------------------- 1 | (regex "^$_//\\\\{2}\"[^$]" 'test') | 'test' 2 | ----- 3 | ^$_//\\{2}"[^$]test|test -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/in_group_3.txt: -------------------------------------------------------------------------------- 1 | (regex 'a|b|c') 'd' 2 | ----- 3 | (?:a|b|c)d -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/in_repetition.txt: -------------------------------------------------------------------------------- 1 | regex 'aaa'? 2 | ----- 3 | (?:aaa)? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/invalid.txt: -------------------------------------------------------------------------------- 1 | #! compile=false 2 | regex '[' 3 | ----- 4 | [ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/regex/literal.txt: -------------------------------------------------------------------------------- 1 | regex "^$_//\\\\{2}\"[^$]" 2 | ----- 3 | ^$_//\\{2}"[^$] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/boundaries.txt: -------------------------------------------------------------------------------- 1 | ^? %+ (%)+ 2 | ----- 3 | (?:^)?(?:\b)+(?:\b)+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/boundaries_1_ruby.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | ^? 3 | ----- 4 | ERROR: Unsupported feature `single repeated assertion` in the `Ruby` regex flavor 5 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/boundaries_2_ruby.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | %+ 3 | ----- 4 | ERROR: Unsupported feature `single repeated assertion` in the `Ruby` regex flavor 5 | SPAN: 0..2 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/boundaries_3_ruby.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | (%)+ 3 | ----- 4 | ERROR: Unsupported feature `single repeated assertion` in the `Ruby` regex flavor 5 | SPAN: 0..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/boundaries_4_ruby.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | ('' %)+ 3 | ----- 4 | ERROR: Unsupported feature `single repeated assertion` in the `Ruby` regex flavor 5 | SPAN: 0..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/braces_1.txt: -------------------------------------------------------------------------------- 1 | # Repetitions have the same syntax as in regexes, except that they are lazy. 2 | 3 | 'a'{1} 'b'{2} 'c'{3,} 'd'{2,4} 'e'{,4} 4 | ----- 5 | ab{2}c{3,}d{2,4}e{0,4} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/braces_2.txt: -------------------------------------------------------------------------------- 1 | 'test'{1,} 'test'{1,} lazy 2 | ----- 3 | (?:test)+(?:test)+? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/braces_3.txt: -------------------------------------------------------------------------------- 1 | 'test'{0,1} 'test'{0,1} lazy 2 | ----- 3 | (?:test)?(?:test)?? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/braces_4.txt: -------------------------------------------------------------------------------- 1 | 'test'{7,7} 'test'{7,7} lazy 2 | ----- 3 | (?:test){7}(?:test){7} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/braces_chained.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'test'{3,4} lazy {7} 3 | ----- 4 | ERROR: Only one repetition allowed 5 | HELP: Add parentheses around the first repetition. 6 | SPAN: 17..20 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/different_exprs.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | 'a'+ [w]+ U+10+ ([w])+ ([w] | '')+ [w d]+ (>> 'test')+ range '0'-'1'+ 3 | ----- 4 | a+\w+\x10+\w+(?:\w|)+[\w\d]+(?=test)+[01]+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/double_plus_error.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'++ 3 | ----- 4 | ERROR: Only one repetition allowed 5 | HELP: Add parentheses around the first repetition. 6 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/double_qm_error.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'?? 3 | ----- 4 | ERROR: Unexpected `?` following a repetition 5 | HELP: If you meant to make the repetition lazy, append the `lazy` keyword instead. 6 | If this is intentional, consider adding parentheses around the inner repetition. 7 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/empty_parens_qm_removed.txt: -------------------------------------------------------------------------------- 1 | # repetitions for groups with empty string literals should be ignored 2 | 3 | :('test') ()? 4 | ----- 5 | (test) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/greedy_and_lazy.txt: -------------------------------------------------------------------------------- 1 | # non-capturing groups are added when necessary 2 | 3 | 'hello'+ greedy | 'world'* lazy 4 | ----- 5 | (?:hello)+|(?:world)*? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/greedy_and_lazy_variables.txt: -------------------------------------------------------------------------------- 1 | let foo = 'foo'*; 2 | let bar = ( 3 | enable lazy; 4 | 'bar'* 5 | ); 6 | 7 | ( 8 | enable lazy; 9 | foo bar ' ' 10 | ( 11 | disable lazy; 12 | foo bar ' ' 13 | ) 14 | ) 15 | foo bar 16 | ----- 17 | (?:foo)*(?:bar)*? (?:foo)*(?:bar)*? (?:foo)*(?:bar)*? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/huge.txt: -------------------------------------------------------------------------------- 1 | #! flavor=Pcre 2 | U+2{,65535} 3 | ----- 4 | \x02{0,65535} -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/leading_zero.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Pcre 2 | U+2{01} 3 | ----- 4 | ERROR: Numbers can't have leading zeroes 5 | SPAN: 4..6 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/leading_zeroes.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Pcre 2 | U+2{0001} 3 | ----- 4 | ERROR: Numbers can't have leading zeroes 5 | SPAN: 4..8 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/lookaround_dotnet.txt: -------------------------------------------------------------------------------- 1 | #! flavor=DotNet 2 | (>> 'test')* (!<< 'test'){3} 3 | ----- 4 | (?=test)*(?> 'test')* (!<< 'test'){3} 3 | ----- 4 | (?=test)*(?> 'test')* (!<< 'test'){3} 3 | ----- 4 | (?=test)*(?> 'test')* (!<< 'test'){3} 3 | ----- 4 | (?=test)*(?> 'test')* (!<< 'test'){3} 3 | ----- 4 | ERROR: Unsupported feature `single repeated assertion` in the `Ruby` regex flavor 5 | SPAN: 0..12 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/lookaround_ruby_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Ruby 2 | (((>> 'test')))* (((!<< 'test'))){3} 3 | ----- 4 | ERROR: Unsupported feature `single repeated assertion` in the `Ruby` regex flavor 5 | SPAN: 0..16 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/plus_after_lazy.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'+lazy + 3 | ----- 4 | ERROR: Only one repetition allowed 5 | HELP: Add parentheses around the first repetition. 6 | SPAN: 13..14 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/plus_and_star.txt: -------------------------------------------------------------------------------- 1 | # non-capturing groups are added when necessary 2 | 3 | 'hello'+ | 'world'* 4 | ----- 5 | (?:hello)+|(?:world)* -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/qm.txt: -------------------------------------------------------------------------------- 1 | # non-capturing groups are added when necessary 2 | 3 | 'hello'? | 'world'? lazy 4 | ----- 5 | (?:hello)?|(?:world)?? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/qm_chained_with_parens.txt: -------------------------------------------------------------------------------- 1 | ('hello'?)? 2 | ----- 3 | (?:hello)? -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/star_and_braces_chained.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | 'hello'*{0,1} 3 | ----- 4 | ERROR: Only one repetition allowed 5 | HELP: Add parentheses around the first repetition. 6 | SPAN: 8..13 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/star_and_plus.txt: -------------------------------------------------------------------------------- 1 | 'hello'* 'world'+ 2 | ----- 3 | (?:hello)*(?:world)+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/too_huge.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=Pcre 2 | U+2{,65536} 3 | ----- 4 | ERROR: number too large 5 | SPAN: 5..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/repetitions/too_huge_re2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=RE2 2 | U+2{,1010} 3 | ----- 4 | ERROR: Unsupported feature `Repetition above 1000` in the `RE2` regex flavor 5 | SPAN: 0..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/strings/escapes.txt: -------------------------------------------------------------------------------- 1 | 'test\' "test\\ \" ." 2 | ----- 3 | test\\test\\ " \. -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/strings/escapes_in_set_string_1.txt: -------------------------------------------------------------------------------- 1 | ["a\"\\"] 2 | ----- 3 | ["\\a] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/strings/escapes_in_set_string_2.txt: -------------------------------------------------------------------------------- 1 | ['a"\'] 2 | ----- 3 | ["\\a] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/strings/invalid_escape.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | "a\ " 3 | ----- 4 | ERROR: Unsupported escape sequence in string 5 | SPAN: 2..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/strings/special_chars_in_strings.txt: -------------------------------------------------------------------------------- 1 | '{}' '[]' '()' '*+?' '.^$' '\' ['^-^'] ['0-^'] 2 | ----- 3 | \{\}\[\]\(\)\*\+\?\.\^\$\\[\-^][\-0^] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/strings/unclosed_string_bc_escaped_quote.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | "a\" 3 | ----- 4 | ERROR: This string literal doesn't have a closing quote 5 | SPAN: 0..4 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/captures.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match 'hello world' as { 1: 'hello', foo: 'world' }; 3 | } 4 | ----- 5 | 6 | MATCH: "hello world" as { 1: "hello", foo: "world", } -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/empty.txt: -------------------------------------------------------------------------------- 1 | test {} 2 | ----- -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/empty_captures.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match 'hello world' as {}; 3 | } 4 | ----- 5 | 6 | MATCH: "hello world" as { } -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/errors/missing_comma_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | test { 3 | match 'hello world' as { foo: '' bar: '' }; 4 | } 5 | ----- 6 | ERROR: Expected `}` 7 | SPAN: 42..45 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/errors/missing_comma_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | test { 3 | match 'hello' 'world' 4 | in 'hello world'; 5 | } 6 | ----- 7 | ERROR: Expected `;` 8 | SPAN: 23..30 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/errors/multiple_strings.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | test { 3 | match 'hello', 'world'; 4 | } 5 | ----- 6 | ERROR: Test cases can't have multiple strings 7 | HELP: Use `in "some string"` to match substrings in a haystack 8 | SPAN: 15..31 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/errors/nested_test_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | ( 3 | test {} 4 | "hello world" 5 | ) 6 | ----- 7 | ERROR: Unit tests may only appear at the top level of the expression 8 | SPAN: 4..11 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/errors/nested_test_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error, flavor=JavaScript 2 | >> test {} 3 | "hello world" 4 | ----- 5 | ERROR: Unit tests may only appear at the top level of the expression 6 | SPAN: 3..10 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/errors/reject_captures.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | test { 3 | reject 'hello' in 'hello world'; 4 | } 5 | ----- 6 | ERROR: Expected `;` 7 | SPAN: 24..26 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/match.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match 'hello world'; 3 | } 4 | 5 | 'hello world' 6 | ----- 7 | hello world 8 | MATCH: "hello world" as { } -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/no_substrings.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match in 'hello world'; 3 | } 4 | ----- 5 | 6 | MATCH_ALL: in "hello world" -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/quotes.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match "hello\"world", 3 | 'foo' as { 0: "bar" } 4 | in "hello\"world foo bar"; 5 | } 6 | ----- 7 | 8 | MATCH_ALL: "hello\"world" as { }, "foo" as { 0: "bar", } in "hello\"world foo bar" -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/reject.txt: -------------------------------------------------------------------------------- 1 | test { 2 | reject 'hello world'; 3 | } 4 | 5 | 'hello world' 6 | ----- 7 | hello world 8 | REJECT: "hello world" -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/reject_substring.txt: -------------------------------------------------------------------------------- 1 | test { 2 | reject in 'hello world'; 3 | } 4 | 5 | 'hello world' 6 | ----- 7 | hello world 8 | REJECT: in "hello world" -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/substrings.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match 'a' as {}, 3 | 'b', 4 | 'c' as { 0: 'c' } 5 | in 'abc'; 6 | } 7 | ----- 8 | 9 | MATCH_ALL: "a" as { }, "b" as { }, "c" as { 0: "c", } in "abc" -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/tests/trailing_comma.txt: -------------------------------------------------------------------------------- 1 | test { 2 | match 'hello world' as { 3 | 1: 'hello', 4 | foo: 'world', 5 | }; 6 | } 7 | ----- 8 | 9 | MATCH: "hello world" as { 1: "hello", foo: "world", } -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/builtins.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | # built-in variables 3 | 4 | Start End Codepoint C Grapheme G 5 | ----- 6 | ^$[\s\S][\s\S]\X\X -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/capturing.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let foo = :foo('foo'); 3 | foo 4 | ----- 5 | ERROR: Capturing groups within `let` statements are currently not supported 6 | SPAN: 10..21 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/capturing_unused.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let foo = :foo('foo'); 3 | 'bar' 4 | ----- 5 | ERROR: Capturing groups within `let` statements are currently not supported 6 | SPAN: 10..21 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/directly_recursive.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let x = x; 3 | x 4 | ----- 5 | ERROR: Variables can't be used recursively 6 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/empty_var.txt: -------------------------------------------------------------------------------- 1 | let _ = ; 2 | _ 3 | ----- -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/let_stmt.txt: -------------------------------------------------------------------------------- 1 | let x = 'a' | 'b'; 2 | 3 | x x x 4 | ----- 5 | [ab][ab][ab] -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/million_hellos.txt: -------------------------------------------------------------------------------- 1 | let a = 'hello'; 2 | let b = a a; 3 | let c = b b; 4 | 5 | c c 6 | ----- 7 | hellohellohellohellohellohellohellohello -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/missing_equals.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let r255 r256 r257 = 'a' 'b' 'c'; 3 | ----- 4 | ERROR: Expected `=` 5 | SPAN: 9..13 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/missing_keyword.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | hello = 'hello'; 3 | hello 4 | ----- 5 | ERROR: A variable declaration must start with the `let` keyword 6 | HELP: Try `let hello = ...` 7 | SPAN: 0..7 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/missing_semicolon.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let r255 = range '0'-'255' 3 | ----- 4 | ERROR: Expected expression or `;` 5 | SPAN: 26..26 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/missing_var_name.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let 3 | ----- 4 | ERROR: Expected identifier 5 | SPAN: 3..3 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/mutually_recursive_1.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let x = y; 3 | let y = x; 4 | y 5 | ----- 6 | ERROR: Variables can't be used recursively 7 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/mutually_recursive_2.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let x = y; 3 | let y = x; 4 | x 5 | ----- 6 | ERROR: Variables can't be used recursively 7 | SPAN: 19..20 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/nested_scopes.txt: -------------------------------------------------------------------------------- 1 | let c = ( 2 | let b = ( 3 | let a = 'hello'; 4 | a a 5 | ); 6 | b b 7 | ); 8 | 9 | c c 10 | ----- 11 | hellohellohellohellohellohellohellohello -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/nested_scopes_shadowing.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | let a = ( 3 | let a = ( 4 | let a = >> 'h'; 5 | let b = << 'h'; 6 | a b 7 | ); 8 | a a 9 | ); 10 | 11 | a a 12 | ----- 13 | (?=h)(?<=h)(?=h)(?<=h)(?=h)(?<=h)(?=h)(?<=h) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/reference_in_let_stmt.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let x = ::1; 3 | ----- 4 | ERROR: References within `let` statements are currently not supported 5 | SPAN: 8..11 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/scoping_of_modes.txt: -------------------------------------------------------------------------------- 1 | let x = (enable lazy; 2 | 'a'+ 3 | ); 4 | 5 | x 'b'+ 6 | ----- 7 | a+?b+ -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/unused_recursive_var.txt: -------------------------------------------------------------------------------- 1 | let x = x; 2 | ----- -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/unused_var.txt: -------------------------------------------------------------------------------- 1 | let _ = .; 2 | ----- -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/var_already_defined_in_scope.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let _ = ( 3 | let a = 'a'; 4 | let a = 'b'; 5 | ); 6 | a 7 | ----- 8 | ERROR: A variable with the same name already exists in this scope 9 | HELP: Use a different name 10 | SPAN: 31..32 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/var_does_not_exist.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let x = y; 3 | x 4 | ----- 5 | ERROR: Variable `y` doesn't exist 6 | SPAN: 8..9 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/var_not_in_scope.txt: -------------------------------------------------------------------------------- 1 | #! expect=error 2 | let _ = ( 3 | let a = 'a'; 4 | ); 5 | a 6 | ----- 7 | ERROR: Variable `a` doesn't exist 8 | SPAN: 28..29 -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/var_used_once.txt: -------------------------------------------------------------------------------- 1 | #! flavor=pcre 2 | let _ = "hello" | "world" << "test"; 3 | _ 4 | ----- 5 | hello|world(?<=test) -------------------------------------------------------------------------------- /pomsky-lib/tests/testcases/variables/var_used_twice.txt: -------------------------------------------------------------------------------- 1 | let r255 = range '0'-'255'; 2 | r255 '+' r255 3 | ----- 4 | (?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-5]?|[6-9])?|[3-9][0-9]?)\+(?:0|1[0-9]{0,2}|2(?:[0-4][0-9]?|5[0-5]?|[6-9])?|[3-9][0-9]?) -------------------------------------------------------------------------------- /pomsky-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pomsky-macro" 3 | description = "Macro for converting pomsky expressions to regexes" 4 | version = "0.11.0" 5 | edition.workspace = true 6 | authors = ["Pomsky developers "] 7 | license = "MIT OR Apache-2.0" 8 | homepage = "https://github.com/pomsky-lang/pomsky/blob/main/pomsky-macro" 9 | repository = "https://github.com/pomsky-lang/pomsky" 10 | documentation = "https://docs.rs/pomsky-macro" 11 | keywords = ["macro", "regexp", "regex", "pomsky"] 12 | categories = ["text-processing"] 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [features] 18 | default = [] 19 | diagnostics = [] 20 | 21 | [dependencies] 22 | pomsky = { version = "0.11.0", path = "../pomsky-lib" } 23 | -------------------------------------------------------------------------------- /pomsky-macro/README.md: -------------------------------------------------------------------------------- 1 | # pomsky-macro 2 | 3 | This Rust procedural macro allows converting a [pomsky expression](../README.md) to a regex 4 | string literal at compile time: 5 | 6 | ```rust 7 | use pomsky_macro::pomsky; 8 | 9 | const REGEX: &str = pomsky!("foo" | "bar"+ greedy); 10 | ``` 11 | 12 | This string can then used with the `regex` crate: 13 | 14 | ```rust 15 | let my_regex = regex::Regex::new(REGEX).unwrap(); 16 | ``` 17 | 18 | ## Diagnostics 19 | 20 | Errors from pomsky are shown at compile time and are highlighted in your IDE. You can improve the 21 | diagnostics by enabling the `diagnostics` feature, which requires Rust Nightly. 22 | 23 | ## Regex flavor 24 | 25 | If you want to use a regex flavor _other than Rust_, you can specify it after a hashtag: 26 | 27 | ```rust 28 | const REGEX: &str = pomsky!( 29 | #flavor = Pcre 30 | >> "test" % 31 | ); 32 | ``` 33 | 34 | ## License 35 | 36 | Dual-licensed under the [MIT license](https://opensource.org/licenses/MIT) or the 37 | [Apache 2.0 license](https://opensource.org/licenses/Apache-2.0). 38 | -------------------------------------------------------------------------------- /pomsky-macro/src/diagnostic.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Write, ops::Range}; 2 | 3 | use pomsky::diagnose::Diagnostic; 4 | use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; 5 | 6 | pub(crate) fn fmt(diagnostic: Diagnostic, _: &Group, input: &str) -> String { 7 | let mut buf = String::new(); 8 | buf.push_str("error: "); 9 | buf.push_str(&diagnostic.msg); 10 | buf.push('\n'); 11 | 12 | if let Some(range) = diagnostic.span.range() { 13 | let slice = &input[range.clone()]; 14 | let Range { start, end } = range; 15 | 16 | let before = input[..start].lines().next_back().unwrap_or_default(); 17 | let after = input[end..].lines().next().unwrap_or_default(); 18 | 19 | let line_number = input[..start].lines().count().max(1); 20 | let line_number_len = (line_number as f32).log10().floor() as usize + 1; 21 | let before_len = before.chars().count(); 22 | let arrow_len = slice.chars().count().max(1); 23 | 24 | write!( 25 | &mut buf, 26 | "\ 27 | {space:line_number_len$} | 28 | {line_number} | {before}{slice}{after} 29 | {space:line_number_len$} | {space:before_len$}{space:^ TokenStream { 46 | let group = vec![respan(Literal::string(s), Span::call_site())].into_iter().collect(); 47 | 48 | vec![ 49 | respan(Ident::new("compile_error", start), start), 50 | respan(Punct::new('!', Spacing::Alone), Span::call_site()), 51 | respan(Group::new(Delimiter::Brace, group), end), 52 | ] 53 | .into_iter() 54 | .collect() 55 | } 56 | 57 | fn respan>(t: T, span: Span) -> TokenTree { 58 | let mut t = t.into(); 59 | t.set_span(span); 60 | t 61 | } 62 | -------------------------------------------------------------------------------- /pomsky-macro/tests/test_macro.rs: -------------------------------------------------------------------------------- 1 | use pomsky_macro::pomsky; 2 | 3 | #[test] 4 | fn rust() { 5 | const REGEX: &str = pomsky! { 6 | // variables 7 | let number = '-'? [d]+; 8 | let op = ["+-*/"]; 9 | number (op number)* 10 | }; 11 | 12 | assert_eq!(REGEX, "-?\\d+(?:[*+\\-/]-?\\d+)*"); 13 | } 14 | 15 | #[test] 16 | fn pcre() { 17 | const REGEX: &str = pomsky!( 18 | #flavor = Pcre 19 | "foo" (!>> "bar") 20 | ); 21 | 22 | assert_eq!(REGEX, "foo(?!bar)"); 23 | } 24 | 25 | #[test] 26 | fn composite_tokens() { 27 | const REGEX: &str = pomsky!( 28 | Start "Test" End 29 | ); 30 | 31 | assert_eq!(REGEX, "^Test$"); 32 | } 33 | -------------------------------------------------------------------------------- /pomsky-syntax/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pomsky-syntax" 3 | description = "Parser for pomsky, a new regular expression language" 4 | version = "0.11.0" 5 | edition.workspace = true 6 | authors = ["Pomsky developers "] 7 | license = "MIT OR Apache-2.0" 8 | homepage = "https://pomsky-lang.org" 9 | repository = "https://github.com/pomsky-lang/pomsky" 10 | documentation = "https://docs.rs/pomsky" 11 | readme = "../README.md" 12 | keywords = ["regexp", "regex", "syntax", "parser", "pomsky"] 13 | categories = ["text-processing", "parser-implementations"] 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [features] 18 | default = [] 19 | dbg = [] 20 | suggestions = ["dep:strsim"] 21 | arbitrary = ["dep:arbitrary"] 22 | 23 | [dependencies] 24 | strsim = { version = "0.10.0", optional = true } 25 | 26 | [dependencies.arbitrary] 27 | version = "1.3.1" 28 | features = ["derive"] 29 | optional = true 30 | -------------------------------------------------------------------------------- /pomsky-syntax/JavaSupportedProps.txt: -------------------------------------------------------------------------------- 1 | Alphabetic 2 | Assigned 3 | Control 4 | Digit 5 | Emoji 6 | Emoji_Component 7 | Emoji_Modifier 8 | Emoji_Modifier_Base 9 | Emoji_Presentation 10 | Extended_Pictographic 11 | Hex_Digit 12 | Ideographic 13 | Join_Control 14 | Letter 15 | Lowercase 16 | Noncharacter_Code_Point 17 | Punctuation 18 | Titlecase 19 | Uppercase 20 | White_Space -------------------------------------------------------------------------------- /pomsky-syntax/SupportedBooleanProps.txt: -------------------------------------------------------------------------------- 1 | # data taken from https://tc39.es/ecma262/multipage/text-processing.html#table-binary-unicode-properties 2 | 3 | bool; ASCII ; ASCII 4 | bool; AHex ; ASCII_Hex_Digit 5 | bool; Alpha ; Alphabetic 6 | bool; Any ; Any 7 | bool; Assigned ; Assigned 8 | bool; Bidi_C ; Bidi_Control 9 | bool; Bidi_M ; Bidi_Mirrored 10 | bool; CI ; Case_Ignorable 11 | bool; Cased ; Cased 12 | bool; CWCF ; Changes_When_Casefolded 13 | bool; CWCM ; Changes_When_Casemapped 14 | bool; CWL ; Changes_When_Lowercased 15 | bool; CWKCF ; Changes_When_NFKC_Casefolded 16 | bool; CWT ; Changes_When_Titlecased 17 | bool; CWU ; Changes_When_Uppercased 18 | bool; Dash ; Dash 19 | bool; DI ; Default_Ignorable_Code_Point 20 | bool; Dep ; Deprecated 21 | bool; Dia ; Diacritic 22 | bool; Emoji ; Emoji 23 | bool; EComp ; Emoji_Component 24 | bool; EMod ; Emoji_Modifier 25 | bool; EBase ; Emoji_Modifier_Base 26 | bool; EPres ; Emoji_Presentation 27 | bool; ExtPict ; Extended_Pictographic 28 | bool; Ext ; Extender 29 | bool; Gr_Base ; Grapheme_Base 30 | bool; Gr_Ext ; Grapheme_Extend 31 | bool; Hex ; Hex_Digit 32 | bool; IDSB ; IDS_Binary_Operator 33 | bool; IDST ; IDS_Trinary_Operator 34 | bool; IDC ; ID_Continue 35 | bool; IDS ; ID_Start 36 | bool; Ideo ; Ideographic 37 | bool; Join_C ; Join_Control 38 | bool; LOE ; Logical_Order_Exception 39 | bool; Lower ; Lowercase 40 | bool; Math ; Math 41 | bool; NChar ; Noncharacter_Code_Point 42 | bool; Pat_Syn ; Pattern_Syntax 43 | bool; Pat_WS ; Pattern_White_Space 44 | bool; QMark ; Quotation_Mark 45 | bool; Radical ; Radical 46 | bool; RI ; Regional_Indicator 47 | bool; STerm ; Sentence_Terminal 48 | bool; SD ; Soft_Dotted 49 | bool; Term ; Terminal_Punctuation 50 | bool; UIdeo ; Unified_Ideograph 51 | bool; Upper ; Uppercase 52 | bool; VS ; Variation_Selector 53 | bool; space ; White_Space 54 | bool; XIDC ; XID_Continue 55 | bool; XIDS ; XID_Start -------------------------------------------------------------------------------- /pomsky-syntax/src/diagnose.rs: -------------------------------------------------------------------------------- 1 | pub use crate::{error::*, warning::*}; 2 | 3 | use crate::Span; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct ParseDiagnostic { 7 | pub kind: ParseDiagnosticKind, 8 | pub span: Span, 9 | } 10 | 11 | #[derive(Debug, Clone)] 12 | pub enum ParseDiagnosticKind { 13 | Error(ParseErrorKind), 14 | Warning(ParseWarningKind), 15 | } 16 | 17 | impl From for ParseDiagnostic { 18 | fn from(value: ParseError) -> Self { 19 | ParseDiagnostic { kind: ParseDiagnosticKind::Error(value.kind), span: value.span } 20 | } 21 | } 22 | 23 | impl From for ParseDiagnostic { 24 | fn from(value: ParseWarning) -> Self { 25 | ParseDiagnostic { kind: ParseDiagnosticKind::Warning(value.kind), span: value.span } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/alternation.rs: -------------------------------------------------------------------------------- 1 | //! Implements [alternation](https://www.regular-expressions.info/alternation.html): 2 | //! `('alt1' | 'alt2' | 'alt3')`. 3 | 4 | use crate::Span; 5 | 6 | use super::Rule; 7 | 8 | /// An [alternation](https://www.regular-expressions.info/alternation.html). 9 | /// This is a list of alternatives. Each alternative is a [`Rule`]. 10 | /// 11 | /// If an alternative consists of multiple expressions (e.g. `'a' | 'b' 'c'`), 12 | /// that alternative is a [`Rule::Group`]. Note that a group's parentheses are 13 | /// removed when compiling to a regex if they aren't required. In other words, 14 | /// `'a' | ('b' 'c')` compiles to `a|bc`. 15 | #[derive(Debug, Clone)] 16 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 17 | pub struct Alternation { 18 | pub rules: Vec, 19 | pub(crate) span: Span, 20 | } 21 | 22 | impl Alternation { 23 | #[cfg(feature = "dbg")] 24 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter, needs_parens: bool) { 25 | if needs_parens { 26 | buf.start_indentation("("); 27 | } 28 | 29 | let len = self.rules.len(); 30 | for (i, rule) in self.rules.iter().enumerate() { 31 | let needs_parens = 32 | matches!(rule, Rule::Alternation(_) | Rule::Lookaround(_) | Rule::StmtExpr(_)); 33 | 34 | buf.push_str("| "); 35 | buf.increase_indentation(2); 36 | rule.pretty_print(buf, needs_parens); 37 | buf.decrease_indentation(2); 38 | if i < len - 1 { 39 | buf.write("\n"); 40 | } 41 | } 42 | 43 | if needs_parens { 44 | buf.end_indentation(")"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/arbitrary.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::{Arbitrary, Unstructured}; 2 | 3 | pub(crate) struct Ident(pub(crate) String); 4 | 5 | impl Ident { 6 | pub(crate) fn create(u: &mut Unstructured<'_>) -> Result { 7 | Ok(Ident::arbitrary(u)?.0) 8 | } 9 | } 10 | 11 | impl Arbitrary<'_> for Ident { 12 | fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result { 13 | let options = [ 14 | "foo", "bar", "baz", "quux", "blabla", "hello", "world", "match", "regular", "based", 15 | "a", "b", "c", "d", "e", "f", "g", 16 | ]; 17 | let idx = u.int_in_range(0..=options.len() as u8 - 1)?; 18 | let name = options[idx as usize]; 19 | Ok(Ident(name.to_string())) 20 | } 21 | 22 | fn size_hint(_depth: usize) -> (usize, Option) { 23 | (1, Some(1)) 24 | } 25 | } 26 | 27 | #[allow(unused)] 28 | pub(crate) struct Digits(pub(crate) Box<[u8]>); 29 | 30 | impl Digits { 31 | pub(crate) fn create( 32 | u: &mut Unstructured<'_>, 33 | radix: u8, 34 | ) -> Result, arbitrary::Error> { 35 | let len = u.arbitrary_len::()?.min(10); 36 | let mut digits = Vec::with_capacity(len); 37 | for _ in 0..len { 38 | digits.push(u.int_in_range(0..=radix - 1)?); 39 | } 40 | Ok(digits.into_boxed_slice()) 41 | } 42 | } 43 | 44 | impl Arbitrary<'_> for Digits { 45 | fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result { 46 | let len = u.arbitrary_len::()?.min(10); 47 | let mut digits = Vec::with_capacity(len); 48 | for _ in 0..len { 49 | digits.push(u.int_in_range(0..=9)?); 50 | } 51 | Ok(Digits(digits.into_boxed_slice())) 52 | } 53 | 54 | fn size_hint(_depth: usize) -> (usize, Option) { 55 | (0, Some(10)) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/boundary.rs: -------------------------------------------------------------------------------- 1 | //! Implements _boundaries_. The analogues in the regex world are 2 | //! [word boundaries](https://www.regular-expressions.info/wordboundaries.html) and 3 | //! [anchors](https://www.regular-expressions.info/anchors.html). 4 | 5 | use crate::Span; 6 | 7 | /// A [word boundary](https://www.regular-expressions.info/wordboundaries.html) or 8 | /// [anchor](https://www.regular-expressions.info/anchors.html), which we combine under the term 9 | /// _boundary_. 10 | /// 11 | /// All boundaries use a variation of the `%` sigil, so they are easy to 12 | /// remember. 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 14 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 15 | pub struct Boundary { 16 | pub kind: BoundaryKind, 17 | pub unicode_aware: bool, 18 | pub span: Span, 19 | } 20 | 21 | impl Boundary { 22 | pub fn new(kind: BoundaryKind, unicode_aware: bool, span: Span) -> Self { 23 | Boundary { kind, unicode_aware, span } 24 | } 25 | 26 | pub fn kind(&self) -> BoundaryKind { 27 | self.kind 28 | } 29 | 30 | #[cfg(feature = "dbg")] 31 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) { 32 | match self.kind { 33 | BoundaryKind::Start => buf.push('^'), 34 | BoundaryKind::End => buf.push('$'), 35 | BoundaryKind::Word => buf.push('%'), 36 | BoundaryKind::NotWord => buf.push_str("!%"), 37 | BoundaryKind::WordStart => buf.push_str("<"), 38 | BoundaryKind::WordEnd => buf.push_str(">"), 39 | } 40 | } 41 | } 42 | 43 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 44 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 45 | pub enum BoundaryKind { 46 | /// `Start`, the start of the string (or start of line in single-line mode) 47 | Start, 48 | /// `End`, the end of the string (or end of line in single-line mode) 49 | End, 50 | /// `%`, a word boundary 51 | Word, 52 | /// `!%`, not a word boundary 53 | NotWord, 54 | /// `<` the beginning of a word 55 | WordStart, 56 | /// `>` the end of a word 57 | WordEnd, 58 | } 59 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/intersection.rs: -------------------------------------------------------------------------------- 1 | //! Implements intersection: `'alt1' & 'alt2' & 'alt3'`. This is not a common feature, 2 | //! and only makes sense in certain scenarios. 3 | 4 | use crate::Span; 5 | 6 | use super::Rule; 7 | 8 | /// An [alternation](https://www.regular-expressions.info/alternation.html). 9 | /// This is a list of alternatives. Each alternative is a [`Rule`]. 10 | /// 11 | /// If an alternative consists of multiple expressions (e.g. `'a' | 'b' 'c'`), 12 | /// that alternative is a [`Rule::Group`]. Note that a group's parentheses are 13 | /// removed when compiling to a regex if they aren't required. In other words, 14 | /// `'a' | ('b' 'c')` compiles to `a|bc`. 15 | #[derive(Debug, Clone)] 16 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 17 | pub struct Intersection { 18 | pub rules: Vec, 19 | pub span: Span, 20 | } 21 | 22 | impl Intersection { 23 | #[cfg(feature = "dbg")] 24 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter, needs_parens: bool) { 25 | if needs_parens { 26 | buf.start_indentation("("); 27 | } 28 | 29 | let len = self.rules.len(); 30 | for (i, rule) in self.rules.iter().enumerate() { 31 | let needs_parens = matches!( 32 | rule, 33 | Rule::Intersection(_) 34 | | Rule::Alternation(_) 35 | | Rule::Lookaround(_) 36 | | Rule::StmtExpr(_) 37 | ); 38 | 39 | buf.push_str("& "); 40 | buf.increase_indentation(2); 41 | rule.pretty_print(buf, needs_parens); 42 | buf.decrease_indentation(2); 43 | if i < len - 1 { 44 | buf.write("\n"); 45 | } 46 | } 47 | 48 | if needs_parens { 49 | buf.end_indentation(")"); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/literal.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | #[derive(Clone, PartialEq, Eq)] 4 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 5 | pub struct Literal { 6 | pub content: String, 7 | pub span: Span, 8 | } 9 | 10 | impl Literal { 11 | pub fn new(content: String, span: Span) -> Self { 12 | Literal { content, span } 13 | } 14 | 15 | #[cfg(feature = "dbg")] 16 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) { 17 | buf.write_debug(&self.content); 18 | } 19 | } 20 | 21 | impl std::fmt::Debug for Literal { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | write!(f, "{:?} at {}", self.content, self.span) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/lookaround.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | use super::Rule; 4 | 5 | #[derive(Debug, Clone)] 6 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 7 | pub struct Lookaround { 8 | pub kind: LookaroundKind, 9 | pub rule: Rule, 10 | pub span: Span, 11 | } 12 | 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 14 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 15 | pub enum LookaroundKind { 16 | Ahead, 17 | Behind, 18 | AheadNegative, 19 | BehindNegative, 20 | } 21 | 22 | impl Lookaround { 23 | pub(crate) fn new(rule: Rule, kind: LookaroundKind, span: Span) -> Self { 24 | Lookaround { kind, rule, span } 25 | } 26 | 27 | #[cfg(feature = "dbg")] 28 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter, needs_parens: bool) { 29 | let s = match self.kind { 30 | LookaroundKind::Ahead => ">>", 31 | LookaroundKind::Behind => "<<", 32 | LookaroundKind::AheadNegative => "!>>", 33 | LookaroundKind::BehindNegative => "!<<", 34 | }; 35 | if needs_parens { 36 | buf.push('('); 37 | buf.start_indentation(s); 38 | } else { 39 | buf.push_str(s); 40 | buf.push(' '); 41 | } 42 | 43 | self.rule.pretty_print(buf, false); 44 | 45 | if needs_parens { 46 | buf.end_indentation(")"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module containing the AST (abstract syntax tree) types for expressions 2 | 3 | pub(crate) mod alternation; 4 | pub(crate) mod boundary; 5 | pub(crate) mod char_class; 6 | pub(crate) mod group; 7 | pub(crate) mod intersection; 8 | pub(crate) mod literal; 9 | pub(crate) mod lookaround; 10 | pub(crate) mod negation; 11 | pub(crate) mod range; 12 | pub(crate) mod recursion; 13 | pub(crate) mod reference; 14 | pub(crate) mod regex; 15 | pub(crate) mod repetition; 16 | pub(crate) mod rule; 17 | pub(crate) mod stmt; 18 | pub mod test; 19 | pub(crate) mod var; 20 | 21 | #[cfg(feature = "arbitrary")] 22 | pub(crate) mod arbitrary; 23 | 24 | pub use self::{ 25 | alternation::Alternation, 26 | boundary::{Boundary, BoundaryKind}, 27 | char_class::{ 28 | Category, CharClass, CharGroup, CodeBlock, GroupItem, GroupName, OtherProperties, Script, 29 | ScriptExtension, 30 | }, 31 | group::{Capture, Group, GroupKind}, 32 | intersection::Intersection, 33 | literal::Literal, 34 | lookaround::{Lookaround, LookaroundKind}, 35 | negation::Negation, 36 | range::Range, 37 | recursion::Recursion, 38 | reference::{Reference, ReferenceTarget}, 39 | regex::Regex, 40 | repetition::{Quantifier, Repetition, RepetitionKind}, 41 | rule::Rule, 42 | stmt::*, 43 | var::Variable, 44 | }; 45 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/negation.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | use super::Rule; 4 | 5 | #[derive(Debug, Clone)] 6 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 7 | pub struct Negation { 8 | pub rule: Rule, 9 | pub not_span: Span, 10 | } 11 | 12 | impl Negation { 13 | #[cfg(feature = "dbg")] 14 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter, needs_parens: bool) { 15 | buf.push('!'); 16 | if needs_parens { 17 | buf.start_indentation("("); 18 | } 19 | 20 | self.rule.pretty_print(buf, false); 21 | 22 | if needs_parens { 23 | buf.end_indentation(")"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/range.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | pub struct Range { 5 | pub start: Box<[u8]>, 6 | pub end: Box<[u8]>, 7 | pub radix: u8, 8 | pub span: Span, 9 | } 10 | 11 | impl Range { 12 | pub(crate) fn new(start: Box<[u8]>, end: Box<[u8]>, radix: u8, span: Span) -> Self { 13 | Range { start, end, radix, span } 14 | } 15 | 16 | #[cfg(feature = "dbg")] 17 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) { 18 | fn hex(n: u8) -> char { 19 | match n { 20 | 0..=9 => (n + b'0') as char, 21 | _ => (n + (b'A' - 10)) as char, 22 | } 23 | } 24 | 25 | buf.push_str("range '"); 26 | buf.extend(self.start.iter().map(|&n| hex(n))); 27 | buf.push_str("'-'"); 28 | buf.extend(self.end.iter().map(|&n| hex(n))); 29 | buf.push('\''); 30 | 31 | if self.radix != 10 { 32 | buf.push_str(" base "); 33 | buf.write_fmt(self.radix); 34 | } 35 | } 36 | } 37 | 38 | #[cfg(feature = "arbitrary")] 39 | impl arbitrary::Arbitrary<'_> for Range { 40 | fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { 41 | let radix = u.int_in_range(2..=36)?; 42 | let start = super::arbitrary::Digits::create(u, radix)?; 43 | let end = super::arbitrary::Digits::create(u, radix)?; 44 | if start.len() > end.len() || (start.len() == end.len() && start > end) { 45 | return Err(arbitrary::Error::IncorrectFormat); 46 | } 47 | Ok(Range { start, end, radix, span: Span::arbitrary(u)? }) 48 | } 49 | 50 | fn size_hint(_depth: usize) -> (usize, Option) { 51 | (1, None) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/recursion.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | #[derive(Debug, Clone)] 4 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 5 | pub struct Recursion { 6 | pub span: Span, 7 | } 8 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/reference.rs: -------------------------------------------------------------------------------- 1 | //! Implements ([relative](https://www.regular-expressions.info/backrefrel.html)) 2 | //! [backreferences](https://www.regular-expressions.info/backref.html), 3 | //! [forward references](https://www.regular-expressions.info/backref2.html#forward) and 4 | //! [named references](https://www.regular-expressions.info/named.html). 5 | 6 | use crate::Span; 7 | 8 | #[derive(Debug, Clone, PartialEq, Eq)] 9 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 10 | pub struct Reference { 11 | pub target: ReferenceTarget, 12 | pub span: Span, 13 | } 14 | 15 | #[derive(Debug, Clone, PartialEq, Eq)] 16 | pub enum ReferenceTarget { 17 | Named(String), 18 | Number(u32), 19 | Relative(i32), 20 | } 21 | 22 | impl Reference { 23 | pub(crate) fn new(target: ReferenceTarget, span: Span) -> Self { 24 | Reference { target, span } 25 | } 26 | 27 | #[cfg(feature = "dbg")] 28 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) { 29 | buf.push_str("::"); 30 | match &self.target { 31 | ReferenceTarget::Named(n) => buf.write(n), 32 | ReferenceTarget::Number(i) => buf.write_fmt(i), 33 | &ReferenceTarget::Relative(o) => { 34 | if o >= 0 { 35 | buf.push('+'); 36 | } 37 | buf.write_fmt(o); 38 | } 39 | } 40 | } 41 | } 42 | 43 | #[cfg(feature = "arbitrary")] 44 | impl arbitrary::Arbitrary<'_> for ReferenceTarget { 45 | fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { 46 | Ok(match u.int_in_range(0u8..=2)? { 47 | 0 => ReferenceTarget::Named(super::arbitrary::Ident::create(u)?), 48 | 1 => ReferenceTarget::Number(u.int_in_range(0u8..=15)? as u32), 49 | _ => ReferenceTarget::Relative(u.int_in_range(-15i8..=15)? as i32), 50 | }) 51 | } 52 | 53 | fn size_hint(depth: usize) -> (usize, Option) { 54 | arbitrary::size_hint::and(super::arbitrary::Ident::size_hint(depth), (3, Some(3))) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/regex.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] 5 | pub struct Regex { 6 | pub content: String, 7 | pub span: Span, 8 | } 9 | 10 | impl Regex { 11 | pub(crate) fn new(content: String, span: Span) -> Self { 12 | Regex { content, span } 13 | } 14 | 15 | #[cfg(feature = "dbg")] 16 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) { 17 | buf.push_str("regex "); 18 | write!(buf, "{:?}", self.content); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pomsky-syntax/src/exprs/var.rs: -------------------------------------------------------------------------------- 1 | use crate::Span; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | pub struct Variable { 5 | pub name: String, 6 | pub span: Span, 7 | } 8 | 9 | impl Variable { 10 | pub(crate) fn new(name: &str, span: Span) -> Self { 11 | Variable { name: name.to_string(), span } 12 | } 13 | 14 | #[cfg(feature = "dbg")] 15 | pub(super) fn pretty_print(&self, buf: &mut crate::PrettyPrinter) { 16 | buf.write(&self.name); 17 | } 18 | } 19 | 20 | #[cfg(feature = "arbitrary")] 21 | impl arbitrary::Arbitrary<'_> for Variable { 22 | fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { 23 | let name = super::arbitrary::Ident::create(u)?; 24 | Ok(Variable { name, span: Span::arbitrary(u)? }) 25 | } 26 | 27 | fn size_hint(depth: usize) -> (usize, Option) { 28 | super::arbitrary::Ident::size_hint(depth) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pomsky-syntax/src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | mod diagnostics; 2 | mod error; 3 | mod micro_regex; 4 | mod token; 5 | mod tokenize; 6 | 7 | pub use error::LexErrorMsg; 8 | pub use token::Token; 9 | 10 | pub(crate) use tokenize::tokenize; 11 | -------------------------------------------------------------------------------- /pomsky-syntax/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # pomsky-syntax 2 | //! 3 | //! Crate for parsing [pomsky expressions](https://pomsky-lang.org). 4 | //! 5 | //! ## Usage 6 | //! 7 | //! ``` 8 | //! let (result, warnings) = pomsky_syntax::parse("let x = 'test'; x*", 256); 9 | //! assert!(result.is_some()); 10 | //! assert!(warnings.is_empty()); 11 | //! ``` 12 | 13 | mod error; 14 | mod lexer; 15 | mod parse; 16 | mod span; 17 | mod util; 18 | mod warning; 19 | 20 | #[cfg(feature = "dbg")] 21 | mod pretty_print; 22 | 23 | pub mod diagnose; 24 | pub mod exprs; 25 | 26 | pub use parse::parse; 27 | pub use span::Span; 28 | 29 | #[cfg(feature = "suggestions")] 30 | pub use util::find_suggestion; 31 | 32 | #[cfg(feature = "dbg")] 33 | use pretty_print::PrettyPrinter; 34 | 35 | #[doc(hidden)] 36 | pub use exprs::char_class::{blocks_supported_in_dotnet, list_shorthands, props_supported_in_java}; 37 | -------------------------------------------------------------------------------- /pomsky-syntax/src/parse/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module for parsing a pomsky expression 2 | 3 | mod helper; 4 | mod parser; 5 | mod parser_impl; 6 | 7 | pub use parser::parse; 8 | 9 | use parser::Parser; 10 | -------------------------------------------------------------------------------- /pomsky-syntax/src/util.rs: -------------------------------------------------------------------------------- 1 | /// Calculates the distance between the provided name and the provided options, 2 | /// and returns the option with the lowest distance, if the distance is lower 3 | /// than a certain threshold. 4 | #[cfg(feature = "suggestions")] 5 | pub fn find_suggestion<'a>(name: &str, options: impl Iterator) -> Option> { 6 | options 7 | .map(|option| (option, strsim::jaro_winkler(option, name))) 8 | .max_by(|(_, score1), (_, score2)| f64::total_cmp(score1, score2)) 9 | .filter(|&(_, score)| score >= 0.8) 10 | .map(|(option, _)| option.into()) 11 | } 12 | -------------------------------------------------------------------------------- /pomsky-wasm/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /pkg/** 3 | !/pkg/package.json -------------------------------------------------------------------------------- /pomsky-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["."] 3 | package.edition = "2021" 4 | 5 | [package] 6 | name = "compiler-web" 7 | description = "A portable, modern regular expression language" 8 | homepage = "https://pomsky-lang.org" 9 | version = "0.11.0" 10 | authors = ["Pomsky developers "] 11 | edition.workspace = true 12 | license = "MIT OR Apache-2.0" 13 | repository = "https://github.com/pomsky-lang/pomsky" 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [features] 19 | default = ["console_error_panic_hook"] 20 | suggestions = ["pomsky/suggestions"] 21 | 22 | [dependencies] 23 | wasm-bindgen = "0.2.88" 24 | js-sys = "0.3.65" 25 | 26 | console_error_panic_hook = { version = "0.1.7", optional = true } 27 | 28 | pomsky = { version = "0.11.0", path = "../pomsky-lib", features = ["miette"] } 29 | 30 | miette = { version = "7.4.0", default-features = false } 31 | 32 | [dev-dependencies] 33 | wasm-bindgen-test = "0.3.33" 34 | 35 | [profile.release] 36 | # Tell `rustc` to optimize for small code size. 37 | opt-level = "z" 38 | -------------------------------------------------------------------------------- /pomsky-wasm/LICENSE_MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Pomsky developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /pomsky-wasm/README-node.md: -------------------------------------------------------------------------------- 1 | # Pomsky WASM module for node 2 | 3 | WASM module of [Pomsky](https://pomsky-lang.org). 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { compile } from '@pomsky-lang/compiler-node' 9 | 10 | const { output, diagnostics } = compile(`^ C* '.' C* $`, 'js') 11 | ``` 12 | 13 | This _should_ just work in Node.js. To use Pomsky in the browser, use [unplugin](https://www.npmjs.com/package/@pomsky-lang/unplugin) if you're using a bundler, or [compiler-web](https://www.npmjs.com/package/@pomsky-lang/compiler-web) if you want to compile Pomsky expressions on the client. 14 | 15 | Don't forget to check if `output === null`, which means that compilation failed, and you have to look at the diagnostics. Even when the expression compiled successfully, `diagnostics` may contain useful warnings. 16 | 17 | ## License 18 | 19 | Dual-licensed under the [MIT license][mit-license] or the [Apache 2.0 license][apache-2-license]. 20 | 21 | [mit-license]: https://opensource.org/licenses/MIT 22 | [apache-2-license]: https://opensource.org/licenses/Apache-2.0 23 | -------------------------------------------------------------------------------- /pomsky-wasm/README-web.md: -------------------------------------------------------------------------------- 1 | # Pomsky WASM module for web 2 | 3 | WASM module of [Pomsky](https://pomsky-lang.org). 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { compile } from '@pomsky-lang/compiler-web' 9 | 10 | const { output, diagnostics } = compile(`^ C* '.' C* $`, 'js') 11 | ``` 12 | 13 | If this doesn't work with your bundler, try initializing the module explicitly: 14 | 15 | ```js 16 | import init, { compile } from '@pomsky-lang/compiler-web' 17 | 18 | await init() 19 | const { output, diagnostics } = compile(`^ C* '.' C* $`, 'js') 20 | ``` 21 | 22 | Don't forget to check if `output === null`, which means that compilation failed, and you have to look at the diagnostics. Even when the expression compiled successfully, `diagnostics` may contain useful warnings. 23 | 24 | ### With vite 25 | 26 | If you're using vite, you also need to update your vite config like this: 27 | 28 | ```diff 29 | import { defineConfig } from 'vite' 30 | 31 | export default defineConfig(({ mode }) => ({ 32 | + optimizeDeps: { 33 | + exclude: mode === 'production' ? [] : ['@pomsky-lang/compiler-web'], 34 | + }, 35 | })) 36 | ``` 37 | 38 | ## License 39 | 40 | Dual-licensed under the [MIT license][mit-license] or the [Apache 2.0 license][apache-2-license]. 41 | 42 | [mit-license]: https://opensource.org/licenses/MIT 43 | [apache-2-license]: https://opensource.org/licenses/Apache-2.0 44 | -------------------------------------------------------------------------------- /pomsky-wasm/js/mod.js: -------------------------------------------------------------------------------- 1 | export class PomskyDiagnostic { 2 | /** 3 | * @param {"error" | "warning"} severity 4 | * @param {string} kind 5 | * @param {string} code 6 | * @param {string} message 7 | * @param {string?} help 8 | * @param {[number, number]} range 9 | */ 10 | constructor(severity, kind, code, message, help, range) { 11 | this.severity = severity 12 | this.kind = kind 13 | this.code = code 14 | this.message = message 15 | this.help = help 16 | this.range = [range[0], range[1]] 17 | } 18 | } 19 | 20 | export class PomskyError extends Error { 21 | /** 22 | * @param {string} message 23 | */ 24 | constructor(message) { 25 | super(message) 26 | } 27 | } 28 | 29 | export class PomskyResult { 30 | /** 31 | * @param {string | null} output 32 | * @param {PomskyDiagnostic[]} diagnostics 33 | * @param {any[]} tests 34 | */ 35 | constructor(output, diagnostics, tests) { 36 | this.output = output 37 | this.diagnostics = diagnostics 38 | this.tests = tests 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pomsky-wasm/justfile: -------------------------------------------------------------------------------- 1 | set export 2 | 3 | just_msg := "\nDone. Make sure to add 'snippets' to the 'files' array in package.json!\nCheck if you set to package name correctly!\nNow copy the appropriate README to the pkg/ directory.\nDon't forget to publish with --access=public!\n" 4 | 5 | build-web: 6 | wasm-pack build --target=web --scope=pomsky-lang -- --features suggestions 7 | echo "$just_msg" 8 | 9 | build-node: 10 | wasm-pack build --scope=pomsky-lang -- --features suggestions 11 | echo "$just_msg" 12 | -------------------------------------------------------------------------------- /pomsky-wasm/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pomsky-lang/compiler-web", 3 | "collaborators": [ 4 | "Ludwig Stecher " 5 | ], 6 | "description": "A portable, modern regular expression language", 7 | "version": "0.11.0", 8 | "license": "MIT OR Apache-2.0", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/pomsky-lang/pomsky" 12 | }, 13 | "files": [ 14 | "snippets", 15 | "compiler_web_bg.wasm", 16 | "compiler_web.js", 17 | "compiler_web.d.ts", 18 | "LICENSE_MIT", 19 | "LICENSE_APACHE" 20 | ], 21 | "module": "compiler_web.js", 22 | "homepage": "https://pomsky-lang.org", 23 | "types": "compiler_web.d.ts", 24 | "sideEffects": [ 25 | "./snippets/*" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /pomsky-wasm/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | // When the `console_error_panic_hook` feature is enabled, we can call the 3 | // `set_panic_hook` function at least once during initialization, and then 4 | // we will get better error messages if our code ever panics. 5 | // 6 | // For more details see 7 | // https://github.com/rustwasm/console_error_panic_hook#readme 8 | #[cfg(feature = "console_error_panic_hook")] 9 | console_error_panic_hook::set_once(); 10 | } 11 | -------------------------------------------------------------------------------- /pomsky-wasm/tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | 3 | #![cfg(target_arch = "wasm32")] 4 | 5 | extern crate wasm_bindgen_test; 6 | use wasm_bindgen_test::*; 7 | 8 | wasm_bindgen_test_configure!(run_in_browser); 9 | 10 | #[wasm_bindgen_test] 11 | fn pass() { 12 | assert_eq!(1 + 1, 2); 13 | } 14 | -------------------------------------------------------------------------------- /regex-test/.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.exe -------------------------------------------------------------------------------- /regex-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "regex-test" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | regex = "1.11.1" 11 | pcre2 = "0.2.9" 12 | onig = "6.4.0" 13 | -------------------------------------------------------------------------------- /regex-test/dotnet/Tester.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | class Tester { 5 | public static void Main(string[] args) { 6 | try { 7 | Regex r = new Regex(args[1], RegexOptions.Compiled); 8 | for (int i = 2; i < args.Length; i++) { 9 | MatchCollection matches = r.Matches(args[i]); 10 | if (matches.Count > 0) { 11 | Match match = matches[0]; 12 | string region = match.Index + ".." + (match.Index + match.Value.Length); 13 | Console.Error.WriteLine("[matches in {0}] {1}", region, args[i]); 14 | } else { 15 | Console.Error.WriteLine("[no match] {0}", args[i]); 16 | } 17 | } 18 | } catch (ArgumentException e) { 19 | Console.Error.WriteLine(e.Message); 20 | Environment.Exit(1); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /regex-test/dotnet/TesterAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | public class TesterAsync { 5 | public static void Main(string[] args) { 6 | string line; 7 | string testLine; 8 | 9 | while ((line = Console.ReadLine()) != null) { 10 | if (!line.StartsWith("REGEX:")) { 11 | continue; 12 | } 13 | 14 | try { 15 | var r = new Regex(line.Substring(6), RegexOptions.Compiled); 16 | Console.WriteLine("success"); 17 | 18 | while ((testLine = Console.ReadLine()) != null && testLine.StartsWith("TEST:")) { 19 | var test = testLine.Substring(5); 20 | if (r.IsMatch(test)) { 21 | Console.WriteLine("test good"); 22 | } else { 23 | throw new ArgumentException($"Regex '{r}' does not match '{test}'"); 24 | } 25 | } 26 | } catch (ArgumentException e) { 27 | string message = e.Message.Replace("\\", @"\\").Replace("\n", @"\n"); 28 | Console.WriteLine(message); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /regex-test/java/Tester.java: -------------------------------------------------------------------------------- 1 | import java.util.regex.Pattern; 2 | import java.util.regex.PatternSyntaxException; 3 | 4 | public class Tester { 5 | public static void main(String[] args) { 6 | String regex = args[1]; 7 | try { 8 | var pattern = Pattern.compile("(?U)" + regex); 9 | for (int i = 2; i < args.length; i++) { 10 | var matcher = pattern.matcher(args[i]); 11 | if (matcher.matches()) { 12 | var region = matcher.regionStart() + ".." + matcher.regionEnd(); 13 | System.err.println("[matches in " + region + "] " + args[i]); 14 | } else { 15 | System.err.println("[no match] " + args[i]); 16 | } 17 | } 18 | } catch (PatternSyntaxException e) { 19 | System.err.println(e.getMessage()); 20 | System.exit(1); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /regex-test/java/TesterAsync.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | import java.util.regex.Pattern; 3 | import java.util.regex.PatternSyntaxException; 4 | 5 | public class TesterAsync { 6 | public static void main(String[] args) { 7 | try (Scanner input = new Scanner(System.in)) { 8 | while (input.hasNext()) { 9 | String regex = input.nextLine(); 10 | if (!regex.startsWith("REGEX:")) { 11 | continue; 12 | } 13 | 14 | try { 15 | Pattern p = Pattern.compile("(?U)" + regex.substring(6)); 16 | System.out.printf("success\n"); 17 | 18 | while (input.hasNext()) { 19 | String line = input.nextLine(); 20 | if (line.startsWith("TEST:")) { 21 | String test = line.substring(5); 22 | if (p.matcher(test).matches()) { 23 | System.out.printf("test good\n"); 24 | } else { 25 | System.out.printf("Regex '%s' does not match '%s'\n", regex, test); 26 | break; 27 | } 28 | } else { 29 | break; 30 | } 31 | } 32 | } catch (PatternSyntaxException e) { 33 | String message = e.getMessage() 34 | .replaceAll("\\\\", "\\\\\\\\") 35 | .replaceAll("\n", "\\\\n"); 36 | System.out.printf("%s\n", message); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /regex-test/js/tester-deno-async.js: -------------------------------------------------------------------------------- 1 | import { readLines } from 'https://deno.land/std@0.170.0/io/mod.ts' 2 | 3 | /** @type {RegExp|undefined} */ 4 | let regex 5 | 6 | for await (const line of readLines(Deno.stdin)) { 7 | if (regex === undefined) { 8 | if (!line.startsWith('REGEX:')) { 9 | continue 10 | } 11 | 12 | try { 13 | regex = new RegExp(line.slice(6), 'u') 14 | console.log('success') 15 | } catch (e) { 16 | console.log(substituteLf(e.message)) 17 | } 18 | } else if (line.startsWith('TEST:')) { 19 | const test = line.slice(5) 20 | 21 | if (regex.test(test)) { 22 | console.log('test good') 23 | } else { 24 | console.log(substituteLf(`Regex '${regex.source}' does not match '${test}'`)) 25 | regex = undefined 26 | } 27 | } else { 28 | regex = undefined 29 | } 30 | } 31 | 32 | function substituteLf(s = '') { 33 | return s.replace(/[\n\\]/g, (c) => (c === '\\' ? '\\\\' : '\\n')) 34 | } 35 | -------------------------------------------------------------------------------- /regex-test/js/tester-deno.js: -------------------------------------------------------------------------------- 1 | const arg = Deno.args[1] 2 | 3 | if (arg === undefined) { 4 | console.error('error: no argument provided') 5 | Deno.exit(1) 6 | } 7 | 8 | try { 9 | new RegExp(arg, 'u') 10 | } catch (e) { 11 | console.error(e.message) 12 | Deno.exit(1) 13 | } 14 | -------------------------------------------------------------------------------- /regex-test/js/tester-node-async.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline') 2 | 3 | const rl = readline.createInterface({ 4 | input: process.stdin, 5 | output: process.stdout, 6 | terminal: false, 7 | }) 8 | 9 | /** @type {RegExp|undefined} */ 10 | let regex 11 | 12 | rl.on('line', (line) => { 13 | if (regex === undefined) { 14 | if (!line.startsWith('REGEX:')) { 15 | return 16 | } 17 | 18 | try { 19 | regex = new RegExp(line.slice(6), 'u') 20 | console.log('success') 21 | } catch (e) { 22 | console.log(substituteLf(e.message)) 23 | } 24 | } else if (line.startsWith('TEST:')) { 25 | const test = line.slice(5) 26 | 27 | if (regex.test(test)) { 28 | console.log('test good') 29 | } else { 30 | console.log(substituteLf(`Regex '${regex.source}' does not match '${test}'`)) 31 | regex = undefined 32 | } 33 | } else { 34 | regex = undefined 35 | } 36 | }) 37 | 38 | function substituteLf(s = '') { 39 | return s.replace(/[\n\\]/g, (c) => (c === '\\' ? '\\\\' : '\\n')) 40 | } 41 | -------------------------------------------------------------------------------- /regex-test/js/tester-node.js: -------------------------------------------------------------------------------- 1 | const arg = process.argv[2] 2 | 3 | if (arg === undefined) { 4 | console.error('error: no argument provided') 5 | process.exit(1) 6 | } 7 | 8 | try { 9 | new RegExp(arg, 'u') 10 | } catch (e) { 11 | console.error(e.message) 12 | process.exit(1) 13 | } 14 | -------------------------------------------------------------------------------- /regex-test/python/tester.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | 4 | try: 5 | re.compile(sys.argv[1]) 6 | except Exception as e: 7 | print(e) 8 | sys.exit(1) 9 | -------------------------------------------------------------------------------- /regex-test/python/tester_async.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | import fileinput 4 | 5 | def substituteLf(s): 6 | return s.replace("\\", "\\\\").replace("\n", "\\n") 7 | 8 | regex = None 9 | 10 | try: 11 | for line in fileinput.input(): 12 | if line.endswith("\r\n"): 13 | line = line[:-2] 14 | elif line.endswith("\n"): 15 | line = line[:-1] 16 | 17 | if regex == None: 18 | if not line.startswith("REGEX:"): 19 | continue 20 | 21 | try: 22 | regex = re.compile(line[6:]) 23 | print("success") 24 | except Exception as e: 25 | print(substituteLf(str(e))) 26 | sys.stdout.flush() 27 | elif line.startswith("TEST:"): 28 | test = line[5:] 29 | if regex.match(test) != None: 30 | print("test good") 31 | else: 32 | print(substituteLf("Regex '" + regex.pattern + "' does not match '" + test + "'")) 33 | regex = None 34 | sys.stdout.flush() 35 | else: 36 | regex = None 37 | except KeyboardInterrupt: 38 | pass 39 | -------------------------------------------------------------------------------- /regex-test/src/count.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | #[derive(Default)] 4 | pub struct Count { 5 | count: AtomicUsize, 6 | } 7 | 8 | impl Count { 9 | pub(super) fn add_one(&self) { 10 | self.count.fetch_add(1, Ordering::AcqRel); 11 | } 12 | 13 | pub fn get_count(&self) -> usize { 14 | self.count.load(Ordering::Relaxed) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /regex-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod count; 2 | mod native; 3 | mod sync; 4 | 5 | #[derive(Debug, PartialEq, Eq, Clone)] 6 | pub enum Outcome { 7 | Success, 8 | Error(String), 9 | } 10 | 11 | pub use native::{onig_version, pcre_version}; 12 | pub use sync::RegexTest; 13 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "Max" 2 | # wrap_comments = true 3 | --------------------------------------------------------------------------------