mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	Add errorhandling helper functions and macros for writing c functions.
This commit is contained in:
		| @@ -106,19 +106,22 @@ Dst dst_array_peek(DstArray *array) { | ||||
| /* C Functions */ | ||||
|  | ||||
| static int cfun_pop(DstArgs args) { | ||||
|     if (args.n != 1 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array"); | ||||
|     dst_fixarity(args, 1); | ||||
|     dst_check(args, 0, DST_ARRAY); | ||||
|     return dst_return(args, dst_array_pop(dst_unwrap_array(args.v[0]))); | ||||
| } | ||||
|  | ||||
| static int cfun_peek(DstArgs args) { | ||||
|     if (args.n != 1 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array"); | ||||
|     dst_fixarity(args, 1); | ||||
|     dst_check(args, 0, DST_ARRAY); | ||||
|     return dst_return(args, dst_array_peek(dst_unwrap_array(args.v[0]))); | ||||
| } | ||||
|  | ||||
| static int cfun_push(DstArgs args) { | ||||
|     DstArray *array; | ||||
|     int32_t newcount; | ||||
|     if (args.n < 1 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array"); | ||||
|     dst_minarity(args, 1); | ||||
|     dst_check(args, 0, DST_ARRAY); | ||||
|     array = dst_unwrap_array(args.v[0]); | ||||
|     newcount = array->count - 1 + args.n; | ||||
|     dst_array_ensure(array, newcount); | ||||
| @@ -129,8 +132,9 @@ static int cfun_push(DstArgs args) { | ||||
|  | ||||
| static int cfun_setcount(DstArgs args) { | ||||
|     int32_t newcount; | ||||
|     if (args.n != 2 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array"); | ||||
|     if (!dst_checktype(args.v[1], DST_INTEGER)) return dst_throw(args, "expected positive integer"); | ||||
|     dst_fixarity(args, 2); | ||||
|     dst_check(args, 0, DST_ARRAY); | ||||
|     dst_check(args, 1, DST_INTEGER); | ||||
|     newcount = dst_unwrap_integer(args.v[1]); | ||||
|     if (newcount < 0) return dst_throw(args, "expected positive integer"); | ||||
|     dst_array_setcount(dst_unwrap_array(args.v[0]), newcount); | ||||
| @@ -139,8 +143,9 @@ static int cfun_setcount(DstArgs args) { | ||||
|  | ||||
| static int cfun_ensure(DstArgs args) { | ||||
|     int32_t newcount; | ||||
|     if (args.n != 2 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array"); | ||||
|     if (!dst_checktype(args.v[1], DST_INTEGER)) return dst_throw(args, "expected positive integer"); | ||||
|     dst_fixarity(args, 2); | ||||
|     dst_check(args, 0, DST_ARRAY); | ||||
|     dst_check(args, 1, DST_INTEGER); | ||||
|     newcount = dst_unwrap_integer(args.v[1]); | ||||
|     if (newcount < 0) return dst_throw(args, "expected positive integer"); | ||||
|     dst_array_ensure(dst_unwrap_array(args.v[0]), newcount); | ||||
| @@ -152,7 +157,10 @@ static int cfun_slice(DstArgs args) { | ||||
|     int32_t len; | ||||
|     DstArray *ret; | ||||
|     int32_t start, end; | ||||
|     if (args.n < 1 || !dst_seq_view(args.v[0], &vals, &len)) return dst_throw(args, "expected array/tuple"); | ||||
|     dst_minarity(args, 1); | ||||
|     dst_maxarity(args, 3); | ||||
|     if (!dst_seq_view(args.v[0], &vals, &len)) | ||||
|         return dst_throw(args, "expected array|tuple"); | ||||
|     /* Get start */ | ||||
|     if (args.n < 2) { | ||||
|         start = 0; | ||||
| @@ -187,7 +195,8 @@ static int cfun_slice(DstArgs args) { | ||||
| static int cfun_concat(DstArgs args) { | ||||
|     int32_t i; | ||||
|     DstArray *array; | ||||
|     if (args.n < 1 || !dst_checktype(args.v[0], DST_ARRAY)) return dst_throw(args, "expected array"); | ||||
|     dst_minarity(args, 1); | ||||
|     dst_check(args, 0, DST_ARRAY); | ||||
|     array = dst_unwrap_array(args.v[0]); | ||||
|     for (i = 1; i < args.n; i++) { | ||||
|         switch (dst_type(args.v[i])) { | ||||
|   | ||||
| @@ -97,6 +97,10 @@ int dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, int32_t leng | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int dst_buffer_push_string(DstBuffer *buffer, const uint8_t *string) { | ||||
|     return dst_buffer_push_bytes(buffer, string, dst_string_length(string)); | ||||
| } | ||||
|  | ||||
| /* Push a single byte to the buffer */ | ||||
| int dst_buffer_push_u8(DstBuffer *buffer, uint8_t byte) { | ||||
|     if (dst_buffer_extra(buffer, 1)) return -1; | ||||
|   | ||||
| @@ -127,7 +127,7 @@ int dst_core_struct(DstArgs args) { | ||||
| } | ||||
|  | ||||
| int dst_core_gensym(DstArgs args) { | ||||
|     if (args.n > 1) return dst_throw(args, "expected one argument"); | ||||
|     dst_maxarity(args, 1); | ||||
|     if (args.n == 0) { | ||||
|         return dst_return(args, dst_wrap_symbol(dst_symbol_gen(NULL, 0))); | ||||
|     } else { | ||||
| @@ -137,14 +137,14 @@ int dst_core_gensym(DstArgs args) { | ||||
| } | ||||
|  | ||||
| int dst_core_length(DstArgs args) { | ||||
|     if (args.n != 1) return dst_throw(args, "expected at least 1 argument"); | ||||
|     dst_checkmany(args, 0, DST_TFLAG_LENGTHABLE); | ||||
|     return dst_return(args, dst_wrap_integer(dst_length(args.v[0]))); | ||||
| } | ||||
|  | ||||
| int dst_core_get(DstArgs args) { | ||||
|     int32_t i; | ||||
|     Dst ds; | ||||
|     if (args.n < 1) return dst_throw(args, "expected at least 1 argument"); | ||||
|     dst_minarity(args, 1); | ||||
|     ds = args.v[0]; | ||||
|     for (i = 1; i < args.n; i++) { | ||||
|         ds = dst_get(ds, args.v[i]); | ||||
| @@ -155,15 +155,15 @@ int dst_core_get(DstArgs args) { | ||||
| } | ||||
|  | ||||
| int dst_core_rawget(DstArgs args) { | ||||
|     if (args.n != 2) return dst_throw(args, "expected 2 arguments"); | ||||
|     if (!dst_checktype(args.v[0], DST_TABLE)) return dst_throw(args, "expected table"); | ||||
|     dst_fixarity(args, 2); | ||||
|     dst_check(args, 0, DST_TABLE); | ||||
|     return dst_return(args, dst_table_rawget(dst_unwrap_table(args.v[0]), args.v[1])); | ||||
| } | ||||
|  | ||||
| int dst_core_getproto(DstArgs args) { | ||||
|     DstTable *t; | ||||
|     if (args.n != 1) return dst_throw(args, "expected 1 argument"); | ||||
|     if (!dst_checktype(args.v[0], DST_TABLE)) return dst_throw(args, "expected table"); | ||||
|     dst_fixarity(args, 1); | ||||
|     dst_check(args, 0, DST_TABLE); | ||||
|     t = dst_unwrap_table(args.v[0]); | ||||
|     return dst_return(args, t->proto | ||||
|             ? dst_wrap_table(t->proto) | ||||
| @@ -171,10 +171,9 @@ int dst_core_getproto(DstArgs args) { | ||||
| } | ||||
|  | ||||
| int dst_core_setproto(DstArgs args) { | ||||
|     if (args.n != 2) return dst_throw(args, "expected 2 arguments"); | ||||
|     if (!dst_checktype(args.v[0], DST_TABLE)) return dst_throw(args, "expected table"); | ||||
|     if (!dst_checktype(args.v[1], DST_TABLE) && !dst_checktype(args.v[1], DST_NIL)) | ||||
|         return dst_throw(args, "expected table"); | ||||
|     dst_fixarity(args, 2); | ||||
|     dst_check(args, 0, DST_TABLE); | ||||
|     dst_checkmany(args, 1, DST_TFLAG_TABLE | DST_TFLAG_NIL); | ||||
|     dst_unwrap_table(args.v[0])->proto = dst_checktype(args.v[1], DST_TABLE) | ||||
|         ? dst_unwrap_table(args.v[1]) | ||||
|         : NULL; | ||||
| @@ -184,7 +183,7 @@ int dst_core_setproto(DstArgs args) { | ||||
| int dst_core_put(DstArgs args) { | ||||
|     Dst ds, key, value; | ||||
|     DstArgs subargs = args; | ||||
|     if (args.n < 3) return dst_throw(args, "expected at least 3 arguments"); | ||||
|     dst_minarity(args, 3); | ||||
|     subargs.n -= 2; | ||||
|     if (dst_core_get(subargs)) return 1; | ||||
|     ds = *args.ret; | ||||
| @@ -201,20 +200,23 @@ int dst_core_gccollect(DstArgs args) { | ||||
| } | ||||
|  | ||||
| int dst_core_gcsetinterval(DstArgs args) { | ||||
|     if (args.n < 1 || | ||||
|             !dst_checktype(args.v[0], DST_INTEGER) || | ||||
|             dst_unwrap_integer(args.v[0]) < 0) | ||||
|     int32_t val; | ||||
|     dst_fixarity(args, 1); | ||||
|     dst_check(args, 0, DST_INTEGER); | ||||
|     val = dst_unwrap_integer(args.v[0]); | ||||
|     if (val < 0) | ||||
|         return dst_throw(args, "expected non-negative integer"); | ||||
|     dst_vm_gc_interval = dst_unwrap_integer(args.v[0]); | ||||
|     return dst_return(args, dst_wrap_integer(dst_vm_gc_interval)); | ||||
|     dst_vm_gc_interval = val; | ||||
|     return dst_return(args, args.v[0]);  | ||||
| } | ||||
|  | ||||
| int dst_core_gcinterval(DstArgs args) { | ||||
|     dst_fixarity(args, 0); | ||||
|     return dst_return(args, dst_wrap_integer(dst_vm_gc_interval)); | ||||
| } | ||||
|  | ||||
| int dst_core_type(DstArgs args) { | ||||
|     if (args.n != 1) return dst_throw(args, "expected 1 argument"); | ||||
|     dst_fixarity(args, 1); | ||||
|     if (dst_checktype(args.v[0], DST_ABSTRACT)) { | ||||
|         return dst_return(args, dst_csymbolv(dst_abstract_type(dst_unwrap_abstract(args.v[0]))->name)); | ||||
|     } else { | ||||
| @@ -225,20 +227,19 @@ int dst_core_type(DstArgs args) { | ||||
| int dst_core_next(DstArgs args) { | ||||
|     Dst ds; | ||||
|     const DstKV *kv; | ||||
|     if (args.n != 2) return dst_throw(args, "expected 2 arguments"); | ||||
|     dst_fixarity(args, 2); | ||||
|     dst_checkmany(args, 0, DST_TFLAG_DICTIONARY); | ||||
|     ds = args.v[0]; | ||||
|     if (dst_checktype(ds, DST_TABLE)) { | ||||
|         DstTable *t = dst_unwrap_table(ds); | ||||
|         kv = dst_checktype(args.v[1], DST_NIL) | ||||
|             ? NULL | ||||
|             : dst_table_find(t, args.v[1]); | ||||
|     } else if (dst_checktype(ds, DST_STRUCT)) { | ||||
|     } else { | ||||
|         const DstKV *st = dst_unwrap_struct(ds); | ||||
|         kv = dst_checktype(args.v[1], DST_NIL) | ||||
|             ? NULL | ||||
|             : dst_struct_find(st, args.v[1]); | ||||
|     } else { | ||||
|         return dst_throw(args, "expected table/struct"); | ||||
|     } | ||||
|     kv = dst_next(ds, kv); | ||||
|     if (kv) { | ||||
| @@ -248,7 +249,7 @@ int dst_core_next(DstArgs args) { | ||||
| } | ||||
|  | ||||
| int dst_core_hash(DstArgs args) { | ||||
|     if (args.n != 1) return dst_throw(args, "expected 1 argument"); | ||||
|     dst_fixarity(args, 1); | ||||
|     return dst_return(args, dst_wrap_integer(dst_hash(args.v[0]))); | ||||
| } | ||||
|  | ||||
| @@ -256,8 +257,10 @@ int dst_core_string_slice(DstArgs args) { | ||||
|     const uint8_t *data; | ||||
|     int32_t len, start, end; | ||||
|     const uint8_t *ret; | ||||
|     if (args.n < 1 || !dst_chararray_view(args.v[0], &data, &len)) | ||||
|         return dst_throw(args, "expected buffer/string"); | ||||
|     dst_minarity(args, 1); | ||||
|     dst_maxarity(args, 3); | ||||
|     if (!dst_chararray_view(args.v[0], &data, &len)) | ||||
|         return dst_throw(args, "expected buffer|string|symbol"); | ||||
|     /* Get start */ | ||||
|     if (args.n < 2) { | ||||
|         start = 0; | ||||
|   | ||||
| @@ -205,3 +205,48 @@ int dst_hashtable_view(Dst tab, const DstKV **data, int32_t *len, int32_t *cap) | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int dst_type_err(DstArgs args, int32_t n, DstType expected) { | ||||
|     DstType actual = n < args.n ? dst_type(args.v[n]) : DST_NIL; | ||||
|     const uint8_t *message = dst_formatc( | ||||
|             "bad argument #%d, expected %t, got %t", | ||||
|             n, | ||||
|             expected, | ||||
|             actual); | ||||
|     return dst_throwv(args, dst_wrap_string(message)); | ||||
| } | ||||
|  | ||||
| int dst_typemany_err(DstArgs args, int32_t n, int expected) { | ||||
|     int i; | ||||
|     int first = 1; | ||||
|     const uint8_t *message; | ||||
|     DstType actual = n < args.n ? dst_type(args.v[n]) : DST_NIL; | ||||
|     DstBuffer buf; | ||||
|     dst_buffer_init(&buf, 20); | ||||
|     dst_buffer_push_string(&buf, dst_formatc("bad argument #%d, expected ", n)); | ||||
|     i = 0; | ||||
|     while (expected) { | ||||
|         if (1 & expected) { | ||||
|             if (first) { | ||||
|                 first = 0; | ||||
|             } else { | ||||
|                 dst_buffer_push_u8(&buf, '|'); | ||||
|             } | ||||
|             dst_buffer_push_cstring(&buf, dst_type_names[i] + 1); | ||||
|         } | ||||
|         i++; | ||||
|         expected >>= 1; | ||||
|     } | ||||
|     dst_buffer_push_cstring(&buf, ", got "); | ||||
|     dst_buffer_push_cstring(&buf, dst_type_names[actual] + 1); | ||||
|     message = dst_string(buf.data, buf.count); | ||||
|     dst_buffer_deinit(&buf); | ||||
|     return dst_throwv(args, dst_wrap_string(message)); | ||||
| } | ||||
|  | ||||
| int dst_arity_err(DstArgs args, int32_t n, const char *prefix) { | ||||
|     return dst_throwv(args, | ||||
|             dst_wrap_string(dst_formatc( | ||||
|                     "expected %s%d argument%s, got %d",  | ||||
|                     prefix, n, n == 1 ? "" : "s", args.n))); | ||||
| } | ||||
|   | ||||
| @@ -53,6 +53,7 @@ void dst_buffer_deinit(DstBuffer *buffer); | ||||
| void dst_buffer_ensure(DstBuffer *buffer, int32_t capacity); | ||||
| int dst_buffer_extra(DstBuffer *buffer, int32_t n); | ||||
| int dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, int32_t len); | ||||
| int dst_buffer_push_string(DstBuffer *buffer, const uint8_t *string); | ||||
| int dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring); | ||||
| int dst_buffer_push_u8(DstBuffer *buffer, uint8_t x); | ||||
| int dst_buffer_push_u16(DstBuffer *buffer, uint16_t x); | ||||
| @@ -180,11 +181,6 @@ void dst_deinit(void); | ||||
| Dst dst_run(DstFiber *fiber); | ||||
| Dst dst_resume(DstFiber *fiber, int32_t argn, const Dst *argv); | ||||
|  | ||||
| /* C Function helpers */ | ||||
| #define dst_throw(a, e) (*((a).ret) = dst_cstringv(e), 1) | ||||
| #define dst_throwv(a, v) (*((a).ret) = (v), 1) | ||||
| #define dst_return(a, v) (*((a).ret) = (v), 0) | ||||
|  | ||||
| /* Env helpers */ | ||||
| void dst_env_def(DstTable *env, const char *name, Dst val); | ||||
| void dst_env_var(DstTable *env, const char *name, Dst val); | ||||
| @@ -195,6 +191,35 @@ DstTable *dst_env_arg(DstArgs args); | ||||
| /* STL */ | ||||
| DstTable *dst_stl_env(void); | ||||
|  | ||||
| /* C Function helpers */ | ||||
| int dst_arity_err(DstArgs args, int32_t n, const char *prefix); | ||||
| int dst_type_err(DstArgs args, int32_t n, DstType expected); | ||||
| int dst_typemany_err(DstArgs args, int32_t n, int expected); | ||||
| #define dst_throw(a, e) (*((a).ret) = dst_cstringv(e), 1) | ||||
| #define dst_throwv(a, v) (*((a).ret) = (v), 1) | ||||
| #define dst_return(a, v) (*((a).ret) = (v), 0) | ||||
| #define dst_maxarity(A, N) do { if ((A).n > (N))\ | ||||
|     return dst_arity_err(A, N, "at most "); } while (0) | ||||
| #define dst_minarity(A, N) do { if ((A).n < (N))\ | ||||
|     return dst_arity_err(A, N, "at least "); } while (0) | ||||
| #define dst_fixarity(A, N) do { if ((A).n != (N))\ | ||||
|     return dst_arity_err(A, N, ""); } while (0) | ||||
| #define dst_check(A, N, T) do {\ | ||||
|     if ((A).n > (N)) {\ | ||||
|        if (!dst_checktype((A).v[(N)], (T))) return dst_type_err(A, N, T);\ | ||||
|     } else {\ | ||||
|        if ((T) != DST_NIL) return dst_type_err(A, N, T);\ | ||||
|     }\ | ||||
| } while (0) | ||||
| #define dst_checkmany(A, N, TS) do {\ | ||||
|     if ((A).n > (N)) {\ | ||||
|         DstType t = dst_type((A).v[(N)]);\ | ||||
|         if (!((1 << t) & (TS))) return dst_typemany_err(A, N, TS);\ | ||||
|     } else {\ | ||||
|        if (!((TS) & DST_NIL)) return dst_type_err(A, N, TS);\ | ||||
|     }\ | ||||
| } while (0) | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -78,6 +78,35 @@ typedef enum DstType { | ||||
|     DST_ABSTRACT | ||||
| } DstType; | ||||
|  | ||||
| #define DST_COUNT_TYPES (DST_ABSTRACT + 1) | ||||
|  | ||||
| /* Type flags */ | ||||
| #define DST_TFLAG_NIL (1 << DST_NIL) | ||||
| #define DST_TFLAG_FALSE (1 << DST_FALSE) | ||||
| #define DST_TFLAG_TRUE (1 << DST_TRUE) | ||||
| #define DST_TFLAG_FIBER (1 << DST_FIBER) | ||||
| #define DST_TFLAG_INTEGER (1 << DST_INTEGER) | ||||
| #define DST_TFLAG_REAL (1 << DST_REAL) | ||||
| #define DST_TFLAG_STRING (1 << DST_STRING) | ||||
| #define DST_TFLAG_SYMBOL (1 << DST_SYMBOL) | ||||
| #define DST_TFLAG_ARRAY (1 << DST_ARRAY) | ||||
| #define DST_TFLAG_TUPLE (1 << DST_TUPLE) | ||||
| #define DST_TFLAG_TABLE (1 << DST_TABLE) | ||||
| #define DST_TFLAG_STRUCT (1 << DST_STRUCT) | ||||
| #define DST_TFLAG_BUFFER (1 << DST_BUFFER) | ||||
| #define DST_TFLAG_FUNCTION (1 << DST_FUNCTION) | ||||
| #define DST_TFLAG_CFUNCTION (1 << DST_CFUNCTION) | ||||
| #define DST_TFLAG_ABSTRACT (1 << DST_ABSTRACT) | ||||
|  | ||||
| /* Some abstractions */ | ||||
| #define DST_TFLAG_BOOLEAN (DST_TFLAG_TRUE | DST_TFLAG_FALSE) | ||||
| #define DST_TFLAG_NUMBER (DST_TFLAG_REAL | DST_TFLAG_INTEGER) | ||||
| #define DST_TFLAG_CALLABLE (DST_TFLAG_FUNCTION | DST_TFLAG_CFUNCTION) | ||||
| #define DST_TFLAG_CHARS (DST_TFLAG_STRING | DST_TFLAG_SYMBOL | DST_TFLAG_BUFFER) | ||||
| #define DST_TFLAG_INDEXED (DST_TFLAG_ARRAY | DST_TFLAG_TUPLE) | ||||
| #define DST_TFLAG_DICTIONARY (DST_TFLAG_TABLE | DST_TFLAG_STRUCT) | ||||
| #define DST_TFLAG_LENGTHABLE (DST_TFLAG_CHARS | DST_TFLAG_INDEXED | DST_TFLAG_DICTIONARY) | ||||
|  | ||||
| /* We provide two possible implemenations of Dsts. The preferred | ||||
|  * nanboxing approach, and the standard C version. Code in the rest of the | ||||
|  * application must interact through exposed interface. */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose