package forester

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file Xmlns_effect.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
open Forester_core

module Xmlns_map =
struct
  type t =
    {prefix_to_xmlns : string String_map.t;
     xmlns_to_prefixes : string list String_map.t}

  let empty =
    {prefix_to_xmlns = String_map.empty;
     xmlns_to_prefixes = String_map.empty}

  let assoc ~prefix ~xmlns env =
    {prefix_to_xmlns = String_map.add prefix xmlns env.prefix_to_xmlns;
     xmlns_to_prefixes = String_map.add_to_list xmlns prefix env.xmlns_to_prefixes}
end


module Make_writer (Elt : sig type t end) =
struct
  type _ Effect.t += Yield : Elt.t -> unit Effect.t

  let yield x = Effect.perform (Yield x)

  let run f =
    let open Effect.Deep in
    try_with (fun () -> let r = f () in [], r) ()
      { effc =
          fun (type a) (eff : a Effect.t) ->
            match eff with
            | Yield x -> Option.some @@ fun (k : (a, _) continuation) ->
              let xs, r = continue k () in
              x::xs, r
            | _ -> None }

  let register_printer f = Printexc.register_printer @@ function
    | Effect.Unhandled (Yield elt) -> f (`Yield elt)
    | _ -> None

  let () = register_printer @@ fun _ -> Some "Unhandled effect; use Make_writer.run"
end

module Make () =
struct
  type xmlns_attr = {prefix: string; xmlns: string}

  module E = Algaeff.State.Make (Xmlns_map)
  module Decls = Make_writer (struct type t = xmlns_attr end)

  let find_xmlns_for_prefix prefix =
    let env = E.get ()  in
    String_map.find_opt prefix env.prefix_to_xmlns

  let rec normalise_qname (qname : xml_qname) =
    let scope = E.get () in
    match qname.xmlns with
    | None ->
      begin
        match String_map.find_opt qname.prefix scope.prefix_to_xmlns with
        | None -> qname
        | Some xmlns -> {qname with xmlns = Some xmlns}
      end

    | Some xmlns ->
      begin
        match
          String_map.find_opt qname.prefix scope.prefix_to_xmlns,
          String_map.find_opt xmlns scope.xmlns_to_prefixes
        with
        | None, (None | Some []) ->
          E.modify (Xmlns_map.assoc ~prefix:qname.prefix ~xmlns);
          Decls.yield {prefix = qname.prefix; xmlns};
          qname
        | Some xmlns', Some prefixes ->
          if xmlns' = xmlns && List.mem qname.prefix prefixes then
            qname
          else
            normalise_qname {qname with prefix = qname.prefix ^ "_"}
        | _, Some (prefix' :: _) ->
          {qname with prefix = prefix'}
        | Some xmlns', None ->
          normalise_qname {qname with prefix = qname.prefix ^ "_"}
      end

  let within_scope kont =
    let old_scope = E.get () in
    let added, r =
      Decls.run @@ fun () ->
      kont ()
    in
    E.set old_scope;
    added, r

  let run ~reserved kont =
    let init =
      let alg env {prefix; xmlns} =
        Xmlns_map.assoc ~prefix ~xmlns env
      in
      List.fold_left alg Xmlns_map.empty reserved
    in
    E.run ~init kont
end
OCaml

Innovation. Community. Security.