(* RE - A regular expression library Copyright (C) 2001 Jerome Vouillon email: Jerome.Vouillon@pps.jussieu.fr This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, with linking exception; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *) (* What we could (should?) do: - a* ==> longest ((shortest (no_group a)* ), a | ()) (!!!) - abc understood as (ab)c - "((a?)|b)" against "ab" should not bind the first subpattern to anything Note that it should be possible to handle "(((ab)c)d)e" efficiently *) module Re = Core exception Parse_error exception Not_supported let parse newline s = let i = ref 0 in let l = String.length s in let eos () = !i = l in let test c = not (eos ()) && s.[!i] = c in let accept c = let r = test c in if r then incr i; r in let get () = let r = s.[!i] in incr i; r in let unget () = decr i in let rec regexp () = regexp' (branch ()) and regexp' left = if accept '|' then regexp' (Re.alt [left; branch ()]) else left and branch () = branch' [] and branch' left = if eos () || test '|' || test ')' then Re.seq (List.rev left) else branch' (piece () :: left) and piece () = let r = atom () in if accept '*' then Re.rep (Re.nest r) else if accept '+' then Re.rep1 (Re.nest r) else if accept '?' then Re.opt r else if accept '{' then match integer () with Some i -> let j = if accept ',' then integer () else Some i in if not (accept '}') then raise Parse_error; begin match j with Some j when j < i -> raise Parse_error | _ -> () end; Re.repn (Re.nest r) i j | None -> unget (); r else r and atom () = if accept '.' then begin if newline then Re.notnl else Re.any end else if accept '(' then begin let r = regexp () in if not (accept ')') then raise Parse_error; Re.group r end else if accept '^' then begin if newline then Re.bol else Re.bos end else if accept '$' then begin if newline then Re.eol else Re.eos end else if accept '[' then begin if accept '^' then Re.diff (Re.compl (bracket [])) (Re.char '\n') else Re.alt (bracket []) end else if accept '\\' then begin if eos () then raise Parse_error; match get () with '|' | '(' | ')' | '*' | '+' | '?' | '[' | '.' | '^' | '$' | '{' | '\\' as c -> Re.char c | _ -> raise Parse_error end else begin if eos () then raise Parse_error; match get () with '*' | '+' | '?' | '{' | '\\' -> raise Parse_error | c -> Re.char c end and integer () = if eos () then None else match get () with '0'..'9' as d -> integer' (Char.code d - Char.code '0') | _ -> unget (); None and integer' i = if eos () then Some i else match get () with '0'..'9' as d -> let i' = 10 * i + (Char.code d - Char.code '0') in if i' < i then raise Parse_error; integer' i' | _ -> unget (); Some i and bracket s = if s <> [] && accept ']' then s else begin let c = char () in if accept '-' then begin if accept ']' then Re.char c :: Re.char '-' :: s else begin let c' = char () in bracket (Re.rg c c' :: s) end end else bracket (Re.char c :: s) end and char () = if eos () then raise Parse_error; let c = get () in if c = '[' then begin if accept '=' then raise Not_supported else if accept ':' then begin raise Not_supported (*XXX*) end else if accept '.' then begin if eos () then raise Parse_error; let c = get () in if not (accept '.') then raise Not_supported; if not (accept ']') then raise Parse_error; c end else c end else c in let res = regexp () in if not (eos ()) then raise Parse_error; res type opt = [`ICase | `NoSub | `Newline] let re ?(opts = []) s = let r = parse (List.memq `Newline opts) s in let r = if List.memq `ICase opts then Re.no_case r else r in let r = if List.memq `NoSub opts then Re.no_group r else r in r let compile re = Re.compile (Re.longest re) let compile_pat ?(opts = []) s = compile (re ~opts s)