package ppx_mikmatch
All checked builds passInstall
dune-project
Dependency
Authors
Maintainers
Sources
md5=3428892cb040a21968e25c451d45e47a
sha512=58ff6f86d962719aa5a34aa5eef2b027c2ba7c0c17cc46ea5eb08b56e9e1e8bc32150722383cce52d74ffc2c231cdaa5f8c9220a595f281e29221c2d308c0178
Description
This syntax extension turns
match%mikmatch x with
| {| re1 |} -> e1
...
| {| reN |} -> eN
| _ -> e0
into suitable invocations to the ocaml-re library. The patterns are plain strings of the form accepted by Re_pcre, except groups can be bound to variables using the syntax (... as var). The type of var will be string if a match is of the groups is guaranteed given a match of the whole pattern, and string option if the variable is bound to or nested below an optionally matched group.
Additional extensions:
let%mikmatch name = {|re|}defines reusable patterns for use within other patternstype name = {%mikmatch| re |}generates record types with parse/pp functions from patterns- let destructuring via
let%pcre/mikmatch {| regex |} = s, whereregexbinds values andsis the input string /regex/flagsfor providing compiler flags.ifor caseless,u(%mikmatch) unanchored- Caseless groups via the
~operator - Mixed matching (raw strings supported) via
function | {%mikmatch| ... |} -> ... | "raw string" -> ... - Matching guards are supported across all match extensions
Added to opam-repository:
README
Made possible by forking ppx_regexp. Our upstream contributions to ppx_regexp come from another repo.
PPX for Working with Regular Expressions
This repo provides a PPX providing regular expression-based routing:
ppx_mikmatch maps to re with the conventional last-match extraction into string and string option.
This syntax extension turns:
function%mikmatch
| {| re1 |} -> e1
...
| {| reN |} -> eN
| _ -> e0into suitable invocations of the Re library, and similar for match%mikmatch.
It also accepts:
let%mikmatch var = {| some regex |}to define reusable patterns, and much more.
Full usage guide
Quick Links
Small Example
(* Match HTTP method and path *)
let handle_request = function%mikmatch
| {| "GET" ' '+ '/' (alpha+ as path) |} -> get_handler path
| {| "POST" ' '+ '/' (alpha+ as path) |} -> post_handler path
| _ -> method_not_allowed
(* Capture and convert to int *)
let parse_id = function%mikmatch
| {| "id=" (digit+ as id : int) |} -> Some id
| _ -> NoneNotice: Patterns are anchored by default
Regexes are anchored at both start and end by default. They can be unanchored with a flag.
(* This matches ONLY "hello", not "hello world" *)
function%mikmatch {| "hello" |} -> "matched"
(* Use /u flag for unanchored matching *)
function%mikmatch {|/ "hello" / u|} -> "matched" (* matches "hello world" *)Built-in Keywords
Available in patterns:
POSIX character classes: lower, upper, alpha, digit, alnum, punct, graph, print, blank, space, cntrl, xdigit
Control sequences:
bos- beginning of string (^)eos- end of string ($)bol- beginning of line (start of string or after newline)eol- end of line (end of string or newline)bnd- word boundary (\b)notnl- any character except newlineany- any character (including newline)
Repetition: *, +, ?, {n}, {n-m}, {n-}
Flags: i (case-insensitive), u (unanchored)
Motivational Examples
URL parsing:
let parse s =
let (scheme, first) =
match s.[4] with
| ':' -> `Http, 7
| 's' -> `Https, 8
| _ -> failwith "parse"
in
let last = String.index_from s first '/' in
let host = String.slice s ~first ~last in
let (host,port) =
match Stre.splitc host ':' with
| exception _ -> host, default_port scheme
| (host,port) -> host, int_of_string port
in
...
(* in mikmatch: *)
let parse s =
match%mikmatch s with
| {|/ "http" ('s' as https)? "://" ([^ '/' ':']+ as host) (":" (digit+ as port : int))? '/'? (_* as rest) /|} ->
let scheme = match https with Some _ -> `Https | None -> `Http in
let port = match port with Some p -> p | None -> default_port scheme in
...
| _ -> failwith "parse"let rex =
let origins = "csv|pdf|html|xlsv|xml"
Re2.create_exn (sprintf {|^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)(?:\.(\d+))?\.(%s)\.(\d+)\.(\d+)$|} origins)
let of_string s =
try
let m = Re2.first_match_exn rex s in
let start = Re2.Match.get_exn ~sub:(`Index 1) m |> U.strptime "%Y-%m-%dT%H:%M:%S%z" |> U.timegm in
let shard = int_of_string (Re2.Match.get_exn ~sub:(`Index 2) m) in
let origin = origin_of_string (Re2.Match.get_exn ~sub:(`Index 3) m) in
let partition = int_of_string (Re2.Match.get_exn ~sub:(`Index 4) m) in
let worker = int_of_string (Re2.Match.get_exn ~sub:(`Index 5) m) in
{ start; shard; origin; partition; worker }
with _ -> invalid_arg (sprintf "error: %s" s)
(* in mikmatch: *)
let%mikmatch origins = {| "csv" | "pdf" | "html" | "xlsv" | "xml" |}
let of_string s =
match%mikmatch s with
| {|/ (digit{4} '-' digit{2} '-' digit{2} 'T' digit{2} ':' digit{2} ':' digit{2} 'Z' as timestamp)
('.' (digit+ as shard : int))?
'.' (origins as origin := origin_of_string)
'.' (digit+ as partition : int)
'.' (digit+ as worker : int) /|} ->
let start = U.strptime "%Y-%m-%dT%H:%M:%S%z" timestamp |> U.timegm in
let shard = match shard with Some s -> s | None -> 0 in
{ start; shard; origin; partition; worker }
| _ -> invalid_arg (sprintf "error: %s" s)Limitations
No Exhaustiveness Check
The syntax extension will always warn if no catch-all case is provided. No exhaustiveness check is attempted. Doing it right would require reimplementing full regular expression parsing and an algorithm which would ideally produce a counter-example.
Bug Reports
The processor is currently new and not well tested. Please break it and file bug reports in the GitHub issue tracker. Any exception raised by generated code except for Match_failure is a bug.
Dependencies (5)
Dev Dependencies (1)
-
ounit2
with-test
Optional Dependencies
None
Used by
None
Conflicts
None
Build status
Results of building ppx_mikmatch with each compiler on check.ci.ocaml.org, run of against opam-repository 8a77e58. All results for this package.
Each result links to its build log. To keep crawlers from overloading the site, build logs require a GitHub sign-in (see ocurrent/opam-health-check). On your first log, GitHub asks you to “Authorize opam-health-check”, the OCurrent app that runs these checks; it sees only your public profile, not your repositories. The sign-in lasts 30 days.
| Version | OCaml 4.11 | OCaml 4.14 | OCaml 5.2 | OCaml 5.4 | OCaml 5.5 |
|---|---|---|---|---|---|
| 1.4 (this version) | good | good | good | good | good |
- good Package successfully built
- partial One of the dependencies failed to build
- bad Package failed to build
- n/a Package is not available in this environment
- internal Internal failure