Skip to content

Instantly share code, notes, and snippets.

@seancribbs
Created October 28, 2008 02:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save seancribbs/20282 to your computer and use it in GitHub Desktop.
Save seancribbs/20282 to your computer and use it in GitHub Desktop.
-module(ehaml).
-compile(export_all).
% -export([indentation_level/1, split_lines/1, strip_indentation/1, is_tag/1]).
-define(CR, 13).
-define(LF, 10).
-define(SPACE, 32).
-define(PERCENT, $%).
-define(APOS, $').
-define(QUOTE, $").
-define(COLON, $:).
-define(ARROW, $=,$>).
-define(COMMA, $,).
-define(LBRACE, ${).
-define(RBRACE, $}).
-define(SLASH, $\\).
parse_attributes(<<?LBRACE:8, Rest/binary>>) ->
lists:reverse(parse_attributes(Rest, []));
parse_attributes(<<>>) ->
[].
% Parsing whole attributes
parse_attributes(<<?COLON:8, Rest/binary>>, AttrList) ->
Key = [],
Value = [],
parse_attributes(Rest, Key, Value, symbol, AttrList);
parse_attributes(<<?SPACE:8, Rest/binary>>, AttrList) ->
parse_attributes(Rest, AttrList);
parse_attributes(<<?RBRACE:8, _Rest/binary>>, AttrList) ->
lists:reverse(AttrList);
parse_attributes(<<>>, AttrList) ->
lists:reverse(AttrList).
% Attribute key as a Ruby Symbol
parse_attributes(<<?SPACE:8, Rest/binary>>, Key, Value, symbol, AttrList) ->
parse_attributes(Rest, list_to_binary(lists:reverse(Key)), Value, arrow, AttrList);
parse_attributes(<<?ARROW, Rest/binary>>, Key, Value, symbol, AttrList) ->
parse_attributes(Rest, list_to_binary(lists:reverse(Key)), Value, arrow, AttrList);
parse_attributes(<<Char:8, Rest/binary>>, Key, Value, symbol, AttrList) ->
parse_attributes(Rest, [Char|Key], Value, symbol, AttrList);
% Read the hash arrow, begin parsing the value
parse_attributes(<<?SPACE:8, Rest/binary>>, Key, Value, arrow, AttrList) ->
parse_attributes(Rest, Key, Value, arrow, AttrList);
parse_attributes(<<?ARROW, Rest/binary>>, Key, Value, arrow, AttrList) ->
parse_attributes(Rest, Key, Value, arrow, AttrList);
parse_attributes(<<?QUOTE:8, Rest/binary>>, Key, Value, arrow, AttrList) ->
parse_attributes(Rest, Key, Value, dqval, AttrList);
parse_attributes(<<?APOS:8, Rest/binary>>, Key, Value, arrow, AttrList) ->
parse_attributes(Rest, Key, Value, sqval, AttrList);
% Single-quoted attribute values
parse_attributes(<<?SLASH:8, ?APOS:8, Rest/binary>>, Key, Value, sqval, AttrList) ->
parse_attributes(Rest, Key, [?APOS|Value], sqval, AttrList);
parse_attributes(<<?APOS:8, Rest/binary>>, Key, Value, sqval, AttrList) ->
parse_attributes(Rest, Key, list_to_binary(lists:reverse(Value)), attrend, AttrList);
parse_attributes(<<Char:8, Rest/binary>>, Key, Value, sqval, AttrList) ->
parse_attributes(Rest, Key, [Char|Value], sqval, AttrList);
% Double-quoted attribute values
parse_attributes(<<?SLASH:8, ?QUOTE:8, Rest/binary>>, Key, Value, dqval, AttrList) ->
parse_attributes(Rest, Key, [?QUOTE|Value], dqval, AttrList);
parse_attributes(<<?QUOTE:8, Rest/binary>>, Key, Value, dqval, AttrList) ->
parse_attributes(Rest, Key, list_to_binary(lists:reverse(Value)), attrend, AttrList);
parse_attributes(<<Char:8, Rest/binary>>, Key, Value, dqval, AttrList) ->
parse_attributes(Rest, Key, [Char|Value], dqval, AttrList);
% Close out the attribute (expect a comma, space, or brace)
parse_attributes(<<?SPACE:8, Rest/binary>>, Key, Value, attrend, AttrList) ->
parse_attributes(Rest, Key, Value, attrend, AttrList);
parse_attributes(<<?COMMA:8, Rest/binary>>, Key, Value, attrend, AttrList) ->
parse_attributes(Rest, [{Key, Value}|AttrList]);
parse_attributes(<<?RBRACE:8, Rest/binary>>, Key, Value, attrend, AttrList) ->
parse_attributes(<<?RBRACE:8, Rest/binary>>, [{Key, Value}|AttrList]);
parse_attributes(<<Char:8, _Rest/binary>>, _Key, _Value, attrend, _AttrList) ->
throw({unexpected_char, Char, {expected, [?COMMA, ?RBRACE, ?SPACE]}}).
is_tag(<<?PERCENT:8, _Rest/binary>>) -> true;
is_tag(Rest) when is_binary(Rest) -> false.
strip_indentation(<<?SPACE:8,?SPACE:8,Rest/binary>>) ->
strip_indentation(Rest);
strip_indentation(<<Rest/binary>>) ->
Rest.
indentation_level(<<?SPACE:8,?SPACE:8,Rest/binary>>) ->
indentation_level(Rest) + 1;
indentation_level(<<?SPACE:8, _Rest/binary>>) ->
throw(illegal_indentation);
indentation_level(<<>>) ->
0;
indentation_level(<<_Rest/binary>>) ->
0.
split_lines(Bin) when is_binary(Bin)->
lists:reverse(split_lines(Bin, [], [])).
split_lines(<<>>, [], Acc) ->
Acc;
split_lines(<<?CR:8, ?LF:8, Rest/binary>>, Line, Acc) ->
split_lines(Rest, [], [list_to_binary(lists:reverse(Line))|Acc]);
split_lines(<<?LF:8, Rest/binary>>, Line, Acc) ->
split_lines(Rest, [], [list_to_binary(lists:reverse(Line))|Acc]);
split_lines(<<?CR:8, Rest/binary>>, Line, Acc) ->
split_lines(Rest, [], [list_to_binary(lists:reverse(Line))|Acc]);
split_lines(<<Char:8, Rest/binary>>, Line, Acc) ->
split_lines(Rest, [Char|Line], Acc);
% Case where no newline terminates file
split_lines(<<>>, Line, Acc) ->
split_lines(<<>>, [], [list_to_binary(lists:reverse(Line))|Acc]).
-module(test_ehaml).
-include_lib("eunit/include/eunit.hrl").
parse_attributes_test_() ->
[?_assertEqual([{<<"id">>, <<"foo">>}, {<<"class">>, <<"bar">>}], ehaml:parse_attributes(<<"{ :id => 'foo', :class => 'bar' }">>)),
?_assertEqual([{<<"id">>, <<"foo">>}], ehaml:parse_attributes(<<"{ :id => 'foo' }">>)),
?_assertEqual([{<<"id">>, <<"foo">>}], ehaml:parse_attributes(<<"{:id=>\"foo\"}">>)),
?_assertEqual([{<<"id">>, <<"fo'o">>}], ehaml:parse_attributes(<<"{:id=>'fo\\'o'}">>)),
?_assertEqual([{<<"id">>, <<"foo">>}], ehaml:parse_attributes(<<"{:id=>'foo'}">>)),
?_assertEqual([], ehaml:parse_attributes(<<"{ }">>)),
?_assertEqual([], ehaml:parse_attributes(<<"{}">>)),
?_assertEqual([], ehaml:parse_attributes(<<>>))].
is_tag_test_() ->
[?_assertEqual(true, ehaml:is_tag(<<"%h1{:id=>'foo'}">>)),
?_assertEqual(true, ehaml:is_tag(<<"%ul">>)),
?_assertEqual(false, ehaml:is_tag(<<"Lorem ipsum.">>))].
strip_indentation_test_() ->
[?_assertEqual(<<>>, ehaml:strip_indentation(<<" ">>)),
?_assertEqual(<<"foo">>, ehaml:strip_indentation(<<" foo">>))].
split_lines_test_() ->
[?_assertEqual([], ehaml:split_lines(<<>>)),
?_assertEqual([<<"foobar">>], ehaml:split_lines(<<"foobar">>)),
?_assertEqual([<<"foo">>,<<"bar">>], ehaml:split_lines(<<"foo",$\n,"bar">>)),
?_assertEqual([<<"foo">>,<<"bar">>], ehaml:split_lines(<<"foo",$\n,"bar",$\n>>)),
?_assertEqual([<<>>,<<>>,<<"foo">>], ehaml:split_lines(<<$\n,$\n,"foo">>))].
indentation_level_test_() ->
[?_assertEqual(0, ehaml:indentation_level(<<>>)),
?_assertEqual(1, ehaml:indentation_level(<<" foo">>)),
?_assertEqual(2, ehaml:indentation_level(<<" foo">>)),
?_assertException(throw, illegal_indentation, ehaml:indentation_level(<<" foo">>))].
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment