anchor.diff:  tokens(Input) ->
       tokens(Input, #decoder{}, []).
   
  +%% @spec to_tokens(html_node()) -> [html_token()]
  +%% @doc Convert a html_node() tree to a list of tokens.
  +to_tokens({Tag, Attrs, Acc}) ->
  +    to_tokens([{Tag, Acc}], [{start_tag, Tag, Attrs, is_singleton(Tag)}]).
  +
  +%% @spec to_html([html_token()]) -> iolist()
  +%% @doc Convert a list of html_token() to a HTML document.
  +to_html(Tokens) ->
  +    to_html(Tokens, []).
  +
  +%% @spec escape(S::string()) -> string()
  +%% @doc Escape a string such that it's safe for HTML (amp; lt; gt;).
  +escape(S) ->
  +    escape(S, []).
  +
  +%% @spec escape(S::string()) -> string()
  +%% @doc Escape a string such that it's safe for HTML attrs
  +%%      (amp; lt; gt; quot;).
  +escape_attr(S) ->
  +    escape_attr(S, []).
  +
   %% @spec test() -> ok
   %% @doc Run tests for mochiweb_html.
   test() ->
       test_destack(),
  +    test_tokens(),
       test_parse_tokens(),
  +    test_escape(),
  +    test_escape_attr(),
       ok.
   
   
   %% Internal API
  +
  +to_html([], Acc) ->
  +    lists:reverse(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]).
  +
  +attrs_to_html([], Acc) ->
  +    lists:reverse(Acc);
  +attrs_to_html([{K, V} | Rest], Acc) ->
  +    attrs_to_html(Rest, [" " ++ 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) ->
  +    escape(Rest, lists:reverse("&gt;", Acc));
  +escape("&" ++ Rest, Acc) ->
  +    escape(Rest, lists:reverse("&amp;", Acc));
  +escape([C | Rest], Acc) ->
  +    escape(Rest, [C | Acc]).
  +
  +escape_attr([], Acc) ->
  +    lists:reverse(Acc);
  +escape_attr("<" ++ Rest, Acc) ->
  +    escape_attr(Rest, lists:reverse("&lt;", Acc));
  +escape_attr(">" ++ Rest, Acc) ->
  +    escape_attr(Rest, lists:reverse("&gt;", Acc));
  +escape_attr("&" ++ Rest, Acc) ->
  +    escape_attr(Rest, lists:reverse("&amp;", Acc));
  +escape_attr([?QUOTE | Rest], Acc) ->
  +    escape_attr(Rest, lists:reverse("&quot;", Acc));
  +escape_attr([C | Rest], Acc) ->
  +    escape_attr(Rest, [C | Acc]).
  +
  +to_tokens([], Acc) ->
  +    lists:reverse(Acc);
  +to_tokens([{Tag, []} | Rest], Acc) ->
  +    to_tokens(Rest, [{end_tag, Tag} | Acc]);
  +to_tokens([{Tag, [{T1, A1, C1} | R1]} | Rest], Acc) ->
  +    case is_singleton(T1) of
  +        true ->
  +            to_tokens([{Tag, R1} | Rest], [{start_tag, T1, A1, true} | Acc]);
  +        false ->
  +            to_tokens([{T1, C1}, {Tag, R1} | Rest],
  +                      [{start_tag, T1, A1, false} | Acc])
  +    end;
  +to_tokens([{Tag, [L | R1]} | Rest], Acc) when is_list(L) ->
  +    to_tokens([{Tag, R1} | Rest], [{data, L, false} | Acc]).
  +
  +test_tokens() ->
  +    [{start_tag, "foo", [{"bar", "baz"},
  +                         {"wibble", "wibble"},
  +                         {"alice", "bob"}], true}] =
  +        tokens("<foo bar=baz wibble='wibble' alice=\"bob\"/>"),
  +    [{start_tag, "foo", [{"bar", "baz"},
  +                         {"wibble", "wibble"},
  +                         {"alice", "bob"}], true}] =
  +        tokens("<foo bar=baz wibble='wibble' alice=bob/>"),
  +    ok.
   
   tokens("", _S, Acc) ->
       lists:reverse(Acc);
  
positive.code: find_gt(">" ++ Rest, S, HasSlash) ->
      {Rest, ?INC_COL(S), HasSlash};
  find_gt([$/ | Rest], S, _) ->
      {Rest, ?INC_COL(S), true};
  find_gt([C | Rest], S, HasSlash) ->
      find_gt(Rest, ?INC_CHAR(S, C), HasSlash).
  
  
negative.code:     {Tag, Rest1, S1} = tokenize_literal(Rest, ?ADV_COL(S, 2), []),
      {Rest2, S2, _} = find_gt(Rest1, S1, false),
      {{end_tag, Tag}, Rest2, S2};
  tokenize(L="<" ++ [C | _Rest], S) when ?IS_WHITESPACE(C) ->
      %% This isn't really strict HTML but we want this for markdown
      tokenize_data(L, ?INC_COL(S), "<", true);
  tokenize("<" ++ Rest, S) ->
      {Tag, Rest1, S1} = tokenize_literal(Rest, ?INC_COL(S), []),
      {Attrs, Rest2, S2} = tokenize_attributes(Rest1, S1, []),
  