diff options
-rw-r--r-- | eval.c | 64 | ||||
-rw-r--r-- | parser.y | 18 | ||||
-rw-r--r-- | tests/00_syntax/13_object_literals | 41 | ||||
-rw-r--r-- | tests/00_syntax/14_array_literals | 57 | ||||
-rw-r--r-- | tests/00_syntax/15_function_declarations | 28 |
5 files changed, 200 insertions, 8 deletions
@@ -925,7 +925,8 @@ static struct json_object * ut_execute_list(struct ut_state *state, uint32_t off) { struct ut_op *op = ut_get_op(state, off); - struct json_object *val, *arr = xjs_new_array(); + struct json_object *ex, *val, *arr = xjs_new_array(); + size_t i; while (op) { val = ut_execute_op(state, ut_get_off(state, op)); @@ -936,7 +937,26 @@ ut_execute_list(struct ut_state *state, uint32_t off) return val; } - json_object_array_add(arr, val); + if (op->is_ellip) { + if (!json_object_is_type(val, json_type_array)) { + ex = ut_new_exception(state, op->off, "Type error: (%s) is not iterable", + json_object_get_string(val)); + + json_object_put(arr); + json_object_put(val); + + return ex; + } + + for (i = 0; i < json_object_array_length(val); i++) + json_object_array_add(arr, json_object_get(json_object_array_get_idx(val, i))); + + json_object_put(val); + } + else { + json_object_array_add(arr, val); + } + op = ut_get_op(state, op->tree.next); } @@ -946,8 +966,10 @@ ut_execute_list(struct ut_state *state, uint32_t off) static struct json_object * ut_execute_object(struct ut_state *state, uint32_t off) { - struct json_object *v, *obj = ut_new_object(NULL); + struct json_object *ex, *v, *obj = ut_new_object(NULL); struct ut_op *key, *val; + char *istr; + size_t i; for (key = ut_get_child(state, off, 0), val = ut_get_op(state, key ? key->tree.next : 0); key != NULL && val != NULL; @@ -960,7 +982,41 @@ ut_execute_object(struct ut_state *state, uint32_t off) return v; } - json_object_object_add(obj, json_object_get_string(key->val), v); + if (key->type == T_ELLIP) { + switch (json_object_get_type(v)) { + case json_type_object: + ; /* a label can only be part of a statement and a declaration is not a statement */ + json_object_object_foreach(v, vk, vv) + json_object_object_add(obj, vk, json_object_get(vv)); + + json_object_put(v); + + break; + + case json_type_array: + for (i = 0; i < json_object_array_length(v); i++) { + xasprintf(&istr, "%zu", i); + json_object_object_add(obj, istr, json_object_get(json_object_array_get_idx(v, i))); + free(istr); + } + + json_object_put(v); + + break; + + default: + ex = ut_new_exception(state, val->off, "Type error: (%s) is not iterable", + json_object_get_string(v)); + + json_object_put(obj); + json_object_put(v); + + return ex; + } + } + else { + json_object_object_add(obj, json_object_get_string(key->val), v); + } } return obj; @@ -310,7 +310,7 @@ unary_exp(A) ::= postfix_exp(B). { A = B; } postfix_exp(A) ::= unary_exp(B) T_INC(C). { A = wrap_op(C, B); ut_get_op(s, A)->is_postfix = 1; } postfix_exp(A) ::= unary_exp(B) T_DEC(C). { A = wrap_op(C, B); ut_get_op(s, A)->is_postfix = 1; } postfix_exp(A) ::= unary_exp(B) T_LPAREN(C) T_RPAREN. { A = wrap_op(C, B); } -postfix_exp(A) ::= unary_exp(B) T_LPAREN(C) arg_exp(D) T_RPAREN. +postfix_exp(A) ::= unary_exp(B) T_LPAREN(C) arg_exps(D) T_RPAREN. { A = wrap_op(C, B, D); } postfix_exp(A) ::= postfix_exp(B) T_DOT(C) T_LABEL(D). { A = wrap_op(C, B, D); } postfix_exp(A) ::= postfix_exp(B) T_LBRACK(C) assign_exp(D) T_RBRACK. @@ -338,7 +338,13 @@ primary_exp(A) ::= T_FUNC T_LPAREN args(B) T_RPAREN cpd_stmt(C). { A = new_op(T_FUNC, NULL, 0, B, C); } array(A) ::= T_LBRACK(B) T_RBRACK. { A = B; } -array(A) ::= T_LBRACK(B) exp(C) T_RBRACK. { A = wrap_op(B, C); } +array(A) ::= T_LBRACK(B) items(C) T_RBRACK. { A = wrap_op(B, C); } + +items(A) ::= items(B) T_COMMA item(C). { A = append_op(B, C); } +items(A) ::= item(B). { A = B; } + +item(A) ::= T_ELLIP assign_exp(B). { A = B; ut_get_op(s, A)->is_ellip = 1; } +item(A) ::= assign_exp(B). { A = B; } object(A) ::= empty_object(B). { A = B; } object(A) ::= T_LBRACE(B) tuples(C) T_RBRACE. { A = wrap_op(B, C); } @@ -350,6 +356,10 @@ tuples(A) ::= tuple(B). { A = B; } tuple(A) ::= T_LABEL(B) T_COLON exp(C). { A = append_op(B, C); } tuple(A) ::= T_STRING(B) T_COLON exp(C). { A = append_op(B, C); } +tuple(A) ::= T_ELLIP(B) assign_exp(C). { A = append_op(B, C); } + +arg_exps(A) ::= arg_exps(B) T_COMMA arg_exp(C). { A = append_op(B, C); ut_get_op(s, A)->is_list = 1; } +arg_exps(A) ::= arg_exp(B). { A = B; ut_get_op(s, A)->is_list = 1; } -arg_exp(A) ::= arg_exp(B) T_COMMA assign_exp(C). { A = append_op(B, C); ut_get_op(s, A)->is_list = 1; } -arg_exp(A) ::= assign_exp(B). { A = B; ut_get_op(s, A)->is_list = 1; } +arg_exp(A) ::= T_ELLIP assign_exp(B). { A = B; ut_get_op(s, A)->is_ellip = 1; } +arg_exp(A) ::= assign_exp(B). { A = B; } diff --git a/tests/00_syntax/13_object_literals b/tests/00_syntax/13_object_literals index 4b4f934..60c9f32 100644 --- a/tests/00_syntax/13_object_literals +++ b/tests/00_syntax/13_object_literals @@ -49,3 +49,44 @@ either JSON or JavaScript notation. print(third_obj, "\n"); print(nested_obj, "\n"); -- End -- + + +Additionally, utpl implements ES6-like spread operators to allow shallow copying +of object properties into other objects. + +-- Expect stdout -- +{ "foo": true, "bar": false } +{ "foo": true, "bar": false, "baz": 123, "qrx": 456 } +{ "foo": false, "bar": true, "baz": 123, "qrx": 456 } +{ "foo": true, "bar": false } +{ "foo": true, "bar": false, "level2": { "baz": 123, "qrx": 456 } } +{ "foo": true, "bar": false, "0": 7, "1": 8, "2": 9 } +-- End -- + +-- Testcase -- +{% + o1 = { foo: true, bar: false }; + o2 = { baz: 123, qrx: 456 }; + arr = [7, 8, 9]; + + print(join("\n", [ + // copying one object into another + { ...o1 }, + + // combining two objects + { ...o1, ...o2 }, + + // copying object and override properties + { ...o1, ...o2, foo: false, bar: true }, + + // default properties overwritten by spread operator + { foo: 123, bar: 456, ...o1 }, + + // nested spread operators + { ...o1, level2: { ...o2 } }, + + // merging array into objects + { ...o1, ...arr } + ]), "\n"); +%} +-- End -- diff --git a/tests/00_syntax/14_array_literals b/tests/00_syntax/14_array_literals index ea5f9c0..941ee4a 100644 --- a/tests/00_syntax/14_array_literals +++ b/tests/00_syntax/14_array_literals @@ -23,3 +23,60 @@ The utpl script language supports declaring arrays using JSON notation. print(empty_array, "\n"); print(json_array, "\n"); -- End -- + + +Additionally, utpl implements ES6-like spread operators to allow shallow copying +of array values into other arrays. + +-- Expect stdout -- +[ 1, 2, 3 ] +[ 1, 2, 3, 4, 5, 6 ] +[ 1, 2, 3, 4, 5, 6, false, true ] +[ 1, 2, 3, false, true, 4, 5, 6 ] +[ 1, 2, 3, [ 4, 5, 6 ] ] +-- End -- + +-- Testcase -- +{% + a1 = [ 1, 2, 3 ]; + a2 = [ 4, 5, 6 ]; + + print(join("\n", [ + // copying one array into another + [ ...a1 ], + + // combining two arrays + [ ...a1, ...a2 ], + + // copying array and append values + [ ...a1, ...a2, false, true ], + + // copy array and interleave values + [ ...a1, false, true, ...a2 ], + + // nested spread operators + [ ...a1, [ ...a2 ] ] + ]), "\n"); +%} +-- End -- + +Contrary to merging arrays into objects, objects cannot be merged into arrays. + +-- Expect stderr -- +Type error: ({ "foo": true, "bar": false }) is not iterable +In line 5, byte 21: + + ` print([ ...arr, ...obj ], "\n");` + Near here -------------^ + + +-- End -- + +-- Testcase -- +{% + arr = [ 1, 2, 3 ]; + obj = { foo: true, bar: false }; + + print([ ...arr, ...obj ], "\n"); +%} +-- End -- diff --git a/tests/00_syntax/15_function_declarations b/tests/00_syntax/15_function_declarations index 1ed6f83..f02f90a 100644 --- a/tests/00_syntax/15_function_declarations +++ b/tests/00_syntax/15_function_declarations @@ -96,3 +96,31 @@ function variadic_2(...args) { ... } ]), "\n"); %} -- End -- + + +Complementary to the "rest" argument syntax, the spread operator may be +used in function call arguments to pass arrays of values as argument list. + +-- Expect stdout -- +[ 1, 2, 3, 4, 5, 6 ] +[ 4, 5, 6, 1, 2, 3 ] +[ 1, 2, 3, 1, 2, 3 ] +[ 1, 2, 3 ] +-- End -- + +-- Testcase -- +{% + arr = [ 1, 2, 3 ]; + + function test(...args) { + return args; + } + + print(join("\n", [ + test(...arr, 4, 5, 6), + test(4, 5, 6, ...arr), + test(...arr, ...arr), + test(...arr) + ]), "\n"); +%} +-- End -- |