From 4ee06d8138a107908a9fb45220fea32055b3c48a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sat, 25 Sep 2021 19:34:14 +0200 Subject: syntax: introduce optional chaining operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce new operators `?.`, `?.[…]` and `?.(…)` to simplify looking up deeply nested property chain in a secure manner. The `?.` operator behaves like the `.` property access operator but yields `null` if the left hand side is `null` or not an object. Like `?.`, the `?.[…]` operator behaves like the `[…]` computed property access but yields `null` if the left hand side is `null` or neither an object or array. Finally the `?.(…)` operator behaves like the function call operator `(…)` but yields `null` if the left hand side is `null` or not a callable function. Signed-off-by: Jo-Philipp Wich --- tests/custom/00_syntax/23_optional_chaining | 99 +++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/custom/00_syntax/23_optional_chaining (limited to 'tests/custom/00_syntax') diff --git a/tests/custom/00_syntax/23_optional_chaining b/tests/custom/00_syntax/23_optional_chaining new file mode 100644 index 0000000..8b89089 --- /dev/null +++ b/tests/custom/00_syntax/23_optional_chaining @@ -0,0 +1,99 @@ +Optional chaining operators allow accessing nested object properties +in a secure manner, without the need to check the entire reference +chain for validity. + + +1. The `?.` operator can be used to lookup a named property in a +left-hand side expression without having to check whether the lhs +value is a proper object. + +-- Expect stdout -- +true +true +-- End -- + +-- Testcase -- +{% + obj = { foo: 1 }; + + print(obj.bar?.baz == null, "\n"); // obj.bar is null + print(obj.foo?.bar == null, "\n"); // obj.foo is not an object +%} +-- End -- + + +2. The `?.[…]` operator complements the `?.` one and applies the +same semantics to computed property accesses. + +-- Expect stdout -- +true +true +true +true +-- End -- + +-- Testcase -- +{% + obj = { foo: 1 }; + arr = [ 1, 2 ]; + + print(obj["bar"]?.["baz"] == null, "\n"); // obj.bar is null + print(obj["foo"]?.["bar"] == null, "\n"); // obj.foo is not an object + print(arr[0]?.["foo"] == null, "\n"); // arr[0] is not an object + print(foo?.[1] == null, "\n"); // foo is not an array +%} +-- End -- + + +3. The `?.(…)` function call operator yields `null` when the left-hand +side value is not a callable function value. + +-- Expect stdout -- +true +true +-- End -- + +-- Testcase -- +{% + foo = 1; + + print(foo?.(1, 2, 3) == null, "\n"); // foo is not a function + print(bar?.("test") == null, "\n"); // bar is null +%} +-- End -- + + +4. Optional chaining operators cannot be used on the left-hand side of +an assignment or increment/decrement expression. + +-- Expect stderr -- +Syntax error: Invalid left-hand side expression for assignment +In line 2, byte 13: + + ` obj?.foo = 1;` + Near here -----^ + + +-- End -- + +-- Testcase -- +{% + obj?.foo = 1; +%} +-- End -- + +-- Expect stderr -- +Syntax error: Invalid increment/decrement operand +In line 2, byte 7: + + ` obj?.foo++;` + ^-- Near here + + +-- End -- + +-- Testcase -- +{% + obj?.foo++; +%} +-- End -- -- cgit v1.2.3