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. *) 6 7(** Generic stack-based context tracker. *) 8module Stack : sig 9 type 'a t 10 11 (** Create an empty context stack. *) 12 val create : unit -> 'a t 13 14 (** Reset the stack to empty. *) 15 val reset : 'a t -> unit 16 17 (** Push a context onto the stack. *) 18 val push : 'a t -> 'a -> unit 19 20 (** Pop a context from the stack. Returns None if empty. *) 21 val pop : 'a t -> 'a option 22 23 (** Get the current (top) context without removing it. *) 24 val current : 'a t -> 'a option 25 26 (** Get current depth (number of items on stack). *) 27 val depth : 'a t -> int 28 29 (** Check if stack is empty. *) 30 val is_empty : 'a t -> bool 31 32 (** Get all ancestors (bottom to top). *) 33 val to_list : 'a t -> 'a list 34 35 (** Check if any ancestor satisfies predicate. *) 36 val exists : 'a t -> ('a -> bool) -> bool 37 38 (** Find first ancestor satisfying predicate (top to bottom). *) 39 val find : 'a t -> ('a -> bool) -> 'a option 40 41 (** Iterate over all contexts (top to bottom). *) 42 val iter : 'a t -> ('a -> unit) -> unit 43end = struct 44 type 'a t = { mutable stack : 'a list; mutable len : int } 45 46 let create () = { stack = []; len = 0 } 47 let reset t = t.stack <- []; t.len <- 0 48 let push t x = t.stack <- x :: t.stack; t.len <- t.len + 1 49 let pop t = match t.stack with 50 | [] -> None 51 | x :: rest -> t.stack <- rest; t.len <- t.len - 1; Some x 52 let current t = match t.stack with 53 | [] -> None 54 | x :: _ -> Some x 55 let depth t = t.len (* O(1) instead of O(n) *) 56 let is_empty t = t.len = 0 57 let to_list t = List.rev t.stack 58 let exists t f = List.exists f t.stack 59 let find t f = List.find_opt f t.stack 60 let iter t f = List.iter f t.stack 61end 62 63(** Simple depth counter for tracking nesting level. *) 64module Depth : sig 65 type t 66 67 (** Create a depth counter starting at 0. *) 68 val create : unit -> t 69 70 (** Reset depth to 0. *) 71 val reset : t -> unit 72 73 (** Increment depth (entering element). *) 74 val enter : t -> unit 75 76 (** Decrement depth (leaving element). Returns false if was already 0. *) 77 val leave : t -> bool 78 79 (** Get current depth. *) 80 val get : t -> int 81 82 (** Check if inside (depth > 0). *) 83 val is_inside : t -> bool 84end = struct 85 type t = { mutable depth : int } 86 87 let create () = { depth = 0 } 88 let reset t = t.depth <- 0 89 let enter t = t.depth <- t.depth + 1 90 let leave t = 91 if t.depth > 0 then begin 92 t.depth <- t.depth - 1; 93 true 94 end else false 95 let get t = t.depth 96 let is_inside t = t.depth > 0 97end 98 99(** Element name stack for tracking ancestors by name. *) 100module Ancestors : sig 101 type t 102 103 (** Create an empty ancestor tracker. *) 104 val create : unit -> t 105 106 (** Reset to empty. *) 107 val reset : t -> unit 108 109 (** Push an element name onto the ancestor stack. *) 110 val push : t -> string -> unit 111 112 (** Pop an element from the ancestor stack. *) 113 val pop : t -> unit 114 115 (** Get the immediate parent element name. *) 116 val parent : t -> string option 117 118 (** Check if an element name is an ancestor. *) 119 val has_ancestor : t -> string -> bool 120 121 (** Get depth (number of ancestors). *) 122 val depth : t -> int 123 124 (** Get all ancestor names (outermost first). *) 125 val to_list : t -> string list 126end = struct 127 type t = { mutable stack : string list; mutable len : int } 128 129 let create () = { stack = []; len = 0 } 130 let reset t = t.stack <- []; t.len <- 0 131 let push t name = t.stack <- name :: t.stack; t.len <- t.len + 1 132 let pop t = match t.stack with 133 | _ :: rest -> t.stack <- rest; t.len <- t.len - 1 134 | [] -> () 135 let parent t = match t.stack with 136 | x :: _ -> Some x 137 | [] -> None 138 let has_ancestor t name = List.mem name t.stack 139 let depth t = t.len (* O(1) instead of O(n) *) 140 let to_list t = List.rev t.stack 141end