My agentic slop goes here. Not intended for anyone else!
at main 5.2 kB view raw
1(* XML *) 2 3module XML = struct 4 include Syndic_xml 5 6 type node = pos * tag * t list 7 8 let xmlbase_tag = (Xmlm.ns_xml, "base") 9 10 let xmlbase_of_attr ~xmlbase attr = 11 try 12 let new_base = List.assoc xmlbase_tag attr in 13 Some (Syndic_xml.resolve ~xmlbase (Uri.of_string new_base)) 14 with Not_found -> xmlbase 15 16 let generate_catcher ?(namespaces = [""]) ?(attr_producer = []) 17 ?(data_producer = []) ?leaf_producer maker = 18 let in_namespaces ((prefix, _), _) = List.mem prefix namespaces in 19 let get_attr_name (((_prefix, name), _) : Xmlm.attribute) = name in 20 let get_attr_value ((_, value) : Xmlm.attribute) = value in 21 let get_tag_name (((_prefix, name), _) : tag) = name in 22 let get_attrs ((_, attrs) : tag) = attrs in 23 let get_producer name map = 24 try Some (List.assoc name map) with _ -> None 25 in 26 let rec catch_attr ~xmlbase acc pos = function 27 | attr :: r -> ( 28 match get_producer (get_attr_name attr) attr_producer with 29 | Some f when in_namespaces attr -> 30 let acc = f ~xmlbase (get_attr_value attr) :: acc in 31 catch_attr ~xmlbase acc pos r 32 | _ -> catch_attr ~xmlbase acc pos r ) 33 | [] -> acc 34 in 35 let rec catch_datas ~xmlbase acc = function 36 | Node (pos, tag, datas) :: r -> ( 37 match get_producer (get_tag_name tag) data_producer with 38 | Some f when in_namespaces tag -> 39 let acc = f ~xmlbase (pos, tag, datas) :: acc in 40 catch_datas ~xmlbase acc r 41 | _ -> catch_datas ~xmlbase acc r ) 42 | Data (pos, str) :: r -> ( 43 match leaf_producer with 44 | Some f -> catch_datas ~xmlbase (f ~xmlbase pos str :: acc) r 45 | None -> catch_datas ~xmlbase acc r ) 46 | [] -> acc 47 in 48 let generate ~xmlbase ((pos, tag, datas) : node) = 49 (* The spec says that "The base URI for a URI reference appearing in any 50 other attribute value, including default attribute values, is the base 51 URI of the element bearing the attribute" so get xml:base first. *) 52 let xmlbase = xmlbase_of_attr ~xmlbase (get_attrs tag) in 53 let acc = catch_attr ~xmlbase [] pos (get_attrs tag) in 54 maker ~pos (catch_datas ~xmlbase acc datas) 55 in 56 generate 57 58 let dummy_of_xml ~ctor = 59 let leaf_producer ~xmlbase _pos data = ctor ~xmlbase data in 60 let head ~pos:_ = function [] -> ctor ~xmlbase:None "" | x :: _ -> x in 61 generate_catcher ~leaf_producer head 62end 63 64(* Util *) 65 66module Util = struct 67 let find f l = try Some (List.find f l) with Not_found -> None 68 69 exception Found of XML.t 70 71 let recursive_find f root = 72 let rec aux = function 73 | [] -> None 74 | x :: _ when f x -> raise (Found x) 75 | XML.Node (_, _, x) :: r -> ( 76 aux x 77 |> function 78 | Some x -> raise (Found x) (* assert false ? *) | None -> aux r ) 79 | XML.Data _ :: r -> aux r 80 in 81 try aux [root] with Found x -> Some x | _ -> None 82 83 let rec filter_map l f = 84 match l with 85 | [] -> [] 86 | x :: tl -> ( 87 match f x with None -> filter_map tl f | Some x -> x :: filter_map tl f ) 88 89 let rec take l n = 90 match l with 91 | [] -> [] 92 | e :: tl -> if n > 0 then e :: take tl (n - 1) else [] 93 94 let tag_is (((_prefix, name), _attrs) : XML.tag) = ( = ) name 95 let attr_is (((_prefix, name), _value) : Xmlm.attribute) = ( = ) name 96 let datas_has_leaf = List.exists (function XML.Data _ -> true | _ -> false) 97 98 let get_leaf l = 99 match find (function XML.Data _ -> true | _ -> false) l with 100 | Some (XML.Data (_, s)) -> s 101 | None -> "" (* Return empty string for empty elements instead of raising Not_found *) 102 | _ -> raise Not_found 103 104 let get_attrs ((_, attrs) : XML.tag) = attrs 105 let get_value ((_, value) : Xmlm.attribute) = value 106 let get_attr_name (((_prefix, name), _) : Xmlm.attribute) = name 107 let get_tag_name (((_prefix, name), _) : XML.tag) = name 108 let is_space c = c = ' ' || c = '\t' || c = '\n' || c = '\r' 109 110 let only_whitespace s = 111 let r = ref true in 112 let i = ref 0 and len = String.length s in 113 while !r && !i < len do 114 r := is_space s.[!i] ; 115 incr i 116 done ; 117 !r 118 119 (* Output feeds to XML *) 120 121 let add_attr name v_opt attr = 122 match v_opt with None | Some "" -> attr | Some v -> (name, v) :: attr 123 124 let add_attr_uri name v_opt attr = 125 match v_opt with None -> attr | Some v -> (name, Uri.to_string v) :: attr 126 127 let tag name = (("", name), []) 128 let dummy_pos = (0, 0) 129 130 (* Do smarter positions make sense? *) 131 132 let node_data tag content = 133 XML.Node (dummy_pos, tag, [XML.Data (dummy_pos, content)]) 134 135 let node_uri tag uri = node_data tag (Uri.to_string uri) 136 137 let add_node_data tag c nodes = 138 match c with 139 | None -> nodes 140 | Some content -> node_data tag content :: nodes 141 142 let add_node_uri tag c nodes = 143 match c with 144 | None -> nodes 145 | Some uri -> node_data tag (Uri.to_string uri) :: nodes 146 147 (* Add to [nodes] those coming from mapping [f] on [els] *) 148 let add_nodes_rev_map f els nodes = 149 List.fold_left (fun nodes el -> f el :: nodes) nodes els 150 151 let add_node_option f op nodes = 152 match op with None -> nodes | Some v -> f v :: nodes 153end