%% Copyright (c) 2017-2018, Loïc Hoguin %% %% Permission to use, copy, modify, and/or distribute this software for any %% purpose with or without fee is hereby granted, provided that the above %% copyright notice and this permission notice appear in all copies. %% %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. %% This pass parses and builds a table from the contents %% of a table block. %% %% Asciidoc User Guide 23 %% %% @todo Rows and cells are currently not annotated. -module(asciideck_tables_pass). -export([run/1]). -define(IS_WS(C), (C =:= $\s) or (C =:= $\t) or (C =:= $\n). run([]) -> []; run([Table={table, _, _, _}|Tail]) -> [table(Table)|run(Tail)]; run([Block|Tail]) -> [Block|run(Tail)]. table({table, Attrs, Contents, Ann}) -> {Cells, NumCols} = parse_table(Contents, Attrs), Children = rows(Cells, NumCols), {table, Attrs, Children, Ann}. -ifdef(TEST). table_test() -> {table, _, [ {row, _, [ {cell, _, [{paragraph, _, <<"1">>, _}], _}, {cell, _, [{paragraph, _, <<"2">>, _}], _}, {cell, _, [{paragraph, _, <<"A">>, _}], _} ], _}, {row, _, [ {cell, _, [{paragraph, _, <<"3">>, _}], _}, {cell, _, [{paragraph, _, <<"4">>, _}], _}, {cell, _, [{paragraph, _, <<"B">>, _}], _} ], _}, {row, _, [ {cell, _, [{paragraph, _, <<"5">>, _}], _}, {cell, _, [{paragraph, _, <<"6">>, _}], _}, {cell, _, [{paragraph, _, <<"C">>, _}], _} ], _} ], _} = table({table, #{}, << "|1 |2 |A\n" "|3 |4 |B\n" "|5 |6 |C">>, #{line => 1}}), ok. -endif. %% If the cols attribute is not specified, the number of %% columns is the number of cells on the first line. parse_table(Contents, #{<<"cols">> := Cols}) -> {parse_cells(Contents, []), num_cols(Cols)}; %% We get the first line, parse the cells in it then %% count the number of columns in the table. Finally %% we parse all the remaining cells. parse_table(Contents, _) -> case binary:split(Contents, <<$\n>>) of %% We only have the one line. Who writes tables like this? [Line] -> Cells = parse_cells(Line, []), {Cells, length(Cells)}; %% We have a useful table with more than one line. Good user! [Line, Rest] -> Cells0 = parse_cells(Line, []), Cells = parse_cells(Rest, lists:reverse(Cells0)), {Cells, length(Cells0)} end. %% @todo Don't discard Specs. num_cols(Cols) -> try binary_to_integer(Cols) of Int -> Int catch _:_ -> Specs0 = binary:split(Cols, <<$,>>, [global]), Specs = [parse_specs(Spec) || Spec <- Specs0], lists:sum([M || #{multiplier := M} <- Specs]) end. -ifdef(TEST). num_cols_test_() -> Tests = [ {<<"4">>, 4}, {<<">s,^m,e">>, 3}, {<<"3,^2,^2,10">>, 4}, {<<"^1,4*2">>, 5}, {<<"e,m,^,>s">>, 4}, {<<"2">>, 5}, {<<"4*<">>, 4}, {<<"3*.^">>, 3}, {<<"2*,.>">>, 3}, {<<".<,.^,.>">>, 3}, {<<".<,.^,^.>">>, 3} ], [{V, fun() -> R = num_cols(V) end} || {V, R} <- Tests]. -endif. %% Asciidoc User Guide 23.4 %% %% [*][][.][][