OCaml HTML5 parser/serialiser based on Python's JustHTML
1(** Reusable context/ancestor tracking for checkers.
2
3 Many checkers need to track element ancestors, depth, or maintain
4 context stacks during DOM traversal. This module provides common
5 utilities to reduce duplication across checkers.
6
7 {2 Available Trackers}
8
9 - {!Stack}: Generic stack for any context type
10 - {!Depth}: Simple integer depth counter
11 - {!Ancestors}: String-based element ancestor tracking *)
12
13(** {2 Generic Stack} *)
14
15(** Generic stack-based context tracker.
16
17 Use this when you need to track complex state at each nesting level.
18 For example, tracking whether each ancestor has certain attributes. *)
19module Stack : sig
20 type 'a t
21
22 (** Create an empty context stack. *)
23 val create : unit -> 'a t
24
25 (** Reset the stack to empty. *)
26 val reset : 'a t -> unit
27
28 (** Push a context onto the stack. *)
29 val push : 'a t -> 'a -> unit
30
31 (** Pop a context from the stack. Returns None if empty. *)
32 val pop : 'a t -> 'a option
33
34 (** Get the current (top) context without removing it. *)
35 val current : 'a t -> 'a option
36
37 (** Get current depth (number of items on stack). *)
38 val depth : 'a t -> int
39
40 (** Check if stack is empty. *)
41 val is_empty : 'a t -> bool
42
43 (** Get all ancestors (bottom to top). *)
44 val to_list : 'a t -> 'a list
45
46 (** Check if any ancestor satisfies predicate. *)
47 val exists : 'a t -> ('a -> bool) -> bool
48
49 (** Find first ancestor satisfying predicate (top to bottom). *)
50 val find : 'a t -> ('a -> bool) -> 'a option
51
52 (** Iterate over all contexts (top to bottom). *)
53 val iter : 'a t -> ('a -> unit) -> unit
54end
55
56(** {2 Depth Counter} *)
57
58(** Simple depth counter for tracking nesting level.
59
60 Use this when you only need to know if you're inside a certain
61 element type, not the full context at each level. *)
62module Depth : sig
63 type t
64
65 (** Create a depth counter starting at 0. *)
66 val create : unit -> t
67
68 (** Reset depth to 0. *)
69 val reset : t -> unit
70
71 (** Increment depth (entering element). *)
72 val enter : t -> unit
73
74 (** Decrement depth (leaving element). Returns false if was already 0. *)
75 val leave : t -> bool
76
77 (** Get current depth. *)
78 val get : t -> int
79
80 (** Check if inside (depth > 0). *)
81 val is_inside : t -> bool
82end
83
84(** {2 Ancestor Tracker} *)
85
86(** Element name stack for tracking ancestors by name.
87
88 Use this when you need to check if certain elements are ancestors,
89 but don't need complex context at each level. *)
90module Ancestors : sig
91 type t
92
93 (** Create an empty ancestor tracker. *)
94 val create : unit -> t
95
96 (** Reset to empty. *)
97 val reset : t -> unit
98
99 (** Push an element name onto the ancestor stack. *)
100 val push : t -> string -> unit
101
102 (** Pop an element from the ancestor stack. *)
103 val pop : t -> unit
104
105 (** Get the immediate parent element name. *)
106 val parent : t -> string option
107
108 (** Check if an element name is an ancestor. *)
109 val has_ancestor : t -> string -> bool
110
111 (** Get depth (number of ancestors). *)
112 val depth : t -> int
113
114 (** Get all ancestor names (outermost first). *)
115 val to_list : t -> string list
116end