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.
This commit is contained in:
Laria 2022-01-15 23:09:24 +01:00
parent 2684f1e61e
commit c9a935b161
4 changed files with 124 additions and 3 deletions

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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)
{