anchor.diff:      escape(Rest, [C | Acc]).
   
   escape_attr([], Acc) ->
  -    lists:reverse(Acc);
  +    list_to_binary(lists:reverse(Acc));
   escape_attr("<" ++ Rest, Acc) ->
       escape_attr(Rest, lists:reverse("&lt;", Acc));
   escape_attr(">" ++ Rest, Acc) ->
  
positive.code:     Expect = <<"<html><head><title>hey!</title></head><body><p class=\"foo\">what's up<br /></p><div>sucka</div><!-- comment! --></body></html>">>,
      Expect = iolist_to_binary(
                 to_html({html, [],
                          [{"head", [],
                            [{title, "hey!"}]},
                           {body, [],
                            [{p, [{class, foo}], ["what's", <<" up">>, {br}]},
                             {'div', <<"sucka">>},
                             {comment, " comment! "}]}]})),
      Expect1 = <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">">>,
      Expect1 = iolist_to_binary(
                  to_html({doctype,
                           ["html", "PUBLIC",
                            "-//W3C//DTD XHTML 1.0 Transitional//EN",
                            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"]})),
      ok.
  to_html([], Acc) ->
      lists:reverse(Acc);
  to_html([{'=', Content} | Rest], Acc) ->
      to_html(Rest, [Content | Acc]);
  to_html([{pi, Tag, Attrs} | Rest], Acc) ->
      Open = ["<?" ++ Tag,
              attrs_to_html(Attrs, []),
              "?>"],
      to_html(Rest, [Open | Acc]);
  to_html([{comment, Comment} | Rest], Acc) ->
      to_html(Rest, [["<!--" ++ Comment, "-->"] | Acc]);
  to_html([{doctype, Parts} | Rest], Acc) ->
      Inside = doctype_to_html(Parts, Acc),
      to_html(Rest, [["<!DOCTYPE" ++ Inside, ">"] | Acc]);
  to_html([{data, Data, _Whitespace} | Rest], Acc) ->
      to_html(Rest, [escape(Data) | Acc]);
  to_html([{start_tag, Tag, Attrs, Singleton} | Rest], Acc) ->
      Open = ["<" ++ Tag,
              attrs_to_html(Attrs, []),
              case Singleton of
                  true -> " />";
                  false -> ">"
              end],
      to_html(Rest, [Open | Acc]);
  to_html([{end_tag, Tag} | Rest], Acc) ->
      to_html(Rest, [["</" ++ Tag, ">"] | Acc]).
  
  doctype_to_html([], Acc) ->
      lists:reverse(Acc);
  doctype_to_html([Word | Rest], Acc) ->
      case lists:all(fun (C) -> ?IS_LITERAL_SAFE(C) end, Word) of
          true ->
              doctype_to_html(Rest, [" " ++ Word | Acc]);
          false ->
              doctype_to_html(Rest, [[" \"" ++ escape_attr(Word), ?QUOTE] | Acc])
      end.
  
  attrs_to_html([], Acc) ->
      lists:reverse(Acc);
  attrs_to_html([{K, V} | Rest], Acc) ->
      attrs_to_html(Rest,
                    [[" " ++ escape(K), "=\"" ++ escape_attr(V), "\""] | Acc]).
      
  test_escape() ->
      "&amp;quot;\"word &lt;&lt;up!&amp;quot;" =
          escape("&quot;\"word <<up!&quot;"),
      ok.
  
  test_escape_attr() ->
      "&amp;quot;&quot;word &lt;&lt;up!&amp;quot;" =
          escape_attr("&quot;\"word <<up!&quot;"),
      ok.
  
  escape([], Acc) ->
      lists:reverse(Acc);
  escape("<" ++ Rest, Acc) ->
      escape(Rest, lists:reverse("&lt;", Acc));
  escape(">" ++ Rest, Acc) ->
  
negative.code: destack([{T1, A1, Acc1}, {T0, A0, Acc0} | Rest]) ->
      destack([{T0, A0, [{T1, A1, lists:reverse(Acc1)} | Acc0]} | Rest]).
  
  is_singleton(<<"br">>) -> true;
  is_singleton(<<"hr">>) -> true;
  is_singleton(<<"img">>) -> true;
  is_singleton(<<"input">>) -> true;
  is_singleton(<<"base">>) -> true;
  is_singleton(<<"meta">>) -> true;
  is_singleton(<<"link">>) -> true;
  is_singleton(<<"area">>) -> true;
  is_singleton(<<"param">>) -> true;
  is_singleton(<<"col">>) -> true;
  is_singleton(_) -> false.
  
  tokenize_data([], S, Acc, Whitespace) ->
      {{data, list_to_binary(lists:reverse(Acc)), Whitespace}, [], S};
  tokenize_data(Rest="<" ++ _, S, Acc, Whitespace) ->
      {{data, list_to_binary(lists:reverse(Acc)), Whitespace}, Rest, S};
  tokenize_data(Rest="&" ++ _, S, Acc, Whitespace) ->
      {{data, list_to_binary(lists:reverse(Acc)), Whitespace}, Rest, S};
  tokenize_data([C | Rest], S, Acc, Whitespace) when ?IS_WHITESPACE(C) ->
      tokenize_data(Rest, S, [C | Acc], Whitespace);
  tokenize_data([C | Rest], S, Acc, _) ->
  