let rec string_of_edge_stmt_point kind = function
    Edge_node_id nid -> string_of_node_id nid
  | Edge_subgraph s -> string_of_subgraph kind s

and string_of_edge_stmt kind =
  let sep =
    match kind with
      Graph -> "--"
    | Digraph -> "->"
  in
  function (p1, lp, attr) ->
    Printf.sprintf "%s%s%s"
      (string_of_edge_stmt_point kind p1)
      (String.concat ""
         (List.map
            (fun p -> Printf.sprintf " %s %s" sep
                (string_of_edge_stmt_point kind p))
            lp
         )
      )
      (string_of_attr_list attr)

and string_of_attr_stmt stmt =
  let (s,attr) =
    match stmt with
      Attr_graph l -> ("graph", l)
    | Attr_node l -> ("node", l)
    | Attr_edge l -> ("edge", l)
  in
  Printf.sprintf "%s %s" s
    (string_of_attr_list attr)

and string_of_stmt kind = function
    Stmt_node (nid, attr) ->
      Printf.sprintf "%s %s"
        (string_of_node_id nid)
        (string_of_attr_list attr)
  | Stmt_equals (id1, id2) ->
      Printf.sprintf "%s=%s"
        (string_of_id id1)
        (string_of_id id2)
  | Stmt_edge s ->
      string_of_edge_stmt kind s
  | Stmt_attr s ->
      string_of_attr_stmt s
  | Stmt_subgraph g ->
      string_of_subgraph kind g

and string_of_stmt_list kind l =
  String.concat "\n"
    (List.map
       (fun s -> Printf.sprintf "%s;" (string_of_stmt kind s)) l)

and string_of_subgraph kind g =
  Printf.sprintf "subgraph %s{\n%s\n  }"
    (match g.sub_id with
      None -> ""
    | Some id -> Printf.sprintf "%s " (string_of_id id)
    )
    (string_of_stmt_list kind g.sub_stmt_list)