anchor.diff:     <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"}],
  +</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"}],
  +                 {<<"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"}],
  +                 {<<"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"}],
  +                 {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"}],
  +                 {<<"link">>,
  +                  [{<<"rel">>,<<"shortcut icon">>},
  +                   {<<"href">>,<<"/static/images/favicon.ico">>},
  +                   {<<"type">>,<<"image/x-icon">>}],
                     []}]},
  -               {"body",
  -                [{"id","home"},
  -                 {"class","tundra"}],
  -                ["&lt;<this<!-- is -->CDATA>&gt;"]}]},
  +               {<<"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"}], []}]}
  +    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),
  +    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) ->
  -    {lists:append(lists:reverse(Acc)), AllWhitespace, Rest}.
  +    {iolist_to_binary(lists:reverse(Acc)), AllWhitespace, Rest}.
   
   tree([], Stack) ->
       {destack(Stack), []};
  
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:     escape_attr(Rest, [C | Acc]).
  
  to_tag(A) when is_atom(A) ->
      norm(atom_to_list(A));
  to_tag(L) ->
      norm(L).
  
  to_tokens([], Acc) ->
      lists:reverse(Acc);
  