diff --git a/CHANGELOG.md b/CHANGELOG.md index ba66c8778..a20eecf08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ -# v1.10.1-alpha0 +# v1.10.1-alpha1 * Improved `dict` export _(cosmetic)_, pr #462. +* Added alternative `wse(f(..))` using `f(..)!` syntax, issue #463, pr #464. # v1.10.0 diff --git a/grammar/grammar.py b/grammar/grammar.py index 2b5f6b6ac..eec2724c8 100644 --- a/grammar/grammar.py +++ b/grammar/grammar.py @@ -86,7 +86,7 @@ class LangDef(Grammar): thing = Sequence(x_thing, List(Sequence(name, ':', Optional(THIS))), '}') array = Sequence(x_array, List(THIS), ']') - function = Sequence(x_function, List(THIS), ')') + function = Sequence(x_function, List(THIS), Regex(r'\)\!?')) instance = Repeat(thing, mi=1, ma=1) # will be exported as `cleri_dup_t` enum_ = Sequence(x_thing, Choice(name, closure), '}') diff --git a/inc/langdef/langdef.h b/inc/langdef/langdef.h index ea6a48b1d..af57f7dbd 100644 --- a/inc/langdef/langdef.h +++ b/inc/langdef/langdef.h @@ -5,7 +5,7 @@ * should be used with the libcleri module. * * Source class: LangDef - * Created at: 2025-11-07 13:13:19 + * Created at: 2026-10-08 11:10:33 */ #ifndef CLERI_EXPORT_LANGDEF_H_ #define CLERI_EXPORT_LANGDEF_H_ diff --git a/inc/ti/version.h b/inc/ti/version.h index 15ff029dd..62f5ccb50 100644 --- a/inc/ti/version.h +++ b/inc/ti/version.h @@ -25,7 +25,7 @@ * "-rc0" * "" */ -#define TI_VERSION_PRE_RELEASE "-alpha0" +#define TI_VERSION_PRE_RELEASE "-alpha1" #define TI_MAINTAINER \ "Jeroen van der Heijden " diff --git a/itest/test_advanced.py b/itest/test_advanced.py index 7cd76ce7e..f03731eb8 100755 --- a/itest/test_advanced.py +++ b/itest/test_advanced.py @@ -1870,7 +1870,7 @@ async def test_closure_as_type_val(self, client): set_type('Test', { func: 'any' }); - .test = Test{func: || .x = 1}; + .test = Test{func: |x| .x = x}; .test.id(); """) @@ -1879,9 +1879,20 @@ async def test_closure_as_type_val(self, client): r"closures with side effects require a change but none is " r"created; use `wse\(...\)` to enforce a change;"): await client.query(f"""//ti - thing({id}).func(); // requires a change + thing({id}).func(123); // requires a change """) + res = await client.query(f"""//ti + wse(); thing({id}).func(123); + """) + self.assertEqual(res, 123) + + # Alternative wse(..) syntax, issue #463, pr #464 + res = await client.query(f"""//ti + thing({id}).func(123)!; + """) + self.assertEqual(res, 123) + async def test_future_or_wrap_ano_to_type(self, client): await client.query(r"""//ti set_type('A', {x: 'any'}); @@ -3065,6 +3076,64 @@ async def test_nested_arr_opt(self, client: Client): self.assertEqual(res, { 'arr': [{'id': 2, 'name': 'Iris'}, {'id': 3, 'name': 'Cato'}]}) + async def test_more_wse(self, client: Client): + q = client.query + await q("""//ti + // setters + new_procedure('set_x', |x| .x = x); + .set_x = |x| .x = x; + .x = 0; + + // getters + .get_x = || .x; + .wse = || .x; // fake test; + """) + + with self.assertRaisesRegex( + OperationError, + r"closures with side effects require a change but none is " + r"created; use `wse\(...\)` to enforce a change;"): + await client.query(f"""//ti + set_x(123); + """) + + with self.assertRaisesRegex( + OperationError, + r"closures with side effects require a change but none is " + r"created; use `wse\(...\)` to enforce a change;"): + await client.query(f"""//ti + .set_x(42); + """) + + res = await q("""//ti + .wse(); + change_id(); + """) + self.assertIs(res, None) + + res = await q("""//ti + set_x(123)!; + """) + self.assertEqual(res, 123) + + res = await q("""//ti + .set_x(42)!; + """) + self.assertEqual(res, 42) + + res = await q("""//ti + .set_x(42)!.bit_count(); + """) + self.assertEqual(res, 3) + + with self.assertRaisesRegex( + SyntaxError, + r"error at line 1, position 11, unexpected character `!`, " + r"expecting: ; or end_of_statement"): + await client.query(f"""//ti + .set_x(42)!!; + """) + if __name__ == '__main__': run_test(TestAdvanced()) diff --git a/src/langdef/langdef.c b/src/langdef/langdef.c index 705d2394f..fa1ed3180 100644 --- a/src/langdef/langdef.c +++ b/src/langdef/langdef.c @@ -5,7 +5,7 @@ * should be used with the libcleri module. * * Source class: LangDef - * Created at: 2025-11-07 13:13:19 + * Created at: 2026-10-08 11:10:33 */ #include @@ -107,7 +107,7 @@ cleri_grammar_t * compile_langdef(void) 3, x_function, cleri_list(CLERI_NONE, CLERI_THIS, cleri_token(CLERI_NONE, ","), 0, 0, 1), - cleri_token(CLERI_NONE, ")") + cleri_regex(CLERI_NONE, "^\\)\\!?") ); cleri_t * instance = cleri_dup(CLERI_GID_INSTANCE, thing); cleri_t * enum_ = cleri_sequence( diff --git a/src/ti/qbind.c b/src/ti/qbind.c index 4de2db307..7173fd1c1 100644 --- a/src/ti/qbind.c +++ b/src/ti/qbind.c @@ -1000,8 +1000,18 @@ static void qbind__function( ((FN__FLAG_ROOT|FN__FLAG_CHAIN) & flags & fmap->flags) && memcmp(fnname->str, fmap->name, n) == 0) ? fmap->flags : 0; + /* set build-in function if one found */ nd->data = fmflags ? fmap->fn : NULL; + /* update the value cache if no build-in function is found */ + q->immutable_n += nd->data == NULL; + + /* list (arguments) */ + nd = nd->children->next->children->next; + + /* set wse from `(..)!` syntax by checking node either `)` or `)!` */ + fmflags |= (nd->next->len == 2) ? FN__FLAG_EV_T|FN__FLAG_EV_C : 0; + /* may set wse flag */ q->flags |= ( ((FN__FLAG_EV_T|FN__FLAG_EV_C) & q->flags & fmflags) && @@ -1010,15 +1020,9 @@ static void qbind__function( ((~fmflags & FN__FLAG_XNSE) || (~q->flags & TI_QBIND_FLAG_NSE)) ) << TI_QBIND_BIT_WSE; - /* update the value cache if no build-in function is found */ - q->immutable_n += nd->data == NULL; - /* only used when no build-in function is found */ fnname->data = NULL; - /* list (arguments) */ - nd = nd->children->next->children->next; - if (fmflags & FN__FLAG_NSE) q->flags |= TI_QBIND_FLAG_NSE;