Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
ansiparse.ml1 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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213type color = Black | Red | Green | Yellow | Blue | Magenta | Cyan | White module Concrete = struct type style = Bold | Faint | Italic | Underline | Blink | Inverse | Hidden | Strike | Fore of color | Back of color | Unknown of int type t = Esc of style list | Reset | Text of string let fmt_of_int = function | 1 -> Bold | 2 -> Faint | 3 -> Italic | 4 -> Underline | 5 -> Blink | 7 -> Inverse | 8 -> Hidden | 9 -> Strike | x -> Unknown x let color_of_int = function | 0 -> Black | 1 -> Red | 2 -> Green | 3 -> Yellow | 4 -> Blue | 5 -> Magenta | 6 -> Cyan | 7 -> White | _ -> assert false let style_of_int = function | x when 30 <= x && x <= 37 -> Fore (color_of_int (x-30)) | x when 40 <= x && x <= 47 -> Back (color_of_int (x-40)) | x -> fmt_of_int x (* Warning: possibly re-inventing the square parser monad here *) (* val extract_esc : int list -> style list * int list *) let rec extract_esc = function | 0 :: ints -> ([], ints) | x :: ints -> let styles, rest = extract_esc ints in (style_of_int x :: styles, rest) | [] -> ([], []) (* val extract_item : int list -> t list * int list *) let extract_item = function | 0 :: ints -> (Reset, ints) | ints -> let styles, rest = extract_esc ints in (Esc styles, rest) (* val items_of_ints : int list -> t list *) let rec items_of_ints ints = let item, ints' = extract_item ints in match ints' with | _ :: _ -> item :: items_of_ints ints' | [] -> item :: [] (* Grammar: Item --> Escape | Text Escape --> csi Styles? cst Styles --> Style ( ';' Style )* Style --> dig+ Text --> [not start of csi]* *) open Angstrom let style = take_while1 (function '0' .. '9' -> true | _ -> false) >>| int_of_string let styles = sep_by (char ';') style let csi_str = "\x1b[" let csi = string csi_str let cst = string "m" module Private = struct let text = peek_char >>= function | Some _ -> take_till (fun c -> c = csi_str.[0]) >>| fun str -> [Text str] | None -> fail "End of input" let escape = csi *> styles <* cst >>| items_of_ints let item = (escape <|> text) (* : t list parser ; needs flattening *) let items = many item >>| List.concat (* Done *) end (* val parse : in_channel -> Concrete.t list *) module B = Buffered let parse in_ch = let rec with_state = function | B.Partial k -> with_state @@ k (try `String (input_line in_ch ^ "\n") with End_of_file -> `Eof) | B.Done (_,result) -> result | B.Fail (_,ss,s) -> Esc [Fore Red] :: Text s :: List.map (fun x -> Text x) ss (* Cheap ... but it shouldn't fail? XD *) in with_state @@ B.parse Private.items let parse_str str = match parse_string ~consume:Consume.All Private.items str with | Ok result -> result | Error err -> [Esc [Fore Red]; Text err] end module C = Concrete module Debug = struct open Angstrom let str = "\x1b[0m\x1b[1;39m[ INFO ]\x1b[0m Something interesting happened." let text = peek_char >>= function | Some _ -> take_till (fun c -> c = C.csi_str.[0]) >>| fun str -> `Shmext str | None -> fail "End of input" let escape = C.csi *> C.styles <* C.cst >>| fun xs -> `Shmints xs let item = escape <|> text let items = many item end module Abstract = struct type weight = Normal | Bold | Faint type style = { weight : weight ; italic : bool ; underline : bool ; blink : bool ; reverse : bool ; strike : bool ; foreground : color option ; background : color option } type 'a t = Base of 'a | Styled of style * 'a t list let default = { weight = Normal ; italic = false ; underline = false ; blink = false ; reverse = false ; strike = false ; foreground = None ; background = None } (* Apply the concrete style to the abstract style *) (* apply_single : C.style -> A.style -> A.style *) let apply_single cstyle astyle = match cstyle with | C.Bold -> { astyle with weight = Bold } | C.Faint -> { astyle with weight = Faint } | C.Italic -> { astyle with italic = true } | C.Underline -> { astyle with underline = true } | C.Blink -> { astyle with blink = true } | C.Inverse -> { astyle with reverse = true } | C.Hidden -> astyle (* Ignore for now... *) | C.Strike -> { astyle with strike = true } | C.Fore col -> { astyle with foreground = Some col } | C.Back col -> { astyle with background = Some col } | C.Unknown _ -> astyle (* Ignore *) (* val apply_multi : C.style list -> A.style -> A.style *) let apply_multi cstyles astyle = List.fold_left (fun x y -> apply_single y x) astyle cstyles (* Further possibility of reinventing the square parser monad *) (* val branch : C.t list -> A.t list * C.t list *) let rec branch = function | [] -> ([], []) | (C.Reset :: _) as items -> ([], items) | x :: items -> let nodes, items' = branch items in match x with | C.Text str -> (Base str :: nodes, items') | C.Esc styles -> let nodes', _items'' = branch items' in (Styled (apply_multi styles default, nodes) :: nodes', items') | C.Reset -> (nodes, items') (* val branch_root : C.t list -> A.t list *) let rec branch_root = function | [] -> [] | C.Reset :: items -> branch_root items | C.Text str :: items -> Base str :: branch_root items | C.Esc styles :: items -> let nodes, items' = branch items in Styled (apply_multi styles default,nodes) :: branch_root items' (* val parse : Concrete.t list -> string Abstract.t *) let parse items = Styled (default,branch_root items) end module A = Abstract let ( ^^^ ) x y = (x && not y) || (y && not x) module Html = struct let string_of_col = function | Black -> "black" | Red -> "red" | Green -> "green" | Yellow -> "yellow" | Blue -> "blue" | Magenta -> "magenta" | Cyan -> "cyan" | White -> "white" open Tyxml.Html let css_of_style ctx_rvs { A.weight; italic; underline; blink; reverse; strike; foreground; background } = let reverse' = reverse ^^^ ctx_rvs in let css_weight = match weight with | A.Normal -> "" | A.Bold -> "font-weight: bold" | A.Faint -> "font-weight: lighter" in let css_style = if italic then "font-style: italic" else "" in (* arbitrarily prioritising strike > blink > underline for single text-decoration property *) let css_decor = if strike then "text-decoration: line-through" else if blink then "text-decoration: blink" else if underline then "text-decoration: underline" else "" in let css_color = match if reverse' then background else foreground with | Some c -> "color: " ^ string_of_col c | None -> "" in let css_bgcol = match if reverse' then foreground else background with | Some c -> "background-color: " ^ string_of_col c | None -> "" in (reverse', String.concat "; " [css_weight; css_style; css_decor; css_color; css_bgcol]) let of_tree tree = let rec per_node reverse = function | A.Base (str:string) -> txt str | A.Styled (style,nodes) -> let reverse', css = css_of_style reverse style in span ~a:[a_style css] (List.map (per_node reverse') nodes) in pre [per_node false tree] end