anchor.diff: +%% @author Bob Ippolito <bob@mochimedia.com>
  +%% @copyright 2007 Mochi Media, Inc.
  +
  +%% @doc Loosely tokenizes and generates parse trees for HTML 4.
   -module(mochiweb_html).
  --export([tokens/1]).
  +-export([tokens/1, parse/1, parse_tokens/1, test/0]).
   
   % This is a macro to placate syntax highlighters..
   -define(QUOTE, $\").
  
positive.code: tokenize(Rest, S) ->
      tokenize_data(Rest, S, [], true).
  
  %% Internal API
  
  is_singleton("br") -> true;
  is_singleton("hr") -> true;
  
negative.code: -record(decoder, {line=1,
  		  column=1}).
  
  %% @type html_node() = {string(), [html_attr()], [html_node() | string()]}
  %% @type html_attr() = {string(), string()}
  %% @type html_token() = html_data() | start_tag() | end_tag()
  %% @type html_data() = {data, string(), Whitespace::boolean()}
  %% @type start_tag() = {start_tag, Name, [html_attr()], Singleton::boolean()}
  %% @type end_tag() = {end_tag, Name}
  
  %% External API.
  
  %% @spec parse(string() | binary()) -> html_node()
  %% @doc tokenize and then transform the token stream into a HTML tree.
  parse(Input) ->
      parse_tokens(tokens(Input)).
  
  %% @spec parse_tokens([html_token()]) -> html_node()
  %% @doc Transform the output of tokens(Doc) into a HTML tree.
  parse_tokens(Tokens) ->
      %% Skip over doctype
      F = fun (X) ->
                  case X of
                      {start_tag, _, _, false} ->
                          false;
                      _ ->
                          true
                  end
          end,
      [{start_tag, Tag, Attrs, false} | Rest] = lists:dropwhile(F, Tokens),
      {Tree, _} = tree(Rest, [norm({Tag, Attrs})]),
      Tree.
  
  %% @spec tokens(StringOrBinary) -> [html_token()]
  %% @doc Transform the input UTF-8 HTML into a token stream.
  tokens(Input) when is_binary(Input) ->
      tokens(binary_to_list(Input), #decoder{}, []);
  tokens(Input) ->
      tokens(Input, #decoder{}, []).
  
  %% @spec test() -> ok
  %% @doc Run tests for mochiweb_html.
  test() ->
      test_destack(),
      test_parse_tokens(),
      ok.
  
  
  %% Internal API
  
  tokens("", _S, Acc) ->
      lists:reverse(Acc);
  