From 301d4c25e7c2850b64d10a86df0d6657ca0cf560 Mon Sep 17 00:00:00 2001 From: Jeroen van der Heijden Date: Mon, 5 Feb 2024 09:53:35 +0100 Subject: [PATCH] Added bitwise NOT (~) (#360) * Added bitnot * Fix message --- CHANGELOG.md | 1 + grammar/grammar.py | 2 +- inc/langdef/langdef.h | 4 +-- inc/ti/version.h | 2 +- itest/lib/__init__.py | 4 +++ itest/test_advanced.py | 5 --- itest/test_operators.py | 77 +++++++++++++++++++++++++++++++++++++---- src/langdef/langdef.c | 8 ++--- src/ti/preopr.c | 64 +++++++++++++++++++++++++++++++--- 9 files changed, 142 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 052ef180d..e63351291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ _(This was marked as deprecated since v0.10.1)_ * Added set operators `<=`, `<`, `>=`, `>` for subset, proper subset, superset and proper superset checking. * Added range `<..>` support for UTF8 type property definitions. +* Added bitwise NOT (`~`) operator. # v1.4.16 diff --git a/grammar/grammar.py b/grammar/grammar.py index a6dc48ba0..1e95a15fb 100644 --- a/grammar/grammar.py +++ b/grammar/grammar.py @@ -48,7 +48,7 @@ class LangDef(Grammar): x_function = Token('(') x_index = Token('[') x_parenthesis = Token('(') - x_preopr = Regex(r'(\s*!|\s*[\-+](?=[^0-9]))*') + x_preopr = Regex(r'(\s*~)*(\s*!|\s*[\-+](?=[^0-9]))*') x_ternary = Token('?') x_thing = Token('{') x_template = Token('`') diff --git a/inc/langdef/langdef.h b/inc/langdef/langdef.h index c139c1b31..da278d235 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: 2024-01-20 12:46:21 + * Created at: 2024-02-04 14:13:28 */ #ifndef CLERI_EXPORT_LANGDEF_H_ #define CLERI_EXPORT_LANGDEF_H_ @@ -15,7 +15,7 @@ cleri_grammar_t * compile_langdef(void); cleri_grammar_t * compile_compat(void); -enum cleri_grammar_compat_ids { +enum cleri_grammar_ids { CLERI_NONE, // used for objects with no name CLERI_GID_ARRAY, CLERI_GID_ASSIGN, diff --git a/inc/ti/version.h b/inc/ti/version.h index bd8d0eebd..a14692931 100644 --- a/inc/ti/version.h +++ b/inc/ti/version.h @@ -25,7 +25,7 @@ * "-rc0" * "" */ -#define TI_VERSION_PRE_RELEASE "-alpha12" +#define TI_VERSION_PRE_RELEASE "-alpha13" #define TI_MAINTAINER \ "Jeroen van der Heijden " diff --git a/itest/lib/__init__.py b/itest/lib/__init__.py index d6b86f698..07fa04dc6 100644 --- a/itest/lib/__init__.py +++ b/itest/lib/__init__.py @@ -74,3 +74,7 @@ def run_test(test: TestBase): loop = asyncio.get_event_loop() cleanup() loop.run_until_complete(_run_test(test)) + + +INT_MIN = -9223372036854775808 +INT_MAX = 9223372036854775807 diff --git a/itest/test_advanced.py b/itest/test_advanced.py index afe32aab1..2bb79f56e 100755 --- a/itest/test_advanced.py +++ b/itest/test_advanced.py @@ -1,13 +1,9 @@ #!/usr/bin/env python -import asyncio -import pickle -import time from lib import run_test from lib import default_test_setup from lib.testbase import TestBase from lib.client import get_client from thingsdb.exceptions import AssertionError -from thingsdb.exceptions import BadDataError from thingsdb.exceptions import LookupError from thingsdb.exceptions import MaxQuotaError from thingsdb.exceptions import NumArgumentsError @@ -16,7 +12,6 @@ from thingsdb.exceptions import SyntaxError from thingsdb.exceptions import TypeError from thingsdb.exceptions import ValueError -from thingsdb.exceptions import ZeroDivisionError class TestAdvanced(TestBase): diff --git a/itest/test_operators.py b/itest/test_operators.py index de7fe5d84..73b94d33d 100755 --- a/itest/test_operators.py +++ b/itest/test_operators.py @@ -1,14 +1,10 @@ #!/usr/bin/env python -import asyncio -import pickle -import time -from lib import run_test +from lib import run_test, INT_MAX, INT_MIN from lib import default_test_setup from lib.testbase import TestBase from lib.client import get_client -from thingsdb.exceptions import AssertionError -from thingsdb.exceptions import BadDataError -from thingsdb.exceptions import LookupError +from thingsdb.exceptions import TypeError +from thingsdb.exceptions import SyntaxError from thingsdb.exceptions import OverflowError @@ -301,6 +297,73 @@ async def test_preopr(self, client): str(|| - !! +5); '''), "|| -!!+5") + async def test_bit_operations(self, client): + res = await client.query(r"""//ti + [ + ~15, + ~-15, + ~~~---88, + ~!6, + ~!!-6, + ~INT_MIN, + ~INT_MAX, + ~~INT_MIN, + ~~INT_MAX, + ]; + """) + self.assertEqual(res, [ + ~15, + ~-15, + ~~~-88, + ~0, + ~1, + ~INT_MIN, + ~INT_MAX, + ~~INT_MIN, + ~~INT_MAX, + ]) + + with self.assertRaisesRegex( + OverflowError, + r'integer overflow'): + await client.query(r"""//ti + ~-INT_MIN; + """) + + with self.assertRaisesRegex( + TypeError, + r'`-/\+` not supported by type `str`'): + await client.query(r"""//ti + +"test"; + """) + + with self.assertRaisesRegex( + TypeError, + r'`~` not supported by type `str`'): + await client.query(r"""//ti + ~"test"; + """) + + with self.assertRaisesRegex( + TypeError, + r'~` not supported by type `float`'): + await client.query(r"""//ti + ~~1.0; + """) + + with self.assertRaisesRegex( + SyntaxError, + r'error at line 1, position 1, unexpected character `~`'): + await client.query(r"""//ti + +~1; + """) + + with self.assertRaisesRegex( + SyntaxError, + r'error at line 1, position 1, unexpected character `~`'): + await client.query(r"""//ti + !~1; + """) if __name__ == '__main__': run_test(TestOperators()) diff --git a/src/langdef/langdef.c b/src/langdef/langdef.c index 8c85fcdb6..158ce7631 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: 2024-01-20 12:46:21 + * Created at: 2024-02-04 14:13:28 */ #include @@ -27,7 +27,7 @@ cleri_grammar_t * compile_langdef(void) cleri_t * x_function = cleri_token(CLERI_GID_X_FUNCTION, "("); cleri_t * x_index = cleri_token(CLERI_GID_X_INDEX, "["); cleri_t * x_parenthesis = cleri_token(CLERI_GID_X_PARENTHESIS, "("); - cleri_t * x_preopr = cleri_regex(CLERI_GID_X_PREOPR, "^(\\s*!|\\s*[\\-+](?=[^0-9]))*"); + cleri_t * x_preopr = cleri_regex(CLERI_GID_X_PREOPR, "^(\\s*~)*(\\s*!|\\s*[\\-+](?=[^0-9]))*"); cleri_t * x_ternary = cleri_token(CLERI_GID_X_TERNARY, "?"); cleri_t * x_thing = cleri_token(CLERI_GID_X_THING, "{"); cleri_t * x_template = cleri_token(CLERI_GID_X_TEMPLATE, "`"); @@ -194,7 +194,7 @@ cleri_grammar_t * compile_langdef(void) CLERI_GID_BLOCK, 3, x_block, - cleri_list(CLERI_NONE, CLERI_THIS, cleri_repeat(CLERI_NONE, cleri_token(CLERI_NONE, ";"), 1, 0), 1, 0, 1), + cleri_list(CLERI_NONE, CLERI_THIS, cleri_repeat(CLERI_NONE, cleri_token(CLERI_NONE, ";"), 0, 0), 1, 0, 1), cleri_token(CLERI_NONE, "}") ); cleri_t * parenthesis = cleri_sequence( @@ -286,7 +286,7 @@ cleri_grammar_t * compile_langdef(void) ), operations ); - cleri_t * START = cleri_list(CLERI_GID_START, statement, cleri_repeat(CLERI_NONE, cleri_token(CLERI_NONE, ";"), 1, 0), 0, 0, 1); + cleri_t * START = cleri_list(CLERI_GID_START, statement, cleri_repeat(CLERI_NONE, cleri_token(CLERI_NONE, ";"), 0, 0), 0, 0, 1); cleri_ref_set(chain, cleri_sequence( CLERI_GID_CHAIN, 4, diff --git a/src/ti/preopr.c b/src/ti/preopr.c index 76bb9b765..8865816af 100644 --- a/src/ti/preopr.c +++ b/src/ti/preopr.c @@ -17,6 +17,8 @@ enum PO__NEGATIVE, PO__AS_NUM, PO__CHK_NUM, + PO__BIT_INV, + PO__BITWISE, }; enum @@ -26,6 +28,8 @@ enum PO__FLAG_NEGATIVE =1<tp != TI_VAL_BOOL) { ex_set(e, EX_TYPE_ERROR, - "`-/+` not supported by type `%s`", + "`%s` not supported by type `%s`", + (preopr & PO__FLAG_BITWISE) ? "~" : "-/+", ti_val_str(v)); return e->nr; } @@ -107,14 +121,54 @@ int ti_preopr_calc(int preopr, ti_val_t ** val, ex_t * e) { _Bool b = (preopr & PO__FLAG_FALSE) ^ ti_val_as_bool(v); ti_val_unsafe_drop(v); - *val = preopr & PO__FLAG_AS_NUM + *val = (preopr & PO__FLAG_BIT_INV) + ? (ti_val_t *) ti_vint_create(preopr & PO__FLAG_NEGATIVE ? ~((int) -b) : ~((int) b)) + : (preopr & PO__FLAG_AS_NUM) ? (ti_val_t *) ti_vint_create(preopr & PO__FLAG_NEGATIVE ? -b : b) : (ti_val_t *) ti_vbool_get(b); return 0; } - assert(preopr & PO__FLAG_AS_NUM); + if (preopr & PO__FLAG_BITWISE) switch(v->tp) + { + case TI_VAL_INT: + if (preopr & PO__FLAG_BIT_INV) + { + int64_t i = VINT(v); + if (preopr & PO__FLAG_NEGATIVE) + { + if (i == LLONG_MIN) + { + ex_set(e, EX_OVERFLOW, "integer overflow"); + return e->nr; + } + i = -i; + } + ti_val_unsafe_drop(v); + *val = (ti_val_t *) ti_vint_create(~i); + if (!*val) + ex_set_mem(e); + return e->nr; + } + break; + case TI_VAL_FLOAT: + ex_set(e, EX_TYPE_ERROR, + "`~` not supported by type `%s`", + ti_val_str(v)); + return e->nr; + case TI_VAL_BOOL: + if (preopr & PO__FLAG_BIT_INV) + { + _Bool b = VBOOL(v); + if (preopr & PO__FLAG_NEGATIVE) + b = -b; + ti_val_unsafe_drop(v); + *val = (ti_val_t *) ti_vint_create(~((int) b)); + return e->nr; + } + break; + } /* * No ! is used in the sequence, so the number is unchanged, or the