Current section
Files
Jump to
Current section
Files
src/conf_parse.peg
%% -------------------------------------------------------------------
%%
%% conf_parse: for all your .conf parsing needs.
%%
%% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
%% A configuration file may have zero-or-more lines.
config <- line* %{
[ L || L <- Node, is_setting(L) ]
%};
%% Lines are actual settings, comments, or horizontal whitespace,
%% terminated by an end-of-line or end-of-file.
line <- ((setting / comment / ws+) (crlf / eof)) / crlf %{
case Node of
[ Line, _EOL ] -> Line;
Line -> Line
end
%};
%% A setting is a key and a value, joined by =, with surrounding
%% whitespace ignored.
setting <- ws* key ws* "=" ws* value ws* comment? %{
[ _, Key, _, _Eq, _, Value, _, _ ] = Node,
{Key, Value}
%};
%% A key is a series of dot-separated identifiers.
key <- head:word tail:("." word)* %{
[{head, H}, {tail, T}] = Node,
[unicode:characters_to_list(H)| [ unicode:characters_to_list(W) || [_, W] <- T]]
%};
%% A value is any character, with trailing whitespace stripped.
value <- (!((ws* crlf) / comment) .)+ %{
case unicode:characters_to_binary(Node, utf8, latin1) of
{_Status, _Begining, _Rest} ->
{error, {conf_to_latin1, line(Idx)}};
Bin ->
binary_to_list(Bin)
end
%};
%% A comment is any line that begins with a # sign, leading whitespace
%% allowed.
comment <- ws* "#" (!crlf .)* `comment`;
%% A word is one or more of letters, numbers and dashes or
%% underscores.
word <- ("\\." / [A-Za-z0-9_-])+ %{
unescape_dots(unicode:characters_to_list(Node))
%};
%% An end-of-line is signified by a line-feed with an optional
%% preceding carriage-return.
crlf <- "\r"? "\n" `ws`;
%% The end-of-file is where no character matches.
eof <- !. `ws`;
%% Whitespace is either spaces or tabs.
ws <- [ \t] `ws`;
% Erlang code
%{
%% -------------------------------------------------------------------
%%
%% conf_parse: for all your .conf parsing needs.
%%
%% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
%% This module implements the parser for a sysctl-style
%% configuration format. Example:
%%
%% ```
%% riak.local.node = riak@127.0.0.1
%% riak.local.http = 127.0.0.1:8098
%% riak.local.pb = 127.0.0.1:8087
%% riak.local.storage.backend = bitcask'''
%%
%% This would parse into the following flat proplist:
%%
%% ```
%% [{<<"riak.local.node">>,<<"riak@127.0.0.1">>},
%% {<<"riak.local.http">>,<<"127.0.0.1:8098">>},
%% {<<"riak.local.pb">>,<<"127.0.0.1:8087">>},
%% {<<"riak.local.storage.backend">>,<<"bitcask">>}]'''
%%
%% Other modules in this application interpret and validate the
%% result of a successful parse.
%% @end
-define(line, true).
-define(FMT(F,A), lists:flatten(io_lib:format(F,A))).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-compile(export_all).
-endif.
%% @doc Only let through lines that are not comments or whitespace.
is_setting(ws) -> false;
is_setting(comment) -> false;
is_setting(_) -> true.
%% @doc Removes escaped dots from keys
unescape_dots([$\\,$.|Rest]) ->
[$.|unescape_dots(Rest)];
unescape_dots([]) -> [];
unescape_dots([C|Rest]) ->
[C|unescape_dots(Rest)].
-ifdef(TEST).
file_test() ->
Conf = conf_parse:file("../test/riak.conf"),
?assertEqual([
{["ring_size"],"32"},
{["anti_entropy"],"debug"},
{["log","error","file"],"/var/log/error.log"},
{["log","console","file"],"/var/log/console.log"},
{["log","syslog"],"on"},
{["listener","http","internal"],"127.0.0.1:8098"},
{["listener","http","external"],"10.0.0.1:80"}
], Conf),
ok.
utf8_test() ->
Conf = conf_parse:parse("setting = thing" ++ [338] ++ "\n"),
?assertEqual([{["setting"],
{error, {conf_to_latin1, 1}}
}], Conf),
ok.
-endif.
%}