summaryrefslogtreecommitdiff
path: root/ocaml-binary-annot/otherlibs/str/str.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocaml-binary-annot/otherlibs/str/str.ml')
-rw-r--r--ocaml-binary-annot/otherlibs/str/str.ml740
1 files changed, 740 insertions, 0 deletions
diff --git a/ocaml-binary-annot/otherlibs/str/str.ml b/ocaml-binary-annot/otherlibs/str/str.ml
new file mode 100644
index 0000000000..f3f760acb9
--- /dev/null
+++ b/ocaml-binary-annot/otherlibs/str/str.ml
@@ -0,0 +1,740 @@
+(***********************************************************************)
+(* *)
+(* Objective Caml *)
+(* *)
+(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
+(* *)
+(* Copyright 1996 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the GNU Library General Public License, with *)
+(* the special exception on linking described in file ../../LICENSE. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+
+(** String utilities *)
+
+let string_before s n = String.sub s 0 n
+
+let string_after s n = String.sub s n (String.length s - n)
+
+let first_chars s n = String.sub s 0 n
+
+let last_chars s n = String.sub s (String.length s - n) n
+
+(** Representation of character sets **)
+
+module Charset =
+ struct
+ type t = string (* of length 32 *)
+
+ (*let empty = String.make 32 '\000'*)
+ let full = String.make 32 '\255'
+
+ let make_empty () = String.make 32 '\000'
+
+ let add s c =
+ let i = Char.code c in
+ s.[i lsr 3] <- Char.chr(Char.code s.[i lsr 3] lor (1 lsl (i land 7)))
+
+ let add_range s c1 c2 =
+ for i = Char.code c1 to Char.code c2 do add s (Char.chr i) done
+
+ let singleton c =
+ let s = make_empty () in add s c; s
+
+ (*let range c1 c2 =
+ let s = make_empty () in add_range s c1 c2; s
+ *)
+ let complement s =
+ let r = String.create 32 in
+ for i = 0 to 31 do
+ r.[i] <- Char.chr(Char.code s.[i] lxor 0xFF)
+ done;
+ r
+
+ let union s1 s2 =
+ let r = String.create 32 in
+ for i = 0 to 31 do
+ r.[i] <- Char.chr(Char.code s1.[i] lor Char.code s2.[i])
+ done;
+ r
+
+ let disjoint s1 s2 =
+ try
+ for i = 0 to 31 do
+ if Char.code s1.[i] land Char.code s2.[i] <> 0 then raise Exit
+ done;
+ true
+ with Exit ->
+ false
+
+ let iter fn s =
+ for i = 0 to 31 do
+ let c = Char.code s.[i] in
+ if c <> 0 then
+ for j = 0 to 7 do
+ if c land (1 lsl j) <> 0 then fn (Char.chr ((i lsl 3) + j))
+ done
+ done
+
+ let expand s =
+ let r = String.make 256 '\000' in
+ iter (fun c -> r.[Char.code c] <- '\001') s;
+ r
+
+ let fold_case s =
+ let r = make_empty() in
+ iter (fun c -> add r (Char.lowercase c); add r (Char.uppercase c)) s;
+ r
+
+ end
+
+(** Abstract syntax tree for regular expressions *)
+
+type re_syntax =
+ Char of char
+ | String of string
+ | CharClass of Charset.t * bool (* true = complemented, false = normal *)
+ | Seq of re_syntax list
+ | Alt of re_syntax * re_syntax
+ | Star of re_syntax
+ | Plus of re_syntax
+ | Option of re_syntax
+ | Group of int * re_syntax
+ | Refgroup of int
+ | Bol
+ | Eol
+ | Wordboundary
+
+(** Representation of compiled regular expressions *)
+
+type regexp = {
+ prog: int array; (* bytecode instructions *)
+ cpool: string array; (* constant pool (string literals) *)
+ normtable: string; (* case folding table (if any) *)
+ numgroups: int; (* number of \(...\) groups *)
+ numregisters: int; (* number of nullable Star or Plus *)
+ startchars: int (* index of set of starting chars, or -1 if none *)
+}
+
+(** Opcodes for bytecode instructions; see strstubs.c for description *)
+
+let op_CHAR = 0
+let op_CHARNORM = 1
+let op_STRING = 2
+let op_STRINGNORM = 3
+let op_CHARCLASS = 4
+let op_BOL = 5
+let op_EOL = 6
+let op_WORDBOUNDARY = 7
+let op_BEGGROUP = 8
+let op_ENDGROUP = 9
+let op_REFGROUP = 10
+let op_ACCEPT = 11
+let op_SIMPLEOPT = 12
+let op_SIMPLESTAR = 13
+let op_SIMPLEPLUS = 14
+let op_GOTO = 15
+let op_PUSHBACK = 16
+let op_SETMARK = 17
+let op_CHECKPROGRESS = 18
+
+(* Encoding of bytecode instructions *)
+
+let instr opc arg = opc lor (arg lsl 8)
+
+(* Computing relative displacements for GOTO and PUSHBACK instructions *)
+
+let displ dest from = dest - from - 1
+
+(** Compilation of a regular expression *)
+
+(* Determine if a regexp can match the empty string *)
+
+let rec is_nullable = function
+ Char c -> false
+ | String s -> s = ""
+ | CharClass(cl, cmpl) -> false
+ | Seq rl -> List.for_all is_nullable rl
+ | Alt (r1, r2) -> is_nullable r1 || is_nullable r2
+ | Star r -> true
+ | Plus r -> is_nullable r
+ | Option r -> true
+ | Group(n, r) -> is_nullable r
+ | Refgroup n -> true
+ | Bol -> true
+ | Eol -> true
+ | Wordboundary -> true
+
+(* first r returns a set of characters C such that:
+ for all string s, s matches r => the first character of s is in C.
+ For convenience, return Charset.full if r is nullable. *)
+
+let rec first = function
+ Char c -> Charset.singleton c
+ | String s -> if s = "" then Charset.full else Charset.singleton s.[0]
+ | CharClass(cl, cmpl) -> if cmpl then Charset.complement cl else cl
+ | Seq rl -> first_seq rl
+ | Alt (r1, r2) -> Charset.union (first r1) (first r2)
+ | Star r -> Charset.full
+ | Plus r -> first r
+ | Option r -> Charset.full
+ | Group(n, r) -> first r
+ | Refgroup n -> Charset.full
+ | Bol -> Charset.full
+ | Eol -> Charset.full
+ | Wordboundary -> Charset.full
+
+and first_seq = function
+ [] -> Charset.full
+ | (Bol | Eol | Wordboundary) :: rl -> first_seq rl
+ | Star r :: rl -> Charset.union (first r) (first_seq rl)
+ | Option r :: rl -> Charset.union (first r) (first_seq rl)
+ | r :: rl -> first r
+
+(* Transform a Char or CharClass regexp into a character class *)
+
+let charclass_of_regexp fold_case re =
+ let (cl1, compl) =
+ match re with
+ | Char c -> (Charset.singleton c, false)
+ | CharClass(cl, compl) -> (cl, compl)
+ | _ -> assert false in
+ let cl2 = if fold_case then Charset.fold_case cl1 else cl1 in
+ if compl then Charset.complement cl2 else cl2
+
+(* The case fold table: maps characters to their lowercase equivalent *)
+
+let fold_case_table =
+ let t = String.create 256 in
+ for i = 0 to 255 do t.[i] <- Char.lowercase(Char.chr i) done;
+ t
+
+module StringMap = Map.Make(struct type t = string let compare = compare end)
+
+(* Compilation of a regular expression *)
+
+let compile fold_case re =
+
+ (* Instruction buffering *)
+ let prog = ref (Array.make 32 0)
+ and progpos = ref 0
+ and cpool = ref StringMap.empty
+ and cpoolpos = ref 0
+ and numgroups = ref 1
+ and numregs = ref 0 in
+ (* Add a new instruction *)
+ let emit_instr opc arg =
+ if !progpos >= Array.length !prog then begin
+ let newlen = ref (Array.length !prog) in
+ while !progpos >= !newlen do newlen := !newlen * 2 done;
+ let nprog = Array.make !newlen 0 in
+ Array.blit !prog 0 nprog 0 (Array.length !prog);
+ prog := nprog
+ end;
+ (!prog).(!progpos) <- (instr opc arg);
+ incr progpos in
+ (* Reserve an instruction slot and return its position *)
+ let emit_hole () =
+ let p = !progpos in incr progpos; p in
+ (* Fill a reserved instruction slot with a GOTO or PUSHBACK instruction *)
+ let patch_instr pos opc dest =
+ (!prog).(pos) <- (instr opc (displ dest pos)) in
+ (* Return the cpool index for the given string, adding it if not
+ already there *)
+ let cpool_index s =
+ try
+ StringMap.find s !cpool
+ with Not_found ->
+ let p = !cpoolpos in
+ cpool := StringMap.add s p !cpool;
+ incr cpoolpos;
+ p in
+ (* Allocate fresh register if regexp is nullable *)
+ let allocate_register_if_nullable r =
+ if is_nullable r then begin
+ let n = !numregs in
+ if n >= 64 then failwith "too many r* or r+ where r is nullable";
+ incr numregs;
+ n
+ end else
+ -1 in
+ (* Main recursive compilation function *)
+ let rec emit_code = function
+ Char c ->
+ if fold_case then
+ emit_instr op_CHARNORM (Char.code (Char.lowercase c))
+ else
+ emit_instr op_CHAR (Char.code c)
+ | String s ->
+ begin match String.length s with
+ 0 -> ()
+ | 1 ->
+ if fold_case then
+ emit_instr op_CHARNORM (Char.code (Char.lowercase s.[0]))
+ else
+ emit_instr op_CHAR (Char.code s.[0])
+ | _ ->
+ try
+ (* null characters are not accepted by the STRING* instructions;
+ if one is found, split string at null character *)
+ let i = String.index s '\000' in
+ emit_code (String (string_before s i));
+ emit_instr op_CHAR 0;
+ emit_code (String (string_after s (i+1)))
+ with Not_found ->
+ if fold_case then
+ emit_instr op_STRINGNORM (cpool_index (String.lowercase s))
+ else
+ emit_instr op_STRING (cpool_index s)
+ end
+ | CharClass(cl, compl) ->
+ let cl1 = if fold_case then Charset.fold_case cl else cl in
+ let cl2 = if compl then Charset.complement cl1 else cl1 in
+ emit_instr op_CHARCLASS (cpool_index cl2)
+ | Seq rl ->
+ emit_seq_code rl
+ | Alt(r1, r2) ->
+ (* PUSHBACK lbl1
+ <match r1>
+ GOTO lbl2
+ lbl1: <match r2>
+ lbl2: ... *)
+ let pos_pushback = emit_hole() in
+ emit_code r1;
+ let pos_goto_end = emit_hole() in
+ let lbl1 = !progpos in
+ emit_code r2;
+ let lbl2 = !progpos in
+ patch_instr pos_pushback op_PUSHBACK lbl1;
+ patch_instr pos_goto_end op_GOTO lbl2
+ | Star r ->
+ (* Implement longest match semantics for compatibility with old Str *)
+ (* General translation:
+ lbl1: PUSHBACK lbl2
+ SETMARK regno
+ <match r>
+ CHECKPROGRESS regno
+ GOTO lbl1
+ lbl2:
+ If r cannot match the empty string, code can be simplified:
+ lbl1: PUSHBACK lbl2
+ <match r>
+ GOTO lbl1
+ lbl2:
+ *)
+ let regno = allocate_register_if_nullable r in
+ let lbl1 = emit_hole() in
+ if regno >= 0 then emit_instr op_SETMARK regno;
+ emit_code r;
+ if regno >= 0 then emit_instr op_CHECKPROGRESS regno;
+ emit_instr op_GOTO (displ lbl1 !progpos);
+ let lbl2 = !progpos in
+ patch_instr lbl1 op_PUSHBACK lbl2
+ | Plus r ->
+ (* Implement longest match semantics for compatibility with old Str *)
+ (* General translation:
+ lbl1: <match r>
+ CHECKPROGRESS regno
+ PUSHBACK lbl2
+ SETMARK regno
+ GOTO lbl1
+ lbl2:
+ If r cannot match the empty string, code can be simplified:
+ lbl1: <match r>
+ PUSHBACK lbl2
+ GOTO_PLUS lbl1
+ lbl2:
+ *)
+ let regno = allocate_register_if_nullable r in
+ let lbl1 = !progpos in
+ emit_code r;
+ if regno >= 0 then emit_instr op_CHECKPROGRESS regno;
+ let pos_pushback = emit_hole() in
+ if regno >= 0 then emit_instr op_SETMARK regno;
+ emit_instr op_GOTO (displ lbl1 !progpos);
+ let lbl2 = !progpos in
+ patch_instr pos_pushback op_PUSHBACK lbl2
+ | Option r ->
+ (* Implement longest match semantics for compatibility with old Str *)
+ (* PUSHBACK lbl
+ <match r>
+ lbl:
+ *)
+ let pos_pushback = emit_hole() in
+ emit_code r;
+ let lbl = !progpos in
+ patch_instr pos_pushback op_PUSHBACK lbl
+ | Group(n, r) ->
+ if n >= 32 then failwith "too many \\(...\\) groups";
+ emit_instr op_BEGGROUP n;
+ emit_code r;
+ emit_instr op_ENDGROUP n;
+ numgroups := max !numgroups (n+1)
+ | Refgroup n ->
+ emit_instr op_REFGROUP n
+ | Bol ->
+ emit_instr op_BOL 0
+ | Eol ->
+ emit_instr op_EOL 0
+ | Wordboundary ->
+ emit_instr op_WORDBOUNDARY 0
+
+ and emit_seq_code = function
+ [] -> ()
+ | Star(Char _ | CharClass _ as r) :: rl
+ when disjoint_modulo_case (first r) (first_seq rl) ->
+ emit_instr op_SIMPLESTAR (cpool_index (charclass_of_regexp fold_case r));
+ emit_seq_code rl
+ | Plus(Char _ | CharClass _ as r) :: rl
+ when disjoint_modulo_case (first r) (first_seq rl) ->
+ emit_instr op_SIMPLEPLUS (cpool_index (charclass_of_regexp fold_case r));
+ emit_seq_code rl
+ | Option(Char _ | CharClass _ as r) :: rl
+ when disjoint_modulo_case (first r) (first_seq rl) ->
+ emit_instr op_SIMPLEOPT (cpool_index (charclass_of_regexp fold_case r));
+ emit_seq_code rl
+ | r :: rl ->
+ emit_code r;
+ emit_seq_code rl
+
+ and disjoint_modulo_case c1 c2 =
+ if fold_case
+ then Charset.disjoint (Charset.fold_case c1) (Charset.fold_case c2)
+ else Charset.disjoint c1 c2
+ in
+
+ emit_code re;
+ emit_instr op_ACCEPT 0;
+ let start = first re in
+ let start' = if fold_case then Charset.fold_case start else start in
+ let start_pos =
+ if start = Charset.full
+ then -1
+ else cpool_index (Charset.expand start') in
+ let constantpool = Array.make !cpoolpos "" in
+ StringMap.iter (fun str idx -> constantpool.(idx) <- str) !cpool;
+ { prog = Array.sub !prog 0 !progpos;
+ cpool = constantpool;
+ normtable = if fold_case then fold_case_table else "";
+ numgroups = !numgroups;
+ numregisters = !numregs;
+ startchars = start_pos }
+
+(** Parsing of a regular expression *)
+
+(* Efficient buffering of sequences *)
+
+module SeqBuffer = struct
+
+ type t = { sb_chars: Buffer.t; mutable sb_next: re_syntax list }
+
+ let create() = { sb_chars = Buffer.create 16; sb_next = [] }
+
+ let flush buf =
+ let s = Buffer.contents buf.sb_chars in
+ Buffer.clear buf.sb_chars;
+ match String.length s with
+ 0 -> ()
+ | 1 -> buf.sb_next <- Char s.[0] :: buf.sb_next
+ | _ -> buf.sb_next <- String s :: buf.sb_next
+
+ let add buf re =
+ match re with
+ Char c -> Buffer.add_char buf.sb_chars c
+ | _ -> flush buf; buf.sb_next <- re :: buf.sb_next
+
+ let extract buf =
+ flush buf; Seq(List.rev buf.sb_next)
+
+end
+
+(* The character class corresponding to `.' *)
+
+let dotclass = Charset.complement (Charset.singleton '\n')
+
+(* Parse a regular expression *)
+
+let parse s =
+ let len = String.length s in
+ let group_counter = ref 1 in
+
+ let rec regexp0 i =
+ let (r, j) = regexp1 i in
+ regexp0cont r j
+ and regexp0cont r1 i =
+ if i + 2 <= len && s.[i] = '\\' && s.[i+1] = '|' then
+ let (r2, j) = regexp1 (i+2) in
+ regexp0cont (Alt(r1, r2)) j
+ else
+ (r1, i)
+ and regexp1 i =
+ regexp1cont (SeqBuffer.create()) i
+ and regexp1cont sb i =
+ if i >= len
+ || i + 2 <= len && s.[i] = '\\' && (let c = s.[i+1] in c = '|' || c = ')')
+ then
+ (SeqBuffer.extract sb, i)
+ else
+ let (r, j) = regexp2 i in
+ SeqBuffer.add sb r;
+ regexp1cont sb j
+ and regexp2 i =
+ let (r, j) = regexp3 i in
+ regexp2cont r j
+ and regexp2cont r i =
+ if i >= len then (r, i) else
+ match s.[i] with
+ '?' -> regexp2cont (Option r) (i+1)
+ | '*' -> regexp2cont (Star r) (i+1)
+ | '+' -> regexp2cont (Plus r) (i+1)
+ | _ -> (r, i)
+ and regexp3 i =
+ match s.[i] with
+ '\\' -> regexpbackslash (i+1)
+ | '[' -> let (c, compl, j) = regexpclass0 (i+1) in
+ (CharClass(c, compl), j)
+ | '^' -> (Bol, i+1)
+ | '$' -> (Eol, i+1)
+ | '.' -> (CharClass(dotclass, false), i+1)
+ | c -> (Char c, i+1)
+ and regexpbackslash i =
+ if i >= len then (Char '\\', i) else
+ match s.[i] with
+ '|' | ')' ->
+ assert false
+ | '(' ->
+ let group_no = !group_counter in
+ if group_no < 32 then incr group_counter;
+ let (r, j) = regexp0 (i+1) in
+ if j + 1 < len && s.[j] = '\\' && s.[j+1] = ')' then
+ if group_no < 32
+ then (Group(group_no, r), j + 2)
+ else (r, j + 2)
+ else
+ failwith "\\( group not closed by \\)"
+ | '1' .. '9' as c ->
+ (Refgroup(Char.code c - 48), i + 1)
+ | 'b' ->
+ (Wordboundary, i + 1)
+ | c ->
+ (Char c, i + 1)
+ and regexpclass0 i =
+ if i < len && s.[i] = '^'
+ then let (c, j) = regexpclass1 (i+1) in (c, true, j)
+ else let (c, j) = regexpclass1 i in (c, false, j)
+ and regexpclass1 i =
+ let c = Charset.make_empty() in
+ let j = regexpclass2 c i i in
+ (c, j)
+ and regexpclass2 c start i =
+ if i >= len then failwith "[ class not closed by ]";
+ if s.[i] = ']' && i > start then i+1 else begin
+ let c1 = s.[i] in
+ if i+2 < len && s.[i+1] = '-' && s.[i+2] <> ']' then begin
+ let c2 = s.[i+2] in
+ Charset.add_range c c1 c2;
+ regexpclass2 c start (i+3)
+ end else begin
+ Charset.add c c1;
+ regexpclass2 c start (i+1)
+ end
+ end in
+
+ let (r, j) = regexp0 0 in
+ if j = len then r else failwith "spurious \\) in regular expression"
+
+(** Parsing and compilation *)
+
+let regexp e = compile false (parse e)
+
+let regexp_case_fold e = compile true (parse e)
+
+let quote s =
+ let len = String.length s in
+ let buf = String.create (2 * len) in
+ let pos = ref 0 in
+ for i = 0 to len - 1 do
+ match s.[i] with
+ '[' | ']' | '*' | '.' | '\\' | '?' | '+' | '^' | '$' as c ->
+ buf.[!pos] <- '\\'; buf.[!pos + 1] <- c; pos := !pos + 2
+ | c ->
+ buf.[!pos] <- c; pos := !pos + 1
+ done;
+ String.sub buf 0 !pos
+
+let regexp_string s = compile false (String s)
+
+let regexp_string_case_fold s = compile true (String s)
+
+(** Matching functions **)
+
+external re_string_match: regexp -> string -> int -> int array
+ = "re_string_match"
+external re_partial_match: regexp -> string -> int -> int array
+ = "re_partial_match"
+external re_search_forward: regexp -> string -> int -> int array
+ = "re_search_forward"
+external re_search_backward: regexp -> string -> int -> int array
+ = "re_search_backward"
+
+let last_search_result = ref [||]
+
+let string_match re s pos =
+ let res = re_string_match re s pos in
+ last_search_result := res;
+ Array.length res > 0
+
+let string_partial_match re s pos =
+ let res = re_partial_match re s pos in
+ last_search_result := res;
+ Array.length res > 0
+
+let search_forward re s pos =
+ let res = re_search_forward re s pos in
+ last_search_result := res;
+ if Array.length res = 0 then raise Not_found else res.(0)
+
+let search_backward re s pos =
+ let res = re_search_backward re s pos in
+ last_search_result := res;
+ if Array.length res = 0 then raise Not_found else res.(0)
+
+let group_beginning n =
+ let n2 = n + n in
+ if n < 0 || n2 >= Array.length !last_search_result then
+ invalid_arg "Str.group_beginning"
+ else
+ let pos = !last_search_result.(n2) in
+ if pos = -1 then raise Not_found else pos
+
+let group_end n =
+ let n2 = n + n in
+ if n < 0 || n2 >= Array.length !last_search_result then
+ invalid_arg "Str.group_end"
+ else
+ let pos = !last_search_result.(n2 + 1) in
+ if pos = -1 then raise Not_found else pos
+
+let matched_group n txt =
+ let n2 = n + n in
+ if n < 0 || n2 >= Array.length !last_search_result then
+ invalid_arg "Str.matched_group"
+ else
+ let b = !last_search_result.(n2)
+ and e = !last_search_result.(n2 + 1) in
+ if b = -1 then raise Not_found else String.sub txt b (e - b)
+
+let match_beginning () = group_beginning 0
+and match_end () = group_end 0
+and matched_string txt = matched_group 0 txt
+
+(** Replacement **)
+
+external re_replacement_text: string -> int array -> string -> string
+ = "re_replacement_text"
+
+let replace_matched repl matched =
+ re_replacement_text repl !last_search_result matched
+
+let substitute_first expr repl_fun text =
+ try
+ let pos = search_forward expr text 0 in
+ String.concat "" [string_before text pos;
+ repl_fun text;
+ string_after text (match_end())]
+ with Not_found ->
+ text
+
+let opt_search_forward re s pos =
+ try Some(search_forward re s pos) with Not_found -> None
+
+let global_substitute expr repl_fun text =
+ let rec replace accu start last_was_empty =
+ let startpos = if last_was_empty then start + 1 else start in
+ if startpos > String.length text then
+ string_after text start :: accu
+ else
+ match opt_search_forward expr text startpos with
+ | None ->
+ string_after text start :: accu
+ | Some pos ->
+ let end_pos = match_end() in
+ let repl_text = repl_fun text in
+ replace (repl_text :: String.sub text start (pos-start) :: accu)
+ end_pos (end_pos = pos)
+ in
+ String.concat "" (List.rev (replace [] 0 false))
+
+let global_replace expr repl text =
+ global_substitute expr (replace_matched repl) text
+and replace_first expr repl text =
+ substitute_first expr (replace_matched repl) text
+
+(** Splitting *)
+
+let opt_search_forward_progress expr text start =
+ match opt_search_forward expr text start with
+ | None -> None
+ | Some pos ->
+ if match_end() > start then
+ Some pos
+ else if start < String.length text then
+ opt_search_forward expr text (start + 1)
+ else None
+
+let bounded_split expr text num =
+ let start =
+ if string_match expr text 0 then match_end() else 0 in
+ let rec split accu start n =
+ if start >= String.length text then accu else
+ if n = 1 then string_after text start :: accu else
+ match opt_search_forward_progress expr text start with
+ | None ->
+ string_after text start :: accu
+ | Some pos ->
+ split (String.sub text start (pos-start) :: accu)
+ (match_end()) (n-1)
+ in
+ List.rev (split [] start num)
+
+let split expr text = bounded_split expr text 0
+
+let bounded_split_delim expr text num =
+ let rec split accu start n =
+ if start > String.length text then accu else
+ if n = 1 then string_after text start :: accu else
+ match opt_search_forward_progress expr text start with
+ | None ->
+ string_after text start :: accu
+ | Some pos ->
+ split (String.sub text start (pos-start) :: accu)
+ (match_end()) (n-1)
+ in
+ if text = "" then [] else List.rev (split [] 0 num)
+
+let split_delim expr text = bounded_split_delim expr text 0
+
+type split_result = Text of string | Delim of string
+
+let bounded_full_split expr text num =
+ let rec split accu start n =
+ if start >= String.length text then accu else
+ if n = 1 then Text(string_after text start) :: accu else
+ match opt_search_forward_progress expr text start with
+ | None ->
+ Text(string_after text start) :: accu
+ | Some pos ->
+ let s = matched_string text in
+ if pos > start then
+ split (Delim(s) :: Text(String.sub text start (pos-start)) :: accu)
+ (match_end()) (n-1)
+ else
+ split (Delim(s) :: accu)
+ (match_end()) (n-1)
+ in
+ List.rev (split [] 0 num)
+
+let full_split expr text = bounded_full_split expr text 0