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:
parent
2684f1e61e
commit
c9a935b161
4 changed files with 124 additions and 3 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
108
src/eval.c
108
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
11
src/value.c
11
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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue