{ (* initial version taken from https://github.com/realworldocaml/mdx *) type arg = | Field of string | NameValue of string * string type block = { line : int; file : string; arguments: arg list; header : string option; contents: string list; } exception Err of string let line_ref = ref 1 let newline lexbuf = Lexing.new_line lexbuf; incr line_ref } let eol = '\n' | eof let ws = ' ' | '\t' rule text = parse | eof { [] } | "```" ([^' ' '\n']* as h) ws* ([^'\n']* as l) eol { let header = if h = "" then None else Some h in let contents = block lexbuf in let arguments = String.split_on_char ' ' l in let arguments = List.map (fun a -> if (String.contains a '=') then ( let a = String.split_on_char '=' a in NameValue (List.nth a 0, List.nth a 1)) else Field a ) arguments in let file = lexbuf.Lexing.lex_start_p.Lexing.pos_fname in newline lexbuf; let line = !line_ref in List.iter (fun _ -> newline lexbuf) contents; newline lexbuf; { file; line; header; arguments; contents; } :: text lexbuf } | [^'\n']* eol { newline lexbuf; text lexbuf } and block = parse | eof | "```" ws* eol { [] } | ([^'\n'] * as str) eol { str :: block lexbuf } { let token lexbuf = try text lexbuf with Failure _ -> raise (Err "incomplete code block") }