package ppx_mikmatch
Install
dune-project
Dependency
Authors
Maintainers
Sources
md5=3428892cb040a21968e25c451d45e47a
sha512=58ff6f86d962719aa5a34aa5eef2b027c2ba7c0c17cc46ea5eb08b56e9e1e8bc32150722383cce52d74ffc2c231cdaa5f8c9220a595f281e29221c2d308c0178
doc/README.html
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.