123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- %%%-------------------------------------------------------------------
- %%% File: erlydtl_scanner.erl
- %%% @author Roberto Saccon <rsaccon@gmail.com> [http://rsaccon.com]
- %%% @author Evan Miller <emmiller@gmail.com>
- %%% @author Andreas Stenius <kaos@astekk.se>
- %%% @copyright 2008 Roberto Saccon, Evan Miller
- %%% @doc
- %%% Template language scanner
- %%% @end
- %%%
- %%% The MIT License
- %%%
- %%% Copyright (c) 2007 Roberto Saccon, Evan Miller
- %%%
- %%% Permission is hereby granted, free of charge, to any person obtaining a copy
- %%% of this software and associated documentation files (the "Software"), to deal
- %%% in the Software without restriction, including without limitation the rights
- %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- %%% copies of the Software, and to permit persons to whom the Software is
- %%% furnished to do so, subject to the following conditions:
- %%%
- %%% The above copyright notice and this permission notice shall be included in
- %%% all copies or substantial portions of the Software.
- %%%
- %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- %%% THE SOFTWARE.
- %%%
- %%% @since 2007-11-11 by Roberto Saccon, Evan Miller
- %%%-------------------------------------------------------------------
- -module(erlydtl_scanner).
- -author('rsaccon@gmail.com').
- -author('emmiller@gmail.com').
- -author('Andreas Stenius <kaos@astekk.se>').
- -export([scan/1, resume/1]).
- -include("erlydtl_ext.hrl").
- %%====================================================================
- %% API
- %%====================================================================
- %%--------------------------------------------------------------------
- %% @spec scan(T::template()) -> {ok, S::tokens()} | {error, Reason}
- %% @type template() = string() | binary(). Template to parse
- %% @type tokens() = [tuple()].
- %% @doc Scan the template string T and return the a token list or
- %% an error.
- %% @end
- %%--------------------------------------------------------------------
- scan(Template) ->
- scan(Template, [], {1, 1}, in_text).
- resume(#scanner_state{ template=Template, scanned=Scanned,
- pos=Pos, state=State}) ->
- scan(Template, Scanned, Pos, State).
- scan([], Scanned, _, in_text) ->
- Tokens = lists:reverse(Scanned),
- FixedTokens = reverse_strings(Tokens),
- MarkedTokens = mark_keywords(FixedTokens),
- AtomizedTokens = atomize_identifiers(MarkedTokens),
- {ok, AtomizedTokens};
- scan([], _Scanned, _, {in_comment, _}) ->
- {error, "Reached end of file inside a comment."};
- scan([], _Scanned, _, _) ->
- {error, "Reached end of file inside a code block."};
- scan("<!--{{" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, [{open_var, {Row, Column}, '<!--{{'} | Scanned], {Row, Column + length("<!--{{")}, {in_code, "}}-->"});
- scan("{{" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, [{open_var, {Row, Column}, '{{'} | Scanned], {Row, Column + length("{{")}, {in_code, "}}"});
- scan("<!--{#" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, Scanned, {Row, Column + length("<!--{#")}, {in_comment, "#}-->"});
- scan("{#" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, Scanned, {Row, Column + length("{#")}, {in_comment, "#}"});
- scan("#}-->" ++ T, Scanned, {Row, Column}, {in_comment, "#}-->"}) ->
- scan(T, Scanned, {Row, Column + length("#}-->")}, in_text);
- scan("#}" ++ T, Scanned, {Row, Column}, {in_comment, "#}"}) ->
- scan(T, Scanned, {Row, Column + length("#}")}, in_text);
- scan("<!--{%" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, [{open_tag, {Row, Column}, '<!--{%'} | Scanned],
- {Row, Column + length("<!--{%")}, {in_code, "%}-->"});
- scan("{%" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, [{open_tag, {Row, Column}, '{%'} | Scanned],
- {Row, Column + length("{%")}, {in_code, "%}"});
- scan([_ | T], Scanned, {Row, Column}, {in_comment, Closer}) ->
- scan(T, Scanned, {Row, Column + 1}, {in_comment, Closer});
- scan("\n" ++ T, Scanned, {Row, Column}, in_text) ->
- scan(T, append_text_char(Scanned, {Row, Column}, $\n), {Row + 1, 1}, in_text);
- scan([H | T], Scanned, {Row, Column}, in_text) ->
- scan(T, append_text_char(Scanned, {Row, Column}, H), {Row, Column + 1}, in_text);
- scan("\"" ++ T, Scanned, {Row, Column}, {in_code, Closer}) ->
- scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_double_quote, Closer});
- scan("\"" ++ T, Scanned, {Row, Column}, {in_identifier, Closer}) ->
- scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_double_quote, Closer});
- scan("\'" ++ T, Scanned, {Row, Column}, {in_code, Closer}) ->
- scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_single_quote, Closer});
- scan("\'" ++ T, Scanned, {Row, Column}, {in_identifier, Closer}) ->
- scan(T, [{string_literal, {Row, Column}, "\""} | Scanned], {Row, Column + 1}, {in_single_quote, Closer});
- scan([$\\ | T], Scanned, {Row, Column}, {in_double_quote, Closer}) ->
- scan(T, append_char(Scanned, $\\), {Row, Column + 1}, {in_double_quote_slash, Closer});
- scan([H | T], Scanned, {Row, Column}, {in_double_quote_slash, Closer}) ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_double_quote, Closer});
- scan([$\\ | T], Scanned, {Row, Column}, {in_single_quote, Closer}) ->
- scan(T, append_char(Scanned, $\\), {Row, Column + 1}, {in_single_quote_slash, Closer});
- scan([H | T], Scanned, {Row, Column}, {in_single_quote_slash, Closer}) ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_single_quote, Closer});
- % end quote
- scan("\"" ++ T, Scanned, {Row, Column}, {in_double_quote, Closer}) ->
- scan(T, append_char(Scanned, 34), {Row, Column + 1}, {in_code, Closer});
- % treat single quotes the same as double quotes
- scan("\'" ++ T, Scanned, {Row, Column}, {in_single_quote, Closer}) ->
- scan(T, append_char(Scanned, 34), {Row, Column + 1}, {in_code, Closer});
- scan([H | T], Scanned, {Row, Column}, {in_double_quote, Closer}) ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_double_quote, Closer});
- scan([H | T], Scanned, {Row, Column}, {in_single_quote, Closer}) ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_single_quote, Closer});
- scan("}}-->" ++ T, Scanned, {Row, Column}, {_, "}}-->"}) ->
- scan(T, [{close_var, {Row, Column}, '}}-->'} | Scanned],
- {Row, Column + length("}}-->")}, in_text);
- scan("}}" ++ T, Scanned, {Row, Column}, {_, "}}"}) ->
- scan(T, [{close_var, {Row, Column}, '}}'} | Scanned], {Row, Column + 2}, in_text);
- scan("%}-->" ++ T, Scanned, {Row, Column}, {_, "%}-->"}) ->
- scan(T, [{close_tag, {Row, Column}, '%}-->'} | Scanned],
- {Row, Column + length("%}-->")}, in_text);
- scan("%}" ++ T, [{identifier, _, "mitabrev"}, {open_tag, _, '{%'}|Scanned], {Row, Column}, {_, "%}"}) ->
- scan(T, [{string, {Row, Column + 2}, ""}|Scanned], {Row, Column + 2}, {in_verbatim, undefined});
- scan("%}" ++ T, [{identifier, _, ReversedTag}, {identifier, _, "mitabrev"}, {open_tag, _, '{%'}|Scanned],
- {Row, Column}, {_, "%}"}) ->
- scan(T, [{string, {Row, Column + 2}, ""}|Scanned], {Row, Column + 2}, {in_verbatim, ReversedTag});
- scan("%}" ++ T, Scanned, {Row, Column}, {_, "%}"}) ->
- scan(T, [{close_tag, {Row, Column}, '%}'} | Scanned],
- {Row, Column + 2}, in_text);
- scan("{%" ++ T, Scanned, {Row, Column}, {in_verbatim, Tag}) ->
- scan(T, Scanned, {Row, Column + 2}, {in_verbatim_code, lists:reverse("{%"), Tag});
- scan(" " ++ T, Scanned, {Row, Column}, {in_verbatim_code, BackTrack, Tag}) ->
- scan(T, Scanned, {Row, Column + 1}, {in_verbatim_code, [$\ |BackTrack], Tag});
- scan("endverbatim%}" ++ T, Scanned, {Row, Column}, {in_verbatim_code, _BackTrack, undefined}) ->
- scan(T, Scanned, {Row, Column + length("endverbatim%}")}, in_text);
- scan("endverbatim " ++ T, Scanned, {Row, Column}, {in_verbatim_code, BackTrack, Tag}) ->
- scan(T, Scanned, {Row, Column + length("endverbatim ")},
- {in_endverbatim_code, "", lists:reverse("endverbatim ", BackTrack), Tag});
- scan(" " ++ T, Scanned, {Row, Column}, {in_endverbatim_code, "", BackTrack, Tag}) ->
- scan(T, Scanned, {Row, Column + 1}, {in_endverbatim_code, "", [$\ |BackTrack], Tag});
- scan([H|T], Scanned, {Row, Column}, {in_endverbatim_code, EndTag, BackTrack, Tag}) when H >= $a, H =< $z; H >= $0, H =< $9; H =:= $_ ->
- scan(T, Scanned, {Row, Column + 1}, {in_endverbatim_code, [H|EndTag], [H|BackTrack], Tag});
- scan(" " ++ T, Scanned, {Row, Column}, {in_endverbatim_code, Tag, BackTrack, Tag}) ->
- scan(T, Scanned, {Row, Column + 1}, {in_endverbatim_code, Tag, [$\ |BackTrack], Tag});
- scan("%}" ++ T, Scanned, {Row, Column}, {in_endverbatim_code, Tag, _BackTrack, Tag}) ->
- scan(T, Scanned, {Row, Column + 2}, in_text);
- scan("%}" ++ T, Scanned, {Row, Column}, {in_endverbatim_code, "", _BackTrack, undefined}) ->
- scan(T, Scanned, {Row, Column + 2}, in_text);
- scan([H|T], [{string, Pos, Data}|Scanned], {Row, Column}, {in_endverbatim_code, _, BackTrack, Tag}) ->
- NewPos = case H of $\n -> {Row + 1, 1}; _ -> {Row, Column + 1} end,
- scan(T, [{string, Pos, [H|BackTrack] ++ Data}|Scanned], NewPos, {in_verbatim, Tag});
- scan([H|T], [{string, Pos, Data}|Scanned], {Row, Column}, {in_verbatim_code, BackTrack, Tag}) ->
- NewPos = case H of $\n -> {Row + 1, 1}; _ -> {Row, Column + 1} end,
- scan(T, [{string, Pos, [H|BackTrack] ++ Data}|Scanned], NewPos, {in_verbatim, Tag});
- scan([H|T], [{string, Pos, Data}|Scanned], {Row, Column}, {in_verbatim, Tag}) ->
- NewPos = case H of $\n -> {Row + 1, 1}; _ -> {Row, Column + 1} end,
- scan(T, [{string, Pos, [H|Data]}|Scanned], NewPos, {in_verbatim, Tag});
- scan("==" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'==', {Row, Column}} | Scanned], {Row, Column + 2}, {in_code, Closer});
- scan("!=" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'!=', {Row, Column}} | Scanned], {Row, Column + 2}, {in_code, Closer});
- scan(">=" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'>=', {Row, Column}} | Scanned], {Row, Column + 2}, {in_code, Closer});
- scan("<=" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'<=', {Row, Column}} | Scanned], {Row, Column + 2}, {in_code, Closer});
- scan("<" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'<', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan(">" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'>', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan("("++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'(', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan(")" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{')', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan("," ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{',', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan("|" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'|', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan("=" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'=', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan(":" ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{':', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan("." ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, [{'.', {Row, Column}} | Scanned], {Row, Column + 1}, {in_code, Closer});
- scan("_(" ++ T, Scanned, {Row, Column}, {in_code, Closer}) ->
- scan(T, lists:reverse([{'_', {Row, Column}}, {'(', {Row, Column + 1}}], Scanned), {Row, Column + 2}, {in_code, Closer});
- scan(" " ++ T, Scanned, {Row, Column}, {_, Closer}) ->
- scan(T, Scanned, {Row, Column + 1}, {in_code, Closer});
- scan([H | T], Scanned, {Row, Column}, {in_code, Closer}) ->
- case char_type(H) of
- letter_underscore ->
- scan(T, [{identifier, {Row, Column}, [H]} | Scanned], {Row, Column + 1}, {in_identifier, Closer});
- hyphen_minus ->
- scan(T, [{number_literal, {Row, Column}, [H]} | Scanned], {Row, Column + 1}, {in_number, Closer});
- digit ->
- scan(T, [{number_literal, {Row, Column}, [H]} | Scanned], {Row, Column + 1}, {in_number, Closer});
- _ ->
- {error, {Row, ?MODULE, lists:concat(["Illegal character in column ", Column])},
- #scanner_state{ template=[H|T], scanned=Scanned, pos={Row, Column}, state={in_code, Closer}}}
- end;
- scan([H | T], Scanned, {Row, Column}, {in_number, Closer}) ->
- case char_type(H) of
- digit ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_number, Closer});
- _ ->
- {error, {Row, ?MODULE, lists:concat(["Illegal character in column ", Column])},
- #scanner_state{ template=[H|T], scanned=Scanned, pos={Row, Column}, state={in_code, Closer}}}
- end;
- scan([H | T], Scanned, {Row, Column}, {in_identifier, Closer}) ->
- case char_type(H) of
- letter_underscore ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_identifier, Closer});
- digit ->
- scan(T, append_char(Scanned, H), {Row, Column + 1}, {in_identifier, Closer});
- _ ->
- {error, {Row, ?MODULE, lists:concat(["Illegal character in column ", Column])},
- #scanner_state{ template=[H|T], scanned=Scanned, pos={Row, Column}, state={in_code, Closer}}}
- end.
- % internal functions
- append_char([{Type, Pos, Chars}|Scanned], Char) ->
- [{Type, Pos, [Char | Chars]} | Scanned].
- append_text_char([], {Row, Column}, Char) ->
- [{string, {Row, Column}, [Char]}];
- append_text_char([{string, StrPos, Chars} |Scanned1], _, Char) ->
- [{string, StrPos, [Char | Chars]} | Scanned1];
- append_text_char(Scanned, {Row, Column}, Char) ->
- [{string, {Row, Column}, [Char]} | Scanned].
- char_type(C) when ((C >= $a) andalso (C =< $z)) orelse ((C >= $A) andalso (C =< $Z)) orelse (C == $_) ->
- letter_underscore;
- char_type(C) when ((C >= $0) andalso (C =< $9)) ->
- digit;
- char_type($-) ->
- hyphen_minus;
- char_type(_C) ->
- undefined.
- reverse_strings(Tokens) ->
- reverse_strings(Tokens, []).
- reverse_strings([], Acc) ->
- lists:reverse(Acc);
- reverse_strings([{Category, Pos, String}|T], Acc) when Category =:= string; Category =:= identifier;
- Category =:= string_literal; Category =:= number_literal ->
- reverse_strings(T, [{Category, Pos, lists:reverse(String)}|Acc]);
- reverse_strings([Other|T], Acc) ->
- reverse_strings(T, [Other|Acc]).
- mark_keywords(Tokens) ->
- mark_keywords(Tokens, []).
- mark_keywords([], Acc) ->
- lists:reverse(Acc);
- mark_keywords([{identifier, Pos, "in" = String}|T], Acc) ->
- mark_keywords(T, [{in_keyword, Pos, String}|Acc]);
- mark_keywords([{identifier, Pos, "not" = String}|T], Acc) ->
- mark_keywords(T, [{not_keyword, Pos, String}|Acc]);
- mark_keywords([{identifier, Pos, "or" = String}|T], Acc) ->
- mark_keywords(T, [{or_keyword, Pos, String}|Acc]);
- mark_keywords([{identifier, Pos, "and" = String}|T], Acc) ->
- mark_keywords(T, [{and_keyword, Pos, String}|Acc]);
- mark_keywords([{identifier, Pos, "as" = String}|T], Acc) ->
- mark_keywords(T, [{as_keyword, Pos, String}|Acc]);
- mark_keywords([{identifier, Pos, "by" = String}|T], Acc) ->
- mark_keywords(T, [{by_keyword, Pos, String}|Acc]);
- mark_keywords([{identifier, Pos, "with" = String}|T], Acc) ->
- mark_keywords(T, [{with_keyword, Pos, String}|Acc]);
- % These must be succeeded by a close_tag
- mark_keywords([{identifier, Pos, "only" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{only_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "parsed" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{parsed_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "noop" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{noop_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "reversed" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{reversed_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "openblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{openblock_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "closeblock" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{closeblock_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "openvariable" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{openvariable_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "closevariable" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{closevariable_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "openbrace" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{openbrace_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "closebrace" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{closebrace_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "opencomment" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{opencomment_keyword, Pos, String}, CloseTag], Acc));
- mark_keywords([{identifier, Pos, "closecomment" = String}, {close_tag, _, _} = CloseTag|T], Acc) ->
- mark_keywords(T, lists:reverse([{closecomment_keyword, Pos, String}, CloseTag], Acc));
- % The rest must be preceded by an open_tag.
- % This allows variables to have the same names as tags.
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "autoescape" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {autoescape_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endautoescape" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endautoescape_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "block" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {block_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endblock" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endblock_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "comment" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {comment_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endcomment" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endcomment_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "cycle" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {cycle_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "extends" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {extends_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "filter" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {filter_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endfilter" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endfilter_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "firstof" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {firstof_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "for" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {for_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "empty" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {empty_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endfor" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endfor_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "if" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {if_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "elif" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {elif_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "else" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {else_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endif" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endif_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "ifchanged" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {ifchanged_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endifchanged" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endifchanged_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "ifequal" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {ifequal_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endifequal" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endifequal_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "ifnotequal" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {ifnotequal_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endifnotequal" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endifnotequal_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "include" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {include_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "now" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {now_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "regroup" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {regroup_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endregroup" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endregroup_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "spaceless" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {spaceless_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endspaceless" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endspaceless_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "ssi" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {ssi_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "templatetag" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {templatetag_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "widthratio" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {widthratio_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "call" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {call_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endwith" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endwith_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "trans" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {trans_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "blocktrans" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {blocktrans_keyword, Pos, String}], Acc));
- mark_keywords([{open_tag, _, _} = OpenToken, {identifier, Pos, "endblocktrans" = String}|T], Acc) ->
- mark_keywords(T, lists:reverse([OpenToken, {endblocktrans_keyword, Pos, String}], Acc));
- mark_keywords([H|T], Acc) ->
- mark_keywords(T, [H|Acc]).
- atomize_identifiers(Tokens) ->
- atomize_identifiers(Tokens, []).
- atomize_identifiers([], Acc) ->
- lists:reverse(Acc);
- atomize_identifiers([{identifier, Pos, String}|T], Acc) ->
- atomize_identifiers(T, [{identifier, Pos, list_to_atom(String)}|Acc]);
- atomize_identifiers([H|T], Acc) ->
- atomize_identifiers(T, [H|Acc]).
|