Skip to content
Browse files

Add support for easy field replacement in inner dicts

Also, add support for proplist-like syntax for flags: #{f} -> [{f, true}]
  • Loading branch information...
1 parent 9fcb2e7 commit cbe15e94803e442b1850b4fea6d09468949b9c9a @alavrik committed Jul 10, 2011
Showing with 124 additions and 21 deletions.
  1. +18 −3 shell/src/erl_parse.yrl
  2. +6 −1 src/Makefile
  3. +18 −3 src/erl_parse.yrl
  4. +40 −6 src/erlson.erl
  5. +17 −2 src/erlson_pt.erl
  6. +25 −6 test/erlson_tests.erl
View
21 shell/src/erl_parse.yrl
@@ -34,6 +34,7 @@ binary_comprehension
tuple
%struct
record_expr record_tuple record_field record_fields
+dict_tuple dict_field dict_fields dict_field_path
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses
try_expr try_catch try_clause try_clauses query_expr
@@ -346,14 +347,14 @@ record_expr -> '#' atom '.' atom :
{record_index,?line('$1'),element(3, '$2'),'$4'}.
record_expr -> '#' atom record_tuple :
{record,?line('$1'),element(3, '$2'),'$3'}.
-record_expr -> '#' record_tuple :
+record_expr -> '#' dict_tuple :
%{record,?line('$1'),'','$2'}.
erlson_pt:make_dict_new(?line('$1'), '$2').
record_expr -> expr_max '#' atom '.' atom :
{record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
record_expr -> expr_max '#' atom record_tuple :
{record,?line('$2'),'$1',element(3, '$3'),'$4'}.
-record_expr -> expr_max '#' record_tuple :
+record_expr -> expr_max '#' dict_tuple :
%{record,?line('$2'),'$1','','$3'}.
erlson_pt:make_dict_store(_InitDict = '$1', '$3').
record_expr -> record_expr '#' atom '.' atom :
@@ -365,7 +366,7 @@ record_expr -> record_expr '.' atom :
record_expr -> record_expr '#' atom record_tuple :
{record,?line('$2'),'$1',element(3, '$3'),'$4'}.
% XXX: is this useful? include anyway for completeness?
-record_expr -> record_expr '#' record_tuple :
+record_expr -> record_expr '#' dict_tuple :
%{record,?line('$2'),'$1','','$3'}.
erlson_pt:make_dict_store(_InitDict = '$1', '$3').
@@ -378,6 +379,20 @@ record_fields -> record_field ',' record_fields : ['$1' | '$3'].
record_field -> var '=' expr : {record_field,?line('$1'),'$1','$3'}.
record_field -> atom '=' expr : {record_field,?line('$1'),'$1','$3'}.
+dict_tuple -> '{' '}' : [].
+dict_tuple -> '{' dict_fields '}' : '$2'.
+
+dict_fields -> dict_field : ['$1'].
+dict_fields -> dict_field ',' dict_fields : ['$1' | '$3'].
+
+dict_field -> dict_field_path :
+ {record_field,?line(hd('$1')),'$1',{atom,?line(hd('$1')),true}}.
+dict_field -> dict_field_path '=' expr :
+ {record_field,?line(hd('$1')),'$1','$3'}.
+
+dict_field_path -> atom : ['$1'].
+dict_field_path -> atom '.' dict_field_path : ['$1' | '$3'].
+
%% N.B. This is called from expr_700.
function_call -> expr_800 argument_list :
View
7 src/Makefile
@@ -13,7 +13,7 @@ ERL_TEST_OBJECTS = $(ERL_TESTS:%.erl=$(EBIN_DIR)/%.beam)
.PHONY: all test
-all: $(ERL_SOURCES) $(ERL_OBJECTS) expand test
+all: $(ERL_SOURCES) erl_parse_shell.yrl $(ERL_OBJECTS) expand test
erlson_tests.erl: $(ERL_SOURCES)
@@ -23,6 +23,11 @@ erlson_tests.erl: $(ERL_SOURCES)
ln -s ../test/$@ $@; \
fi
+
+erl_parse_shell.yrl:
+ test -s $@ || ln -s ../shell/src/erl_parse.yrl $@
+
+
$(ERL_OBJECTS) $(ERL_TEST_OBJECTS): %.beam: %.erl
$(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $<
View
21 src/erl_parse.yrl
@@ -34,6 +34,7 @@ binary_comprehension
tuple
%struct
record_expr record_tuple record_field record_fields
+dict_tuple dict_field dict_fields dict_field_path
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses
try_expr try_catch try_clause try_clauses query_expr
@@ -342,13 +343,13 @@ record_expr -> '#' atom '.' atom :
{record_index,?line('$1'),element(3, '$2'),'$4'}.
record_expr -> '#' atom record_tuple :
{record,?line('$1'),element(3, '$2'),'$3'}.
-record_expr -> '#' record_tuple :
+record_expr -> '#' dict_tuple :
{record,?line('$1'),'','$2'}.
record_expr -> expr_max '#' atom '.' atom :
{record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
record_expr -> expr_max '#' atom record_tuple :
{record,?line('$2'),'$1',element(3, '$3'),'$4'}.
-record_expr -> expr_max '#' record_tuple :
+record_expr -> expr_max '#' dict_tuple :
{record,?line('$2'),'$1','','$3'}.
record_expr -> record_expr '#' atom '.' atom :
{record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
@@ -358,7 +359,7 @@ record_expr -> record_expr '.' atom :
record_expr -> record_expr '#' atom record_tuple :
{record,?line('$2'),'$1',element(3, '$3'),'$4'}.
% XXX: is this useful? include anyway for completeness?
-record_expr -> record_expr '#' record_tuple :
+record_expr -> record_expr '#' dict_tuple :
{record,?line('$2'),'$1','','$3'}.
record_tuple -> '{' '}' : [].
@@ -370,6 +371,20 @@ record_fields -> record_field ',' record_fields : ['$1' | '$3'].
record_field -> var '=' expr : {record_field,?line('$1'),'$1','$3'}.
record_field -> atom '=' expr : {record_field,?line('$1'),'$1','$3'}.
+dict_tuple -> '{' '}' : [].
+dict_tuple -> '{' dict_fields '}' : '$2'.
+
+dict_fields -> dict_field : ['$1'].
+dict_fields -> dict_field ',' dict_fields : ['$1' | '$3'].
+
+dict_field -> dict_field_path :
+ {record_field,?line(hd('$1')),'$1',{atom,?line(hd('$1')),true}}.
+dict_field -> dict_field_path '=' expr :
+ {record_field,?line(hd('$1')),'$1','$3'}.
+
+dict_field_path -> atom : ['$1'].
+dict_field_path -> atom '.' dict_field_path : ['$1' | '$3'].
+
%% N.B. This is called from expr_700.
function_call -> expr_800 argument_list :
View
46 src/erlson.erl
@@ -31,35 +31,69 @@
% dictionary represented as an ordered list of name-value pairs
-type orddict() :: [ {name(), value()} ].
-type name() :: atom() | binary().
+-type path() :: atom() | [ atom() ].
-type value() :: name().
-spec fetch/2 :: (
- Name :: atom(),
+ Path :: path(),
Dict :: orddict() ) -> value().
-fetch(Name, Dict) ->
+fetch(Path, Dict) ->
try
- fetch_val(Name, Dict)
+ case is_atom(Path) of
+ true -> fetch_val(Path, Dict);
+ false -> fetch_path(Path, Dict)
+ end
catch
'erlson_not_found' ->
- erlang:error('erlson_not_found', [Name, Dict])
+ erlang:error('erlson_not_found', [Path, Dict])
end.
+fetch_path([H|T], Dict) ->
+ Val = fetch_val(H, Dict),
+ fetch_path(T, Val);
+fetch_path([], Val) -> Val.
+
+
fetch_val(Name, [{N, _V} | T]) when Name > N ->
fetch_val(Name, T);
fetch_val(Name, [{N, V} | _T]) when Name =:= N -> V;
fetch_val(_Name, _) ->
+ not_found().
+
+
+not_found() ->
throw('erlson_not_found').
-spec store/3 :: (
- Name :: atom(),
+ Path :: path(),
Value :: any(),
Dict :: orddict() ) -> orddict().
-store(Name, Value, Dict) ->
+store(Name, Value, Dict) when is_atom(Name) ->
+ store_val(Name, Value, Dict);
+store(Path, Value, Dict) ->
+ try
+ store_path(Path, Value, Dict)
+ catch
+ 'erlson_not_found' ->
+ erlang:error('erlson_not_found', [Path, Dict])
+ end.
+
+
+store_path([N], Value, Dict) ->
+ store_val(N, Value, Dict);
+store_path([H|T], Value, Dict) ->
+ InnerDict = fetch_val(H, Dict),
+ % replace the existing value with the new inner dictionary
+ NewInnerDict = store_path(T, Value, InnerDict),
+ store_val(H, NewInnerDict, Dict).
+
+
+store_val(Name, Value, Dict) ->
orddict:store(Name, Value, Dict).
View
19 src/erlson_pt.erl
@@ -149,8 +149,14 @@ make_dict_store(InitDict, L) ->
make_dict_store_1(InitDict, []) -> InitDict;
make_dict_store_1(InitDict, [H|T]) ->
- {record_field,LINE,Key,Value} = H,
- Args = [Key, Value, make_dict_store_1(InitDict, T)],
+ {record_field,LINE,Name,Value} = H,
+ NamePath =
+ case Name of
+ [X] -> X; % path contains a single atom, like in normal records
+ L -> % path contains several atoms separated by '.'
+ make_list(L)
+ end,
+ Args = [NamePath, Value, make_dict_store_1(InitDict, T)],
Res = make_call(LINE, 'erlson', 'store', Args),
%?PRINT("make_dict_store_1: ~p~n", [Res]),
Res.
@@ -173,6 +179,15 @@ make_call(LINE, ModName, FunName, Args) ->
Res.
+make_list(L) ->
+ make_list(L, 0).
+
+make_list([H|T], _LINE) ->
+ LINE = element(2, H),
+ {cons,LINE,H,make_list(T, LINE)};
+make_list([], LINE) -> {nil, LINE}.
+
+
% firt two clauses: 'record_field' form a sequence of dot-separated atoms,
% for example: foo.bar.baz , foo, .foo
is_valid_dict_path(E) when is_atom(E) -> false;
View
31 test/erlson_tests.erl
@@ -60,13 +60,32 @@ basic_test() ->
extended_test() ->
-% (t()).foo,
-% F = fun () -> #{foo = 1} end,
-% (F()).foo,
- ok.
-
+ % setting a new dict with a field set to 'true'
+ D = #{foo},
+ true = D.foo,
+
+ D1 = D#{foo = false, bar = 10},
+ 10 = D1.bar,
+ false = D1.foo,
+
+ % creating a nested dict
+ D2 = #{foo = #{}},
+
+ % now, setting a field in the nested dict
+ D3 = D2#{foo.bar = 1},
+ 1 = D3.foo.bar,
+
+ % setting several fields in the nested dicts
+ D4 = #{
+ foo = #{},
+ foo.bar = 1,
+ foo.baz = #{},
+ foo.baz.fum % = true
+ },
+ 1 = D4.foo.bar,
+ true = D4.foo.baz.fum,
-%t() -> #{foo = 1}.
+ ok.
grammar_test() ->

0 comments on commit cbe15e9

Please sign in to comment.
Something went wrong with that request. Please try again.