From c9a935b161d644dab64a27cf9de933aec1681131 Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Sat, 15 Jan 2022 23:09:24 +0100 Subject: [PATCH] Support assigning into dictionaries You can now set keys in dictionaries to a value. If a key in a key path is missing, we automatically create an empty dictionary. Otherwise setting deeply nested keys becomes annoying. --- README.md | 7 ++++ src/apfl.h | 1 + src/eval.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/value.c | 11 ++++++ 4 files changed, 124 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 16f4fe0..f16982b 100644 --- a/README.md +++ b/README.md @@ -356,3 +356,10 @@ Also instead of assigning into variables you can assign into keys of a dictionar key = "baz" [d@key _] = [3 4] # d is now the dictionary ["foo" -> 1, "bar" -> 2, "baz" -> 3] + +The leftmost part must be a variable that contains a dictionary. Keys into that dictionary must either point to a dictionary or must not yet exist (in which case an empty dictionary is created automatically). + + d = [->] + d.foo = 1 + # d.foo.bar = 2 # Would result in an error as d.foo is not a dictionary. + d.bar.baz = 3 # Valid, the missing dict d.bar is autmatically created diff --git a/src/apfl.h b/src/apfl.h index 4b9d3fe..c64e64d 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -602,6 +602,7 @@ typedef struct apfl_editable_dict_data *apfl_editable_dict; apfl_editable_dict apfl_editable_dict_new(void); apfl_editable_dict apfl_editable_dict_new_from_dict(apfl_dict); +bool apfl_editable_dict_get(apfl_editable_dict, struct apfl_value key, struct apfl_value *out); bool apfl_editable_dict_set(apfl_editable_dict, struct apfl_value key, struct apfl_value value); void apfl_editable_dict_delete(apfl_editable_dict, struct apfl_value key); void apfl_editable_dict_destroy(apfl_editable_dict); diff --git a/src/eval.c b/src/eval.c index 1198ef6..ed5dcda 100644 --- a/src/eval.c +++ b/src/eval.c @@ -785,6 +785,77 @@ match_pattern_match(struct match_pattern *pattern, struct apfl_value value) return MATCH_FATAL_ERROR; } +static enum match_result +match_pattern_apply_value_keys( + struct match_pattern_value *pattern_value, + apfl_editable_dict ed, + size_t i +) { + struct apfl_value *key = &pattern_value->member_keys[i]; + + if (i == 0) { + if (!apfl_editable_dict_set( + ed, + apfl_value_incref(*key), + apfl_value_move(&pattern_value->value) + )) { + return MATCH_FATAL_ERROR; + } + return MATCH_OK; + } + + apfl_editable_dict next_ed; + + struct apfl_value value; + if (apfl_editable_dict_get( + ed, + apfl_value_incref(*key), + &value + )) { + if (value.type != APFL_VALUE_DICT) { + apfl_value_deinit(&value); + return MATCH_ERROR; + } + + next_ed = apfl_editable_dict_new_from_dict(value.dict); + } else { + // Be nice and create the missing dictionary + next_ed = apfl_editable_dict_new(); + } + + if (next_ed == NULL) { + return MATCH_FATAL_ERROR; + } + + enum match_result result = match_pattern_apply_value_keys( + pattern_value, + next_ed, + i - 1 + ); + if (result != MATCH_OK) { + apfl_editable_dict_destroy(next_ed); + return result; + } + + apfl_dict next_dict = apfl_editable_dict_finalize(next_ed); + if (next_dict == NULL) { + return MATCH_FATAL_ERROR; + } + + if (!apfl_editable_dict_set( + ed, + apfl_value_incref(*key), + (struct apfl_value) { + .type = APFL_VALUE_DICT, + .dict = next_dict, + } + )) { + return MATCH_FATAL_ERROR; + } + + return MATCH_OK; +} + static enum match_result match_pattern_apply_value(struct match_pattern_value *pattern_value) { @@ -792,11 +863,42 @@ match_pattern_apply_value(struct match_pattern_value *pattern_value) return false; } - if (pattern_value->member_keys_len > 0) { - return MATCH_NOT_YET_IMPLEMENTED; + if (pattern_value->member_keys_len == 0) { + variable_set(pattern_value->var, apfl_value_move(&pattern_value->value)); + return MATCH_OK; } - variable_set(pattern_value->var, apfl_value_move(&pattern_value->value)); + if (pattern_value->var->value.type != APFL_VALUE_DICT) { + return MATCH_ERROR; + } + + apfl_editable_dict ed = apfl_editable_dict_new_from_dict( + apfl_dict_incref(pattern_value->var->value.dict) + ); + if (ed == NULL) { + return MATCH_FATAL_ERROR; + } + + enum match_result result = match_pattern_apply_value_keys( + pattern_value, + ed, + pattern_value->member_keys_len - 1 + ); + if (result != MATCH_OK) { + apfl_editable_dict_destroy(ed); + return result; + } + + apfl_dict d = apfl_editable_dict_finalize(ed); + if (d == NULL) { + return MATCH_FATAL_ERROR; + } + + variable_set(pattern_value->var, (struct apfl_value) { + .type = APFL_VALUE_DICT, + .dict = d, + }); + return MATCH_OK; } diff --git a/src/value.c b/src/value.c index 6a9f1d7..61ba4b7 100644 --- a/src/value.c +++ b/src/value.c @@ -506,6 +506,17 @@ apfl_editable_dict_new_from_dict(apfl_dict dict) return ed; } +bool +apfl_editable_dict_get( + apfl_editable_dict ed, + struct apfl_value key, + struct apfl_value *out +) { + bool ok = apfl_hashmap_get(ed->map, &key, out); + apfl_value_deinit(&key); + return ok; +} + bool apfl_editable_dict_set(apfl_editable_dict ed, struct apfl_value key, struct apfl_value value) {