diff --git a/CHANGELOG.md b/CHANGELOG.md index 4723c62c1..522c0e06a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.10.3-alpha0 + +* Add `int.of_type(..)` to convert an ID to a stored intance, pr #466. + # v1.10.2 * Fixed `export()` with wse shortcut (`!`) syntax, issue #465. diff --git a/inc/doc.h b/inc/doc.h index c6b881a06..549829a33 100644 --- a/inc/doc.h +++ b/inc/doc.h @@ -241,6 +241,7 @@ #define DOC_FUTURE_ELSE DOC_SEE("data-types/future/else") #define DOC_FUTURE_THEN DOC_SEE("data-types/future/then") #define DOC_INT_BIT_COUNT DOC_SEE("data-types/int/bit_count") +#define DOC_INT_OF_TYPE DOC_SEE("data-types/int/of_type") #define DOC_LIST_CHOICE DOC_SEE("data-types/list/choice") #define DOC_LIST_CLEAR DOC_SEE("data-types/list/clear") #define DOC_LIST_COPY DOC_SEE("data-types/list/copy") diff --git a/inc/ti/fn/fnoftype.h b/inc/ti/fn/fnoftype.h new file mode 100644 index 000000000..731f85f7b --- /dev/null +++ b/inc/ti/fn/fnoftype.h @@ -0,0 +1,49 @@ +#include + + +static int do__f_of_type(ti_query_t * query, cleri_node_t * nd, ex_t * e) +{ + const int nargs = fn_get_nargs(nd); + ti_thing_t * thing; + ti_type_t * type; + uint64_t id; + + if (!ti_val_is_int(query->rval)) + return fn_call_try("of_type", query, nd, e); + + if (fn_nargs("of_type", DOC_INT_OF_TYPE, 1, nargs, e)) + return e->nr; + + id = VINT(query->rval); + ti_val_unsafe_drop(query->rval); + query->rval = NULL; + + if (ti_do_statement(query, nd->children, e) || + fn_arg_str("of_type", DOC_INT_OF_TYPE, 1, query->rval, e)) + return e->nr; + + type = query->collection + ? ti_types_by_raw(query->collection->types, (ti_raw_t *) query->rval) + : NULL; + if (!type) + { + (void) ti_raw_err_not_found((ti_raw_t *) query->rval, "type", e); + return e->nr; + } + + if (!(thing = ti_query_thing_from_id(query, id, e))) + return e->nr; + + if (thing->type_id != type->type_id) + { + ex_set(e, EX_TYPE_ERROR, + TI_THING_ID" is of type `%s`, not `%s`", + thing->id, ti_val_str((ti_val_t *) thing), type->name); + ti_decref(thing); + return e->nr; + } + + ti_val_unsafe_drop(query->rval); + query->rval = (ti_val_t *) thing; + return e->nr; +} diff --git a/inc/ti/version.h b/inc/ti/version.h index a2779cfc9..71a55ffa4 100644 --- a/inc/ti/version.h +++ b/inc/ti/version.h @@ -6,7 +6,7 @@ #define TI_VERSION_MAJOR 1 #define TI_VERSION_MINOR 10 -#define TI_VERSION_PATCH 2 +#define TI_VERSION_PATCH 3 /* The syntax version is used to test compatibility with functions * using the `ti_nodes_check_syntax()` function */ @@ -25,7 +25,7 @@ * "-rc0" * "" */ -#define TI_VERSION_PRE_RELEASE "" +#define TI_VERSION_PRE_RELEASE "-alpha0" #define TI_MAINTAINER \ "Jeroen van der Heijden " diff --git a/itest/test_collection_functions.py b/itest/test_collection_functions.py index a5f84f0da..c1d0147ac 100755 --- a/itest/test_collection_functions.py +++ b/itest/test_collection_functions.py @@ -6021,6 +6021,83 @@ async def test_bit_count(self, client): """) self.assertEqual(res, 42) + async def test_of_type(self, client): + with self.assertRaisesRegex( + LookupError, + 'type `nil` has no function `of_type`'): + await client.query('nil.of_type("T");') + + with self.assertRaisesRegex( + LookupError, + 'function `of_type` is undefined'): + await client.query('of_type("T");') + + with self.assertRaisesRegex( + NumArgumentsError, + 'function `of_type` takes 1 argument ' + 'but 0 were given'): + await client.query('(4).of_type();') + + with self.assertRaisesRegex( + TypeError, + r'function `of_type` expects argument 1 to be of ' + r'type `str` but got type `nil` instead'): + await client.query('(4).of_type(nil);') + + with self.assertRaisesRegex( + ValueError, + 'type name must follow the naming rules'): + await client.query('(4).of_type("");') + + with self.assertRaisesRegex( + LookupError, + 'type `T` not found'): + await client.query('(4).of_type("T");') + + with self.assertRaisesRegex( + LookupError, + 'type `T` not found'): + await client.query('(4).of_type("T");', scope='/t') + + t_id, o_id, f_id = await client.query("""//ti + set_type('T', {name: 'str<::unknown>'}); + set_type('F', {name: 'str<::unknown>'}); + .t = T{}; + .o = {}; + .f = F{}; + [.t.id(), .o.id(), .f.id()]; + """) + + with self.assertRaisesRegex( + TypeError, + f'#{o_id}` is of type `thing`, not `T`'): + await client.query("""//ti + o_id.of_type(kind); + """, o_id=o_id, kind='T') + + with self.assertRaisesRegex( + TypeError, + f'`#{f_id}` is of type `F`, not `T`'): + await client.query("""//ti + f_id.of_type(kind); + """, f_id=f_id, kind='T') + + with self.assertRaisesRegex( + LookupError, + 'collection `stuff` has no `thing` with id 123456'): + await client.query("""//ti + t_id.of_type(kind); + """, t_id=123456, kind='T') + + res = await client.query("""//ti + t_id.of_type(kind); + """, t_id=t_id, kind='T') + + self.assertEqual(res, { + "#": t_id, + "name": "unknown", + }) + async def test_type_all(self, client): q = client.query diff --git a/src/ti/qbind.c b/src/ti/qbind.c index 7173fd1c1..e5b88b734 100644 --- a/src/ti/qbind.c +++ b/src/ti/qbind.c @@ -181,6 +181,7 @@ #include #include #include +#include #include #include #include @@ -313,11 +314,11 @@ static void qbind__statement(ti_qbind_t * qbind, cleri_node_t * nd); */ enum { - TOTAL_KEYWORDS = 288, + TOTAL_KEYWORDS = 289, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 17, MIN_HASH_VALUE = 42, - MAX_HASH_VALUE = 831 + MAX_HASH_VALUE = 781 }; /* @@ -329,32 +330,32 @@ static inline unsigned int qbind__hash( { static unsigned short asso_values[] = { - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 18, 17, - 17, 832, 17, 832, 17, 832, 17, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 17, 832, 22, 67, 123, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 18, 17, + 17, 782, 18, 782, 18, 782, 17, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 17, 782, 22, 118, 123, 26, 19, 171, 217, 229, 17, 19, 66, 19, 61, - 22, 24, 78, 82, 18, 17, 18, 26, 283, 189, - 339, 182, 25, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832, 832, 832, 832, 832, - 832, 832, 832, 832, 832, 832 + 22, 24, 78, 82, 18, 17, 18, 26, 233, 189, + 339, 182, 25, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782, 782, 782, 782, 782, + 782, 782, 782, 782, 782, 782 }; register unsigned int hval = n; @@ -676,6 +677,7 @@ qbind__fmap_t qbind__fn_mapping[TOTAL_KEYWORDS] = { {.name="now", .fn=do__f_now, ROOT_NE}, {.name="nse", .fn=do__f_nse, XROOT_NSE}, {.name="num_arguments_err", .fn=do__f_num_arguments_err, ROOT_NE}, + {.name="of_type", .fn=do__f_of_type, CHAIN_NE}, {.name="one", .fn=do__f_one, CHAIN_NE}, {.name="operation_err", .fn=do__f_operation_err, ROOT_NE}, {.name="overflow_err", .fn=do__f_overflow_err, ROOT_NE},