anchor.diff:      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!"}]},
  +                        [{<<"head">>, [],
  +                          [{title, <<"hey!">>}]},
                            {body, [],
  -                          [{p, [{class, foo}], ["what's", <<" up">>, {br}]},
  +                          [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]},
                              {'div', <<"sucka">>},
  -                           {comment, " comment! "}]}]})),
  +                           {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"]})),
  +                         [<<"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,
  +    Open = [<<"<?">>,
  +            Tag,
               attrs_to_html(Attrs, []),
  -            "?>"],
  +            <<"?>">>],
       to_html(Rest, [Open | Acc]);
   to_html([{comment, Comment} | Rest], Acc) ->
  -    to_html(Rest, [["<!--" ++ Comment, "-->"] | 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(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,
  +    Open = [<<"<">>,
  +            Tag,
               attrs_to_html(Attrs, []),
               case Singleton of
  -                true -> " />";
  -                false -> ">"
  +                true -> <<" />">>;
  +                false -> <<">">>
               end],
       to_html(Rest, [Open | Acc]);
   to_html([{end_tag, Tag} | Rest], Acc) ->
  -    to_html(Rest, [["</" ++ Tag, ">"] | 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
  +    case lists:all(fun (C) -> ?IS_LITERAL_SAFE(C) end,
  +                   binary_to_list(iolist_to_binary(Word))) of
           true ->
  -            doctype_to_html(Rest, [" " ++ Word | Acc]);
  +            doctype_to_html(Rest, [[<<" ">>, Word] | Acc]);
           false ->
  -            doctype_to_html(Rest, [[" \"" ++ escape_attr(Word), ?QUOTE] | Acc])
  +            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]).
  +                  [[<<" ">>, escape(K), <<"=\"">>,
  +                    escape_attr(V), <<"\"">>] | Acc]).
       
   test_escape() ->
  -    "&amp;quot;\"word &lt;&lt;up!&amp;quot;" =
  -        escape("&quot;\"word <<up!&quot;"),
  +    <<"&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;"),
  +    <<"&amp;quot;&quot;word &lt;&lt;up!&amp;quot;">> =
  +        escape_attr(<<"&quot;\"word <<up!&quot;">>),
       ok.
   
   escape([], Acc) ->
  -    lists:reverse(Acc);
  +    list_to_binary(lists:reverse(Acc));
   escape("<" ++ Rest, Acc) ->
       escape(Rest, lists:reverse("&lt;", Acc));
   escape(">" ++ Rest, Acc) ->
  
positive.code:     %% Native {"p", [{"class", "foo"}], ["content"]}
      Tag = to_tag(Tag0),
      T1 = to_tag(T0),
      case is_singleton(T1) of
          true ->
              to_tokens([{Tag, R1} | Rest], [{start_tag, T1, A1, true} | Acc]);
          false ->
  
negative.code:    <link rel=\"shortcut icon\" href=\"/static/images/favicon.ico\" type=\"image/x-icon\">
   </head>
   <body id=\"home\" class=\"tundra\"><![CDATA[&lt;<this<!-- is -->CDATA>&gt;]]></body>
  </html>">>,
      Expect = {<<"html">>, [],
                [{<<"head">>, [],
                  [{<<"meta">>,
                    [{<<"http-equiv">>,<<"Content-Type">>},
                     {<<"content">>,<<"text/html; charset=UTF-8">>}],
                    []},
                   {<<"title">>,[],[<<"Foo">>]},
                   {<<"link">>,
                    [{<<"rel">>,<<"stylesheet">>},
                     {<<"type">>,<<"text/css">>},
                     {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>},
                     {<<"media">>,<<"screen">>}],
                    []},
                   {<<"link">>,
                    [{<<"rel">>,<<"stylesheet">>},
                     {<<"type">>,<<"text/css">>},
                     {<<"href">>,<<"/static/foo.css">>},
                     {<<"media">>,<<"screen">>}],
                    []},
                   {comment,<<"[if lt IE 7]>\n   <style type=\"text/css\">\n     .no_ie { display: none; }\n   </style>\n   <![endif]">>},
                   {<<"link">>,
                    [{<<"rel">>,<<"icon">>},
                     {<<"href">>,<<"/static/images/favicon.ico">>},
                     {<<"type">>,<<"image/x-icon">>}],
                    []},
                   {<<"link">>,
                    [{<<"rel">>,<<"shortcut icon">>},
                     {<<"href">>,<<"/static/images/favicon.ico">>},
                     {<<"type">>,<<"image/x-icon">>}],
                    []}]},
                 {<<"body">>,
                  [{<<"id">>,<<"home">>},
                   {<<"class">>,<<"tundra">>}],
                  [<<"&lt;<this<!-- is -->CDATA>&gt;">>]}]},
      Expect = parse(D0),
      ok.
  
  test_parse_tokens() ->
      D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]},
            {data,<<"\n">>,true},
            {start_tag,<<"html">>,[],false}],
      {<<"html">>, [], []} = parse_tokens(D0),
      D1 = D0 ++ [{end_tag, <<"html">>}],
      {<<"html">>, [], []} = parse_tokens(D1),
      D2 = D0 ++ [{start_tag, <<"body">>, [], false}],
      {<<"html">>, [], [{<<"body">>, [], []}]} = parse_tokens(D2),
      D3 = D0 ++ [{start_tag, <<"head">>, [], false},
                  {end_tag, <<"head">>},
                  {start_tag, <<"body">>, [], false}],
      {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]} = parse_tokens(D3),
      D4 = D3 ++ [{data,<<"\n">>,true},
                  {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false},
                  {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false},
                  {end_tag,<<"a">>},
                  {end_tag,<<"div">>},
                  {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false},
                  {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false},
                  {end_tag,<<"div">>},
                  {end_tag,<<"div">>}],
      {<<"html">>, [],
       [{<<"head">>, [], []},
        {<<"body">>, [],
         [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]},
          {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]}
         ]}]} = parse_tokens(D4),
      D5 = [{start_tag,<<"html">>,[],false},
            {data,<<"\n">>,true},
            {data,<<"boo">>,false},
            {data,<<"hoo">>,false},
            {data,<<"\n">>,true},
            {end_tag,<<"html">>}],
      {<<"html">>, [], [<<"\nboohoo\n">>]} = parse_tokens(D5),
      D6 = [{start_tag,<<"html">>,[],false},
            {data,<<"\n">>,true},
            {data,<<"\n">>,true},
            {end_tag,<<"html">>}],
      {<<"html">>, [], []} = parse_tokens(D6),
      D7 = [{start_tag,<<"html">>,[],false},
            {start_tag,<<"ul">>,[],false},
            {start_tag,<<"li">>,[],false},
            {data,<<"word">>,false},
            {start_tag,<<"li">>,[],false},
            {data,<<"up">>,false},
            {end_tag,<<"li">>},
            {start_tag,<<"li">>,[],false},
            {data,<<"fdsa">>,false},
            {start_tag,<<"br">>,[],true},
            {data,<<"asdf">>,false},
            {end_tag,<<"ul">>},
            {end_tag,<<"html">>}],
      {<<"html">>, [],
       [{<<"ul">>, [],
         [{<<"li">>, [], [<<"word">>]},
          {<<"li">>, [], [<<"up">>]},
          {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]} = parse_tokens(D7),
      ok.
  
  tree_data([{data, Data, Whitespace} | Rest], AllWhitespace, Acc) ->
      tree_data(Rest, (Whitespace andalso AllWhitespace), [Data | Acc]);
  tree_data(Rest, AllWhitespace, Acc) ->
      {iolist_to_binary(lists:reverse(Acc)), AllWhitespace, Rest}.
  
  tree([], Stack) ->
      {destack(Stack), []};
  