From b2e6141f65b381c2823918466e032dea3bc5f643 Mon Sep 17 00:00:00 2001 From: Teemu Harju Date: Thu, 21 Jun 2012 14:39:24 +0300 Subject: [PATCH 1/5] Use string:tokens instead of props_path_parser since regular expressions take way too much CPU. Naturally, some props functionality will be gone like list indexes. --- src/props.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/props.erl b/src/props.erl index 69d7cdf..b6acf7f 100644 --- a/src/props.erl +++ b/src/props.erl @@ -36,7 +36,8 @@ to_string/1, to_proplist/1, from_proplist/1, - from_mochijson2/1]). + from_mochijson2/1, + path_parse/1]). -export_type([prop_value/0, props/0, prop_path/0]). @@ -69,7 +70,7 @@ get(Path, Props, Default) when is_atom(Path) -> get(Path, Props, Default) when is_binary(Path) -> do_get([{prop, Path}], Props, Default); get(Path, Props, Default) -> - PathTokens = props_path_parser:parse(Path), + PathTokens = path_parse(Path), do_get(PathTokens, Props, Default). %% @doc Internal getter which operates on path tokens. @@ -131,7 +132,7 @@ set(Path, Value, Props) when is_atom(Path) -> set(Path, Value, Props) when is_binary(Path) -> do_set([{prop, Path}], Value, Props); set(Path, Value, Props) -> - PathTokens = props_path_parser:parse(Path), + PathTokens = path_parse(Path), do_set(PathTokens, Value, Props). %% @doc Internal naive recursive setter. @@ -205,7 +206,7 @@ do_drop(Path, Props) when is_atom(Path) -> do_drop(Path, Props) when is_binary(Path) -> do_drop_path([{prop, Path}], Props); do_drop(Path, Props) -> - PathTokens = props_path_parser:parse(Path), + PathTokens = path_parse(Path), do_drop_path(PathTokens, Props). do_drop_path([{prop, Key}], {PropList}) -> PropList2 = lists:keydelete(Key, 1, PropList), @@ -476,3 +477,8 @@ match(Props, {[{MKey, MVal} | MProps]}) -> _ -> false end. + +-spec path_parse(list()) -> list(). +path_parse(Path) -> + Tokens = string:tokens(Path, "."), + [{prop, list_to_binary(Token)} || Token <- Tokens]. From d0c7f9ee6d416b4fae22f46ba346d28a48a93a6f Mon Sep 17 00:00:00 2001 From: Teemu Harju Date: Fri, 29 Jun 2012 08:03:02 +0300 Subject: [PATCH 2/5] Implemented full path parsing using string:tokens. --- src/props.erl | 13 +++------- src/props_path_parser.peg | 51 --------------------------------------- 2 files changed, 4 insertions(+), 60 deletions(-) delete mode 100644 src/props_path_parser.peg diff --git a/src/props.erl b/src/props.erl index b6acf7f..3b71bf0 100644 --- a/src/props.erl +++ b/src/props.erl @@ -36,8 +36,7 @@ to_string/1, to_proplist/1, from_proplist/1, - from_mochijson2/1, - path_parse/1]). + from_mochijson2/1]). -export_type([prop_value/0, props/0, prop_path/0]). @@ -70,7 +69,7 @@ get(Path, Props, Default) when is_atom(Path) -> get(Path, Props, Default) when is_binary(Path) -> do_get([{prop, Path}], Props, Default); get(Path, Props, Default) -> - PathTokens = path_parse(Path), + PathTokens = props_path_parser:parse(Path), do_get(PathTokens, Props, Default). %% @doc Internal getter which operates on path tokens. @@ -132,7 +131,7 @@ set(Path, Value, Props) when is_atom(Path) -> set(Path, Value, Props) when is_binary(Path) -> do_set([{prop, Path}], Value, Props); set(Path, Value, Props) -> - PathTokens = path_parse(Path), + PathTokens = props_path_parser:parse(Path), do_set(PathTokens, Value, Props). %% @doc Internal naive recursive setter. @@ -206,7 +205,7 @@ do_drop(Path, Props) when is_atom(Path) -> do_drop(Path, Props) when is_binary(Path) -> do_drop_path([{prop, Path}], Props); do_drop(Path, Props) -> - PathTokens = path_parse(Path), + PathTokens = props_path_parser:parse(Path), do_drop_path(PathTokens, Props). do_drop_path([{prop, Key}], {PropList}) -> PropList2 = lists:keydelete(Key, 1, PropList), @@ -478,7 +477,3 @@ match(Props, {[{MKey, MVal} | MProps]}) -> false end. --spec path_parse(list()) -> list(). -path_parse(Path) -> - Tokens = string:tokens(Path, "."), - [{prop, list_to_binary(Token)} || Token <- Tokens]. diff --git a/src/props_path_parser.peg b/src/props_path_parser.peg deleted file mode 100644 index 8ab4840..0000000 --- a/src/props_path_parser.peg +++ /dev/null @@ -1,51 +0,0 @@ -%% Property path DSL -%% props_path_parser.peg -%% -%% Copyright 2011-2012 Grey Area -%% -%% Licensed 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. -%% -%% -%% Very similar to JSON paths in JavaScript: -%% -%% Example: -%% foo.bar[2].baz -%% -%% Paths can be either a string or an atom so both -%% foo.bar -%% and -%% "foo.bar" -%% will work the same. -%% -%% Indexes are 1-based, just like Erlang's lists module. -%% -%% Inspired by: -%% http://www.progski.net/blog/2010/destructuring_json_in_erlang_made_easy.html - -path <- var ('[' int ']')? ('.' path)? ` - case Node of - [Var, [], []] -> - [{prop, Var}]; - [Var, [_, I, _], []] -> - [{prop, Var}, {index, I}]; - [Var, [], [_, Rest]] -> - [{prop, Var}] ++ Rest; - [Var, [_, I, _], [_, Rest]] -> - [{prop, Var}, {index, I}] ++ Rest - end`; - -int <- [0-9]+ ` - list_to_integer(binary_to_list(list_to_binary(Node)))`; - -var <- [-_a-zA-Z0-9] [-:\s_a-zA-Z0-9]* ` - list_to_binary(Node)`; From f91404029abfd910668c5b528e5fddbbbf5ac8bd Mon Sep 17 00:00:00 2001 From: Teemu Harju Date: Fri, 29 Jun 2012 08:03:19 +0300 Subject: [PATCH 3/5] Removed neotoma dependency. --- rebar.config | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index bbb96d6..d94ed9a 100644 --- a/rebar.config +++ b/rebar.config @@ -12,6 +12,4 @@ {stylesheet, ""}]}. {deps, [{edown, ".*", - {git, "git://github.com/esl/edown.git", "master"}}, - {neotoma, "1.5", - {git, "git://github.com/seancribbs/neotoma.git", "master"}}]}. + {git, "git://github.com/esl/edown.git", "master"}}]}. From f9ba04069b9a44afcd43323d6e7195b44dfd817c Mon Sep 17 00:00:00 2001 From: Teemu Harju Date: Fri, 29 Jun 2012 08:06:56 +0300 Subject: [PATCH 4/5] Updated .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 47e402b..3402482 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ erl_crash.dump *.beam -src/props_path_parser.erl - deps ebin logs From f9f2d0d1f871fc3b8a2f7274d98a5eb1b218ec50 Mon Sep 17 00:00:00 2001 From: Teemu Harju Date: Fri, 29 Jun 2012 08:07:17 +0300 Subject: [PATCH 5/5] Added missing props_path_parser. --- src/props_path_parser.erl | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/props_path_parser.erl diff --git a/src/props_path_parser.erl b/src/props_path_parser.erl new file mode 100644 index 0000000..61c02b7 --- /dev/null +++ b/src/props_path_parser.erl @@ -0,0 +1,47 @@ +%% props_path_parser.erl +%% +%% Copyright 2011-2012 Grey Area +%% +%% Licensed 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. +%% +%% +%% Very similar to JSON paths in JavaScript: +%% +%% Example: +%% foo.bar[2].baz +%% +%% Paths can be either a string or an atom so both +%% foo.bar +%% and +%% "foo.bar" +%% will work the same. +%% +%% Indexes are 1-based, just like Erlang's lists module. + +-module(props_path_parser). +-export([parse/1]). + +-spec parse(binary() | list()) -> [{prop, binary()} | {index, integer()}]. +parse(Path) when is_binary(Path) -> parse(binary_to_list(Path)); +parse(Path) -> + Tokens = string:tokens(Path, "."), + Parsed = lists:map(fun(Token) -> + case string:tokens(Token, "[]") of + [P, I] -> + {Index, _} = string:to_integer(I), + [{prop, list_to_binary(P)}, {index, Index}]; + [P] -> + [{prop, list_to_binary(P)}] + end + end, Tokens), + lists:flatten(Parsed).