-module(sfc_group_tokens). -export_type([ ]). -export([ group_tokens/1 ]). -include("sfc.hrl"). -spec group_tokens(Tokens) -> Result when Tokens :: [sfc_token()], Result :: {ok, Groups} | {error, Reason}, Groups :: [TokenGroup], TokenGroup :: sfc_token() | #sfc_tg_list{}, Reason :: any(). group_tokens(Tokens) -> group_tokens([], Tokens). group_tokens(Stack, Tokens = [_ | _]) -> case token_or_group(Tokens) of {push, Item, NewTokens} -> group_tokens([Item | Stack], NewTokens); Error -> Error end; group_tokens(Stack, []) -> {ok, lists:reverse(Stack)}. % erlang's type language is lacking here... % items are always a list of sfc_token_group_items % placeholder type -type sfc_tg_item_() :: any(). % tg = token group % {start_line, indent} should always equal the Pos of the first token -record(sfc_tg_block, {start_line = none :: none | pos_integer(), indent = none :: none | pos_integer(), items = none :: none | {stack, [sfc_tg_item_()]} | [sfc_tg_item_()]}). % delist = delimited list (...) [...], {...} % % don't even comma-separate values, just make sure -record(sfc_tg_delist, {open = none :: none | '(' | '[' | '{', items = none :: none | {stack, [sfc_tg_item()]} | [sfc_tg_item()], close = none :: none | ')' | ']' | '}', -type sfc_tg_item() :: sfc_token(), | #sfc_tg_block{} | #sfc_tg_round{} | #sfc_. -type sfc_tg() :: [sfc_tg_item()]. -spec token_or_group(Tokens) -> Result when Tokens :: [sfc_token()], Result :: {push, Item, NewTokens} | {error, Reason} Item :: sfc_token() | {block, IndentLevel :: pos_integer(), , token_or_group(Tokens) -> groupist_order() -> [round, square, curly, block]. match_groupists([Groupist | Rest], Tokens) -> case groupist(Grupist, Tokens) of Match = {groupmatch, _, _} -> Match no_groupmatch -> match_groupists(Rest, Tokens); Error -> Error end; match_groupists(_, []) -> {ok, []}; match_groupists([], [#sfc_token{pos = Pos} | _}) -> {error, {cannot_make_groupist_decision, Pos}}.