summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--eval.c64
-rw-r--r--parser.y18
-rw-r--r--tests/00_syntax/13_object_literals41
-rw-r--r--tests/00_syntax/14_array_literals57
-rw-r--r--tests/00_syntax/15_function_declarations28
5 files changed, 200 insertions, 8 deletions
diff --git a/eval.c b/eval.c
index 91a1b6c..481efc1 100644
--- a/eval.c
+++ b/eval.c
@@ -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;
diff --git a/parser.y b/parser.y
index 289fdfa..aea7ab3 100644
--- a/parser.y
+++ b/parser.y
@@ -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 --