Current section
Files
Jump to
Current section
Files
src/tomerl_parser.yrl
Nonterminals
toml
root_section section_list
section section_name section_body
key key_component value key_value
array value_list nls maybe_space
inline_table inline_kv_list
.
Terminals
'[' ']' '{' '}'
'.' '=' ','
nl space
datetime_tz local_datetime local_date local_time bool maybe_key float integer
bare_key basic_string literal_string basic_string_ml literal_string_ml
.
Rootsymbol toml.
%%%---------------------------------------------------------------------------
toml -> root_section : '$1'.
toml -> root_section section_list : lists:flatten(['$1', lists:reverse('$2')]).
toml -> section_list : lists:flatten(lists:reverse('$1')).
root_section -> section_body : [{table, 1, [], '$1'}].
section_list -> section : ['$1'].
section_list -> section_list section : ['$2' | '$1'].
%%----------------------------------------------------------
%% sections (a.k.a. tables)
% XXX: the only places where 'space' is reported is before "[" or "]"
section -> maybe_space '[' section_name maybe_space ']' nl :
[{table, line('$2'), '$3', #{}}].
section -> maybe_space '[' section_name maybe_space ']' nl section_body :
[{table, line('$2'), '$3', '$7'}].
section -> maybe_space '[' '[' section_name maybe_space ']' ']' nl :
[{array_table, line('$2'), '$4', #{}}].
section -> maybe_space '[' '[' section_name maybe_space ']' ']' nl section_body :
[{array_table, line('$2'), '$4', '$9'}].
section_name -> key : '$1'.
% section_name -> section_name '.' key : ['$3' | '$1'].
section_body -> nl : #{}.
section_body -> key_value nl : to_map('$1', #{}).
section_body -> section_body nl : '$1'.
section_body -> section_body key_value nl : to_map('$2', '$1').
%%----------------------------------------------------------
key_value -> key '=' value : {key_value, '$1', '$3'}.
%%----------------------------------------------------------
key -> key_component : ['$1'].
key -> key_component '.' key : ['$1' | '$3'].
key_component -> bare_key : key_string_value('$1').
key_component -> basic_string : key_string_value('$1').
key_component -> literal_string : key_string_value('$1').
key_component -> maybe_key : key_string_value('$1').
key_component -> bool : key_string_value('$1').
%%----------------------------------------------------------
value -> basic_string : string_value('$1').
value -> basic_string_ml : string_value('$1').
value -> literal_string : string_value('$1').
value -> literal_string_ml : string_value('$1').
value -> maybe_key : value('$1').
value -> integer : value('$1').
value -> float : value('$1').
value -> bool : value('$1').
value -> datetime_tz : value('$1').
value -> local_datetime : value('$1').
value -> local_date : value('$1').
value -> local_time : value('$1').
value -> array : '$1'.
value -> inline_table : '$1'.
%%----------------------------------------------------------
array -> maybe_space '[' nls maybe_space ']' :
{[], line('$2')}.
array -> maybe_space '[' nls value_list nls maybe_space ']' :
{'$4', line('$2')}.
array -> maybe_space '[' nls value_list nls ',' nls maybe_space ']' :
{'$4', line('$2')}.
value_list -> value : [element(1, '$1')].
value_list -> value_list nls ',' nls value : [element(1, '$5') | '$1'].
nls -> '$empty'.
nls -> nls nl.
% XXX: the only places where 'space' is reported is before "[" or "]"
maybe_space -> '$empty'.
maybe_space -> space.
%%----------------------------------------------------------
inline_table -> '{' '}' : {#{}, line('$1')}.
inline_table -> '{' inline_kv_list '}' : {'$2', line('$1')}.
% NOTE: as per spec and reference grammar, trailing comma is not allowed
inline_kv_list -> key_value : to_map('$1', #{}).
inline_kv_list -> inline_kv_list ',' key_value : to_map('$3', '$1').
%%----------------------------------------------------------
%%%---------------------------------------------------------------------------
Erlang code.
-ignore_xref([
format_error/1,
parse_and_scan/1
]).
value({maybe_key, Line, {_Type, _RawValue, ParsedValue}}) ->
{ParsedValue, Line};
value({_TermName, Line, Value}) ->
{Value, Line}.
-spec line({_, pos_integer(), _}) -> pos_integer();
({_, pos_integer()}) -> pos_integer().
line({_, Line, _}) ->
Line;
line({_, Line}) ->
Line.
-spec string_value(_) -> {binary(), pos_integer()}.
string_value({maybe_key, Line, {_Type, RawValue, _}}) ->
{to_binary(RawValue), Line};
string_value({_, Line, Val}) ->
{to_binary(Val), Line}.
-spec key_string_value(_) -> binary().
key_string_value(Token) ->
{Str, _} = string_value(Token),
Str.
-spec to_map({key_value, [binary()], {_, _}}, #{ binary() => _ }) -> #{ binary() => _ }.
to_map({key_value, [H], {Value, _Line}}, Map) ->
case maps:is_key(H, Map) of
true ->
% TODO: Show line number in error
error(overwritten);
_ ->
Map#{ H => Value }
end;
to_map({key_value, [H|T], Value}, Map) ->
Map#{ H => to_map({key_value, T, Value}, maps:get(H, Map, #{})) }.
to_binary(Atom) when is_atom(Atom) ->
atom_to_binary(Atom);
to_binary(List) ->
case unicode:characters_to_binary(List) of
{error, _Parsed, _RestData} ->
% TODO: Show line number in error
error(invalid_string);
Res when is_binary(Res) ->
Res
end.
%%%---------------------------------------------------------------------------
%%% vim:ft=erlang