diff --git a/examples/as_pattern.ml b/examples/as_pattern.ml new file mode 100644 index 00000000..f86805c9 --- /dev/null +++ b/examples/as_pattern.ml @@ -0,0 +1,59 @@ +let string_of_uchars us = + String.of_seq (Array.to_seq (Array.map Uchar.to_char us)) + +let rec token1 buf = + match%sedlex buf with + | eof -> print_endline "\tEnd" + | white_space -> print_endline "\tWhitespace"; token1 buf + | (Plus ('a' .. 'z' | 'A' .. 'Z')) as text -> + print_endline (string_of_uchars text); token1 buf + | (',' | '.') as x -> + print_endline (string_of_uchars x); token1 buf + | any -> print_endline "other"; token1 buf + | _ -> failwith "Internal failure: Reached impossible place" + +let rec token2 buf = + match%sedlex buf with + | eof -> print_endline "\tEnd" + | white_space -> print_endline "\tWhitespace"; token2 buf + | "a" as x -> + print_endline (string_of_uchars x); token2 buf + | "b" as x -> + print_endline (string_of_uchars x); token2 buf + | "cde" as x -> + print_endline (string_of_uchars x); token2 buf + | ("f" as x), "g" -> + print_endline (string_of_uchars x); token2 buf + | "h", ("i" as x) -> + print_endline (string_of_uchars x); token2 buf + | ("j" as x | "k" as x) -> + print_endline (string_of_uchars x); token2 buf + | ("l" | "m") as x -> + print_endline (string_of_uchars x); token2 buf + | (Star "n" as x), (Star "o" as y) -> + print_endline (string_of_uchars x^"+"^string_of_uchars y); token2 buf + | (Plus "p" as x), (Plus "q" as y) -> + print_endline (string_of_uchars x^"+"^string_of_uchars y); token2 buf + | any -> print_endline "other"; token1 buf + | _ -> failwith "Internal failure: Reached impossible place" + + +let () = + token1 (Sedlexing.Utf8.from_string "It takes all the running you can do, to keep in the same place."); + token2 (Sedlexing.Utf8.from_string "a b cde fg hi j k l m n o nn no oo nnn nno noo ooo pq ppq pqq pppq ppqq pqqq") + + + +(* +let rec token buf = + match%sedlex buf with + | eof -> print_endline "\tEnd" + | white_space -> print_endline "\tWhitespace"; token buf + | (Star 'a' as a), ('b' as b) -> print_endline (string_of_uchars a^"\t"^string_of_uchars b); token buf + | any -> print_endline "Other"; token buf + | _ -> failwith "Internal failure: Reached impossible place" + +let () = + let lexbuf = Sedlexing.Utf8.from_string "b ab aab aaab aaaab" in + token lexbuf +*) diff --git a/examples/dune b/examples/dune index 2eb6eaaf..821017ab 100644 --- a/examples/dune +++ b/examples/dune @@ -1,5 +1,5 @@ (executables - (names tokenizer regressions complement subtraction repeat performance) + (names tokenizer regressions complement subtraction repeat as_pattern performance) (libraries sedlex sedlex_ppx) (preprocess (pps sedlex.ppx)) @@ -40,6 +40,13 @@ (action (run %{<}))) +(rule + (alias runtest) + (deps + (:< as_pattern.exe)) + (action + (run %{<}))) + (rule (alias runtest) (deps diff --git a/src/syntax/ppx_sedlex.ml b/src/syntax/ppx_sedlex.ml index e184313d..536f4b81 100644 --- a/src/syntax/ppx_sedlex.ml +++ b/src/syntax/ppx_sedlex.ml @@ -175,6 +175,9 @@ let partition (name, p) = [%e body] ] +(* Alias offset slot counter *) +let alias_slot_counter = ref (-1) + (* Code generation for the automata *) let best_final final = @@ -184,27 +187,143 @@ let best_final final = done; !fin +let gen_alisas_slots auto = + let loc = default_loc in + let module S = Set.Make(struct type t = string let compare = compare end) in + let slots = ref [] in + let seen = ref S.empty in + Array.iter (fun (trans, _, nacts) -> + List.iter (function + | `may_init_capture_slot slot | `may_finish_capture_slot slot -> + if S.mem slot !seen then () + else slots := slot :: !slots; seen := S.add slot !seen + | _ -> failwith "can't generate aliases: unsupported action") nacts; + Array.iter (fun (_, _, tacts) -> + List.iter (function + | `step_capture_slot slot -> + if S.mem slot !seen then () + else slots := slot :: !slots; seen := S.add slot !seen + | _ -> failwith "can't generate aliases: unsupported action") tacts) trans) auto; + List.map (fun slot -> value_binding ~loc ~pat:(pvar ~loc slot) ~expr:[%expr ref (None, None, false, [])]) !slots + +let gen_aliases re = + let loc = default_loc in + List.map (fun name -> + let slot = Sedlex.get_slot name re in + value_binding ~loc + ~pat:(pvar ~loc name) + ~expr:([%expr (function + | (_, _, _, x :: _) -> x + | _ -> [||]) ![%e evar ~loc slot]])) + (Sedlex.get_names re) + let state_fun state = Printf.sprintf "__sedlex_state_%i" state -let call_state lexbuf auto state = - let (trans, final) = auto.(state) in +let eaction ~loc lexbuf = + let lexbuf = evar ~loc lexbuf in + let init_pos = "__sedlex_eaction_init_pos" in + let curr_pos = "__sedlex_eaction_curr_pos" in + let fin_flag = "__sedlex_eaction_fin_flag" in + let result = "__sedlex_eaction_results" in + let init_pos_e = evar ~loc init_pos in + let curr_pos_e = evar ~loc curr_pos in + let fin_flag_e = evar ~loc fin_flag in + let result_e = evar ~loc result in + let init_pos_p = pvar ~loc init_pos in + let curr_pos_p = pvar ~loc curr_pos in + let fin_flag_p = pvar ~loc fin_flag in + let result_p = pvar ~loc result in + let get_pos = [%expr (snd (Sedlexing.loc [%e lexbuf]))] in + let get_sub init curr = + [%expr Sedlexing.sub_lexeme + [%e lexbuf] + ([%e init] - (fst (Sedlexing.loc [%e lexbuf]))) + ([%e curr] - [%e init])] in + function + | `may_init_capture_slot slot -> + let slot = evar ~loc slot in + [%expr match (! [%e slot]) with + | (None, None, [%p fin_flag_p], [%p result_p]) -> + [%e slot] := (Some [%e get_pos], None, [%e fin_flag_e], [%e result_e]) + | (None, Some [%p curr_pos_p], [%p fin_flag_p], [%p result_p]) -> + [%e slot] := (Some [%e get_pos], Some [%e curr_pos_e], [%e fin_flag_e], [%e result_e]) + | (Some [%p init_pos_p], None, [%p fin_flag_p], [%p result_p]) -> + [%e slot] := (Some [%e get_pos], None, [%e fin_flag_e], [%e result_e]) + | (Some [%p init_pos_p], Some [%p curr_pos_p], [%p fin_flag_p], [%p result_p]) -> + if [%e get_pos] = [%e curr_pos_e] + 1 then + [%e slot] := (Some [%e init_pos_e], Some [%e get_pos], [%e fin_flag_e], [%e result_e]) + else ()] + | `step_capture_slot slot -> + let slot = evar ~loc slot in + [%expr match (! [%e slot]) with + | (None, [%p curr_pos_p], _, [%p result_p]) -> + [%e slot] := (None, [%e curr_pos_e], false, [%e result_e]) + | (Some [%p init_pos_p], None, _, [%p result_p]) -> + if [%e get_pos] - [%e init_pos_e] < 2 then + [%e slot] := (Some [%e init_pos_e], Some [%e get_pos], false, [%e result_e]) + else () + | (Some [%p init_pos_p], Some [%p curr_pos_p], _, [%p result_p]) -> + if [%e get_pos] = [%e curr_pos_e] + 1 || [%e get_pos] = [%e init_pos_e] then + [%e slot] := (Some [%e init_pos_e], Some [%e get_pos], false, [%e result_e]) + else ()] + | `may_finish_capture_slot slot -> + let slot = evar ~loc slot in + [%expr match (! [%e slot]) with + | (None, None, _, [%p result_p]) -> () + | (Some [%p init_pos_p], None, _, [%p result_p]) -> + [%e slot] := (Some [%e init_pos_e], None, true, [||] :: [%e result_e]) + | (Some [%p init_pos_p], Some [%p curr_pos_p], [%p fin_flag_p], [%p result_p]) -> + if [%e get_pos] = [%e curr_pos_e] && not [%e fin_flag_e] then + [%e slot] := (Some [%e init_pos_e], Some [%e curr_pos_e], true, [%e get_sub init_pos_e curr_pos_e] :: [%e result_e]) + else + [%e slot] := (Some [%e init_pos_e], Some [%e curr_pos_e], true, [%e result_e]) + | ([%p init_pos_p], [%p curr_pos_p], _, [%p result_p]) -> + [%e slot] := ([%e init_pos_e], [%e curr_pos_e], true, [%e result_e])] + | `save_offset _ -> failwith "unsupported action" + +let reorder_actions : Sedlex.generic_action list -> Sedlex.generic_action list = fun acts -> + let int_of_action = function + | `may_init_capture_slot _ -> 0 + | `step_capture_slot _ -> 1 + | `may_finish_capture_slot _ -> 2 + | `save_offset _ -> 3 + | _ -> failwith "unsupported action" in + List.sort (fun a1 a2 -> compare (int_of_action a1) (int_of_action a2)) acts + +let call_state lexbuf auto state (trans_acts : Sedlex.transition_action list) = + let loc = default_loc in + let (trans, final, (node_acts : Sedlex.node_action list)) = auto.(state) in + let acts : Sedlex.generic_action list = + (trans_acts :> Sedlex.generic_action list) + @ (node_acts :> Sedlex.generic_action list) in + let acts = acts |> reorder_actions |> List.map (eaction ~loc lexbuf) in + let trans_acts = trans_acts |> List.map (eaction ~loc lexbuf) in if Array.length trans = 0 then match best_final final with - | Some i -> eint ~loc:default_loc i + | Some i -> esequence ~loc (acts @ [eint ~loc i]) | None -> assert false - else appfun (state_fun state) [evar ~loc:default_loc lexbuf] + else esequence ~loc (trans_acts @ [appfun (state_fun state) [evar ~loc lexbuf]]) -let gen_state lexbuf auto i (trans, final) = +let gen_state lexbuf auto i (trans, final, actions) = let loc = default_loc in - let partition = Array.map fst trans in - let cases = Array.mapi (fun i (_, j) -> case ~lhs:(pint ~loc i) ~guard:None ~rhs:(call_state lexbuf auto j)) trans in + let partition = Array.map (fun (f,_,_) -> f) trans in + let actions = + (actions :> Sedlex.generic_action list) + |> reorder_actions + |> List.map (eaction ~loc lexbuf) in + let cases = Array.mapi (fun i (_, j, acts) -> + case ~lhs:(pint ~loc i) ~guard:None ~rhs:(call_state lexbuf auto j acts)) trans in let cases = Array.to_list cases in let body () = - pexp_match ~loc - (appfun (partition_name partition) [[%expr Sedlexing.__private__next_int [%e evar ~loc lexbuf]]]) - (cases @ [case ~lhs:[%pat? _] ~guard:None ~rhs:[%expr Sedlexing.backtrack [%e evar ~loc lexbuf]]]) + esequence ~loc + (actions @ + [pexp_match ~loc + (appfun (partition_name partition) [[%expr Sedlexing.__private__next_int [%e evar ~loc lexbuf]]]) + (cases @ [case ~lhs:[%pat? _] ~guard:None ~rhs:[%expr Sedlexing.backtrack [%e evar ~loc lexbuf]]])]) in - let ret body = [ value_binding ~loc ~pat:(pvar ~loc (state_fun i)) ~expr:(pexp_function ~loc [case ~lhs:(pvar ~loc lexbuf) ~guard:None ~rhs:body]) ] in + let ret body = [ value_binding ~loc + ~pat:(pvar ~loc (state_fun i)) + ~expr:(pexp_function ~loc [case ~lhs:(pvar ~loc lexbuf) ~guard:None ~rhs:body]) ] in match best_final final with | None -> ret (body ()) | Some _ when Array.length trans = 0 -> [] @@ -215,10 +334,10 @@ let gen_recflag auto = in states with no further transitions. *) try Array.iter - (fun (trans_i, _) -> + (fun (trans_i, _, _) -> Array.iter - (fun (_, j) -> - let (trans_j, _) = auto.(j) in + (fun (_, j, _) -> + let (trans_j, _, _) = auto.(j) in if Array.length trans_j > 0 then raise Exit) trans_i) auto; @@ -230,16 +349,26 @@ let gen_definition lexbuf l error = let loc = default_loc in let brs = Array.of_list l in let auto = Sedlex.compile (Array.map fst brs) in - let cases = Array.to_list (Array.mapi (fun i (_, e) -> case ~lhs:(pint ~loc i) ~guard:None ~rhs:e) brs) in + let alias_slots = gen_alisas_slots auto in + let aliases = Array.map (fun (re, _) -> gen_aliases re) brs in + let cases = Array.to_list (Array.mapi (fun i (_, e) -> + let expression = match aliases.(i) with + | [] -> e + | aliases -> pexp_let ~loc Nonrecursive aliases e in + case ~lhs:(pint ~loc i) ~guard:None ~rhs:expression) brs) in let states = Array.mapi (gen_state lexbuf auto) auto in let states = List.flatten (Array.to_list states) in - pexp_let ~loc (gen_recflag auto) states - (pexp_sequence ~loc - [%expr Sedlexing.start [%e evar ~loc lexbuf]] - (pexp_match ~loc (appfun (state_fun 0) [evar ~loc lexbuf]) - (cases @ [case ~lhs:(ppat_any ~loc) ~guard:None ~rhs:error]) - ) - ) + let body () = + pexp_let ~loc (gen_recflag auto) states + (pexp_sequence ~loc + [%expr Sedlexing.start [%e evar ~loc lexbuf]] + (pexp_match ~loc (appfun (state_fun 0) [evar ~loc lexbuf]) + (cases @ [case ~lhs:(ppat_any ~loc) ~guard:None ~rhs:error]) + ) + ) in + match alias_slots with + | [] -> body () + | _ -> pexp_let ~loc Nonrecursive alias_slots (body ()) (* Lexer specification parser *) @@ -353,12 +482,29 @@ let regexp_of_pattern env = with Not_found -> err p.ppat_loc (Printf.sprintf "unbound regexp %s" x) end + | Ppat_alias (pat, {txt=var}) -> + incr alias_slot_counter; + let capture_slot_var = "__sedlex_"^var^"_capture_slot"^(string_of_int !alias_slot_counter) in + (* let begin_offset_slot_var = "__sedlex_"^var^"_begin_offset_"^(string_of_int !alias_slot_counter) in *) + (* let end_offset_slot_var = "__sedlex_"^var^"_end_offset_"^(string_of_int !alias_slot_counter) in *) + aux pat + (* |> Sedlex.set_slots var (begin_offset_slot_var, end_offset_slot_var) *) + |> Sedlex.set_slot var capture_slot_var + |> Sedlex.add_transition_action_to_all_internal_transitions (`step_capture_slot capture_slot_var) + + (* XXX *) + |> Sedlex.set_pre_action (`may_init_capture_slot capture_slot_var) + |> Sedlex.set_post_action (`may_finish_capture_slot capture_slot_var) + + + (* |> Sedlex.set_post_action (`save_offset (Sedlex.Save_end_offset_assign end_offset_slot_var)) *) + (* |> Sedlex.set_pre_action (`save_offset (Sedlex.Save_begin_offset_assign begin_offset_slot_var)) *) + | _ -> - err p.ppat_loc "this pattern is not a valid regexp" + err p.ppat_loc "this pattern is not a valid regexp" in aux - let previous = ref [] let regexps = ref [] let should_set_cookies = ref false diff --git a/src/syntax/sedlex.ml b/src/syntax/sedlex.ml index aeb42d36..4534891a 100644 --- a/src/syntax/sedlex.ml +++ b/src/syntax/sedlex.ml @@ -4,70 +4,163 @@ module Cset = Sedlex_cset +module StringMap = Map.Make(struct + type t = string + let compare = compare +end) + (* NFA *) type node = { id : int; + mutable action : node_action list; mutable eps : node list; - mutable trans : (Cset.t * node) list; + mutable trans : (Cset.t * node * transition_action list) list; } +and node_action = [ + | `save_offset of save_offset_action + | `may_init_capture_slot of string + | `may_finish_capture_slot of string + ] +and transition_action = [`step_capture_slot of string] +and save_offset_action = + | Save_begin_offset_assign of string + | Save_end_offset_assign of string +type generic_action = [ transition_action | node_action ] (* Compilation regexp -> NFA *) -type regexp = node -> node +type regexp = { + nfa : node -> node; + named_groups : string StringMap.t; +} + +let get_names re = StringMap.fold (fun name _ acc -> name :: acc) re.named_groups [] + +let get_slot name re = + StringMap.find name re.named_groups + +let set_slot name slot re = + {re with named_groups = + StringMap.add name slot re.named_groups} + +(* XXX : consider name changing - set_ => add_ *) +let set_pre_action act re = + {re with + nfa = (fun succ -> + let init = re.nfa succ in + init.action <- act :: init.action; + init)} + +let set_post_action act re = + {re with + nfa = (fun succ -> + succ.action <- act :: succ.action; + re.nfa succ)} + +module NodeSet = Set.Make(struct + type t = node + let compare a b = compare a.id b.id + end) + +let iter_all_nodes func init = + let visited = ref NodeSet.empty in + let rec aux n = + if not (NodeSet.mem n !visited) then ( + func n; visited := NodeSet.add n !visited; + List.iter aux n.eps; + List.iter (fun (_, n, _) -> aux n) n.trans + ) in + aux init + +let add_transition_action_to_all_internal_transitions act re : regexp = + { re with + nfa = begin + let nfa0 = re.nfa in + fun succ -> + let sub : node = nfa0 succ in + iter_all_nodes (fun node -> + node.trans <- + node.trans + |> List.map (fun (c, n, acts) -> c, n, act::acts)) sub; + sub + end + } let cur_id = ref 0 let new_node () = incr cur_id; - { id = !cur_id; eps = []; trans = [] } + { id = !cur_id; action = []; eps = []; trans = [] } + +let regexp_of_nfa nfa = + {nfa = nfa; + named_groups = StringMap.empty;} -let seq r1 r2 succ = r1 (r2 succ) +let merge_named_groups g1 g2 = + StringMap.merge (fun _ s1_opt s2_opt -> + match s1_opt, s2_opt with + | Some s, None | None, Some s -> Some s + | None, None -> None + | Some _, Some _ -> failwith "duplicate named_slots with the same name.") + g1 g2 + +let seq r1 r2 = + {nfa = (fun succ -> r1.nfa (r2.nfa succ)); + named_groups = merge_named_groups r1.named_groups r2.named_groups;} let is_chars final = function - | {eps = []; trans = [c, f]} when f == final -> Some c + | {eps = []; trans = [c, f, _]} when f == final -> Some c | _ -> None -let chars c succ = - let n = new_node () in - n.trans <- [c,succ]; - n - -let alt r1 r2 succ = - let nr1 = r1 succ and nr2 = r2 succ in - match is_chars succ nr1, is_chars succ nr2 with - | Some c1, Some c2 -> chars (Cset.union c1 c2) succ - | _ -> +let chars c = regexp_of_nfa (fun succ -> let n = new_node () in - n.eps <- [r1 succ; r2 succ]; - n - -let rep r succ = - let n = new_node () in - n.eps <- [r n; succ]; - n - -let plus r succ = - let n = new_node () in - let nr = r n in - n.eps <- [nr; succ]; - nr - -let eps succ = succ (* eps for epsilon *) + n.trans <- [c,succ,[]]; + n) + +let alt r1 r2 = + {nfa = (fun succ -> + let nr1 = r1.nfa succ and nr2 = r2.nfa succ in + match is_chars succ nr1, is_chars succ nr2 with + | Some c1, Some c2 -> (chars (Cset.union c1 c2)).nfa succ + | _ -> + let n = new_node () in + n.eps <- [r1.nfa succ; r2.nfa succ]; + n); + named_groups = merge_named_groups r1.named_groups r2.named_groups;} + +let rep r = + {r with + nfa = (fun succ -> + let n = new_node () in + n.eps <- [r.nfa n; succ]; + n);} + +let plus r = + {r with + nfa = (fun succ -> + let n = new_node () in + let nr = r.nfa n in + n.eps <- [nr; succ]; + nr);} + +let eps = regexp_of_nfa (fun succ -> succ) (* eps for epsilon *) let compl r = let n = new_node () in - match is_chars n (r n) with + match is_chars n (r.nfa n) with | Some c -> - Some (chars (Cset.difference Cset.any c)) + Some {(chars (Cset.difference Cset.any c)) with + named_groups = r.named_groups;} | _ -> None let pair_op f r0 r1 = (* Construct subtract or intersection *) let n = new_node () in - let to_chars r = is_chars n (r n) in + let to_chars r = is_chars n (r.nfa n) in match to_chars r0, to_chars r1 with | Some c0, Some c1 -> - Some (chars (f c0 c1)) + Some {(chars (f c0 c1)) with + named_groups = merge_named_groups r0.named_groups r1.named_groups;} | _ -> None @@ -77,7 +170,7 @@ let intersection = pair_op Cset.intersection let compile_re re = let final = new_node () in - (re final, final) + (re.nfa final, final) (* Determinization *) @@ -89,36 +182,35 @@ let rec add_node state node = and add_nodes state nodes = List.fold_left add_node state nodes - let transition (state : state) = (* Merge transition with the same target *) let rec norm = function - | (c1, n1)::((c2, n2)::q as l) -> - if n1 == n2 then norm ((Cset.union c1 c2, n1)::q) - else (c1, n1)::(norm l) + | (c1, n1, acts1)::((c2, n2, acts2)::q as l) -> + if n1 == n2 then norm ((Cset.union c1 c2, n1, acts1@acts2)::q) + else (c1, n1, acts1)::(norm l) | l -> l in let t = List.concat (List.map (fun n -> n.trans) state) in - let t = norm (List.sort (fun (_, n1) (_, n2) -> n1.id - n2.id) t) in + let t = norm (List.sort (fun (_, n1, _) (_, n2, _) -> n1.id - n2.id) t) in (* Split char sets so as to make them disjoint *) - let split (all, t) (c0, n0) = + let split (all, t) (c0, n0, acts0) = let t = - (Cset.difference c0 all, [n0]) :: - List.map (fun (c, ns) -> (Cset.intersection c c0, n0::ns)) t @ - List.map (fun (c, ns) -> (Cset.difference c c0, ns)) t + (Cset.difference c0 all, [n0], acts0) :: + List.map (fun (c, ns, acts) -> (Cset.intersection c c0, n0::ns, acts0@acts)) t @ + List.map (fun (c, ns, acts) -> (Cset.difference c c0, ns, acts)) t in Cset.union all c0, - List.filter (fun (c, _) -> not (Cset.is_empty c)) t + List.filter (fun (c, _, _) -> not (Cset.is_empty c)) t in let (_,t) = List.fold_left split (Cset.empty,[]) t in (* Epsilon closure of targets *) - let t = List.map (fun (c, ns) -> (c, add_nodes [] ns)) t in + let t = List.map (fun (c, ns, acts) -> (c, add_nodes [] ns, acts)) t in (* Canonical ordering *) let t = Array.of_list t in - Array.sort (fun (c1, _) (c2, _) -> compare c1 c2) t; + Array.sort (fun (c1, _, _) (c2, _, _) -> compare c1 c2) t; t let compile rs = @@ -133,9 +225,13 @@ let compile rs = incr counter; Hashtbl.add states state i; let trans = transition state in - let trans = Array.map (fun (p, t) -> (p, aux t)) trans in + let trans = + Array.map + (fun (p, t, acts) -> (p, aux t, acts)) + trans in let finals = Array.map (fun (_, f) -> List.memq f state) rs in - Hashtbl.add states_def i (trans, finals); + let actions = List.concat_map (fun n -> n.action) state in + Hashtbl.add states_def i (trans, finals, actions); i in let init = ref [] in diff --git a/src/syntax/sedlex.mli b/src/syntax/sedlex.mli index d0e03926..22f884be 100644 --- a/src/syntax/sedlex.mli +++ b/src/syntax/sedlex.mli @@ -4,6 +4,24 @@ type regexp +type transition_action = [`step_capture_slot of string] +type node_action = [ + | `save_offset of save_offset_action + | `may_init_capture_slot of string + | `may_finish_capture_slot of string + ] +and save_offset_action = + | Save_begin_offset_assign of string + | Save_end_offset_assign of string +type generic_action = [ transition_action | node_action ] + +val get_names: regexp -> string list +val get_slot: string -> regexp -> string +val set_slot: string -> string -> regexp -> regexp +val set_pre_action: node_action -> regexp -> regexp +val set_post_action: node_action -> regexp -> regexp +val add_transition_action_to_all_internal_transitions : transition_action -> regexp -> regexp + val chars: Sedlex_cset.t -> regexp val seq: regexp -> regexp -> regexp val alt: regexp -> regexp -> regexp @@ -21,4 +39,6 @@ val intersection: regexp -> regexp -> regexp option (* If each argument is a single [chars] regexp, returns a regexp which matches the intersection set. Otherwise returns [None]. *) -val compile: regexp array -> ((Sedlex_cset.t * int) array * bool array) array +val compile: + regexp array -> + ((Sedlex_cset.t * int * transition_action list) array * bool array * node_action list) array