let str ~pattern ?(transform_text:text_transformation= `no) (set:Biblio.set) =
let rex_field = Pcre.regexp "@\\{[^\\}]+\\}" in
let strfield ?authors_style ?title_style f e =
let str = Biblio.field_or_empty ?authors_style ?title_style f e in
do_sanitization transform_text str in
let sub_eq s i o m =
if String.length s < o + i then false else (String.sub s i o =$= m) in
let is_write stack =
Stack.is_empty stack || Stack.top stack =@= `write in
let subs stack entry = function
| "@{id}" when is_write stack -> strfield `id entry
| "@{authors}" when is_write stack -> strfield `authors entry
| "@{authors-and}" when is_write stack ->
strfield ~authors_style:`comas_and `authors entry
| "@{authors-bibtex}" when is_write stack ->
strfield ~authors_style:`bibtex `authors entry
| "@{authors-acm}" when is_write stack ->
strfield ~authors_style:`acm `authors entry
| "@{authors-etal}" when is_write stack ->
strfield ~authors_style:`et_al `authors entry
| "@{title}" when is_write stack -> strfield `title entry
| "@{title-punct}" when is_write stack ->
strfield ~title_style:`punct `title entry
| "@{how}" when is_write stack -> strfield `how entry
| "@{year}" when is_write stack -> strfield `year entry
| "@{url}" when is_write stack -> strfield `url entry
| "@{pdfurl}" when is_write stack -> strfield `pdfurl entry
| "@{comment}" when is_write stack ->
strfield (`comment "main") entry
| s when sub_eq s 0 10 "@{comment-" ->
let lgth = Str.length s in
let opt = (Str.sub s 10 (lgth - 11)) in
strfield (`comment opt) entry
| "@{bibtex}" when is_write stack -> BibTeX.format_entry entry
| "@{abstract}" when is_write stack -> strfield `abstract entry
| "@{doi}" when is_write stack -> strfield `doi entry
| "@{citation}" when is_write stack -> strfield `citation entry
| "@{tags}" when is_write stack -> strfield `tags entry
| "@{keywords}" when is_write stack -> strfield `keywords entry
| "@{@}" when is_write stack -> "@"
| "@{n}" when is_write stack -> "\n"
| s when sub_eq s 0 4 "@{if" ->
let lgth = String.length s in
let expr = Request.of_string (String.sub s 4 (lgth - 5)) in
begin match is_write stack, Request.is_ok entry expr with
| true, true -> Stack.push `write stack
| true, false -> Stack.push `no_write stack
| false, _ -> Stack.push `silent stack
end;
""
| "@{else}" ->
begin try
match Stack.pop stack with
| `write -> Stack.push `no_write stack; ""
| `no_write -> Stack.push `write stack; ""
| `silent -> Stack.push `silent stack ; ""
with e -> failwith "@{else} does not match any @{if ...}"
end
| "@{endif}" ->
begin try
let _ = Stack.pop stack in ""
with e -> failwith "@{endif} does not match any @{if ...}"
end
| s -> if is_write stack then s else ""
in
String.concat ""
(Ls.concat
(Ls.map
(fun entry ->
let stack = Stack.create () in
Ls.map (function
| Pcre.Text t -> if is_write stack then t else ""
| Pcre.Delim s -> subs stack entry s
| _ -> "")
(Pcre.full_split ~rex:rex_field pattern)) set))