(*  Title:      Pure/PIDE/xml0.ML
    Author:     Makarius
Untyped XML trees and YXML notation: minimal bootstrap version.
*)
(** XML **)
signature XML =
sig
  type attributes = (string * string) list
  datatype tree =
      Elem of (string * attributes) * tree list
    | Text of string
  type body = tree list
  val xml_elemN: string
  val xml_nameN: string
  val xml_bodyN: string
  val wrap_elem: ((string * attributes) * body) * body -> tree
  val unwrap_elem: tree -> (((string * attributes) * body) * body) option
  val unwrap_elem_body: tree -> body option
  val traverse_text: (string -> 'a -> 'a) -> tree -> 'a -> 'a
  val traverse_texts: (string -> 'a -> 'a) -> body -> 'a -> 'a
  val content_of: body -> string
end
structure XML: XML =
struct
type attributes = (string * string) list;
datatype tree =
    Elem of (string * attributes) * tree list
  | Text of string;
type body = tree list;
(* wrapped elements *)
val xml_elemN = "xml_elem";
val xml_nameN = "xml_name";
val xml_bodyN = "xml_body";
fun wrap_elem (((a, atts), body1), body2) =
  Elem ((xml_elemN, (xml_nameN, a) :: atts), Elem ((xml_bodyN, []), body1) :: body2);
fun unwrap_elem (Elem ((name, (n, a) :: atts), Elem ((name', []), body1) :: body2)) =
      if name = xml_elemN andalso n = xml_nameN andalso name' = xml_bodyN
      then SOME (((a, atts), body1), body2) else NONE
  | unwrap_elem _ = NONE;
fun unwrap_elem_body (Elem ((name, (n, _) :: _), Elem ((name', []), _) :: body)) =
      if name = xml_elemN andalso n = xml_nameN andalso name' = xml_bodyN
      then SOME body else NONE
  | unwrap_elem_body _ = NONE;
(* traverse text content *)
fun traverse_text f tree =
  (case unwrap_elem_body tree of
    SOME ts => traverse_texts f ts
  | NONE =>
      (case tree of
        Elem (_, ts) => traverse_texts f ts
      | Text s => f s))
and traverse_texts _ [] res = res
  | traverse_texts f (t :: ts) res = traverse_texts f ts (traverse_text f t res);
fun content_of body =
  String.concat (rev (traverse_texts (fn x => fn xs => x :: xs) body []));
end;
(** YXML **)
signature YXML =
sig
  val X_char: char
  val Y_char: char
  val X: string
  val Y: string
  val XY: string
  val XYX: string
  val detect: string -> bool
  val output_markup: string * XML.attributes -> string * string
end;
structure YXML: YXML =
struct
(* markers *)
val X_char = Char.chr 5;
val Y_char = Char.chr 6;
val X = String.str X_char;
val Y = String.str Y_char;
val XY = X ^ Y;
val XYX = XY ^ X;
fun detect s = Char.contains s X_char orelse Char.contains s Y_char;
(* output markup *)
fun output_markup ("", _) = ("", "")
  | output_markup (name, atts) =
      let
        val bgs = XY :: name :: List.foldr (fn ((a, b), ps) => Y :: a :: "=" :: b :: ps) [X] atts;
        val en = XYX;
      in (String.concat bgs, en) end;
end;