diff --git a/CHANGELOG.md b/CHANGELOG.md index dca6f24e..1776787d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ -# v1.8.2 +# v1.8.2-alpha3 -* Allow converting `mpdata` to `bytes, issue #427. +* Allow converting `mpdata` to `bytes`, issue #427. * Fixed: map cache for nested structure not cleared, issue #428. +* Add global `enum` definition, issue #429. +* Add _anonymous_ wrap only type syntax (`&{..}`), pr #430. +* Added `ano()` function, pr #430. +* Definition `*str` now exposes enumerator keys, issue #431. # v1.8.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2923ff8e..82fdc9e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,7 @@ set(SOURCES src/cleri/tokens.c src/cleri/version.c src/ti/access.c + src/ti/ano.c src/ti/api.c src/ti/archfile.c src/ti/archive.c @@ -226,6 +227,7 @@ set(SOURCES src/ti/vint.c src/ti/vset.c src/ti/vtask.c + src/ti/wano.c src/ti/warn.c src/ti/watch.c src/ti/web.c diff --git a/grammar/grammar.py b/grammar/grammar.py index 72dccb80..2b5f6b6a 100644 --- a/grammar/grammar.py +++ b/grammar/grammar.py @@ -51,6 +51,7 @@ class LangDef(Grammar): x_preopr = Regex(r'(\s*~)*(\s*!|\s*[\-+](?=[^0-9]))*') x_ternary = Token('?') x_thing = Token('{') + x_ano = Token('&{') x_template = Token('`') template = Sequence( @@ -81,6 +82,7 @@ class LangDef(Grammar): chain = Ref() closure = Sequence(x_closure, List(var), '|', THIS) + t_ano = Sequence(x_ano, List(Sequence(name, ':', Optional(THIS))), '}') thing = Sequence(x_thing, List(Sequence(name, ':', Optional(THIS))), '}') array = Sequence(x_array, List(THIS), ']') @@ -184,6 +186,7 @@ class LangDef(Grammar): t_int, t_string, t_regex, + t_ano, # end immutable values template, var_opt_more, diff --git a/inc/doc.h b/inc/doc.h index e1469a1a..c185bd60 100644 --- a/inc/doc.h +++ b/inc/doc.h @@ -13,6 +13,7 @@ /* Collection API */ #define DOC_ALT_RAISE DOC_SEE("collection-api/alt_raise") +#define DOC_ANO DOC_SEE("collection-api/ano") #define DOC_ASSERT DOC_SEE("collection-api/assert") #define DOC_BASE64_DECODE DOC_SEE("collection-api/base64_decode") #define DOC_BASE64_ENCODE DOC_SEE("collection-api/base64_encode") diff --git a/inc/doc.inline.h b/inc/doc.inline.h index 9059226c..d0bb3db5 100644 --- a/inc/doc.inline.h +++ b/inc/doc.inline.h @@ -145,6 +145,7 @@ static inline const char * doc_copy(ti_val_t * val) case TI_VAL_WRAP: return DOC_WTYPE_COPY; case TI_VAL_ARR: return DOC_LIST_COPY; case TI_VAL_SET: return DOC_SET_COPY; + case TI_VAL_WANO: return DOC_WTYPE_COPY; default: return NULL; } } @@ -157,6 +158,7 @@ static inline const char * doc_dup(ti_val_t * val) case TI_VAL_WRAP: return DOC_WTYPE_DUP; case TI_VAL_ARR: return DOC_LIST_DUP; case TI_VAL_SET: return DOC_SET_DUP; + case TI_VAL_WANO: return DOC_WTYPE_DUP; default: return NULL; } } diff --git a/inc/langdef/langdef.h b/inc/langdef/langdef.h index 8e7968d6..ea6a48b1 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-02-11 20:55:25 + * Created at: 2025-11-07 13:13:19 */ #ifndef CLERI_EXPORT_LANGDEF_H_ #define CLERI_EXPORT_LANGDEF_H_ @@ -58,6 +58,7 @@ enum cleri_grammar_ids { CLERI_GID_STATEMENT, CLERI_GID_TEMPLATE, CLERI_GID_THING, + CLERI_GID_T_ANO, CLERI_GID_T_FALSE, CLERI_GID_T_FLOAT, CLERI_GID_T_INT, @@ -67,6 +68,7 @@ enum cleri_grammar_ids { CLERI_GID_T_TRUE, CLERI_GID_VAR, CLERI_GID_VAR_OPT_MORE, + CLERI_GID_X_ANO, CLERI_GID_X_ARRAY, CLERI_GID_X_ASSIGN, CLERI_GID_X_BLOCK, diff --git a/inc/ti/ano.h b/inc/ti/ano.h new file mode 100644 index 00000000..d899a16b --- /dev/null +++ b/inc/ti/ano.h @@ -0,0 +1,55 @@ +/* + * ti/ano.h + */ +#ifndef TI_ANO_H_ +#define TI_ANO_H_ + +#include +#include +#include +#include +#include + +#define ANO_T(__x) ((ti_ano_t *) (__x))->type + +ti_ano_t * ti_ano_new(void); +int ti_ano_init( + ti_ano_t * ano, + ti_collection_t * collection, + ti_raw_t * spec_raw, + ex_t * e); +ti_ano_t * ti_ano_from_raw( + ti_collection_t * collection, + ti_raw_t * spec_raw, + ex_t * e); +ti_ano_t * ti_ano_create( + ti_collection_t * collection, + const unsigned char * bin, + size_t n, + ex_t * e); +void ti_ano_destroy(ti_ano_t * ano); + +static inline int ti_ano_to_client_pk( + ti_ano_t * ano, + msgpack_packer * pk) +{ + return mp_pack_append(pk, ano->spec_raw->data, ano->spec_raw->n); +} + +static inline _Bool ti_ano_uninitialized(ti_ano_t * ano) +{ + return !ano->spec_raw; +} + +static inline int ti_ano_to_store_pk( + ti_ano_t * ano, + msgpack_packer * pk) +{ + return mp_pack_ext( + pk, + MPACK_EXT_ANO, + ano->spec_raw->data, + ano->spec_raw->n); +} + +#endif /* TI_ANO_H_ */ diff --git a/inc/ti/ano.t.h b/inc/ti/ano.t.h new file mode 100644 index 00000000..802ca207 --- /dev/null +++ b/inc/ti/ano.t.h @@ -0,0 +1,21 @@ +/* + * ti/ano.t.h + */ +#ifndef TI_ANO_T_H_ +#define TI_ANO_T_H_ + +typedef struct ti_ano_s ti_ano_t; + +#include +#include + +struct ti_ano_s +{ + uint32_t ref; + uint8_t tp; + int:24; + ti_raw_t * spec_raw; /* mpdata, in log use */ + ti_type_t * type; +}; + +#endif /* TI_ANO_T_H_ */ diff --git a/inc/ti/collection.t.h b/inc/ti/collection.t.h index 994d5b54..11e78147 100644 --- a/inc/ti/collection.t.h +++ b/inc/ti/collection.t.h @@ -34,6 +34,7 @@ struct ti_collection_s vec_t * access; /* ti_auth_t */ smap_t * procedures; /* ti_procedure_t */ smap_t * named_rooms; /* weak map for ti_room_t (only named rooms) */ + smap_t * ano_types; /* weak map for ti_ano_t */ ti_thing_t * root; /* without extra reference */ ti_types_t * types; ti_enums_t * enums; diff --git a/inc/ti/do.h b/inc/ti/do.h index 0d9c80cb..435091ca 100644 --- a/inc/ti/do.h +++ b/inc/ti/do.h @@ -41,6 +41,7 @@ int ti_do_prepare_for_loop(ti_query_t * query, cleri_node_t * vars_nd); int ti_do_init(void); void ti_do_drop(void); + static inline int ti_do_statement( ti_query_t * query, cleri_node_t * nd, diff --git a/inc/ti/fn/fn.h b/inc/ti/fn/fn.h index cbfb4ffd..1507b58f 100644 --- a/inc/ti/fn/fn.h +++ b/inc/ti/fn/fn.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -246,6 +247,23 @@ static inline int fn_arg_str( return e->nr; } +static inline int fn_arg_str_ano( + const char * name, + const char * doc, + int argn, + ti_val_t * val, + ex_t * e) +{ + if (!ti_val_is_str_ano(val)) + ex_set(e, EX_TYPE_ERROR, + "function `%s` expects argument %d to be of " + "type `"TI_VAL_STR_S"` or `"TI_VAL_ANO_S"` " + "but got type `%s` instead%s", + name, argn, ti_val_str(val), doc); + return e->nr; +} + + static inline int fn_arg_bytes( const char * name, const char * doc, @@ -662,6 +680,71 @@ static int fn_call_w_try_n( return e->nr; } +static int fn_call_wa_try_n( + const char * name, + size_t n, + ti_query_t * query, + cleri_node_t * nd, + ex_t * e) +{ + ti_name_t * name_; + ti_method_t * method; + ti_wano_t * wano = (ti_wano_t *) query->rval; + ti_thing_t * thing = wano->thing; + + name_ = ti_names_weak_get_strn(name, n); + if (!name_) + goto no_method_err; + + method = ti_type_get_method(wano->ano->type, name_); + if (!method) + goto no_method_err; + + ti_incref(thing); + ti_val_unsafe_drop(query->rval); + query->rval = (ti_val_t *) thing; + + return ti_method_call(method, wano->ano->type, query, nd, e); + +no_method_err: + ex_set(e, EX_LOOKUP_ERROR, + "type `%s` has no method `%.*s`", + wano->ano->type->name, (int) n, name); + return e->nr; +} + +static int fn_call_a_try_n( + const char * name, + size_t n, + ti_query_t * query, + cleri_node_t * nd, + ex_t * e) +{ + ti_name_t * name_; + ti_method_t * method; + ti_ano_t * ano = (ti_ano_t *) query->rval; + + name_ = ti_names_weak_get_strn(name, n); + if (!name_) + goto no_method_err; + + method = ti_type_get_method(ano->type, name_); + if (!method) + goto no_method_err; + + + ti_incref(method->closure); + ti_val_unsafe_drop(query->rval); + query->rval = (ti_val_t *) method->closure; + return fn_call(query, nd, e); + +no_method_err: + ex_set(e, EX_LOOKUP_ERROR, + "type `%s` has no method `%.*s`", + ano->type->name, (int) n, name); + return e->nr; +} + static int fn_call_f_try_n( const char * name, size_t n, @@ -729,6 +812,12 @@ static int fn_call_try_n( if (ti_val_is_wrap(query->rval)) return fn_call_w_try_n(name, n, query, nd, e); + if (ti_val_is_wano(query->rval)) + return fn_call_wa_try_n(name, n, query, nd, e); + + if (ti_val_is_ano(query->rval)) + return fn_call_a_try_n(name, n, query, nd, e); + if (ti_val_is_module(query->rval)) return fn_call_f_try_n(name, n, query, nd, e); diff --git a/inc/ti/fn/fnano.h b/inc/ti/fn/fnano.h new file mode 100644 index 00000000..b3b07ebe --- /dev/null +++ b/inc/ti/fn/fnano.h @@ -0,0 +1,40 @@ +#include + +static int do__f_ano(ti_query_t * query, cleri_node_t * nd, ex_t * e) +{ + const int nargs = fn_get_nargs(nd); + ti_raw_t * spec_raw; + + if (fn_not_collection_scope("ano", query, e) || + fn_nargs("ano", DOC_ANO, 1, nargs, e) || + ti_do_statement(query, nd->children, e) || + fn_arg_thing("ano", DOC_ANO, 1, query->rval, e)) + return e->nr; + + spec_raw = ti_type_spec_raw_from_thing( + (ti_thing_t *) query->rval, + query->rval, + e); + if (!spec_raw) + return e->nr; + + ti_val_unsafe_drop(query->rval); + + /* first try cache for equal ano */ + query->rval = smap_getn( + query->collection->ano_types, + (const char *) spec_raw->data, + spec_raw->n); + + /* if no success, try new one */ + if (query->rval) + ti_incref(query->rval); + else + query->rval = (ti_val_t *) ti_ano_from_raw( + query->collection, + spec_raw, + e); + + ti_val_unsafe_drop((ti_val_t *) spec_raw); + return e->nr; +} diff --git a/inc/ti/fn/fnclosure.h b/inc/ti/fn/fnclosure.h index c66f4c67..699ee241 100644 --- a/inc/ti/fn/fnclosure.h +++ b/inc/ti/fn/fnclosure.h @@ -62,6 +62,8 @@ static int do__f_closure_new(ti_query_t * query, cleri_node_t * nd, ex_t * e) case TI_VAL_ERROR: case TI_VAL_MEMBER: case TI_VAL_MPDATA: + case TI_VAL_ANO: + case TI_VAL_WANO: case TI_VAL_FUTURE: case TI_VAL_MODULE: ex_set(e, EX_TYPE_ERROR, diff --git a/inc/ti/fn/fncopy.h b/inc/ti/fn/fncopy.h index afe3e97b..16dcacf4 100644 --- a/inc/ti/fn/fncopy.h +++ b/inc/ti/fn/fncopy.h @@ -28,7 +28,7 @@ static int do__f_copy(ti_query_t * query, cleri_node_t * nd, ex_t * e) query->rval = val; } - if (ti_val_is_wrap(query->rval) && ti_wrap_cp(query, deep, e)) + if (ti_val_is_wrap_wano(query->rval) && ti_wrap_cp(query, deep, e)) return e->nr; /* quit without fail0, e is set*/ else if (ti_val_copy(&query->rval, NULL, NULL, deep)) ex_set_mem(e); diff --git a/inc/ti/fn/fnid.h b/inc/ti/fn/fnid.h index 04db515a..98eb32f8 100644 --- a/inc/ti/fn/fnid.h +++ b/inc/ti/fn/fnid.h @@ -61,6 +61,26 @@ static int do__f_id_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) return e->nr; } +static int do__f_id_wano(ti_query_t * query, cleri_node_t * nd, ex_t * e) +{ + const int nargs = fn_get_nargs(nd); + ti_wano_t * wtype; + + if (fn_nargs("id", DOC_WTYPE_ID, 0, nargs, e)) + return e->nr; + + wtype = (ti_wano_t *) query->rval; + query->rval = wtype->thing->id + ? (ti_val_t *) ti_vint_create((int64_t) wtype->thing->id) + : (ti_val_t *) ti_nil_get(); + + if (!query->rval) + ex_set_mem(e); + + ti_val_unsafe_drop((ti_val_t *) wtype); + return e->nr; +} + static int do__f_id_task(ti_query_t * query, cleri_node_t * nd, ex_t * e) { const int nargs = fn_get_nargs(nd); @@ -89,6 +109,8 @@ static inline int do__f_id(ti_query_t * query, cleri_node_t * nd, ex_t * e) ? do__f_id_room(query, nd, e) : ti_val_is_wrap(query->rval) ? do__f_id_wrap(query, nd, e) + : ti_val_is_wano(query->rval) + ? do__f_id_wano(query, nd, e) : ti_val_is_task(query->rval) ? do__f_id_task(query, nd, e) : fn_call_try("id", query, nd, e); diff --git a/inc/ti/fn/fnmapid.h b/inc/ti/fn/fnmapid.h index 6edff9ff..55450e21 100644 --- a/inc/ti/fn/fnmapid.h +++ b/inc/ti/fn/fnmapid.h @@ -47,6 +47,11 @@ static int do__f_map_id(ti_query_t * query, cleri_node_t * nd, ex_t * e) ti_wrap_t * wtype = (ti_wrap_t *) val; id = wtype->thing->id; } + else if (ti_val_is_wano(val)) + { + ti_wano_t * wtype = (ti_wano_t *) val; + id = wtype->thing->id; + } else if (ti_val_is_room(val)) { ti_room_t * room = (ti_room_t *) val; diff --git a/inc/ti/fn/fnmapwrap.h b/inc/ti/fn/fnmapwrap.h index 0bccab1e..96954c78 100644 --- a/inc/ti/fn/fnmapwrap.h +++ b/inc/ti/fn/fnmapwrap.h @@ -3,18 +3,23 @@ typedef struct { ti_type_t * type; + ti_ano_t * ano; ti_varr_t * varr; ex_t * e; } map_wrap__walk_t; static int map_wrap__walk_set(ti_thing_t * thing, map_wrap__walk_t * w) { - ti_wrap_t * wrap; + void * wrap; if (w->type) { wrap = ti_wrap_create(thing, w->type->type_id); } + else if (w->ano) + { + wrap = ti_wano_create(thing, w->ano); + } else if (ti_thing_is_instance(thing)) { wrap = ti_wrap_create(thing, thing->via.type->type_id); @@ -45,6 +50,7 @@ static int do__f_map_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) size_t n; ti_varr_t * varr; ti_type_t * type = NULL; + ti_ano_t * ano = NULL; ti_val_t * iterable; doc = doc_map_wrap(query->rval); @@ -61,21 +67,27 @@ static int do__f_map_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) if (nargs == 1) { if (ti_do_statement(query, nd->children, e) || - fn_arg_str("map_wrap", doc, 1, query->rval, e)) + fn_arg_str_ano("map_wrap", doc, 1, query->rval, e)) goto fail0; - type = query->collection - ? ti_types_by_raw( - query->collection->types, - (ti_raw_t *) query->rval) - : NULL; - if (!type) + if (ti_val_is_ano(query->rval)) { - (void) ti_raw_err_not_found((ti_raw_t *) query->rval, "type", e); - goto fail0; + ano = (ti_ano_t *) query->rval; + } + else + { + 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); + goto fail0; + } + ti_val_unsafe_drop(query->rval); } - - ti_val_unsafe_drop(query->rval); query->rval = NULL; } @@ -90,7 +102,7 @@ static int do__f_map_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) case TI_VAL_ARR: for (vec_each(VARR(iterable), ti_val_t, val)) { - ti_wrap_t * wrap; + void * wrap; ti_thing_t * thing; if (!ti_val_is_thing(val)) @@ -109,6 +121,10 @@ static int do__f_map_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) { wrap = ti_wrap_create(thing, type->type_id); } + else if (ano) + { + wrap = ti_wano_create(thing, ano); + } else if (ti_thing_is_instance(thing)) { wrap = ti_wrap_create(thing, thing->via.type->type_id); @@ -134,6 +150,7 @@ static int do__f_map_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) { map_wrap__walk_t w = { .type=type, + .ano=ano, .varr=varr, .e=e, }; @@ -145,11 +162,13 @@ static int do__f_map_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) ti_val_unsafe_drop(iterable); query->rval = (ti_val_t *) varr; + ti_val_drop((ti_val_t *) ano); return e->nr; fail1: ti_val_unsafe_drop((ti_val_t *) varr); fail0: ti_val_unsafe_drop(iterable); + ti_val_drop((ti_val_t *) ano); return e->nr; } diff --git a/inc/ti/fn/fnsearch.h b/inc/ti/fn/fnsearch.h index 2973df25..61df6e7b 100644 --- a/inc/ti/fn/fnsearch.h +++ b/inc/ti/fn/fnsearch.h @@ -202,9 +202,13 @@ static int do__search_thing( }; return imap_walk(VSET(val), (imap_cb) search__walk_set, &ws); } - case TI_VAL_TEMPLATE: + case TI_VAL_ANO: + return 0; + case TI_VAL_WANO: + return search__do_thing(((ti_wano_t *) val)->thing, root, key, w); case TI_VAL_FUTURE: case TI_VAL_MODULE: + case TI_VAL_TEMPLATE: return 0; } assert(0); diff --git a/inc/ti/fn/fnunwrap.h b/inc/ti/fn/fnunwrap.h index 4f2a277c..45ef3cbf 100644 --- a/inc/ti/fn/fnunwrap.h +++ b/inc/ti/fn/fnunwrap.h @@ -5,13 +5,15 @@ static int do__f_unwrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) const int nargs = fn_get_nargs(nd); ti_thing_t * thing; - if (!ti_val_is_wrap(query->rval)) + if (!ti_val_is_wrap_wano(query->rval)) return fn_call_try("unwrap", query, nd, e); if (fn_nargs("unwrap", DOC_WTYPE_UNWRAP, 0, nargs, e)) return e->nr; - thing = ((ti_wrap_t *) query->rval)->thing; + thing = ti_val_is_wrap(query->rval) + ? ((ti_wrap_t *) query->rval)->thing + : ((ti_wano_t *) query->rval)->thing; ti_incref(thing); ti_val_unsafe_drop(query->rval); query->rval = (ti_val_t *) thing; diff --git a/inc/ti/fn/fnwrap.h b/inc/ti/fn/fnwrap.h index dbd63f1f..c601aed4 100644 --- a/inc/ti/fn/fnwrap.h +++ b/inc/ti/fn/fnwrap.h @@ -3,8 +3,8 @@ static int do__f_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) { const int nargs = fn_get_nargs(nd); - ti_type_t * type; ti_thing_t * thing; + ti_type_t * type; if (!ti_val_is_thing(query->rval)) return fn_call_try("wrap", query, nd, e); @@ -18,9 +18,17 @@ static int do__f_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) if (nargs == 1) { if (ti_do_statement(query, nd->children, e) || - fn_arg_str("wrap", DOC_THING_WRAP, 1, query->rval, e)) + fn_arg_str_ano("wrap", DOC_THING_WRAP, 1, query->rval, e)) goto fail0; + if (ti_val_is_ano(query->rval)) + { + ti_ano_t * ano = (ti_ano_t *) query->rval; + query->rval = (ti_val_t *) ti_wano_create(thing, ano); + ti_val_unsafe_drop((ti_val_t *) ano); + goto done; + } + type = query->collection ? ti_types_by_raw(query->collection->types, (ti_raw_t *) query->rval) : NULL; @@ -47,6 +55,7 @@ static int do__f_wrap(ti_query_t * query, cleri_node_t * nd, ex_t * e) } query->rval = (ti_val_t *) ti_wrap_create(thing, type->type_id); +done: if (!query->rval) ex_set_mem(e); diff --git a/inc/ti/forloop.h b/inc/ti/forloop.h index ace83a4c..5e899ab7 100644 --- a/inc/ti/forloop.h +++ b/inc/ti/forloop.h @@ -39,7 +39,7 @@ typedef int (*ti_forloop_t) ( cleri_node_t *, ex_t *); -static const ti_forloop_t ti_forloop_callbacks[22] = { +static const ti_forloop_t ti_forloop_callbacks[24] = { ti_forloop_no_iter, /* TI_VAL_NIL */ ti_forloop_no_iter, /* TI_VAL_INT */ ti_forloop_no_iter, /* TI_VAL_FLOAT */ @@ -59,6 +59,8 @@ static const ti_forloop_t ti_forloop_callbacks[22] = { ti_forloop_no_iter, /* TI_VAL_MEMBER */ ti_forloop_no_iter, /* TI_VAL_MPDATA */ ti_forloop_no_iter, /* TI_VAL_CLOSURE */ + ti_forloop_no_iter, /* TI_VAL_ANO */ + ti_forloop_no_iter, /* TI_VAL_WANO */ ti_forloop_no_iter, /* TI_VAL_FUTURE */ ti_forloop_no_iter, /* TI_VAL_MODULE */ ti_forloop_no_iter, /* TI_VAL_TEMPLATE */ diff --git a/inc/ti/opr/oprinc.h b/inc/ti/opr/oprinc.h index f4c211e8..41a26d3d 100644 --- a/inc/ti/opr/oprinc.h +++ b/inc/ti/opr/oprinc.h @@ -35,6 +35,8 @@ typedef enum OPR_NIL_SET =TI_VAL_NIL<<5|TI_VAL_SET, OPR_NIL_ERROR =TI_VAL_NIL<<5|TI_VAL_ERROR, OPR_NIL_MEMBER =TI_VAL_NIL<<5|TI_VAL_MEMBER, + OPR_NIL_ANO =TI_VAL_NIL<<5|TI_VAL_ANO, + OPR_NIL_WANO =TI_VAL_NIL<<5|TI_VAL_WANO, OPR_INT_NIL =TI_VAL_INT<<5|TI_VAL_NIL, OPR_INT_INT =TI_VAL_INT<<5|TI_VAL_INT, @@ -51,6 +53,8 @@ typedef enum OPR_INT_SET =TI_VAL_INT<<5|TI_VAL_SET, OPR_INT_ERROR =TI_VAL_INT<<5|TI_VAL_ERROR, OPR_INT_MEMBER =TI_VAL_INT<<5|TI_VAL_MEMBER, + OPR_INT_ANO =TI_VAL_INT<<5|TI_VAL_ANO, + OPR_INT_WANO =TI_VAL_INT<<5|TI_VAL_WANO, OPR_FLOAT_NIL =TI_VAL_FLOAT<<5|TI_VAL_NIL, OPR_FLOAT_INT =TI_VAL_FLOAT<<5|TI_VAL_INT, @@ -67,6 +71,8 @@ typedef enum OPR_FLOAT_SET =TI_VAL_FLOAT<<5|TI_VAL_SET, OPR_FLOAT_ERROR =TI_VAL_FLOAT<<5|TI_VAL_ERROR, OPR_FLOAT_MEMBER =TI_VAL_FLOAT<<5|TI_VAL_MEMBER, + OPR_FLOAT_ANO =TI_VAL_FLOAT<<5|TI_VAL_ANO, + OPR_FLOAT_WANO =TI_VAL_FLOAT<<5|TI_VAL_WANO, OPR_BOOL_NIL =TI_VAL_BOOL<<5|TI_VAL_NIL, OPR_BOOL_INT =TI_VAL_BOOL<<5|TI_VAL_INT, @@ -83,6 +89,8 @@ typedef enum OPR_BOOL_SET =TI_VAL_BOOL<<5|TI_VAL_SET, OPR_BOOL_ERROR =TI_VAL_BOOL<<5|TI_VAL_ERROR, OPR_BOOL_MEMBER =TI_VAL_BOOL<<5|TI_VAL_MEMBER, + OPR_BOOL_ANO =TI_VAL_BOOL<<5|TI_VAL_ANO, + OPR_BOOL_WANO =TI_VAL_BOOL<<5|TI_VAL_WANO, OPR_DATETIME_NIL =TI_VAL_DATETIME<<5|TI_VAL_NIL, OPR_DATETIME_INT =TI_VAL_DATETIME<<5|TI_VAL_INT, @@ -99,6 +107,8 @@ typedef enum OPR_DATETIME_SET =TI_VAL_DATETIME<<5|TI_VAL_SET, OPR_DATETIME_ERROR =TI_VAL_DATETIME<<5|TI_VAL_ERROR, OPR_DATETIME_MEMBER =TI_VAL_DATETIME<<5|TI_VAL_MEMBER, + OPR_DATETIME_ANO =TI_VAL_DATETIME<<5|TI_VAL_ANO, + OPR_DATETIME_WANO =TI_VAL_DATETIME<<5|TI_VAL_WANO, OPR_NAME_NIL =TI_VAL_NAME<<5|TI_VAL_NIL, OPR_NAME_INT =TI_VAL_NAME<<5|TI_VAL_INT, @@ -115,6 +125,8 @@ typedef enum OPR_NAME_SET =TI_VAL_NAME<<5|TI_VAL_SET, OPR_NAME_ERROR =TI_VAL_NAME<<5|TI_VAL_ERROR, OPR_NAME_MEMBER =TI_VAL_NAME<<5|TI_VAL_MEMBER, + OPR_NAME_ANO =TI_VAL_NAME<<5|TI_VAL_ANO, + OPR_NAME_WANO =TI_VAL_NAME<<5|TI_VAL_WANO, OPR_STR_NIL =TI_VAL_STR<<5|TI_VAL_NIL, OPR_STR_INT =TI_VAL_STR<<5|TI_VAL_INT, @@ -131,6 +143,9 @@ typedef enum OPR_STR_SET =TI_VAL_STR<<5|TI_VAL_SET, OPR_STR_ERROR =TI_VAL_STR<<5|TI_VAL_ERROR, OPR_STR_MEMBER =TI_VAL_STR<<5|TI_VAL_MEMBER, + OPR_STR_ANO =TI_VAL_STR<<5|TI_VAL_ANO, + OPR_STR_WANO =TI_VAL_STR<<5|TI_VAL_WANO, + OPR_BYTES_NIL =TI_VAL_BYTES<<5|TI_VAL_NIL, OPR_BYTES_INT =TI_VAL_BYTES<<5|TI_VAL_INT, @@ -146,7 +161,9 @@ typedef enum OPR_BYTES_ARR =TI_VAL_BYTES<<5|TI_VAL_ARR, OPR_BYTES_SET =TI_VAL_BYTES<<5|TI_VAL_SET, OPR_BYTES_ERROR =TI_VAL_BYTES<<5|TI_VAL_ERROR, - OPR_BYTES_MEMBER =TI_VAL_BYTES<<5|TI_VAL_MEMBER, + OPR_BYTES_MEMBER =TI_VAL_BYTES<<5|TI_VAL_MEMBER, + OPR_BYTES_ANO =TI_VAL_BYTES<<5|TI_VAL_ANO, + OPR_BYTES_WANO =TI_VAL_BYTES<<5|TI_VAL_WANO, OPR_REGEX_NIL =TI_VAL_REGEX<<5|TI_VAL_NIL, OPR_REGEX_INT =TI_VAL_REGEX<<5|TI_VAL_INT, @@ -164,6 +181,8 @@ typedef enum OPR_REGEX_SET =TI_VAL_REGEX<<5|TI_VAL_SET, OPR_REGEX_ERROR =TI_VAL_REGEX<<5|TI_VAL_ERROR, OPR_REGEX_MEMBER =TI_VAL_REGEX<<5|TI_VAL_MEMBER, + OPR_REGEX_ANO =TI_VAL_REGEX<<5|TI_VAL_ANO, + OPR_REGEX_WANO =TI_VAL_REGEX<<5|TI_VAL_WANO, OPR_THING_NIL =TI_VAL_THING<<5|TI_VAL_NIL, OPR_THING_INT =TI_VAL_THING<<5|TI_VAL_INT, @@ -180,6 +199,8 @@ typedef enum OPR_THING_SET =TI_VAL_THING<<5|TI_VAL_SET, OPR_THING_ERROR =TI_VAL_THING<<5|TI_VAL_ERROR, OPR_THING_MEMBER =TI_VAL_THING<<5|TI_VAL_MEMBER, + OPR_THING_ANO =TI_VAL_THING<<5|TI_VAL_ANO, + OPR_THING_WANO =TI_VAL_THING<<5|TI_VAL_WANO, OPR_WRAP_NIL =TI_VAL_WRAP<<5|TI_VAL_NIL, OPR_WRAP_INT =TI_VAL_WRAP<<5|TI_VAL_INT, @@ -197,6 +218,8 @@ typedef enum OPR_WRAP_SET =TI_VAL_WRAP<<5|TI_VAL_SET, OPR_WRAP_ERROR =TI_VAL_WRAP<<5|TI_VAL_ERROR, OPR_WRAP_MEMBER =TI_VAL_WRAP<<5|TI_VAL_MEMBER, + OPR_WRAP_ANO =TI_VAL_WRAP<<5|TI_VAL_ANO, + OPR_WRAP_WANO =TI_VAL_WRAP<<5|TI_VAL_WANO, OPR_ARR_NIL =TI_VAL_ARR<<5|TI_VAL_NIL, OPR_ARR_INT =TI_VAL_ARR<<5|TI_VAL_INT, @@ -213,6 +236,8 @@ typedef enum OPR_ARR_SET =TI_VAL_ARR<<5|TI_VAL_SET, OPR_ARR_ERROR =TI_VAL_ARR<<5|TI_VAL_ERROR, OPR_ARR_MEMBER =TI_VAL_ARR<<5|TI_VAL_MEMBER, + OPR_ARR_ANO =TI_VAL_ARR<<5|TI_VAL_ANO, + OPR_ARR_WANO =TI_VAL_ARR<<5|TI_VAL_WANO, OPR_SET_NIL =TI_VAL_SET<<5|TI_VAL_NIL, OPR_SET_INT =TI_VAL_SET<<5|TI_VAL_INT, @@ -229,6 +254,8 @@ typedef enum OPR_SET_SET =TI_VAL_SET<<5|TI_VAL_SET, OPR_SET_ERROR =TI_VAL_SET<<5|TI_VAL_ERROR, OPR_SET_MEMBER =TI_VAL_SET<<5|TI_VAL_MEMBER, + OPR_SET_ANO =TI_VAL_SET<<5|TI_VAL_ANO, + OPR_SET_WANO =TI_VAL_SET<<5|TI_VAL_WANO, OPR_ERROR_NIL =TI_VAL_ERROR<<5|TI_VAL_NIL, OPR_ERROR_INT =TI_VAL_ERROR<<5|TI_VAL_INT, @@ -246,6 +273,8 @@ typedef enum OPR_ERROR_SET =TI_VAL_ERROR<<5|TI_VAL_SET, OPR_ERROR_ERROR =TI_VAL_ERROR<<5|TI_VAL_ERROR, OPR_ERROR_MEMBER =TI_VAL_ERROR<<5|TI_VAL_MEMBER, + OPR_ERROR_ANO =TI_VAL_ERROR<<5|TI_VAL_ANO, + OPR_ERROR_WANO =TI_VAL_ERROR<<5|TI_VAL_WANO, OPR_MEMBER_NIL =TI_VAL_MEMBER<<5|TI_VAL_NIL, OPR_MEMBER_INT =TI_VAL_MEMBER<<5|TI_VAL_INT, @@ -262,6 +291,45 @@ typedef enum OPR_MEMBER_SET =TI_VAL_MEMBER<<5|TI_VAL_SET, OPR_MEMBER_ERROR =TI_VAL_MEMBER<<5|TI_VAL_ERROR, OPR_MEMBER_MEMBER =TI_VAL_MEMBER<<5|TI_VAL_MEMBER, + OPR_MEMBER_ANO =TI_VAL_MEMBER<<5|TI_VAL_ANO, + OPR_MEMBER_WANO =TI_VAL_MEMBER<<5|TI_VAL_WANO, + + OPR_ANO_NIL =TI_VAL_ANO<<5|TI_VAL_NIL, + OPR_ANO_INT =TI_VAL_ANO<<5|TI_VAL_INT, + OPR_ANO_FLOAT =TI_VAL_ANO<<5|TI_VAL_FLOAT, + OPR_ANO_BOOL =TI_VAL_ANO<<5|TI_VAL_BOOL, + OPR_ANO_DATETIME =TI_VAL_ANO<<5|TI_VAL_DATETIME, + OPR_ANO_NAME =TI_VAL_ANO<<5|TI_VAL_NAME, + OPR_ANO_STR =TI_VAL_ANO<<5|TI_VAL_STR, + OPR_ANO_BYTES =TI_VAL_ANO<<5|TI_VAL_BYTES, + OPR_ANO_REGEX =TI_VAL_ANO<<5|TI_VAL_REGEX, + OPR_ANO_THING =TI_VAL_ANO<<5|TI_VAL_THING, + OPR_ANO_WRAP =TI_VAL_ANO<<5|TI_VAL_WRAP, + OPR_ANO_ARR =TI_VAL_ANO<<5|TI_VAL_ARR, + OPR_ANO_SET =TI_VAL_ANO<<5|TI_VAL_SET, + OPR_ANO_ERROR =TI_VAL_ANO<<5|TI_VAL_ERROR, + OPR_ANO_MEMBER =TI_VAL_ANO<<5|TI_VAL_MEMBER, + OPR_ANO_ANO =TI_VAL_ANO<<5|TI_VAL_ANO, + OPR_ANO_WANO =TI_VAL_ANO<<5|TI_VAL_WANO, + + OPR_WANO_NIL =TI_VAL_WANO<<5|TI_VAL_NIL, + OPR_WANO_INT =TI_VAL_WANO<<5|TI_VAL_INT, + OPR_WANO_FLOAT =TI_VAL_WANO<<5|TI_VAL_FLOAT, + OPR_WANO_BOOL =TI_VAL_WANO<<5|TI_VAL_BOOL, + OPR_WANO_DATETIME =TI_VAL_WANO<<5|TI_VAL_DATETIME, + OPR_WANO_NAME =TI_VAL_WANO<<5|TI_VAL_NAME, + OPR_WANO_STR =TI_VAL_WANO<<5|TI_VAL_STR, + OPR_WANO_BYTES =TI_VAL_WANO<<5|TI_VAL_BYTES, + OPR_WANO_REGEX =TI_VAL_WANO<<5|TI_VAL_REGEX, + OPR_WANO_THING =TI_VAL_WANO<<5|TI_VAL_THING, + OPR_WANO_WRAP =TI_VAL_WANO<<5|TI_VAL_WRAP, + OPR_WANO_ARR =TI_VAL_WANO<<5|TI_VAL_ARR, + OPR_WANO_SET =TI_VAL_WANO<<5|TI_VAL_SET, + OPR_WANO_ERROR =TI_VAL_WANO<<5|TI_VAL_ERROR, + OPR_WANO_MEMBER =TI_VAL_WANO<<5|TI_VAL_MEMBER, + OPR_WANO_ANO =TI_VAL_WANO<<5|TI_VAL_ANO, + OPR_WANO_WANO =TI_VAL_WANO<<5|TI_VAL_WANO, + } ti_opr_perm_t; #endif /* TI_OPR_OPRINC_H_ */ diff --git a/inc/ti/spec.h b/inc/ti/spec.h index cc20c567..6fe56fba 100644 --- a/inc/ti/spec.h +++ b/inc/ti/spec.h @@ -10,9 +10,10 @@ #include #include #include +#include ti_spec_rval_enum ti__spec_check_nested_val(uint16_t spec, ti_val_t * val); -_Bool ti__spec_maps_to_nested_val(uint16_t spec, ti_val_t * val); +_Bool ti__spec_maps_to_nested_val(ti_field_t * field, ti_val_t * val); const char * ti_spec_approx_type_str(uint16_t spec); ti_spec_mod_enum ti_spec_check_mod( diff --git a/inc/ti/spec.inline.h b/inc/ti/spec.inline.h index 9c6f923f..74f4e2e8 100644 --- a/inc/ti/spec.inline.h +++ b/inc/ti/spec.inline.h @@ -36,12 +36,12 @@ static inline ti_spec_rval_enum ti_spec_check_nested_val( : ti__spec_check_nested_val(spec & TI_SPEC_MASK_NILLABLE, val); } -static inline _Bool ti_spec_maps_to_nested_val(uint16_t spec, ti_val_t * val) +static inline _Bool ti_spec_maps_to_nested_val(ti_field_t * field, ti_val_t * val) { return ( - spec == TI_SPEC_ANY || - ((spec & TI_SPEC_NILLABLE) && ti_val_is_nil(val)) || - ti__spec_maps_to_nested_val(spec & TI_SPEC_MASK_NILLABLE, val) + field->nested_spec == TI_SPEC_ANY || + ((field->nested_spec & TI_SPEC_NILLABLE) && ti_val_is_nil(val)) || + ti__spec_maps_to_nested_val(field, val) ); } diff --git a/inc/ti/spec.t.h b/inc/ti/spec.t.h index 6b645733..037b5d9a 100644 --- a/inc/ti/spec.t.h +++ b/inc/ti/spec.t.h @@ -39,6 +39,7 @@ typedef enum TI_SPEC_EMAIL, /* `email` */ TI_SPEC_URL, /* `url` */ TI_SPEC_TEL, /* `tel` */ + TI_SPEC_ENUM, /* `enum` */ TI_SPEC_REMATCH=0x5000, /* `/.../` */ TI_SPEC_INT_RANGE, /* `int<:> */ diff --git a/inc/ti/type.h b/inc/ti/type.h index f1561730..48e9c947 100644 --- a/inc/ti/type.h +++ b/inc/ti/type.h @@ -77,7 +77,12 @@ int ti_type_methods_info_to_pk( int ti_type_required_by_non_wpo(ti_type_t * type, ex_t * e); int ti_type_requires_wpo(ti_type_t * type, ex_t * e); int ti_type_rename(ti_type_t * type, ti_raw_t * nname); +ti_raw_t * ti_type_spec_raw_from_thing( + ti_thing_t * thing, + ti_val_t * val, /* may be equal to thing, or an ti_varr_t */ + ex_t * e); ti_raw_t * ti__type_nested_from_val(ti_type_t * type, ti_val_t * val, ex_t * e); +_Bool ti_type_has_dependencies(ti_type_t * type); static inline int ti_type_use(ti_type_t * type, ex_t * e) { @@ -164,5 +169,6 @@ static inline ti_method_t * ti_type_get_method( return NULL; } + #endif /* TI_TYPE_H_ */ diff --git a/inc/ti/val.h b/inc/ti/val.h index af6d02fd..87dd5bda 100644 --- a/inc/ti/val.h +++ b/inc/ti/val.h @@ -31,6 +31,7 @@ extern ti_val_t * val__parent_name; extern ti_val_t * val__parent_type_name; extern ti_val_t * val__key_name; extern ti_val_t * val__key_type_name; +extern ti_val_t * val__anonymous_name; /* string */ extern ti_val_t * val__sany; @@ -83,10 +84,12 @@ int ti_val_bytes_to_str(ti_val_t ** val, ex_t * e); int ti_val_regex_to_str(ti_val_t ** val, ex_t * e); int ti_val_thing_to_str(ti_val_t ** val, ex_t * e); int ti_val_wrap_to_str(ti_val_t ** val, ex_t * e); +int ti_val_wano_to_str(ti_val_t ** val, ex_t * e); int ti_val_room_to_str(ti_val_t ** val, ex_t * e); int ti_val_vtask_to_str(ti_val_t ** val, ex_t * e); int ti_val_error_to_str(ti_val_t ** val, ex_t * e); int ti_val_member_to_str(ti_val_t ** val, ex_t * e); int ti_val_closure_to_str(ti_val_t ** val, ex_t * e); +int ti_val_ano_to_str(ti_val_t ** val, ex_t * e); #endif /* TI_VAL_H_ */ diff --git a/inc/ti/val.inline.h b/inc/ti/val.inline.h index 48b1f8ec..1cad777e 100644 --- a/inc/ti/val.inline.h +++ b/inc/ti/val.inline.h @@ -5,6 +5,7 @@ #define TI_VAL_INLINE_H_ #include +#include #include #include #include @@ -29,8 +30,10 @@ #include #include #include +#include #include #include +#include #include static inline int val__str_to_str(ti_val_t ** UNUSED(v), ex_t * UNUSED(e)); @@ -76,6 +79,10 @@ static inline _Bool val__as_bool_wrap(ti_val_t * val) { return !!ti_thing_n(((ti_wrap_t *) val)->thing); } +static inline _Bool val__as_bool_wano(ti_val_t * val) +{ + return !!ti_thing_n(((ti_wano_t *) val)->thing); +} static inline _Bool val__as_bool_room(ti_val_t * val) { return !!((ti_room_t *) val)->id; @@ -173,6 +180,15 @@ static inline const char * val__module_type_str(ti_val_t * UNUSED(val)) { return TI_VAL_MODULE_S; } +static inline const char * val__ano_type_str(ti_val_t * UNUSED(val)) +{ + return TI_VAL_ANO_S; +} +static inline const char * val__wano_type_str(ti_val_t * UNUSED(val)) +{ + return TI_VAL_WANO_S; +} + static inline int val__nil_to_client_pk(ti_val_t * UNUSED(v), ti_vp_t * vp, int UNUSED(d), int UNUSED(f)) { return msgpack_pack_nil(&vp->pk); @@ -229,6 +245,10 @@ static inline int val__error_to_client_pk(ti_val_t * val, ti_vp_t * vp, int UNUS { return ti_verror_to_client_pk((ti_verror_t *) val, &vp->pk); } +static inline int val__ano_to_client_pk(ti_val_t * val, ti_vp_t * vp, int UNUSED(d), int UNUSED(f)) +{ + return ti_ano_to_client_pk((ti_ano_t *) val, &vp->pk); +} static inline int val__mpdata_to_client_pk(ti_val_t * val, ti_vp_t * vp, int UNUSED(d), int UNUSED(f)) { return ti_raw_mpdata_to_client_pk((ti_raw_t *) val, &vp->pk); @@ -259,6 +279,11 @@ static inline int val__wrap_to_arr(ti_val_t ** UNUSED(v), ti_varr_t * varr, ex_t return varr->flags |= TI_VARR_FLAG_MHT, 0; } +static inline int val__wano_to_arr(ti_val_t ** UNUSED(v), ti_varr_t * varr, ex_t * UNUSED(e)) +{ + return varr->flags |= TI_VARR_FLAG_MHT, 0; +} + static inline int val__room_to_arr(ti_val_t ** UNUSED(v), ti_varr_t * varr, ex_t * UNUSED(e)) { return varr->flags |= TI_VARR_FLAG_MHR, 0; @@ -284,8 +309,7 @@ static inline int val__set_to_arr(ti_val_t ** v, ti_varr_t * varr, ex_t * e) } static inline int val__member_to_arr(ti_val_t ** v, ti_varr_t * varr, ex_t * e); -static inline int val__future_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * UNUSED(e)); -static inline int val__module_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * UNUSED(e)); +static inline int val__as_nil_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * UNUSED(e)); static inline int val__closure_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * e) { @@ -313,7 +337,7 @@ typedef struct } ti_val_type_t; -static ti_val_type_t ti_val_type_props[22] = { +static ti_val_type_t ti_val_type_props[24] = { /* TI_VAL_NIL */ { .destroy = (ti_val_destroy_cb) free, @@ -523,11 +547,34 @@ static ti_val_type_t ti_val_type_props[22] = { .as_bool = val__as_bool_true, .allowed_as_vtask_arg = false, }, + /* TI_VAL_ANO */ + { + .destroy = (ti_val_destroy_cb) ti_ano_destroy, + .to_str = ti_val_ano_to_str, + .to_arr_cb = val__to_arr_cb, + .to_client_pk = val__ano_to_client_pk, + .to_store_pk = (ti_val_to_store_pk_cb) ti_ano_to_store_pk, + .get_type_str = val__ano_type_str, + .as_bool = val__as_bool_true, + .allowed_as_vtask_arg = false, + }, + /* TI_VAL_WANO */ + { + .destroy = (ti_val_destroy_cb) ti_wano_destroy, + .to_str = ti_val_wano_to_str, + .to_arr_cb = val__wano_to_arr, + .to_client_pk = (ti_val_to_client_pk_cb) ti_wano_to_client_pk, + .to_store_pk = (ti_val_to_store_pk_cb) ti_wano_to_store_pk, + .get_type_str = val__wano_type_str, + .as_bool = val__as_bool_wano, + .allowed_as_vtask_arg = false, + }, + /* TI_VAL_FUTURE */ { .destroy = (ti_val_destroy_cb) ti_future_destroy, .to_str = val__no_to_str, - .to_arr_cb = val__future_to_arr, + .to_arr_cb = val__as_nil_to_arr, .to_client_pk = (ti_val_to_client_pk_cb) val__future_to_client_pk, .get_type_str = val__future_type_str, .as_bool = val__as_bool_true, @@ -537,7 +584,7 @@ static ti_val_type_t ti_val_type_props[22] = { { .destroy = (ti_val_destroy_cb) ti_module_destroy, .to_str = val__no_to_str, - .to_arr_cb = val__module_to_arr, + .to_arr_cb = val__as_nil_to_arr, .to_client_pk = (ti_val_to_client_pk_cb) val__module_to_client_pk, .get_type_str = val__module_type_str, .as_bool = val__as_bool_true, @@ -734,6 +781,13 @@ static inline _Bool ti_val_is_str_nil(ti_val_t * val) val->tp == TI_VAL_NIL; } +static inline _Bool ti_val_is_str_ano(ti_val_t * val) +{ + return val->tp == TI_VAL_STR || + val->tp == TI_VAL_NAME || + val->tp == TI_VAL_ANO; +} + static inline _Bool ti_val_is_str_regex(ti_val_t * val) { return val->tp == TI_VAL_STR || @@ -794,6 +848,16 @@ static inline _Bool ti_val_is_wrap(ti_val_t * val) return val->tp == TI_VAL_WRAP; } +static inline _Bool ti_val_is_wrap_wano(ti_val_t * val) +{ + return val->tp == TI_VAL_WRAP || val->tp == TI_VAL_WANO; +} + +static inline _Bool ti_val_is_wano(ti_val_t * val) +{ + return val->tp == TI_VAL_WANO; +} + static inline _Bool ti_val_is_room(ti_val_t * val) { return val->tp == TI_VAL_ROOM; @@ -855,6 +919,11 @@ static inline _Bool ti_val_is_module(ti_val_t * val) return val->tp == TI_VAL_MODULE; } +static inline _Bool ti_val_is_ano(ti_val_t * val) +{ + return val->tp == TI_VAL_ANO; +} + static inline _Bool ti_val_overflow_cast(double d) { return !(d >= -VAL__CAST_MAX && d < VAL__CAST_MAX); @@ -914,6 +983,16 @@ static inline ti_val_t * ti_val_gmt_offset_name(void) return ti_incref(val__gmt_offset_name), val__gmt_offset_name; } +static inline ti_val_t * ti_val_anonymous_name(void) +{ + return ti_incref(val__anonymous_name), val__anonymous_name; +} + +static inline ti_val_t * ti_val_borrow_anonymous_name(void) +{ + return val__anonymous_name; +} + static inline ti_val_t * ti_val_borrow_async_name(void) { return val__async_name; @@ -1120,6 +1199,8 @@ static inline void ti_val_attach( case TI_VAL_ERROR: case TI_VAL_MEMBER: case TI_VAL_CLOSURE: + case TI_VAL_ANO: + case TI_VAL_WANO: case TI_VAL_FUTURE: case TI_VAL_MODULE: return; @@ -1192,6 +1273,9 @@ static inline int ti_val_make_assignable( return 0; case TI_VAL_CLOSURE: return ti_closure_unbound((ti_closure_t *) *val, e); + case TI_VAL_ANO: + case TI_VAL_WANO: + return 0; case TI_VAL_FUTURE: case TI_VAL_MODULE: ti_val_unsafe_drop(*val); @@ -1309,9 +1393,10 @@ static inline _Bool val__spec_enum_eq_to_val(uint16_t spec, ti_val_t * val) * TI_SPEC_EMAIL, * TI_SPEC_URL, * TI_SPEC_TEL, + * TI_SPEC_ENUM, */ -static ti_val_spec_t ti_val_spec_map[24] = { +static ti_val_spec_t ti_val_spec_map[25] = { {.is_spec=ti_val_is_thing}, {.is_spec=ti_val_is_raw}, {.is_spec=ti_val_is_str}, @@ -1336,6 +1421,7 @@ static ti_val_spec_t ti_val_spec_map[24] = { {.is_spec=ti_val_is_email}, {.is_spec=ti_val_is_url}, {.is_spec=ti_val_is_tel}, + {.is_spec=ti_val_is_member}, }; static inline _Bool ti_val_is_spec(ti_val_t * val, uint16_t spec) @@ -1393,14 +1479,7 @@ static inline int val__member_to_arr(ti_val_t ** v, ti_varr_t * varr, ex_t * e) return e->nr; } -static inline int val__future_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * UNUSED(e)) -{ - ti_val_unsafe_drop(*v); - *v = (ti_val_t *) ti_nil_get(); - return 0; -} - -static inline int val__module_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * UNUSED(e)) +static inline int val__as_nil_to_arr(ti_val_t ** v, ti_varr_t * UNUSED(varr), ex_t * UNUSED(e)) { ti_val_unsafe_drop(*v); *v = (ti_val_t *) ti_nil_get(); diff --git a/inc/ti/val.t.h b/inc/ti/val.t.h index 901d0d30..9f77fed1 100644 --- a/inc/ti/val.t.h +++ b/inc/ti/val.t.h @@ -26,6 +26,8 @@ #define TI_VAL_FUTURE_S "future" #define TI_VAL_MODULE_S "module" #define TI_VAL_TASK_S "task" +#define TI_VAL_ANO_S "" +#define TI_VAL_WANO_S "<>" #define TI_KIND_S_INSTANCE "." /* Internally, New typed thing */ #define TI_KIND_S_OBJECT "," /* Internally, New thing */ @@ -33,6 +35,7 @@ #define TI_KIND_S_SET "$" /* Internally, Set */ #define TI_KIND_S_ERROR "!" /* Internally, Error */ #define TI_KIND_S_WRAP "&" /* Internally, Wrapped thing */ +#define TI_KIND_S_WANO " " /* Internally, Wrapped ano thing */ #define TI_KIND_S_MEMBER "%%" /* Internally, Enum member */ #define TI_KIND_S_DATETIME "'" /* Internally, Date/Time */ #define TI_KIND_S_TIMEVAL "\"" /* Internally, Time value */ @@ -64,6 +67,9 @@ typedef enum TI_VAL_MEMBER, /* enum member */ TI_VAL_MPDATA, /* msgpack data */ TI_VAL_CLOSURE, + TI_VAL_ANO, /* anonymous wrap-only type */ + TI_VAL_WANO, /* wrapped with anonymous type */ + /* future, module and template are never stored */ TI_VAL_FUTURE, /* future */ TI_VAL_MODULE, /* module */ TI_VAL_TEMPLATE, /* template to generate TI_VAL_STR @@ -95,6 +101,7 @@ typedef enum TI_KIND_C_MEMBER ='%', TI_KIND_C_DATETIME ='\'', TI_KIND_C_TIMEVAL ='"', + TI_KIND_C_WANO =' ', /* Obsolete, but still required for backwards compatibility */ TI_KIND_C_THING_OBSOLETE_ ='#', TI_KIND_C_CLOSURE_OBSOLETE_ ='/', diff --git a/inc/ti/version.h b/inc/ti/version.h index cc9ade84..58637dd1 100644 --- a/inc/ti/version.h +++ b/inc/ti/version.h @@ -25,7 +25,7 @@ * "-rc0" * "" */ -#define TI_VERSION_PRE_RELEASE "-alpha1" +#define TI_VERSION_PRE_RELEASE "-alpha3" #define TI_MAINTAINER \ "Jeroen van der Heijden " diff --git a/inc/ti/wano.h b/inc/ti/wano.h new file mode 100644 index 00000000..caf28f39 --- /dev/null +++ b/inc/ti/wano.h @@ -0,0 +1,16 @@ +/* + * ti/wano.h + */ +#ifndef TI_WANO_H_ +#define TI_WANO_H_ + +#include +#include +#include + +ti_wano_t * ti_wano_create(ti_thing_t * thing, ti_ano_t * ano); +void ti_wano_destroy(ti_wano_t * wano); +int ti_wano_copy(ti_wano_t ** wano, uint8_t deep); +int ti_wano_dup(ti_wano_t ** wano, uint8_t deep); + +#endif /* TI_WANO_H_ */ diff --git a/inc/ti/wano.inline.h b/inc/ti/wano.inline.h new file mode 100644 index 00000000..aef5658c --- /dev/null +++ b/inc/ti/wano.inline.h @@ -0,0 +1,44 @@ +/* + * ti/wano.inline.h + */ +#ifndef TI_WANO_INLINE_H_ +#define TI_WANO_INLINE_H_ + +#include +#include +#include +#include +#include + +static inline ti_raw_t * ti_wano_str(ti_wano_t * wano) +{ + return wano->thing->id + ? ti_str_from_fmt(TI_VAL_WANO_S":%"PRIu64, wano->thing->id) + : ti_str_from_str(TI_VAL_WANO_S":nil"); +} + +static inline int ti_wano_to_client_pk( + ti_wano_t * wano, + ti_vp_t * vp, + int deep, + int flags) +{ + return deep > 0 + ? ti_wrap_field_thing_type(wano->thing, vp, wano->ano->type, deep, flags) + : (!wano->thing->id || (flags & TI_FLAGS_NO_IDS)) + ? ti_thing_empty_to_client_pk(&vp->pk) + : ti_thing_id_to_client_pk(wano->thing, &vp->pk); +} + +static inline int ti_wano_to_store_pk(ti_wano_t * wano, msgpack_packer * pk) +{ + return -( + msgpack_pack_map(pk, 1) || + mp_pack_strn(pk, TI_KIND_S_WANO, 1) || + msgpack_pack_array(pk, 2) || + mp_pack_bin(pk, wano->ano->spec_raw->data, wano->ano->spec_raw->n) || + ti_thing_to_store_pk(wano->thing, pk) + ); +} + +#endif /* TI_WANO_INLINE_H_ */ diff --git a/inc/ti/wano.t.h b/inc/ti/wano.t.h new file mode 100644 index 00000000..4363e758 --- /dev/null +++ b/inc/ti/wano.t.h @@ -0,0 +1,21 @@ +/* + * ti/wano.t.h + */ +#ifndef TI_WANO_T_H_ +#define TI_WANO_T_H_ + +typedef struct ti_wano_s ti_wano_t; + +#include +#include + +struct ti_wano_s +{ + uint32_t ref; + uint8_t tp; + int:24; + ti_thing_t * thing; /* with reference */ + ti_ano_t * ano; /* with reference */ +}; + +#endif /* TI_WANO_T_H_ */ diff --git a/inc/ti/wrap.h b/inc/ti/wrap.h index e1195f4e..84b32753 100644 --- a/inc/ti/wrap.h +++ b/inc/ti/wrap.h @@ -21,6 +21,12 @@ int ti__wrap_field_thing( uint16_t spec, int deep, int flags); +int ti_wrap_field_thing_type( + ti_thing_t * thing, + ti_vp_t * vp, + ti_type_t * t_type, + int deep, + int flags); int ti_wrap_cp(ti_query_t * query, uint8_t deep, ex_t * e); int ti_wrap_copy(ti_wrap_t ** wrap, uint8_t deep); int ti_wrap_dup(ti_wrap_t ** wrap, uint8_t deep); diff --git a/inc/util/mpack.h b/inc/util/mpack.h index 3795c00a..28c15e5d 100644 --- a/inc/util/mpack.h +++ b/inc/util/mpack.h @@ -24,6 +24,7 @@ typedef enum MPACK_EXT_TASK, MPACK_EXT_THING, MPACK_EXT_NAME, + MPACK_EXT_ANO, } mpack_ext_t; typedef struct diff --git a/itest/lib/vars.py b/itest/lib/vars.py index f508b2cc..1660f90e 100644 --- a/itest/lib/vars.py +++ b/itest/lib/vars.py @@ -53,7 +53,7 @@ f'--suppressions={MEMLEAK_SUPP}', '--errors-for-leak-kinds=all', ], - # full and vebose memcheck + # full and verbose memcheck [ 'valgrind', '--tool=memcheck', @@ -66,6 +66,20 @@ '--errors-for-leak-kinds=all', '-v', ], + # full, verbose memcheck and gen suppressions + [ + 'valgrind', + '--tool=memcheck', + '--error-exitcode=1', + '--leak-check=full', + '--show-leak-kinds=all', + '--track-origins=yes', + '--show-reachable=yes', + '--gen-suppressions=all', + f'--suppressions={MEMLEAK_SUPP}', + '--errors-for-leak-kinds=all', + '-v', + ], ][int(os.environ.get('THINGSDB_MEMCHECK', 1))] except ValueError: diff --git a/itest/run_all_tests.py b/itest/run_all_tests.py index cdfd5122..0b32b6fc 100755 --- a/itest/run_all_tests.py +++ b/itest/run_all_tests.py @@ -3,6 +3,7 @@ from lib import run_test, vars from test_advanced import TestAdvanced +from test_ano import TestAno from test_arguments import TestArguments from test_backup import TestBackup from test_changes import TestChanges @@ -75,6 +76,7 @@ def no_mem_test(test_class): args = parser.parse_args() run_test(TestAdvanced(), hide_version=hide_version()) + run_test(TestAno(), hide_version=hide_version()) run_test(TestArguments(), hide_version=hide_version()) run_test(TestBackup(), hide_version=hide_version()) run_test(TestChanges(), hide_version=hide_version()) diff --git a/itest/test_advanced.py b/itest/test_advanced.py index 7c2c987a..529afe6a 100755 --- a/itest/test_advanced.py +++ b/itest/test_advanced.py @@ -1873,15 +1873,15 @@ async def test_closure_as_type_val(self, client): thing({id}).func(); // requires a change """) - async def test_future_to_type(self, client): + async def test_future_or_wrap_ano_to_type(self, client): await client.query(r"""//ti set_type('A', {x: 'any'}); """) with self.assertRaisesRegex( TypeError, - r'mismatch in type `A`; property `x` allows `any` type ' - r"with the exception of the `future` and `module` type"): + r'mismatch in type `A`; property `x` allows `any` type with ' + r'the exception of the `future` and `module` type'): await client.query("""//ti A{ x: future(||nil) @@ -2928,6 +2928,5 @@ async def test_mod_nested(self, client): }) - if __name__ == '__main__': run_test(TestAdvanced()) diff --git a/itest/test_ano.py b/itest/test_ano.py new file mode 100755 index 00000000..cefc5179 --- /dev/null +++ b/itest/test_ano.py @@ -0,0 +1,303 @@ +#!/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 ValueError +from thingsdb.exceptions import TypeError +from thingsdb.exceptions import NumArgumentsError +from thingsdb.exceptions import BadDataError +from thingsdb.exceptions import LookupError +from thingsdb.exceptions import OverflowError +from thingsdb.exceptions import ZeroDivisionError +from thingsdb.exceptions import OperationError + + +class TestAno(TestBase): + + title = 'Test anonymous type' + + @default_test_setup(num_nodes=1, seed=1, threshold_full_storage=10) + async def async_run(self): + + await self.node0.init_and_run() + + client = await get_client(self.node0) + client.set_default_scope('//stuff') + + await self.run_tests(client.query, client.run) + + client.close() + await client.wait_closed() + + async def test_ano_err(self, q, r): + await q(r"""//ti + set_type('A', {x: 'any'}); + """) + + with self.assertRaisesRegex( + LookupError, + r'function `ano` is undefined in the `@thingsdb` scope; ' + r'you might want to query a `@collection` scope\?;'): + await q("""//ti + ano({}); + """, scope='/t') + + with self.assertRaisesRegex( + NumArgumentsError, + r'function `ano` takes 1 argument but 0 were given'): + await q("""//ti + ano(); + """) + + with self.assertRaisesRegex( + TypeError, + r'function `ano` expects argument 1 to be of ' + r'type `thing` but got type `nil` instead;'): + await q("""//ti + ano(nil); + """) + + with self.assertRaisesRegex( + TypeError, + r'expecting a nested structure \(`list` or `thing`\), ' + r'a method of type `closure` or a definition ' + r'of type `str` but got type `int` instead'): + await q( + """//ti + ano({ + x: 0 + }); + """) + + with self.assertRaisesRegex( + LookupError, + r'anonymous types are not supported in the the `@thingsdb` ' + r'scope'): + await q("""//ti + &{}; + """, scope='/t') + + with self.assertRaisesRegex( + TypeError, + r'the type definition contains dependencies ' + r'on other types or enumerators, which is not permitted; ' + r'to resolve this, create a fully named wrap-only type'): + await q("""//ti + new_type('T'); + &{nested: {t: 'T'}}; + """) + + with self.assertRaisesRegex( + ValueError, + r'type keys must follow the naming rules'): + await q("""//ti + t = {}; + t['key with spaces'] = 'str'; + ano(t); + """) + + async def test_more_ano_props(self, q, r): + res = await q("""//ti + .a = &{ + id: '#', + a: 'int', + b: 'str', + f: |a, b| a + b, + }; + .a.f(20, 22); + """) + self.assertEqual(res, 42) + r1, r2 = await q( + """//ti + o = { + name: 'Iris', + numbers: [{ + x: 1, + y: 2, + }, { + x: 4, + }, { + y: 5, + }] + }; + [ + o.wrap(&{ + name: |this| this.name.upper(), + numbers: [{ + x: 'int' + }] + }), + o.wrap(ano({ + name: |this| this.name.upper(), + numbers: [{ + x: 'int' + }] + })), + ]; + """) + self.assertEqual(r1, { + "name": "IRIS", + "numbers": [{"x": 1}, {"x": 4}, {}] + }) + self.assertEqual(r1, r2) + await q( + """//ti + o = { + name: 'Iris', + numbers: [{ + x: 1, + y: 2, + }, { + x: 4, + }, { + y: 5, + }] + }; + .a1 = &{ + name: |this| this.name.upper(), + numbers: [{ + x: 'int' + }] + }; + .a2 = ano({ + name: |this| this.name.upper(), + numbers: [{ + x: 'int' + }] + }); + .wano = [ + o.wrap(&{ + name: |this| this.name.upper(), + numbers: [{ + x: 'int' + }] + }), + o.wrap(ano({ + name: |this| this.name.upper(), + numbers: [{ + x: 'int' + }] + })), + o.wrap(.a1), + o.wrap(.a2), + ]; + """) + r1, r2, r3, r4 = await q("""//ti + .wano; + """) + self.assertEqual(r1, { + "name": "IRIS", + "numbers": [{"x": 1,}, {"x": 4}, {}] + }) + self.assertEqual(r1, r2) + self.assertEqual(r2, r3) + self.assertEqual(r3, r4) + res = await q( + """//ti + .wano[0] == .wano[1] && + .wano[1] == .wano[2] && + .wano[2] == .wano[3]; + + """) + self.assertTrue(res) + + res = await q("""//ti + type(&{}); + """) + self.assertEqual(res, "") + + res = await q("""//ti + type({}.wrap(&{})); + """) + self.assertEqual(res, "<>") + + res = await q("""//ti + .a1; + """) + self.assertEqual(res, { + 'name': '|this|this.name.upper()', + 'numbers': [ + {'x': 'int'} + ] + }) + + res = await q("""//ti + str(.a1); + """) + self.assertEqual(res, "") + + res = await q("""//ti + str({}.wrap(.a1)); + """) + self.assertEqual(res, "<>:nil") + + async def test_after_mod(self, q, r): + await q("""//ti + set_type('P', {name: 'str'}); + .p = P{name: 'foo'}; + .a = &{name: 'str', age: 'int', other: 'any'}; + new_procedure('get', || { + [.p.wrap(&{name: 'any', age: 'any'}), .p.wrap(.a)]; + }); + """) + script = """//ti + ' +This is a query with will be cached as it is long enough to be stored, +it should have at least 160 characters, so we need a longs string. +I guess we are good now... +'; +[.p.wrap(&{name: 'str', age: 'int'}), .p.wrap(.a)]; + """ + r0, r1 = await q(script) + self.assertEqual(r0, {"name": "foo"}) + self.assertEqual(r0, r1) + await q("""//ti + mod_type('P', 'add', 'age', 'int', 12); + """) + r0, r1 = await q(script) + self.assertEqual(r0, {"name": "foo", "age": 12}) + self.assertEqual(r0, r1) + r0, r1 = await r('get') + self.assertEqual(r0, {"name": "foo", "age": 12}) + self.assertEqual(r0, r1) + + res = await q( + """//ti + range(3).map(|i| P{name: `person{i}`, age: i}).map_wrap(&{ + upper: |this| this.name.upper(), + age: 'int' + }); + """) + self.assertEqual(res, [ + {'age': 0, 'upper': 'PERSON0'}, + {'age': 1, 'upper': 'PERSON1'}, + {'age': 2, 'upper': 'PERSON2'}]) + + await q("""//ti + t = &{name: 'str'}; + .w = {nested: {t: {name: 'foo'}}}.wrap(&{nested: {t:,}}); + """) + res = await q('.w;') + self.assertEqual(res, {"nested": {'t': {'name': 'foo'}}}) + res = await q("""//ti + c = .w.copy(); + assert(is_thing(c)); + assert(is_nil(c.id())); + return c, 3; + """) + self.assertEqual(res, {"nested": {'t': {'name': 'foo'}}}) + res = await q("""//ti + c = .w.dup(); + assert(is_thing(c.unwrap())); + assert(is_nil(c.id())); + c; + """) + self.assertEqual(res, {"nested": {'t': {'name': 'foo'}}}) + +if __name__ == '__main__': + run_test(TestAno()) diff --git a/itest/test_collection_functions.py b/itest/test_collection_functions.py index 37d71d68..b80c3835 100755 --- a/itest/test_collection_functions.py +++ b/itest/test_collection_functions.py @@ -503,7 +503,7 @@ async def test_base64_encode(self, client): with self.assertRaisesRegex( TypeError, r'function `base64_encode` expects argument 1 to be of ' - r'type `str` or type `bytes` but got type `int` instead'): + r'type `str`, `bytes` or `mpdata` but got type `int` instead'): await client.query('base64_encode(1);') self.assertEqual(await client.query('base64_encode("");'), '') @@ -2630,7 +2630,7 @@ async def test_map_wrap(self, client): with self.assertRaisesRegex( TypeError, r'function `map_wrap` expects argument 1 to be of ' - r'type `str` but got type `nil` instead;'): + r'type `str` or `` but got type `nil` instead;'): await client.query('[].map_wrap(nil);') with self.assertRaisesRegex( diff --git a/itest/test_enum.py b/itest/test_enum.py index b470fd95..d5f69fad 100755 --- a/itest/test_enum.py +++ b/itest/test_enum.py @@ -1117,6 +1117,71 @@ async def test_def_max_enum(self, client): 'reached the maximum number of enumerators'): await client.query(r'''set_enum('E', {X: 0});''') + async def test_wrap_enum_name_str(self, client): + # bug #431 + await client.query( + """//ti + set_enum('E', {A: 41, B: 42}); + .e = E{B}; + """) + res = await client.query("""//ti + .wrap(&{e: 'str'}) + """) + self.assertEqual(res, {}) + res = await client.query("""//ti + .wrap(&{e: '*str'}) + """) + self.assertEqual(res, {"e": 'B'}) + res = await client.query("""//ti + .wrap(&{e: 'int'}) + """) + self.assertEqual(res, {"e": 42}) + res = await client.query("""//ti + .wrap(&{e: '*int'}) + """) + self.assertEqual(res, {}) + res = await client.query("""//ti + .wrap(&{e: 'enum'}) + """) + self.assertEqual(res, {"e": 42}) + res = await client.query("""//ti + .wrap(&{e: '*enum'}) + """) + self.assertEqual(res, {"e": 'B'}) + + async def test_wrap_enum_name_str_on_type(self, client): + # bug #431 (like above, but then a type mapping) + await client.query( + """//ti + set_enum('E', {A: 41, B: 42}); + set_type('R', {e: 'E'}); + .to_type('R'); + """) + res = await client.query("""//ti + .wrap(&{e: 'str'}) + """) + self.assertEqual(res, {}) + res = await client.query("""//ti + .wrap(&{e: '*str'}) + """) + self.assertEqual(res, {"e": 'A'}) + res = await client.query("""//ti + .wrap(&{e: 'int'}) + """) + self.assertEqual(res, {"e": 41}) + res = await client.query("""//ti + .wrap(&{e: '*int'}) + """) + self.assertEqual(res, {}) + res = await client.query("""//ti + .wrap(&{e: 'enum'}) + """) + self.assertEqual(res, {"e": 41}) + res = await client.query("""//ti + .wrap(&{e: '*enum'}) + """) + self.assertEqual(res, {"e": 'A'}) + if __name__ == '__main__': run_test(TestEnum()) diff --git a/itest/test_tasks.py b/itest/test_tasks.py index e4c9e58f..d48006b9 100755 --- a/itest/test_tasks.py +++ b/itest/test_tasks.py @@ -36,7 +36,7 @@ async def async_run(self): client.close() await client.wait_closed() - async def _test_task(self, client): + async def test_task(self, client): with self.assertRaisesRegex( LookupError, r'function `task` is undefined in the `@node` scope; ' @@ -138,7 +138,7 @@ async def _test_task(self, client): await asyncio.sleep(num_nodes*3) self.assertEqual(await client.query('.result'), 'WoW!!') - async def _test_is_task(self, client): + async def test_is_task(self, client): with self.assertRaisesRegex( NumArgumentsError, 'function `is_task` takes 1 argument but 0 were given'): @@ -147,7 +147,7 @@ async def _test_is_task(self, client): self.assertTrue(await client.query('is_task(task(datetime(), ||0));')) self.assertFalse(await client.query('is_task( "bla" ); ')) - async def _test_task_bool(self, client): + async def test_task_bool(self, client): self.assertTrue(await client.query('bool(task(datetime(), ||0));')) res = await client.query("""//ti .t = task(datetime(), ||1/1); @@ -171,7 +171,7 @@ async def _test_task_bool(self, client): self.assertFalse(await client.query('is_err(.t.err());')) self.assertTrue(await client.query('is_err(.f.err());')) - async def _test_tasks(self, client): + async def test_tasks(self, client): with self.assertRaisesRegex( LookupError, r'function `tasks` is undefined in the `@node` scope; ' @@ -193,7 +193,7 @@ async def _test_tasks(self, client): self.assertEqual(await client.query('tasks();', scope='/t'), []) self.assertEqual(await client.query('tasks();', scope='//stuff'), []) - async def _test_again_in(self, client): + async def test_again_in(self, client): await client.query("""//ti s = datetime(); .t1 = task(s, |t| t.again_in()); @@ -252,7 +252,7 @@ async def _test_again_in(self, client): await asyncio.sleep(num_nodes*5) self.assertEqual(await client.query('.x;'), 3) - async def _test_again_at(self, client): + async def test_again_at(self, client): await client.query("""//ti s = datetime(); .t1 = task(s, |t| t.again_at()); @@ -297,7 +297,7 @@ async def _test_again_at(self, client): await asyncio.sleep(num_nodes*5) self.assertEqual(await client.query('.x;'), 3) - async def _test_task_with_future(self, client): + async def test_task_with_future(self, client): await client.query("""//ti .x = 1; task(datetime(), |t| { @@ -339,7 +339,7 @@ async def _test_task_with_future(self, client): await asyncio.sleep(num_nodes*5) self.assertEqual(await client.query('.y;'), 3) - async def _test_repr_task(self, client): + async def test_repr_task(self, client): tasks = await client.query("""//ti .tasks = [ task(datetime(), |t| t.again_in('seconds', 1)), @@ -362,7 +362,7 @@ async def _test_repr_task(self, client): self.assertEqual('task:nil', tasks[1]) self.assertNotEqual('task:nil', tasks[2]) - async def _test_set_closure(self, client): + async def test_set_closure(self, client): # bug #274 _task = await client.query("""//ti .task = task(datetime().move('days', 1), ||true); @@ -375,7 +375,7 @@ async def _test_set_closure(self, client): closure = await client1.query('.task.closure();') self.assertEqual(closure, '||false') - async def _test_set_owner(self, client): + async def test_set_owner(self, client): # bug #275 task_id = await client.query("""//ti new_user('alisa'); @@ -391,7 +391,7 @@ async def _test_set_owner(self, client): owner = await client1.query('task(id).owner();', id=task_id) self.assertEqual(owner, 'alisa') - async def _test_set_owner_access_too_much(self, client): + async def test_set_owner_access_too_much(self, client): await client.query("""//ti new_user('iris'); set_password('iris', 'siri'); diff --git a/itest/test_thingsdb_functions.py b/itest/test_thingsdb_functions.py index fe06fe39..e76e420f 100755 --- a/itest/test_thingsdb_functions.py +++ b/itest/test_thingsdb_functions.py @@ -118,7 +118,7 @@ async def test_collections_info(self, client): collections = await client.query('collections_info();') self.assertEqual(len(collections), 1) - self.assertEqual(len(collections[0]), 8) + self.assertEqual(len(collections[0]), 9) self.assertIn("collection_id", collections[0]) self.assertIn("next_free_id", collections[0]) @@ -127,6 +127,7 @@ async def test_collections_info(self, client): self.assertIn("created_at", collections[0]) self.assertIn("time_zone", collections[0]) self.assertIn("default_deep", collections[0]) + self.assertIn("ano_types", collections[0]) self.assertIn("commit_history", collections[0]) self.assertTrue(isinstance(collections[0]["collection_id"], int)) @@ -136,6 +137,7 @@ async def test_collections_info(self, client): self.assertTrue(isinstance(collections[0]["created_at"], int)) self.assertTrue(isinstance(collections[0]["time_zone"], str)) self.assertTrue(isinstance(collections[0]["default_deep"], int)) + self.assertTrue(isinstance(collections[0]["ano_types"], int)) self.assertEqual(collections[0]["commit_history"], "disabled") # at least one info should be checked for a correct created_at info diff --git a/itest/test_type.py b/itest/test_type.py index 3042a010..457a6afe 100755 --- a/itest/test_type.py +++ b/itest/test_type.py @@ -1185,6 +1185,7 @@ async def test_type_definitions(self, client): set_type('_Email', {test: 'email'}); set_type('_Url', {test: 'url'}); set_type('_Tel', {test: 'tel'}); + set_type('_Enum', {test: 'enum'}); set_type('_Any', {test: 'any'}); ''') @@ -2330,6 +2331,48 @@ async def test_to_type_no_store(self, client0): """) self.assertEqual(r, "default") + async def test_global_enum(self, client): + await client.query( + """//ti + set_enum('E', {A: 0}); + set_type('T', { + e: 'E' + }); + set_type('F', { + e: 'enum' + }); + """) + res = await client.query( + """//ti + .t = T{}; + .f = F{}; + [.t.e, .f.e]; + """) + self.assertEqual(res, [0, None]) + res = await client.query( + """//ti + .t = T{}; + .f = F{}; + return [.t.wrap('F'), .f.wrap('T')], 1, NO_IDS; + """) + self.assertEqual(res, [{'e': 0}, {}]) + res = await client.query( + """//ti + .f.e = nil; + .f.e = E{A}; + """) + self.assertEqual(res, 0) + + with self.assertRaisesRegex( + OperationError, + r'cannot apply type declaration `E` to `e` on type `F` ' + r'without a closure to migrate existing instances; ' + r'the old declaration `enum` is not compatible with ' + r'the new declaration;'): + await client.query("""//ti + mod_type('F', 'mod', 'e', 'E'); + """) + if __name__ == '__main__': run_test(TestType()) diff --git a/itest/test_wrap.py b/itest/test_wrap.py index 39ddf9d0..33e533c5 100755 --- a/itest/test_wrap.py +++ b/itest/test_wrap.py @@ -90,6 +90,52 @@ async def test_set_to_arr(self, client): ''') self.assertEqual(res, {}) + async def test_wrap_err(self, client): + with self.assertRaisesRegex( + NumArgumentsError, + r'function `wrap` takes at most 1 argument but 2 were given'): + await client.query(r''' + {}.wrap(&{}, nil); + ''') + + with self.assertRaisesRegex( + TypeError, + r'function `wrap` expects argument 1 to be of type `str` or ' + r'`` but got type `int` instead'): + await client.query(r''' + {}.wrap(123); + ''') + + with self.assertRaisesRegex( + LookupError, + r'type `nil` has no function `wrap`'): + await client.query(r''' + nil.wrap(&{}); + ''') + + with self.assertRaisesRegex( + NumArgumentsError, + r'function `map_wrap` takes at most 1 argument but 2 were ' + r'given'): + await client.query(r''' + [].map_wrap(&{}, nil); + ''') + + with self.assertRaisesRegex( + TypeError, + r'function `map_wrap` expects argument 1 to be of type `str` ' + r'or `` but got type `int` instead'): + await client.query(r''' + [].map_wrap(123); + ''') + + with self.assertRaisesRegex( + LookupError, + r'type `nil` has no function `map_wrap`'): + await client.query(r''' + nil.map_wrap(&{}); + ''') + async def test_wrap_method(self, client): res = await client.query(r''' set_type('Math', { diff --git a/itest/test_wrap_tree.py b/itest/test_wrap_tree.py index 31e4dcae..7213a2d3 100755 --- a/itest/test_wrap_tree.py +++ b/itest/test_wrap_tree.py @@ -96,7 +96,7 @@ async def test_define_err(self, client): with self.assertRaisesRegex( ValueError, - r'nested type definition on type `W` too large'): + r'wpo type definition too large'): await client.query("""//ti nested = {}; range(2000).map(|x| {nested[`key{x}`] = {x: 'int'}}); diff --git a/memleak.supp b/memleak.supp index 9ca9fd03..a5179e2d 100644 --- a/memleak.supp +++ b/memleak.supp @@ -88,4 +88,10 @@ fun:lws_create_context ... } - +{ + + Memcheck:Leak + ... + obj:/usr/lib/x86_64-linux-gnu/libcrypto.so* + ... +} diff --git a/src/langdef/langdef.c b/src/langdef/langdef.c index a91cb621..705d2394 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-02-11 20:55:25 + * Created at: 2025-11-07 13:13:19 */ #include @@ -30,6 +30,7 @@ cleri_grammar_t * compile_langdef(void) 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_ano = cleri_token(CLERI_GID_X_ANO, "&{"); cleri_t * x_template = cleri_token(CLERI_GID_X_TEMPLATE, "`"); cleri_t * template = cleri_sequence( CLERI_GID_TEMPLATE, @@ -68,6 +69,19 @@ cleri_grammar_t * compile_langdef(void) cleri_token(CLERI_NONE, "|"), CLERI_THIS ); + cleri_t * t_ano = cleri_sequence( + CLERI_GID_T_ANO, + 3, + x_ano, + cleri_list(CLERI_NONE, cleri_sequence( + CLERI_NONE, + 3, + name, + cleri_token(CLERI_NONE, ":"), + cleri_optional(CLERI_NONE, CLERI_THIS) + ), cleri_token(CLERI_NONE, ","), 0, 0, 1), + cleri_token(CLERI_NONE, "}") + ); cleri_t * thing = cleri_sequence( CLERI_GID_THING, 3, @@ -252,7 +266,7 @@ cleri_grammar_t * compile_langdef(void) cleri_choice( CLERI_NONE, CLERI_FIRST_MATCH, - 13, + 14, chain, t_false, t_nil, @@ -261,6 +275,7 @@ cleri_grammar_t * compile_langdef(void) t_int, t_string, t_regex, + t_ano, template, var_opt_more, thing, diff --git a/src/langdef/translate.c b/src/langdef/translate.c index 98334c3c..9822bff5 100644 --- a/src/langdef/translate.c +++ b/src/langdef/translate.c @@ -19,11 +19,13 @@ const char * langdef_translate(cleri_t * elem) case CLERI_GID_OPR6_COMPARE: case CLERI_GID_OPR7_CMP_AND: case CLERI_GID_OPR8_CMP_OR: + case CLERI_GID_T_ANO: case CLERI_GID_T_FALSE: case CLERI_GID_T_INT: case CLERI_GID_T_NIL: case CLERI_GID_T_STRING: case CLERI_GID_T_TRUE: + case CLERI_GID_X_ANO: case CLERI_GID_X_ARRAY: case CLERI_GID_X_ASSIGN: case CLERI_GID_X_BLOCK: diff --git a/src/ti/ano.c b/src/ti/ano.c new file mode 100644 index 00000000..06a7f365 --- /dev/null +++ b/src/ti/ano.c @@ -0,0 +1,168 @@ +/* + * ti/ano.c + */ +#include +#include +#include +#include +#include + +ti_ano_t * ti_ano_new(void) +{ + ti_ano_t * ano = calloc(1, sizeof(ti_ano_t)); + if (!ano) + return NULL; + ano->ref = 1; + ano->tp = TI_VAL_ANO; + return ano; +} + +int ano__dep_check(ti_type_t * type, ex_t * e) +{ + if (ti_type_has_dependencies(type)) + ex_set(e, EX_TYPE_ERROR, + "the "TI_VAL_ANO_S" type definition " + "contains dependencies on other types or enumerators, " + "which is not permitted; to resolve this, create a fully " + "named wrap-only type"DOC_SET_TYPE); + return e->nr; +} + +int ti_ano_init( + ti_ano_t * ano, + ti_collection_t * collection, + ti_raw_t * spec_raw, + ex_t * e) +{ + uint8_t flags = TI_TYPE_FLAG_WRAP_ONLY | TI_TYPE_FLAG_HIDE_ID; + ti_type_t * type; + ti_val_t * val; + mp_unp_t up; + ti_vup_t vup = { + .isclient = true, + .collection = collection, + .up = &up, + }; + mp_unp_init(&up, spec_raw->data, spec_raw->n); + + val = ti_val_from_vup_e(&vup, e); + if (!val) + return e->nr; + + if (!ti_val_is_object(val)) + { + ex_set_internal(e); /* only with corrupt internal data */ + goto fail0; + } + + /* can't be a dict; this is not allowed by the &{..} and ano() function */ + for (vec_each(((ti_thing_t *) val)->items.vec, ti_prop_t, prop)) + { + ti_raw_t * sr = (ti_raw_t *) prop->val; + if (ti_val_is_raw(prop->val) && sr->n) + { + if (sr->data[0] == '#') + flags &= ~TI_TYPE_FLAG_HIDE_ID; + + if (sr->data[0] == '|') + { + ti_qbind_t syntax = { + .immutable_n = 0, + .flags = TI_QBIND_FLAG_COLLECTION, + }; + ti_closure_t * closure = ti_closure_from_strn( + &syntax, + (const char *) sr->data, + sr->n, + e); + if (!closure) + goto fail0; + + ti_val_unsafe_drop(prop->val); + prop->val = (ti_val_t *) closure; + } + } + } + + type = ti_type_create_anonymous( + collection->types, + (ti_raw_t *) ti_val_borrow_anonymous_name(), + flags); + if (!type) + { + ex_set_mem(e); + goto fail0; + } + + if (ti_type_init_from_thing(type, (ti_thing_t *) val, e) || + ano__dep_check(type, e)) + ti_type_drop_anonymous(type); + else + { + ano->type = type; + ano->spec_raw = spec_raw; + ti_incref(spec_raw); + (void) smap_addn( + collection->ano_types, + (const char *) spec_raw->data, + spec_raw->n, + ano); + } +fail0: + ti_val_unsafe_drop(val); + return e->nr; +} + +ti_ano_t * ti_ano_from_raw( + ti_collection_t * collection, + ti_raw_t * spec_raw, + ex_t * e) +{ + ti_ano_t * ano = ti_ano_new(); + if (!ano) + { + ex_set_mem(e); + return NULL; + } + if (ti_ano_init(ano, collection, spec_raw, e)) + { + ti_ano_destroy(ano); + return NULL; + } + return ano; +} + +ti_ano_t * ti_ano_create( + ti_collection_t * collection, + const unsigned char * bin, + size_t n, + ex_t * e) +{ + ti_raw_t * spec_raw; + ti_ano_t * ano = smap_getn(collection->ano_types, (const char *) bin, n); + if (ano) + { + ti_incref(ano); + return ano; + } + spec_raw = ti_bin_create(bin, n); + if (!spec_raw) + return NULL; + + ano = ti_ano_from_raw(collection, spec_raw, e); + ti_val_unsafe_drop((ti_val_t *) spec_raw); + return ano; +} + +void ti_ano_destroy(ti_ano_t * ano) +{ + /* we must check type as we might have spec_raw without a type */ + if (ano->type) + (void) smap_popn( + ano->type->types->collection->ano_types, + (const char *) ano->spec_raw->data, + ano->spec_raw->n); + ti_val_drop((ti_val_t *) ano->spec_raw); + ti_type_drop_anonymous(ano->type); + free(ano); +} diff --git a/src/ti/collection.c b/src/ti/collection.c index ac2e018c..7e929b67 100644 --- a/src/ti/collection.c +++ b/src/ti/collection.c @@ -61,6 +61,7 @@ ti_collection_t * ti_collection_create( collection->gc = queue_new(20); collection->access = vec_new(1); collection->procedures = smap_create(); + collection->ano_types = smap_create(); collection->types = ti_types_create(collection); collection->enums = ti_enums_create(collection); collection->lock = malloc(sizeof(uv_mutex_t)); @@ -77,7 +78,7 @@ ti_collection_t * ti_collection_create( !collection->access || !collection->procedures || !collection->lock || !collection->types || !collection->enums || !collection->futures || !collection->rooms || !collection->named_rooms || - uv_mutex_init(collection->lock)) + !collection->ano_types || uv_mutex_init(collection->lock)) { ti_collection_drop(collection); return NULL; @@ -104,6 +105,7 @@ void ti_collection_destroy(ti_collection_t * collection) vec_destroy(collection->commits, (vec_destroy_cb) ti_commit_destroy); smap_destroy(collection->procedures, (smap_destroy_cb) ti_procedure_destroy); smap_destroy(collection->named_rooms, NULL); + smap_destroy(collection->ano_types, NULL); ti_types_destroy(collection->types); ti_enums_destroy(collection->enums); uv_mutex_destroy(collection->lock); @@ -132,7 +134,7 @@ void ti_collection_drop(ti_collection_t * collection) int ti_collection_to_pk(ti_collection_t * collection, msgpack_packer * pk) { return -( - msgpack_pack_map(pk, 8) || + msgpack_pack_map(pk, 9) || mp_pack_str(pk, "collection_id") || msgpack_pack_uint64(pk, collection->id) || @@ -158,7 +160,10 @@ int ti_collection_to_pk(ti_collection_t * collection, msgpack_packer * pk) mp_pack_str(pk, "commit_history") || (collection->commits ? msgpack_pack_uint32(pk, collection->commits->n) - : mp_pack_str(pk, "disabled")) + : mp_pack_str(pk, "disabled")) || + + mp_pack_str(pk, "ano_types") || + msgpack_pack_uint64(pk, collection->ano_types->n) ); } diff --git a/src/ti/do.c b/src/ti/do.c index b2203aff..3090d65c 100644 --- a/src/ti/do.c +++ b/src/ti/do.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -1228,18 +1229,19 @@ static int do__fixed_name(ti_query_t * query, cleri_node_t * nd, ex_t * e) return e->nr; } -static int do__thing(ti_query_t * query, cleri_node_t * nd, ex_t * e) +static int do__thing( + ti_query_t * query, + cleri_node_t * nd, + ex_t * e, + uintptr_t sz) { /* * Sequence('{', List(Sequence(name, ':', scope)), '}') */ assert(e->nr == 0); - ti_thing_t * thing; cleri_node_t * child; - uintptr_t sz = (uintptr_t) nd->data; - - thing = ti_thing_o_create(0, sz, query->collection); + ti_thing_t * thing = ti_thing_o_create(0, sz, query->collection); if (!thing) goto failed_save; @@ -1302,6 +1304,66 @@ static int do__thing(ti_query_t * query, cleri_node_t * nd, ex_t * e) return e->nr; } +static int do__ano(ti_query_t * query, cleri_node_t * nd, ex_t * e) +{ + ti_ano_t * ano; + if (!nd->data) + { + /* this will be an always incomplete ano, without type; this is + * because we actually need the spec_raw, not the ano type but ncache + * must contain a pointer to the spec_raw but is not able to + * calculate the spec_raw itself; */ + nd->data = ti_ano_new(); + if (!nd->data) + { + ex_set_mem(e); + return e->nr; + } + assert(vec_space(query->immutable_cache)); + VEC_push(query->immutable_cache, nd->data); + } + ano = nd->data; + if (ti_ano_uninitialized(nd->data)) + { + if (!query->collection) + { + ex_set(e, EX_LOOKUP_ERROR, + "anonymous types are not supported in the the `%s` scope", + ti_query_scope_name(query)); + return e->nr; + } + + if (do__thing(query, nd, e, 7)) + return e->nr; + + ano->spec_raw = ti_type_spec_raw_from_thing( + (ti_thing_t *) query->rval, + query->rval, + e); + if (!ano->spec_raw) + return e->nr; + + ti_val_unsafe_drop(query->rval); + query->rval = NULL; + } + + /* first try cache for equal ano */ + query->rval = smap_getn( + query->collection->ano_types, + (const char *) ano->spec_raw->data, + ano->spec_raw->n); + + /* if no success, try new one */ + if (query->rval) + ti_incref(query->rval); + else + query->rval = (ti_val_t *) ti_ano_from_raw( + query->collection, + ano->spec_raw, + e); + return e->nr; +} + static int do__instance(ti_query_t * query, cleri_node_t * nd, ex_t * e) { /* @@ -1868,6 +1930,10 @@ int ti_do_expression(ti_query_t * query, cleri_node_t * nd, ex_t * e) /* nothing is possible after a chain */ goto preopr; + case CLERI_GID_T_ANO: + if (do__ano(query, nd, e)) + return e->nr; + break; case CLERI_GID_T_FALSE: query->rval = (ti_val_t *) ti_vbool_get(false); break; @@ -1978,7 +2044,7 @@ int ti_do_expression(ti_query_t * query, cleri_node_t * nd, ex_t * e) } break; case CLERI_GID_THING: - if (do__thing(query, nd, e)) + if (do__thing(query, nd, e, (uintptr_t) nd->data)) return e->nr; break; case CLERI_GID_ARRAY: diff --git a/src/ti/enum.c b/src/ti/enum.c index 72782595..5c2390d9 100644 --- a/src/ti/enum.c +++ b/src/ti/enum.c @@ -728,6 +728,8 @@ ti_member_t * ti_enum_member_by_val_e( case TI_VAL_CLOSURE: case TI_VAL_ERROR: case TI_VAL_MEMBER: + case TI_VAL_ANO: + case TI_VAL_WANO: case TI_VAL_FUTURE: case TI_VAL_MODULE: case TI_VAL_TEMPLATE: diff --git a/src/ti/export.c b/src/ti/export.c index d898d120..c04109f8 100644 --- a/src/ti/export.c +++ b/src/ti/export.c @@ -362,17 +362,6 @@ static int export__val(ti_fmt_t * fmt, ti_val_t * val) switch((ti_val_enum) val->tp) { - case TI_VAL_NAME: - case TI_VAL_STR: - return ti_fmt_ti_string(fmt, (ti_raw_t *) val); - case TI_VAL_MPDATA: - case TI_VAL_BYTES: - return buf_append_str(buf, "bytes() /* WARN: not exported */"); - case TI_VAL_REGEX: - { - ti_raw_t * pattern = ((ti_regex_t *) val)->pattern; - return buf_append(buf, (const char *) pattern->data, pattern->n); - } case TI_VAL_NIL: return buf_append_str(buf, "nil"); case TI_VAL_INT: @@ -385,6 +374,16 @@ static int export__val(ti_fmt_t * fmt, ti_val_t * val) : buf_append_str(buf, "false"); case TI_VAL_DATETIME: return buf_append_fmt(buf, "datetime(%ld)", DATETIME(val)); + case TI_VAL_NAME: + case TI_VAL_STR: + return ti_fmt_ti_string(fmt, (ti_raw_t *) val); + case TI_VAL_BYTES: + return buf_append_str(buf, "bytes() /* WARN: not exported */"); + case TI_VAL_REGEX: + { + ti_raw_t * pattern = ((ti_regex_t *) val)->pattern; + return buf_append(buf, (const char *) pattern->data, pattern->n); + } case TI_VAL_THING: return export__thing(fmt, (ti_thing_t *) val); case TI_VAL_WRAP: @@ -428,8 +427,28 @@ static int export__val(ti_fmt_t * fmt, ti_val_t * val) buf_write(buf, ')') ); } + case TI_VAL_ERROR: + return buf_append_str(buf, "error() /* WARN: not exported */"); + case TI_VAL_MEMBER: + { + ti_member_t * member = (ti_member_t *) val; + return buf_append_fmt( + buf, + "%s{%.*s}", + member->enum_->name, + member->name->n, member->name->str); + } + case TI_VAL_MPDATA: + return buf_append_str(buf, "bytes() /* WARN: not exported */"); case TI_VAL_CLOSURE: return ti_fmt_nd(fmt, ((ti_closure_t * ) val)->node); + case TI_VAL_ANO: + return -( + buf_write(buf, '&') || + export__thing(fmt, (ti_thing_t *) val) + ); + case TI_VAL_WANO: + return buf_append_str(buf, "&{}.wrap() /* WARN: not exported */"); case TI_VAL_FUTURE: return buf_append_str(buf, "future(||nil) /* WARN: not exported */"); case TI_VAL_MODULE: @@ -440,17 +459,6 @@ static int export__val(ti_fmt_t * fmt, ti_val_t * val) "%.*s /* WARN: module must be installed */", module->name->n, module->name->str); } - case TI_VAL_ERROR: - return buf_append_str(buf, "error() /* WARN: not exported */"); - case TI_VAL_MEMBER: - { - ti_member_t * member = (ti_member_t *) val; - return buf_append_fmt( - buf, - "%s{%.*s}", - member->enum_->name, - member->name->n, member->name->str); - } case TI_VAL_TEMPLATE: assert(0); } @@ -493,11 +501,13 @@ static int export__set_enum_cb(ti_enum_t * enum_, ti_fmt_t * fmt) case TI_VAL_TASK: case TI_VAL_ARR: case TI_VAL_SET: - case TI_VAL_CLOSURE: case TI_VAL_ERROR: case TI_VAL_MEMBER: + case TI_VAL_CLOSURE: + case TI_VAL_ANO: case TI_VAL_FUTURE: case TI_VAL_MODULE: + case TI_VAL_WANO: case TI_VAL_TEMPLATE: assert(0); break; diff --git a/src/ti/field.c b/src/ti/field.c index e1b064e0..3facd838 100644 --- a/src/ti/field.c +++ b/src/ti/field.c @@ -294,11 +294,11 @@ static inline void field__set_cb(ti_field_t * field, ti_field_dval_cb cb) */ enum { - TOTAL_KEYWORDS = 25, + TOTAL_KEYWORDS = 26, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 8, MIN_HASH_VALUE = 2, - MAX_HASH_VALUE = 29 + MAX_HASH_VALUE = 27 }; static inline unsigned int field__hash( @@ -307,32 +307,32 @@ static inline unsigned int field__hash( { static unsigned short asso_values[] = { - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 13, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 8, 30, 8, 30, 30, 30, 9, 6, 0, - 0, 4, 3, 1, 9, 0, 30, 4, 0, 4, - 1, 0, 9, 30, 0, 8, 0, 0, 0, 0, - 1, 0, 30, 0, 30, 0, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30 + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 14, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 11, 28, 11, 28, 28, 28, 8, 1, 6, + 0, 1, 8, 6, 4, 3, 28, 0, 0, 6, + 1, 0, 9, 28, 0, 8, 0, 0, 0, 3, + 5, 1, 28, 0, 28, 0, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28 }; register unsigned int hval = n; @@ -393,6 +393,7 @@ ti_field_map_t field__mapping[TOTAL_KEYWORDS] = { {.name="email", .spec=TI_SPEC_EMAIL, .dval_cb=field__dval_str}, {.name="url", .spec=TI_SPEC_URL, .dval_cb=field__dval_str}, {.name="tel", .spec=TI_SPEC_TEL, .dval_cb=field__dval_str}, + {.name="enum", .spec=TI_SPEC_ENUM, .dval_cb=field__dval_nil}, }; static ti_field_map_t * field__map[MAX_HASH_VALUE+1]; @@ -930,7 +931,6 @@ static int field__init(ti_field_t * field, ex_t * e) DOC_T_TYPE, field->name->str, field->type->name, field->spec_raw->n, (const char *) field->spec_raw->data); - return e->nr; circular_dep: @@ -1698,7 +1698,7 @@ static _Bool field__maps_arr_to_arr(ti_field_t * field, ti_varr_t * varr) return true; for (vec_each(varr->vec, ti_val_t, val)) - if (!ti_spec_maps_to_nested_val(field->nested_spec, val)) + if (!ti_spec_maps_to_nested_val(field, val)) return false; return true; @@ -1714,7 +1714,7 @@ static _Bool field__maps_arr_to_type(ti_varr_t * varr) static int field__map_restrict_cb(ti_prop_t * prop, ti_field_t * field) { - return !ti_spec_maps_to_nested_val(field->nested_spec, prop->val); + return !ti_spec_maps_to_nested_val(field, prop->val); } static _Bool field__maps_restricted(ti_field_t * field, ti_thing_t * thing) @@ -1735,7 +1735,7 @@ static _Bool field__maps_restricted(ti_field_t * field, ti_thing_t * thing) field); for (vec_each(thing->items.vec, ti_prop_t, prop)) - if (!ti_spec_maps_to_nested_val(field->nested_spec, prop->val)) + if (!ti_spec_maps_to_nested_val(field, prop->val)) return false; return true; @@ -1819,7 +1819,6 @@ int ti_field_make_assignable( case TI_VAL_FLOAT: case TI_VAL_BOOL: case TI_VAL_DATETIME: - case TI_VAL_MPDATA: case TI_VAL_NAME: case TI_VAL_STR: case TI_VAL_BYTES: @@ -1833,15 +1832,20 @@ int ti_field_make_assignable( return field__varr_assign(field, (ti_varr_t **) val, parent, e); case TI_VAL_SET: return field__vset_assign(field, (ti_vset_t **) val, parent, e); - case TI_VAL_CLOSURE: - return ti_closure_unbound((ti_closure_t *) *val, e); case TI_VAL_ERROR: case TI_VAL_MEMBER: - case TI_VAL_TEMPLATE: + case TI_VAL_MPDATA: + break; + case TI_VAL_CLOSURE: + return ti_closure_unbound((ti_closure_t *) *val, e); + case TI_VAL_ANO: + case TI_VAL_WANO: break; case TI_VAL_FUTURE: case TI_VAL_MODULE: goto future_module_error; + case TI_VAL_TEMPLATE: + break; } return 0; case TI_SPEC_OBJECT: @@ -1956,6 +1960,10 @@ int ti_field_make_assignable( (ti_raw_t *) *val)) goto tel_error; return 0; + case TI_SPEC_ENUM: + if (ti_val_is_member(*val) || ti_val_is_nil(*val)) + return 0; + goto type_error; case TI_SPEC_ARR: if (ti_val_is_array(*val)) return field__varr_assign(field, (ti_varr_t **) val, parent, e); @@ -2202,7 +2210,14 @@ _Bool ti_field_maps_to_val(ti_field_t * field, ti_val_t * val) return ti_spec_enum_eq_to_val(spec, val); if (ti_val_is_member(val)) - val = VMEMBER(val); + { + if (spec == TI_SPEC_ENUM) + return true; + + val = (field->flags & TI_FIELD_FLAG_ENAME) + ? (ti_val_t *) ((ti_member_t *) val)->name + : VMEMBER(val); + } switch ((ti_spec_enum_t) spec) { @@ -2261,6 +2276,8 @@ _Bool ti_field_maps_to_val(ti_field_t * field, ti_val_t * val) return ti_val_is_str(val) && ti_regex_test_or_empty( (ti_regex_t *) ti_val_borrow_re_tel(), (ti_raw_t *) val); + case TI_SPEC_ENUM: + return (ti_val_is_member(val) || ti_val_is_nil(val)); case TI_SPEC_ARR: /* we can map a set to an array */ return (( @@ -2329,10 +2346,20 @@ static _Bool field__maps_to_nested(ti_field_t * t_field, ti_field_t * f_field) if (f_spec >= TI_ENUM_ID_FLAG) { - ti_enum_t * enum_ = ti_enums_by_id( + if (t_spec == TI_SPEC_ENUM) + return true; + + if (t_field->flags & TI_FIELD_FLAG_ENAME) + { + f_spec = TI_SPEC_STR; + } + else + { + ti_enum_t * enum_ = ti_enums_by_id( f_field->type->types->collection->enums, f_spec & TI_ENUM_ID_MASK); - f_spec = ti_enum_spec(enum_); + f_spec = ti_enum_spec(enum_); + } } if (t_spec == f_spec) @@ -2384,6 +2411,7 @@ static _Bool field__maps_to_nested(ti_field_t * t_field, ti_field_t * f_field) case TI_SPEC_EMAIL: case TI_SPEC_URL: case TI_SPEC_TEL: + case TI_SPEC_ENUM: case TI_SPEC_ARR: case TI_SPEC_SET: case TI_SPEC_REMATCH: @@ -2425,10 +2453,20 @@ _Bool ti_field_maps_to_field(ti_field_t * t_field, ti_field_t * f_field) if (f_spec >= TI_ENUM_ID_FLAG) { - ti_enum_t * enum_ = ti_enums_by_id( + if (t_spec == TI_SPEC_ENUM) + return true; + + if (t_field->flags & TI_FIELD_FLAG_ENAME) + { + f_spec = TI_SPEC_STR; + } + else + { + ti_enum_t * enum_ = ti_enums_by_id( f_field->type->types->collection->enums, f_spec & TI_ENUM_ID_MASK); - f_spec = ti_enum_spec(enum_); + f_spec = ti_enum_spec(enum_); + } } switch ((ti_spec_enum_t) t_spec) @@ -2508,6 +2546,7 @@ _Bool ti_field_maps_to_field(ti_field_t * t_field, ti_field_t * f_field) case TI_SPEC_EMAIL: case TI_SPEC_URL: case TI_SPEC_TEL: + case TI_SPEC_ENUM: return f_spec == t_spec; case TI_SPEC_ARR: return ( diff --git a/src/ti/fmt.c b/src/ti/fmt.c index a04d4f77..61e317a6 100644 --- a/src/ti/fmt.c +++ b/src/ti/fmt.c @@ -378,6 +378,11 @@ static int fmt__expr_choice(ti_fmt_t * fmt, cleri_node_t * nd) { case CLERI_GID_CHAIN: return fmt__chain(fmt, nd, false); + case CLERI_GID_T_ANO: + return -( + buf_write(&fmt->buf, '&') || + fmt__thing(fmt, nd) + ); case CLERI_GID_T_FALSE: case CLERI_GID_T_FLOAT: case CLERI_GID_T_INT: diff --git a/src/ti/index.c b/src/ti/index.c index 712f4592..f29b1387 100644 --- a/src/ti/index.c +++ b/src/ti/index.c @@ -673,6 +673,8 @@ int ti_index(ti_query_t * query, cleri_node_t * nd, ex_t * e) case TI_VAL_MEMBER: case TI_VAL_MPDATA: case TI_VAL_CLOSURE: + case TI_VAL_ANO: + case TI_VAL_WANO: case TI_VAL_FUTURE: case TI_VAL_MODULE: if (do_slice) diff --git a/src/ti/ncache.c b/src/ti/ncache.c index 95efd201..d4705e71 100644 --- a/src/ti/ncache.c +++ b/src/ti/ncache.c @@ -336,6 +336,26 @@ static int ncache__expr_choice( } return e->nr; } + case CLERI_GID_T_ANO: + { + cleri_node_t * child = nd /* sequence */ + ->children->next /* list */ + ->children; + for (; child; child = child->next ? child->next->next : NULL) + { + /* sequence(name: statement) (only investigate the statements */ + if (child->children->next->next == NULL + ? ncache__gen_name(vcache, child->children, e) + : ncache__statement( + syntax, + vcache, + child->children->next->next, + e)) + return e->nr; + } + nd->data = ti_ano_new(); + break; + } case CLERI_GID_ARRAY: return ncache__list( syntax, @@ -429,7 +449,7 @@ static int ncache__return_statement( { if (!nd->children->next->children) return 0; - + if (ncache__statement(syntax, vcache, nd->children->next->children, e)) return -1; diff --git a/src/ti/opr.c b/src/ti/opr.c index c7354c59..05be313c 100644 --- a/src/ti/opr.c +++ b/src/ti/opr.c @@ -108,6 +108,16 @@ _Bool ti__opr_eq_(ti_val_t * a, ti_val_t * b) return ti_vset_eq((ti_vset_t *) a, (ti_vset_t *) b); case OPR_ERROR_ERROR: return ((ti_verror_t *) a)->code == ((ti_verror_t *) b)->code; + case OPR_ANO_ANO: + return ti_raw_eq( + ((ti_ano_t *) a)->spec_raw, + ((ti_ano_t *) b)->spec_raw); + case OPR_WANO_WANO: + return + ((ti_wano_t *) a)->thing == ((ti_wano_t *) b)->thing && + ti_raw_eq( + ((ti_wano_t *) a)->ano->spec_raw, + ((ti_wano_t *) b)->ano->spec_raw); } return false; } diff --git a/src/ti/qbind.c b/src/ti/qbind.c index 271604f2..015ec88c 100644 --- a/src/ti/qbind.c +++ b/src/ti/qbind.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -302,15 +303,15 @@ static void qbind__statement(ti_qbind_t * qbind, cleri_node_t * nd); * * Command: * - * pcregrep -o1 '\.name\=\"(\w+)' qbind.c | gperf -E -k '*,1,$' -m 200 + * pcregrep -o1 '\.name\=\"(\w+)' qbind.c | gperf -E -k '*,1,$' -m 400 */ enum { - TOTAL_KEYWORDS = 281, + TOTAL_KEYWORDS = 282, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 17, - MIN_HASH_VALUE = 38, - MAX_HASH_VALUE = 796 + MIN_HASH_VALUE = 24, + MAX_HASH_VALUE = 776 }; /* @@ -322,32 +323,32 @@ static inline unsigned int qbind__hash( { static unsigned short asso_values[] = { - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 16, 15, - 18, 797, 19, 797, 18, 797, 16, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 15, 797, 20, 58, 82, - 24, 17, 152, 371, 112, 15, 18, 73, 17, 25, - 20, 22, 131, 15, 16, 15, 16, 52, 174, 300, - 204, 140, 29, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, - 797, 797, 797, 797, 797, 797 + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 7, 7, + 7, 777, 8, 777, 8, 777, 8, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 7, 777, 24, 42, 69, + 40, 9, 127, 359, 240, 7, 7, 120, 13, 40, + 12, 14, 155, 39, 8, 7, 8, 48, 230, 241, + 174, 64, 62, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777, 777, 777, 777, 777, + 777, 777, 777, 777, 777, 777 }; register unsigned int hval = n; @@ -495,6 +496,7 @@ qbind__fmap_t qbind__fn_mapping[TOTAL_KEYWORDS] = { {.name="again_at", .fn=do__f_again_at, CHAIN_BE}, {.name="again_in", .fn=do__f_again_in, CHAIN_BE}, {.name="alt_raise", .fn=do__f_alt_raise, ROOT_NE}, + {.name="ano", .fn=do__f_ano, ROOT_NE}, {.name="args", .fn=do__f_args, CHAIN_NE}, {.name="assert", .fn=do__f_assert, ROOT_NE}, {.name="assert_err", .fn=do__f_assert_err, ROOT_NE}, @@ -1261,6 +1263,11 @@ static void qbind__expr_choice(ti_qbind_t * qbind, cleri_node_t * nd) ++qbind->immutable_n; nd->data = NULL; /* initialize data to null */ return; + case CLERI_GID_T_ANO: + qbind__thing(qbind, nd); + ++qbind->immutable_n; + nd->data = NULL; /* initialize data to null */ + return; case CLERI_GID_TEMPLATE: { cleri_node_t * child = nd /* sequence */ diff --git a/src/ti/query.c b/src/ti/query.c index 0aef2640..70c407d2 100644 --- a/src/ti/query.c +++ b/src/ti/query.c @@ -1491,10 +1491,14 @@ static int query__get_things(ti_val_t * val, imap_t * imap) case TI_VAL_MEMBER: /* things as a member have an id */ case TI_VAL_MPDATA: case TI_VAL_CLOSURE: + case TI_VAL_ANO: break; + case TI_VAL_WANO: + return query__var_walk_thing(((ti_wano_t *) val)->thing, imap); case TI_VAL_FUTURE: return VFUT(val) ? query__get_things(VFUT(val), imap) : 0; case TI_VAL_MODULE: + break; case TI_VAL_TEMPLATE: break; } diff --git a/src/ti/spec.c b/src/ti/spec.c index ffe517cc..a054da87 100644 --- a/src/ti/spec.c +++ b/src/ti/spec.c @@ -27,6 +27,7 @@ * pcregrep -o1 ' :: `(\w+)' spec.c | gperf -E -k '*,1,$' -m 200 :: `any` + :: `ano` :: `bool` :: `break` :: `bytes` @@ -69,11 +70,11 @@ enum { - TOTAL_KEYWORDS = 38, + TOTAL_KEYWORDS = 39, MIN_WORD_LENGTH = 2, MAX_WORD_LENGTH = 8, MIN_HASH_VALUE = 3, - MAX_HASH_VALUE = 41 + MAX_HASH_VALUE = 46 }; static inline unsigned int spec__hash( @@ -82,32 +83,32 @@ static inline unsigned int spec__hash( { static unsigned char asso_values[] = { - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 19, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 5, 6, 14, - 10, 0, 15, 24, 0, 5, 42, 17, 2, 8, - 0, 3, 19, 42, 2, 0, 0, 3, 13, 29, - 6, 16, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42 + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 17, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 15, 10, 13, + 9, 0, 11, 13, 0, 5, 47, 8, 2, 8, + 0, 3, 16, 47, 2, 0, 0, 3, 2, 13, + 11, 19, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47 }; register unsigned int hval = n; @@ -162,30 +163,32 @@ _Bool ti_spec_is_reserved(register const char * s, register size_t n) "enum", "union", "time", - "bool", - "date", - "room", - "try", "if", "for", - "any", - "number", - "task", - "bytes", + "room", + "ano", + "bool", + "thing", + "try", "pint", "tuple", - "float", + "task", + "date", + "number", "closure", - "final", - "continue", - "thing", - "break", - "datetime", "regex", - "catch", + "continue", "raw", + "bytes", + "utf8", + "float", + "any", + "final", "timeval", - "utf8" + "break", + "", "", "", "", + "datetime", + "catch" }; if (n <= MAX_WORD_LENGTH && n >= MIN_WORD_LENGTH) @@ -405,6 +408,10 @@ ti_spec_rval_enum ti__spec_check_nested_val(uint16_t spec, ti_val_t * val) : ti_regex_test_or_empty( (ti_regex_t *) ti_val_borrow_re_tel(), (ti_raw_t *) val) ? 0 : TI_SPEC_RVAL_TEL_ERROR; + case TI_SPEC_ENUM: + return (ti_val_is_member(val) || ti_val_is_nil(val)) + ? 0 + : TI_SPEC_RVAL_TYPE_ERROR; case TI_SPEC_REMATCH: case TI_SPEC_INT_RANGE: case TI_SPEC_FLOAT_RANGE: @@ -429,15 +436,21 @@ ti_spec_rval_enum ti__spec_check_nested_val(uint16_t spec, ti_val_t * val) : TI_SPEC_RVAL_TYPE_ERROR; } -_Bool ti__spec_maps_to_nested_val(uint16_t spec, ti_val_t * val) +_Bool ti__spec_maps_to_nested_val(ti_field_t * field, ti_val_t * val) { - assert(~spec & TI_SPEC_NILLABLE); + uint16_t spec = field->nested_spec & TI_SPEC_MASK_NILLABLE; if (spec >= TI_ENUM_ID_FLAG) return ti_spec_enum_eq_to_val(spec, val); if (ti_val_is_member(val)) - val = VMEMBER(val); + { + if (spec == TI_SPEC_ENUM) + return true; + val = (field->flags & TI_FIELD_FLAG_ENAME) + ? (ti_val_t *) ((ti_member_t *) val)->name + : VMEMBER(val); + } switch ((ti_spec_enum_t) spec) { @@ -501,6 +514,8 @@ _Bool ti__spec_maps_to_nested_val(uint16_t spec, ti_val_t * val) return ti_val_is_str(val) && ti_regex_test_or_empty( (ti_regex_t *) ti_val_borrow_re_tel(), (ti_raw_t *) val); + case TI_SPEC_ENUM: + return (ti_val_is_member(val) || ti_val_is_nil(val)); case TI_SPEC_REMATCH: case TI_SPEC_INT_RANGE: case TI_SPEC_FLOAT_RANGE: @@ -553,6 +568,7 @@ const char * ti_spec_approx_type_str(uint16_t spec) case TI_SPEC_EMAIL: return "email"; case TI_SPEC_URL: return "url"; case TI_SPEC_TEL: return "tel"; + case TI_SPEC_ENUM: return "enum"; case TI_SPEC_TYPE: case TI_SPEC_ARR_TYPE: assert(0); /* not possible for wrap-only */ @@ -670,6 +686,11 @@ ti_spec_mod_enum ti_spec_check_mod( case TI_SPEC_URL: case TI_SPEC_TEL: return ospec == nspec ? TI_SPEC_MOD_SUCCESS : TI_SPEC_MOD_ERR; + case TI_SPEC_ENUM: + return ( + ospec == TI_SPEC_ENUM || + ti_spec_is_enum(ospec) + ) ? TI_SPEC_MOD_SUCCESS : TI_SPEC_MOD_ERR; case TI_SPEC_REMATCH: return TI_SPEC_MOD_ERR; case TI_SPEC_INT_RANGE: diff --git a/src/ti/thing.c b/src/ti/thing.c index a6a8b4d0..144ecc87 100644 --- a/src/ti/thing.c +++ b/src/ti/thing.c @@ -426,6 +426,8 @@ int ti_thing_p_prop_add_assign( case TI_VAL_TASK: case TI_VAL_ERROR: case TI_VAL_MEMBER: + case TI_VAL_ANO: + case TI_VAL_WANO: ti_incref(val); break; case TI_VAL_ARR: @@ -527,6 +529,8 @@ int ti_thing_i_item_add_assign( case TI_VAL_TASK: case TI_VAL_ERROR: case TI_VAL_MEMBER: + case TI_VAL_ANO: + case TI_VAL_WANO: ti_incref(val); break; case TI_VAL_ARR: diff --git a/src/ti/type.c b/src/ti/type.c index 8935ad5e..03afe95d 100644 --- a/src/ti/type.c +++ b/src/ti/type.c @@ -108,7 +108,8 @@ ti_type_t * ti_type_create_anonymous( type->type_id = TI_SPEC_TYPE; type->flags = TI_TYPE_FLAG_WRAP_ONLY|flags; type->name = strndup((const char *) name->data, name->n); - type->rname = ti_grab(name); /* name must be equal to the master type; + type->rname = ti_grab(name); /* for nested structure, name must be equal + to the master type; in fields.c, this is compared for circular references */ type->wname = NULL; @@ -173,9 +174,19 @@ static int type__map_cleanup(ti_type_t * t_haystack, ti_type_t * t_needle) return 0; } +/* used as a callback function and removes all cached type mappings */ +static int type__map_cleanup_ano_cb(ti_ano_t * ano, ti_type_t * t_needle) +{ + return type__map_cleanup(ano->type, t_needle); +} + void ti_type_map_cleanup(ti_type_t * type) { (void) imap_walk(type->types->imap, (imap_cb) type__map_cleanup, type); + (void) smap_values( + type->types->collection->ano_types, + (smap_val_cb) type__map_cleanup_ano_cb, + type); imap_clear(type->t_mappings, (imap_destroy_cb) ti_map_destroy); } @@ -399,9 +410,11 @@ static int type__init_type_cb(ti_item_t * item, ex_t * e) return e->nr; } -ti_raw_t * ti__type_nested_from_val(ti_type_t * type, ti_val_t * val, ex_t * e) +ti_raw_t * ti_type_spec_raw_from_thing( + ti_thing_t * thing, + ti_val_t * val, + ex_t * e) { - ti_thing_t * thing; ti_raw_t * spec_raw = NULL; msgpack_sbuffer buffer; ti_vp_t vp = { @@ -410,6 +423,40 @@ ti_raw_t * ti__type_nested_from_val(ti_type_t * type, ti_val_t * val, ex_t * e) nesting, set a low size limit */ }; + if (ti_thing_is_dict(thing) && + smap_values(thing->items.smap, (smap_val_cb) type__init_type_cb, e)) + return NULL; + + if (mp_sbuffer_alloc_init(&buffer, vp.size_limit, 0)) + { + ex_set_mem(e); + return NULL; + } + + msgpack_packer_init(&vp.pk, &buffer, msgpack_sbuffer_write); + + if (ti_val_to_client_pk(val, &vp, TI_MAX_DEEP, TI_FLAGS_NO_IDS)) + { + if (buffer.size > vp.size_limit) + ex_set(e, EX_VALUE_ERROR, "wpo type definition too large"); + else + ex_set_mem(e); + goto fail0; + } + + spec_raw = ti_mp_create((const unsigned char *) buffer.data, buffer.size); + if (!spec_raw) + ex_set_mem(e); + +fail0: + msgpack_sbuffer_destroy(&buffer); + return spec_raw; +} + +ti_raw_t * ti__type_nested_from_val(ti_type_t * type, ti_val_t * val, ex_t * e) +{ + ti_thing_t * thing; + if (ti_val_is_array(val)) { ti_varr_t * varr = (ti_varr_t *) val; @@ -446,37 +493,18 @@ ti_raw_t * ti__type_nested_from_val(ti_type_t * type, ti_val_t * val, ex_t * e) type->name); return NULL; } + return ti_type_spec_raw_from_thing(thing, val, e); +} - if (ti_thing_is_dict(thing) && - smap_values(thing->items.smap, (smap_val_cb) type__init_type_cb, e)) - return NULL; - - if (mp_sbuffer_alloc_init(&buffer, vp.size_limit, 0)) - { - ex_set_mem(e); - return NULL; - } - - msgpack_packer_init(&vp.pk, &buffer, msgpack_sbuffer_write); - - if (ti_val_to_client_pk(val, &vp, TI_MAX_DEEP, TI_FLAGS_NO_IDS)) - { - if (buffer.size > vp.size_limit) - ex_set(e, EX_VALUE_ERROR, - "nested type definition on type `%s` too large", - type->name); - else - ex_set_mem(e); - goto fail0; - } - - spec_raw = ti_mp_create((const unsigned char *) buffer.data, buffer.size); - if (!spec_raw) - ex_set_mem(e); - -fail0: - msgpack_sbuffer_destroy(&buffer); - return spec_raw; +_Bool ti_type_has_dependencies(ti_type_t * type) +{ + if (type->dependencies->n) + return true; + for (vec_each(type->fields, ti_field_t, field)) + if (ti_field_is_nested(field) && + ti_type_has_dependencies(field->condition.type)) + return true; + return false; } static inline int type__assign( diff --git a/src/ti/val.c b/src/ti/val.c index 98ecb2d1..d0d8212f 100644 --- a/src/ti/val.c +++ b/src/ti/val.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,8 @@ static ti_val_t * val__empty_bin; static ti_val_t * val__empty_str; static ti_val_t * val__default_closure; static ti_val_t * val__default_re; +static ti_val_t * val__sano; +static ti_val_t * val__swano; static ti_val_t * val__sbool; static ti_val_t * val__sbytes; static ti_val_t * val__sclosure; @@ -92,6 +95,7 @@ ti_val_t * val__parent_name; ti_val_t * val__parent_type_name; ti_val_t * val__key_name; ti_val_t * val__key_type_name; +ti_val_t * val__anonymous_name; /* string */ ti_val_t * val__sany; @@ -250,6 +254,53 @@ static ti_val_t * val__unp_map(ti_vup_t * vup, size_t sz, ex_t * e) ex_set_mem(e); return (ti_val_t *) wrap; } + case TI_KIND_C_WANO: + { + mp_obj_t mp_spec_raw; + ti_val_t * vthing; + ti_ano_t * ano; + ti_wano_t * wano; + + if (sz != 1 || + mp_next(vup->up, &mp_val) != MP_ARR || mp_val.via.sz != 2 || + mp_next(vup->up, &mp_spec_raw) != MP_BIN) + { + ex_set(e, EX_BAD_DATA, + "wano type must be written according the " + "following syntax: {\""TI_KIND_S_WANO"\": [spec_raw, {...}]"); + return NULL; + } + + vthing = ti_val_from_vup_e(vup, e); + if (!vthing) + return NULL; + + if (!ti_val_is_thing(vthing)) + { + ex_set(e, EX_BAD_DATA, + "wano type is expecting a wrapped `"TI_VAL_THING_S"` but " + "got type `%s` instead", + ti_val_str(vthing)); + ti_val_unsafe_drop(vthing); + return NULL; + } + ano = ti_ano_create( + vup->collection, + mp_spec_raw.via.bin.data, + mp_spec_raw.via.bin.n, + e); + if (!ano) + { + ti_val_unsafe_drop(vthing); + return NULL; + } + wano = ti_wano_create((ti_thing_t *) vthing, ano); + if (!wano) + ex_set_mem(e); + ti_val_unsafe_drop(vthing); + ti_val_unsafe_drop((ti_val_t *) ano); + return (ti_val_t *) wano; + } case TI_KIND_C_MEMBER: { mp_obj_t mp_enum_id, mp_idx; @@ -440,6 +491,10 @@ static int val__push(ti_varr_t * varr, ti_val_t * val, ex_t * e) case TI_VAL_ERROR: case TI_VAL_MPDATA: case TI_VAL_CLOSURE: + case TI_VAL_ANO: + break; + case TI_VAL_WANO: + varr->flags |= TI_VARR_FLAG_MHT; break; case TI_VAL_ARR: { @@ -698,6 +753,24 @@ ti_val_t * ti_val_from_vup_e(ti_vup_t * vup, ex_t * e) } return (ti_val_t *) name; } + case MPACK_EXT_ANO: + { + ti_ano_t * ano; + if (!vup->collection) + { + ex_set(e, EX_BAD_DATA, + "cannot unpack `anonymous` type without a collection"); + return NULL; + } + ano = ti_ano_create( + vup->collection, + obj.via.ext.data, + obj.via.ext.n, + e); + if (!ano) + return NULL; + return (ti_val_t *) ano; + } } ex_set(e, EX_BAD_DATA, "msgpack extension type %d is not supported by ThingsDB", @@ -726,6 +799,8 @@ int ti_val_init_common(void) val__snil = (ti_val_t *) ti_str_from_str(TI_VAL_NIL_S); val__strue = (ti_val_t *) ti_str_from_str("true"); val__sfalse = (ti_val_t *) ti_str_from_str("false"); + val__sano = (ti_val_t *) ti_str_from_str(TI_VAL_ANO_S); + val__swano = (ti_val_t *) ti_str_from_str(TI_VAL_WANO_S); val__sbool = (ti_val_t *) ti_str_from_str(TI_VAL_BOOL_S); val__sdatetime = (ti_val_t *) ti_str_from_str(TI_VAL_DATETIME_S); val__stimeval = (ti_val_t *) ti_str_from_str(TI_VAL_TIMEVAL_S); @@ -774,6 +849,7 @@ int ti_val_init_common(void) val__parent_type_name = (ti_val_t *) ti_names_from_str_slow("parent_type"); val__key_name = (ti_val_t *) ti_names_from_str_slow("key"); val__key_type_name = (ti_val_t *) ti_names_from_str_slow("key_type"); + val__anonymous_name = (ti_val_t *) ti_names_from_str_slow(""); val__re_email = (ti_val_t *) ti_regex_from_str("/^[a-zA-Z0-9.!#$%%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/"); val__re_url = (ti_val_t *) ti_regex_from_str("/^(https?|ftp):\\/\\/[-a-zA-Z0-9@:%%._\\+~#=]{1,256}\\b([-a-zA-Z0-9@:%%_\\+.~#?&//=]*)$/"); val__re_tel = (ti_val_t *) ti_regex_from_str("/^[\\+]?(\\([0-9]{1,4}\\)[-\\s\\.]?){0,2}([0-9]{1,4}[-\\s]?){3,5}$/"); @@ -792,7 +868,8 @@ int ti_val_init_common(void) !val__beautify_name || !val__parent_name || !val__parent_type_name || !val__key_name || !val__key_type_name || !val__flags_name || !val__data_name || !val__time_name || !val__re_email || - !val__smodule || !val__re_url || !val__re_tel || !val__async_name) + !val__smodule || !val__re_url || !val__re_tel || !val__async_name || + !val__anonymous_name || !val__sano || !val__swano) { return -1; } @@ -809,6 +886,8 @@ void ti_val_drop_common(void) ti_val_drop(val__snil); ti_val_drop(val__strue); ti_val_drop(val__sfalse); + ti_val_drop(val__sano); + ti_val_drop(val__swano); ti_val_drop(val__sbool); ti_val_drop(val__sdatetime); ti_val_drop(val__stimeval); @@ -983,7 +1062,7 @@ int ti_val_convert_to_bytes(ti_val_t ** val, ex_t * e) { case TI_VAL_NAME: case TI_VAL_STR: - case TI_VAL_MPDATA: /* allow mpdata, issue #427 */ + case TI_VAL_MPDATA: /* allow mpdata, issue #427 */ { ti_raw_t * r = (ti_raw_t *) (*val); if (r->ref == 1 && r->tp != TI_VAL_NAME) @@ -1015,6 +1094,8 @@ int ti_val_convert_to_bytes(ti_val_t ** val, ex_t * e) case TI_VAL_ARR: case TI_VAL_SET: case TI_VAL_CLOSURE: + case TI_VAL_ANO: + case TI_VAL_WANO: case TI_VAL_FUTURE: case TI_VAL_MODULE: case TI_VAL_ERROR: @@ -1045,23 +1126,6 @@ int ti_val_convert_to_int(ti_val_t ** val, ex_t * e) int64_t i = 0; switch((ti_val_enum) (*val)->tp) { - case TI_VAL_NIL: - case TI_VAL_REGEX: - case TI_VAL_THING: - case TI_VAL_WRAP: - case TI_VAL_ROOM: - case TI_VAL_TASK: - case TI_VAL_ARR: - case TI_VAL_SET: - case TI_VAL_CLOSURE: - case TI_VAL_FUTURE: - case TI_VAL_MODULE: - case TI_VAL_MPDATA: - case TI_VAL_BYTES: - ex_set(e, EX_TYPE_ERROR, - "cannot convert type `%s` to `"TI_VAL_INT_S"`", - ti_val_str(*val)); - return e->nr; case TI_VAL_INT: return 0; case TI_VAL_FLOAT: @@ -1122,6 +1186,25 @@ int ti_val_convert_to_int(ti_val_t ** val, ex_t * e) *val = v; return 0; } + case TI_VAL_NIL: + case TI_VAL_REGEX: + case TI_VAL_THING: + case TI_VAL_WRAP: + case TI_VAL_ROOM: + case TI_VAL_TASK: + case TI_VAL_ARR: + case TI_VAL_SET: + case TI_VAL_CLOSURE: + case TI_VAL_ANO: + case TI_VAL_WANO: + case TI_VAL_FUTURE: + case TI_VAL_MODULE: + case TI_VAL_MPDATA: + case TI_VAL_BYTES: + ex_set(e, EX_TYPE_ERROR, + "cannot convert type `%s` to `"TI_VAL_INT_S"`", + ti_val_str(*val)); + return e->nr; case TI_VAL_TEMPLATE: assert(0); } @@ -1141,23 +1224,6 @@ int ti_val_convert_to_float(ti_val_t ** val, ex_t * e) double d = 0.0; switch((ti_val_enum) (*val)->tp) { - case TI_VAL_NIL: - case TI_VAL_REGEX: - case TI_VAL_THING: - case TI_VAL_WRAP: - case TI_VAL_ROOM: - case TI_VAL_TASK: - case TI_VAL_ARR: - case TI_VAL_SET: - case TI_VAL_CLOSURE: - case TI_VAL_FUTURE: - case TI_VAL_MODULE: - case TI_VAL_MPDATA: - case TI_VAL_BYTES: - ex_set(e, EX_TYPE_ERROR, - "cannot convert type `%s` to `"TI_VAL_FLOAT_S"`", - ti_val_str(*val)); - return e->nr; case TI_VAL_INT: d = (double) VINT(*val); break; @@ -1219,6 +1285,25 @@ int ti_val_convert_to_float(ti_val_t ** val, ex_t * e) *val = v; return 0; } + case TI_VAL_NIL: + case TI_VAL_REGEX: + case TI_VAL_THING: + case TI_VAL_WRAP: + case TI_VAL_ROOM: + case TI_VAL_TASK: + case TI_VAL_ARR: + case TI_VAL_SET: + case TI_VAL_CLOSURE: + case TI_VAL_ANO: + case TI_VAL_WANO: + case TI_VAL_FUTURE: + case TI_VAL_MODULE: + case TI_VAL_MPDATA: + case TI_VAL_BYTES: + ex_set(e, EX_TYPE_ERROR, + "cannot convert type `%s` to `"TI_VAL_FLOAT_S"`", + ti_val_str(*val)); + return e->nr; case TI_VAL_TEMPLATE: assert(0); } @@ -1252,9 +1337,11 @@ int ti_val_convert_to_array(ti_val_t ** val, ex_t * e) case TI_VAL_THING: case TI_VAL_WRAP: case TI_VAL_ROOM: + case TI_VAL_ERROR: case TI_VAL_TASK: case TI_VAL_CLOSURE: - case TI_VAL_ERROR: + case TI_VAL_ANO: + case TI_VAL_WANO: case TI_VAL_MEMBER: case TI_VAL_FUTURE: case TI_VAL_MODULE: @@ -1272,28 +1359,6 @@ int ti_val_convert_to_set(ti_val_t ** val, ex_t * e) { switch((ti_val_enum) (*val)->tp) { - case TI_VAL_NIL: - case TI_VAL_INT: - case TI_VAL_FLOAT: - case TI_VAL_BOOL: - case TI_VAL_DATETIME: - case TI_VAL_MPDATA: - case TI_VAL_NAME: - case TI_VAL_STR: - case TI_VAL_BYTES: - case TI_VAL_REGEX: - case TI_VAL_WRAP: - case TI_VAL_ROOM: - case TI_VAL_TASK: - case TI_VAL_CLOSURE: - case TI_VAL_ERROR: - case TI_VAL_MEMBER: - case TI_VAL_FUTURE: - case TI_VAL_MODULE: - ex_set(e, EX_TYPE_ERROR, - "cannot convert type `%s` to `"TI_VAL_SET_S"`", - ti_val_str(*val)); - break; case TI_VAL_THING: { ti_vset_t * vset = ti_vset_create(); @@ -1332,6 +1397,30 @@ int ti_val_convert_to_set(ti_val_t ** val, ex_t * e) } case TI_VAL_SET: break; + case TI_VAL_NIL: + case TI_VAL_INT: + case TI_VAL_FLOAT: + case TI_VAL_BOOL: + case TI_VAL_DATETIME: + case TI_VAL_MPDATA: + case TI_VAL_NAME: + case TI_VAL_STR: + case TI_VAL_BYTES: + case TI_VAL_REGEX: + case TI_VAL_WRAP: + case TI_VAL_ROOM: + case TI_VAL_TASK: + case TI_VAL_ERROR: + case TI_VAL_MEMBER: + case TI_VAL_CLOSURE: + case TI_VAL_ANO: + case TI_VAL_WANO: + case TI_VAL_FUTURE: + case TI_VAL_MODULE: + ex_set(e, EX_TYPE_ERROR, + "cannot convert type `%s` to `"TI_VAL_SET_S"`", + ti_val_str(*val)); + break; case TI_VAL_TEMPLATE: assert(0); } @@ -1372,10 +1461,18 @@ size_t ti_val_get_len(ti_val_t * val) return VSET(val)->n; case TI_VAL_CLOSURE: break; + case TI_VAL_ANO: + return ( + ((ti_ano_t *) val)->type->fields->n + + ((ti_ano_t *) val)->type->methods->n + + !!((ti_ano_t *) val)->type->idname); + case TI_VAL_WANO: + return ti_thing_n(((ti_wano_t *) val)->thing); case TI_VAL_MEMBER: return ti_val_get_len(VMEMBER(val)); case TI_VAL_FUTURE: case TI_VAL_MODULE: + break; case TI_VAL_ERROR: break; } @@ -1405,22 +1502,11 @@ int ti_val_gen_ids(ti_val_t * val) case TI_VAL_FLOAT: case TI_VAL_BOOL: case TI_VAL_DATETIME: - case TI_VAL_MPDATA: case TI_VAL_NAME: case TI_VAL_STR: case TI_VAL_BYTES: case TI_VAL_REGEX: - case TI_VAL_TASK: - case TI_VAL_MEMBER: - /* enum can be skipped; even a thing on an enum is guaranteed to - * have an ID since they are triggered when creating or changing the - * enumerator. - * - * task can be skipped as id's for task are created immediately on - * the creation of a task. - */ break; - case TI_VAL_THING: if (!((ti_thing_t *) val)->id) return ti_thing_gen_id((ti_thing_t *) val); @@ -1441,6 +1527,8 @@ int ti_val_gen_ids(ti_val_t * val) if (!((ti_room_t *) val)->id) return ti_room_gen_id((ti_room_t *) val); break; + case TI_VAL_TASK: + break; case TI_VAL_ARR: /* * Here the code really benefits from the `may-have-things` flag since @@ -1453,8 +1541,27 @@ int ti_val_gen_ids(ti_val_t * val) break; case TI_VAL_SET: return imap_walk(VSET(val), (imap_cb) val__walk_gen_id_set, NULL); - case TI_VAL_CLOSURE: + case TI_VAL_ERROR: + case TI_VAL_MEMBER: + /* enum can be skipped; even a thing on an enum is guaranteed to + * have an ID since they are triggered when creating or changing the + * enumerator. + * + * task can be skipped as id's for task are created immediately on + * the creation of a task. + */ + case TI_VAL_MPDATA: + case TI_VAL_CLOSURE: + case TI_VAL_ANO: + break; + case TI_VAL_WANO: + if (!((ti_wano_t *) val)->thing->id) + return ti_thing_gen_id(((ti_wano_t *) val)->thing); + /* + * New things 'under' an existing thing will get their own task, + * so here we do not need recursion. + */ break; case TI_VAL_FUTURE: case TI_VAL_MODULE: @@ -1497,6 +1604,10 @@ _Bool ti_val_has_ids(ti_val_t * val) case TI_VAL_SET: return imap_walk(VSET(val), (imap_cb) val__walk_has_id_set, NULL); case TI_VAL_CLOSURE: + case TI_VAL_ANO: + return false; + case TI_VAL_WANO: + return ti_thing_has_id(((ti_wano_t *) val)->thing); case TI_VAL_ERROR: case TI_VAL_FUTURE: case TI_VAL_MODULE: @@ -1534,6 +1645,10 @@ size_t ti_val_alloc_size(ti_val_t * val) return 65536; case TI_VAL_CLOSURE: return 4096; + case TI_VAL_ANO: + return ((ti_ano_t *) val)->spec_raw->n + 9; + case TI_VAL_WANO: + return 65536; case TI_VAL_ERROR: return ((ti_verror_t *) val)->msg_n + 128; case TI_VAL_MEMBER: @@ -1561,7 +1676,6 @@ ti_val_t * ti_val_strv(ti_val_t * val) return ti_datetime_is_timeval((ti_datetime_t *) val) ? ti_grab(val__stimeval) : ti_grab(val__sdatetime); - case TI_VAL_MPDATA: return ti_grab(val__smpdata); case TI_VAL_NAME: case TI_VAL_STR: return ti_grab(val__sstr); case TI_VAL_BYTES: return ti_grab(val__sbytes); @@ -1579,10 +1693,13 @@ ti_val_t * ti_val_strv(ti_val_t * val) ? ti_grab(val__slist) : ti_grab(val__stuple); case TI_VAL_SET: return ti_grab(val__sset); - case TI_VAL_CLOSURE: return ti_grab(val__sclosure); case TI_VAL_ERROR: return ti_grab(val__serror); case TI_VAL_MEMBER: return (ti_val_t *) ti_member_enum_get_rname((ti_member_t *) val); + case TI_VAL_MPDATA: return ti_grab(val__smpdata); + case TI_VAL_CLOSURE: return ti_grab(val__sclosure); + case TI_VAL_ANO: return ti_grab(val__sano); + case TI_VAL_WANO: return ti_grab(val__swano); case TI_VAL_FUTURE: return ti_grab(val__sfuture); case TI_VAL_MODULE: return ti_grab(val__smodule); case TI_VAL_TEMPLATE: @@ -1639,6 +1756,10 @@ int ti_val_copy(ti_val_t ** val, ti_thing_t * parent, void * key, uint8_t deep) ex_t e = {0}; return ti_closure_unbound((ti_closure_t * ) *val, &e); } + case TI_VAL_ANO: + return 0; + case TI_VAL_WANO: + return ti_wano_copy((ti_wano_t **) val, deep); case TI_VAL_FUTURE: case TI_VAL_MODULE: ti_val_unsafe_drop(*val); @@ -1698,6 +1819,10 @@ int ti_val_dup(ti_val_t ** val, ti_thing_t * parent, void * key, uint8_t deep) ex_t e = {0}; return ti_closure_unbound((ti_closure_t * ) *val, &e); } + case TI_VAL_ANO: + return 0; + case TI_VAL_WANO: + return ti_wano_dup((ti_wano_t **) val, deep); case TI_VAL_FUTURE: case TI_VAL_MODULE: ti_val_unsafe_drop(*val); @@ -1822,6 +1947,18 @@ int ti_val_wrap_to_str(ti_val_t ** val, ex_t * e) *val = v; return 0; } +int ti_val_wano_to_str(ti_val_t ** val, ex_t * e) +{ + ti_val_t * v = (ti_val_t *) ti_wano_str((ti_wano_t *) *val); + if (!v) + { + ex_set_mem(e); + return e->nr; + } + ti_val_unsafe_drop(*val); + *val = v; + return 0; +} int ti_val_room_to_str(ti_val_t ** val, ex_t * e) { ti_val_t * v = (ti_val_t *) ti_room_str((ti_room_t *) *val); @@ -1885,3 +2022,9 @@ int ti_val_closure_to_str(ti_val_t ** val, ex_t * e) *val = v; return 0; } +int ti_val_ano_to_str(ti_val_t ** val, ex_t * UNUSED(e)) +{ + ti_val_unsafe_drop(*val); + *val = ti_val_anonymous_name(); + return 0; +} diff --git a/src/ti/varr.c b/src/ti/varr.c index f41671b5..ada9f8e0 100644 --- a/src/ti/varr.c +++ b/src/ti/varr.c @@ -340,6 +340,7 @@ static int varr__copy(ti_val_t ** val, uint8_t deep) case TI_VAL_ERROR: case TI_VAL_MEMBER: case TI_VAL_CLOSURE: + case TI_VAL_ANO: return 0; case TI_VAL_THING: return ti_thing_copy((ti_thing_t **) val, deep); @@ -354,6 +355,8 @@ static int varr__copy(ti_val_t ** val, uint8_t deep) if (varr__copy(v, deep)) return -1; return 0; + case TI_VAL_WANO: + return ti_wano_copy((ti_wano_t **) val, deep); case TI_VAL_FUTURE: case TI_VAL_MODULE: case TI_VAL_SET: @@ -383,6 +386,7 @@ static int varr__dup(ti_val_t ** val, uint8_t deep) case TI_VAL_ERROR: case TI_VAL_MEMBER: case TI_VAL_CLOSURE: + case TI_VAL_ANO: return 0; case TI_VAL_THING: return ti_thing_dup((ti_thing_t **) val, deep); @@ -397,6 +401,8 @@ static int varr__dup(ti_val_t ** val, uint8_t deep) if (varr__dup(v, deep)) return -1; return 0; + case TI_VAL_WANO: + return ti_wano_dup((ti_wano_t **) val, deep); case TI_VAL_FUTURE: case TI_VAL_MODULE: case TI_VAL_SET: diff --git a/src/ti/wano.c b/src/ti/wano.c new file mode 100644 index 00000000..722b843f --- /dev/null +++ b/src/ti/wano.c @@ -0,0 +1,62 @@ +/* + * ti/wano.c + */ +#include +#include + + +ti_wano_t * ti_wano_create(ti_thing_t * thing, ti_ano_t * ano) +{ + ti_wano_t * wano = malloc(sizeof(ti_wano_t)); + if (!wano) + return NULL; + wano->ref = 1; + wano->tp = TI_VAL_WANO; + wano->ano = ano; + wano->thing = thing; + ti_incref(ano); + ti_incref(thing); + return wano; +} + +void ti_wano_destroy(ti_wano_t * wano) +{ + ti_val_unsafe_drop((ti_val_t *) wano->ano); + ti_val_unsafe_gc_drop((ti_val_t *) wano->thing); + free(wano); +} + +int ti_wano_copy(ti_wano_t ** wano, uint8_t deep) +{ + ti_thing_t * thing = (*wano)->thing; + ti_wano_t * _wano = ti_wano_create(thing, (*wano)->ano); + if (!_wano) + return -1; + + if (ti_thing_copy(&_wano->thing, deep)) + { + ti_val_unsafe_drop((ti_val_t *) _wano); + return -1; + } + ti_val_unsafe_drop((ti_val_t *) *wano); + *wano = _wano; + return 0; +} + +int ti_wano_dup(ti_wano_t ** wano, uint8_t deep) +{ + assert(deep); + ti_wano_t * _wano = ti_wano_create((*wano)->thing, (*wano)->ano); + if (!_wano) + return -1; + + if (ti_thing_dup(&_wano->thing, deep)) + { + ti_wano_destroy(_wano); + return -1; + } + + ti_val_unsafe_drop((ti_val_t *) *wano); + *wano = _wano; + return 0; +} \ No newline at end of file diff --git a/src/ti/wrap.c b/src/ti/wrap.c index 29f82f42..315633fa 100644 --- a/src/ti/wrap.c +++ b/src/ti/wrap.c @@ -3,6 +3,7 @@ */ #include #include +#include #include #include #include @@ -26,13 +27,6 @@ #include #include -static int wrap__field_thing_type( - ti_thing_t * thing, - ti_vp_t * vp, - ti_type_t * t_type, - int deep, - int flags); - ti_wrap_t * ti_wrap_create(ti_thing_t * thing, uint16_t type_id) { @@ -81,7 +75,7 @@ typedef struct static int wrap__walk_with_type(ti_thing_t * thing, wrap__walk_with_type_t * w) { - return wrap__field_thing_type(thing, w->vp, w->t_type, w->deep, w->flags); + return ti_wrap_field_thing_type(thing, w->vp, w->t_type, w->deep, w->flags); } static int wrap__set( @@ -174,7 +168,7 @@ static int wrap__field_val( return ti_regex_to_client_pk((ti_regex_t *) val, &vp->pk); case TI_VAL_THING: return ((*spec & TI_SPEC_MASK_NILLABLE) == TI_SPEC_TYPE) - ? wrap__field_thing_type( + ? ti_wrap_field_thing_type( (ti_thing_t *) val, vp, t_field->condition.type, @@ -188,7 +182,7 @@ static int wrap__field_val( flags); case TI_VAL_WRAP: return ((*spec & TI_SPEC_MASK_NILLABLE) == TI_SPEC_TYPE) - ? wrap__field_thing_type( + ? ti_wrap_field_thing_type( ((ti_wrap_t *) val)->thing, vp, t_field->condition.type, @@ -246,6 +240,15 @@ static int wrap__field_val( return ti_raw_mpdata_to_client_pk((ti_raw_t *) val, &vp->pk); case TI_VAL_CLOSURE: return ti_closure_to_client_pk((ti_closure_t *) val, &vp->pk); + case TI_VAL_ANO: + return ti_ano_to_client_pk((ti_ano_t * ) val, &vp->pk); + case TI_VAL_WANO: + return ti_wrap_field_thing_type( + ((ti_wano_t *) val)->thing, + vp, + ((ti_wano_t *) val)->ano->type, + deep, + flags); case TI_VAL_FUTURE: return VFUT(val) ? wrap__field_val( @@ -595,7 +598,7 @@ static int wrap__field_thing( return -1; } -static int wrap__field_thing_type( +int ti_wrap_field_thing_type( ti_thing_t * thing, ti_vp_t * vp, ti_type_t * t_type, @@ -685,6 +688,7 @@ int ti__wrap_field_thing( return wrap__field_thing(thing, vp, t_type, deep, flags); } +/* works with both ti_wrap_t and ti_wano_t */ int ti_wrap_cp(ti_query_t * query, uint8_t deep, ex_t * e) { ti_val_t * val; @@ -700,7 +704,7 @@ int ti_wrap_cp(ti_query_t * query, uint8_t deep, ex_t * e) .up = &up, }; - assert(ti_val_is_wrap(query->rval)); + assert(ti_val_is_wrap_wano(query->rval)); if (mp_sbuffer_alloc_init(&buffer, ti_val_alloc_size(query->rval), 0)) {