diff --git a/.gitignore b/.gitignore index 208f797..a5d7544 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ perf.data.old .cproject .project .settings +.gse tests/JSONTestSuite/test_parsing tests/JSONTestSuite/test_transform tclobjs_remaining diff --git a/doc/json.n b/doc/json.n index f656409..27548da 100644 --- a/doc/json.n +++ b/doc/json.n @@ -4,15 +4,15 @@ '\" See the file "LICENSE" for information on usage and redistribution '\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. '\" -.TH json n 0.14.0 rl_json "RubyLane/JSON Package Commands" +.TH json n 0.15.0 rl_json "RubyLane/JSON Package Commands" .so man.macros .BS '\" Note: do not modify the .SH NAME line immediately below! .SH NAME -json \- Parse, manipulate and produce JSON documents +json \- Parse, manipulate and produce JSON documents .SH SYNOPSIS .nf -\fBpackage require rl_json\fR ?\fB0.14.0\fR? +\fBpackage require rl_json\fR ?\fB0.15.0\fR? \fBjson get\fR ?\fB-default\fR \fIdefaultValue\fR? \fIjsonValue\fR ?\fIkey ...\fR? \fBjson extract\fR ?\fB-default\fR \fIdefaultValue\fR? \fIjsonValue\fR ?\fIkey ...\fR? @@ -28,6 +28,7 @@ json \- Parse, manipulate and produce JSON documents \fBjson boolean\fR \fIvalue\fR \fBjson object\fR \fI?key value ?key value ...??\fR \fBjson array\fR \fIelem ...\fR +\fBjson autoarray\fR \fIvalue ...\fR \fBjson bool\fR \fIvalue\fR \fBjson normalize\fR \fIjsonValue\fR \fBjson pretty\fR ?\fB-indent\fR \fIindent\fR? \fIjsonValue\fR ?\fIkey ...\fR? @@ -170,6 +171,26 @@ Return a JSON array containing each of the elements given. \fIelem\fR is a list of two elements, the first being the type {string, number, boolean, null, object, array, json}, and the second being the value. .TP +\fBjson autoarray \fI?value ...?\fR +. +Return a JSON array containing each of the values given, with automatic type detection. +Unlike \fBjson array\fR which requires explicit type specification, \fBjson autoarray\fR +automatically determines the appropriate JSON type for each value: +.RS +.IP \(bu 3 +Values exactly matching "true" or "false" (case-sensitive) are converted to JSON booleans. +.IP \(bu 3 +Values that can be parsed as valid JSON numbers are converted to JSON numbers. +.IP \(bu 3 +All other values are converted to JSON strings. +.RE +.PP +For example: +.CS + json autoarray 1 2.5 true false "hello world" 42 + # Returns: [1,2.5,true,false,"hello world",42] +.CE +.TP \fBjson foreach \fIvarList1 jsonValue1\fR ?\fIvarList2 jsonValue2 ...\fR? \fIscript\fR . Evaluate \fIscript\fR in a loop in a similar way to the \fBforeach\fR command. @@ -232,12 +253,38 @@ Return a version of the input \fIjsonValue\fR, i.e., with all optional whitespace trimmed. .TP -\fBjson pretty\fR ?\fB-indent\fR \fIindent\fR? \fIjsonValue\fR ?\fIkey ...\fR? +\fBjson pretty\fR ?\fB-indent\fR \fIindent\fR? ?\fB-compact\fR? ?\fB-arrays\fR \fImode\fR? \fIjsonValue\fR ?\fIkey ...\fR? . Returns a pretty-printed string representation of \fIjsonValue\fR, found by following the path of \fIkey\fRs. Useful for debugging or inspecting the -structure of JSON data. If \fB-indent\fR is supplied, use \fIindent\fR for -each level of indent, otherwise default to four spaces. +structure of JSON data. +.RS +.PP +The following options control the formatting: +.TP +\fB-indent\fR \fIindent\fR +. +Use \fIindent\fR for each level of indent. Defaults to four spaces if not specified. +.TP +\fB-compact\fR +. +Return a compact, single-line representation with no extra whitespace. This is equivalent +to \fBjson normalize\fR but provided for convenience when using other pretty options. +When this option is used, \fB-indent\fR and \fB-arrays\fR are ignored. +.TP +\fB-arrays\fR \fImode\fR +. +Control how arrays are formatted. \fImode\fR must be one of: +.RS +.IP \fBinline\fR 10 +All arrays are formatted on a single line: [1,2,3] +.IP \fBmultiline\fR 10 +All arrays are formatted with one element per line. +.RE +.PP +If not specified, arrays with 3 or fewer elements are formatted inline, while larger +arrays are formatted with one element per line. +.RE .TP \fBjson decode \fIbytes\fR ?\fIencoding\fR? . diff --git a/generic/api.c b/generic/api.c index 8d31dfd..86457c6 100644 --- a/generic/api.c +++ b/generic/api.c @@ -937,7 +937,7 @@ int JSON_Normalize(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** normalized) //{{{ } //}}} -int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** prettyString) //{{{ +int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int nopadding, int compact, int arrays_inline, Tcl_Obj** prettyString) //{{{ { int retval = TCL_OK; Tcl_DString ds; @@ -945,6 +945,13 @@ int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** pre Tcl_Obj* pad = NULL; struct interp_cx* l = Tcl_GetAssocData(interp, "rl_json", NULL); + // Handle compact mode - just normalize (remove all whitespace) + if (compact) { + retval = JSON_Normalize(interp, obj, prettyString); + return retval; + } + + // Normal pretty printing with formatting options if (indent == NULL) { replace_tclobj(&lindent, get_string(l, " ", 4)); indent = lindent; @@ -952,7 +959,7 @@ int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** pre replace_tclobj(&pad, l->tcl_empty); Tcl_DStringInit(&ds); - retval = json_pretty(interp, obj, indent, pad, &ds); + retval = json_pretty(interp, obj, indent, nopadding, pad, arrays_inline, &ds); if (retval == TCL_OK) replace_tclobj(prettyString, Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds))); diff --git a/generic/parser.c b/generic/parser.c index 4e49d8d..0dc6b32 100644 --- a/generic/parser.c +++ b/generic/parser.c @@ -20,9 +20,12 @@ void throw_parse_error(Tcl_Interp* interp, struct parse_error* details) //{{{ { char char_ofs_buf[20]; // 20 bytes allows for 19 bytes of decimal max 64 bit size_t, plus null terminator - snprintf(char_ofs_buf, 20, "%ld", details->char_ofs); - - Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error parsing JSON value: %s at offset %ld", details->errmsg, details->char_ofs)); + snprintf(char_ofs_buf, 20, "%llu", (unsigned long long)details->char_ofs); +#if TCL_MAJOR_VERSION == 8 + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error parsing JSON value: %s at offset %ld", details->errmsg, (unsigned long)details->char_ofs)); +#else + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Error parsing JSON value: %s at offset %llu", details->errmsg, (unsigned long long)details->char_ofs)); +#endif Tcl_SetErrorCode(interp, "RL", "JSON", "PARSE", details->errmsg, details->doc, char_ofs_buf, NULL); } diff --git a/generic/rl_json.c b/generic/rl_json.c index eb04c37..e3b4077 100644 --- a/generic/rl_json.c +++ b/generic/rl_json.c @@ -1341,7 +1341,7 @@ static int foreach(Tcl_Interp* interp, int objc, Tcl_Obj *const objv[], enum col } //}}} -int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds) //{{{ +int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, int nopadding, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds) //{{{ { int pad_len, next_pad_len, count; enum json_types type; @@ -1379,15 +1379,19 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad TEST_OK_LABEL(finally, retval, Tcl_DictObjFirst(interp, val, &search, &k, &v, &done)); - for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { - Tcl_GetStringFromObj(k, &k_len); - if (k_len <= 20 && k_len > max) - max = k_len; - } - Tcl_DictObjDone(&search); + // keep the default behaviour, if wanted add the -nopadding option + // and the output will be condensed + if (!nopadding) { + for (; !done; Tcl_DictObjNext(&search, &k, &v, &done)) { + Tcl_GetStringFromObj(k, &k_len); + if (k_len <= 20 && k_len > max) + max = k_len; + } + Tcl_DictObjDone(&search); - if (max > 20) - max = 20; // If this cap is changed be sure to adjust the key_pad_buf length above + if (max > 20) + max = 20; // If this cap is changed be sure to adjust the key_pad_buf length above + } replace_tclobj(&next_pad, Tcl_DuplicateObj(pad)); Tcl_AppendObjToObj(next_pad, indent); @@ -1403,11 +1407,14 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad append_json_string(&scx, k); Tcl_DStringAppend(ds, ": ", 2); - Tcl_GetStringFromObj(k, &k_len); - if (k_len < max) - Tcl_DStringAppend(ds, key_pad_buf, max-k_len); - - if (json_pretty(interp, v, indent, next_pad, ds) != TCL_OK) { + // keep the default behaviour, if wanted add the -nopadding option + if (!nopadding) { + Tcl_GetStringFromObj(k, &k_len); + if (k_len < max) + Tcl_DStringAppend(ds, key_pad_buf, max-k_len); + } + + if (json_pretty(interp, v, indent, nopadding, next_pad, arrays_inline, ds) != TCL_OK) { Tcl_DictObjDone(&search); retval = TCL_ERROR; goto finally; @@ -1431,6 +1438,7 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad { int i, oc; Tcl_Obj** ov; + int force_inline, force_multiline, should_inline; TEST_OK_LABEL(finally, retval, Tcl_ListObjGetElements(interp, val, &oc, &ov)); @@ -1438,20 +1446,38 @@ int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad Tcl_AppendObjToObj(next_pad, indent); next_pad_str = Tcl_GetStringFromObj(next_pad, &next_pad_len); + // Determine array formatting: inline vs multiline + force_inline = (arrays_inline == 1); + force_multiline = (arrays_inline == 0); + // Auto heuristic: small arrays (<=3 elements) inline by default + should_inline = (!force_multiline) && (force_inline || oc <= 3); + if (oc == 0) { Tcl_DStringAppend(ds, "[]", 2); + } else if (should_inline) { + // Inline format: [1,2,3] + Tcl_DStringAppend(ds, "[", 1); + count = 0; + for (i=0; ijson_true)); + } else if (len == 5 && strcmp(str, "false") == 0) { + replace_tclobj(&elem, JSON_NewJvalObj(JSON_BOOL, l->json_false)); + } else { + // Try to parse as a number + int is_number = (force_json_number(interp, l, objv[i], &forced) == TCL_OK); + + if (is_number) { + // It's a valid JSON number + replace_tclobj(&elem, JSON_NewJvalObj(JSON_NUMBER, forced)); + release_tclobj(&forced); + } else { + // Default to string + // Clear any error message from failed number conversion + Tcl_ResetResult(interp); + replace_tclobj(&elem, JSON_NewJvalObj(JSON_STRING, objv[i])); + } + } + + TEST_OK_LABEL(finally, retval, Tcl_ListObjAppendElement(interp, val, elem)); + } + Tcl_SetObjResult(interp, JSON_NewJvalObj(JSON_ARRAY, val)); + +finally: + release_tclobj(&elem); + release_tclobj(&val); + release_tclobj(&forced); + return retval; +} + //}}} static int jsonDecode(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *const objv[]) //{{{ { @@ -3183,18 +3257,36 @@ static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *c Tcl_Obj* indent = NULL; Tcl_Obj* target = NULL; int argbase = 1; + int compact = 0; + int nopadding = 0; + int arrays_inline = -1; // -1 = default/auto, 0 = multiline, 1 = inline static const char* opts[] = { "-indent", + "-compact", + "-nopadding", + "-arrays", "--", // Unnecessary for this case, but supported for convention NULL }; enum { OPT_INDENT, + OPT_COMPACT, + OPT_NOPADDING, + OPT_ARRAYS, OPT_END_OPTIONS }; + static const char* array_modes[] = { + "inline", + "multiline", + NULL + }; + enum { + ARRAYS_INLINE, + ARRAYS_MULTILINE + }; enum {A_cmd, A_VAL, A_args}; - CHECK_MIN_ARGS_LABEL(finally, code, "pretty ?-indent indent? json_val ?key ...?"); + CHECK_MIN_ARGS_LABEL(finally, code, "pretty ?-indent indent? ?-compact? ?-nopadding? ?-arrays inline|multiline? json_val ?key ...?"); // Consume any leading options {{{ while (argbase < objc) { @@ -3216,18 +3308,40 @@ static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *c argbase += 2; break; + case OPT_COMPACT: + compact = 1; + argbase++; + break; + + case OPT_NOPADDING: + nopadding = 1; + argbase++; + break; + + case OPT_ARRAYS: { + int array_mode; + if (objc - argbase < 2) { + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); + THROW_ERROR_LABEL(finally, code, "missing argument to \"-arrays\""); + } + TEST_OK_LABEL(finally, code, Tcl_GetIndexFromObj(interp, objv[argbase+1], array_modes, "array mode", TCL_EXACT, &array_mode)); + arrays_inline = (array_mode == ARRAYS_INLINE) ? 1 : 0; + argbase += 2; + break; + } + case OPT_END_OPTIONS: argbase++; goto endoptions; default: - THROW_ERROR_LABEL(finally, code, "Unhandled get option idx"); + THROW_ERROR_LABEL(finally, code, "Unhandled pretty option idx"); } } endoptions: if (objc == argbase) { - Tcl_WrongNumArgs(interp, 1, objv, "?-default defaultValue? json_val ?key ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "?-indent indent? ?-compact? ?-nopadding? ?-arrays inline|multiline? json_val ?key ...?"); code = TCL_ERROR; goto finally; } @@ -3239,7 +3353,7 @@ static int jsonPretty(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *c replace_tclobj(&target, objv[argbase]); } - TEST_OK_LABEL(finally, code, JSON_Pretty(interp, target, indent, &pretty)); + TEST_OK_LABEL(finally, code, JSON_Pretty(interp, target, indent, nopadding, compact, arrays_inline, &pretty)); Tcl_SetObjResult(interp, pretty); @@ -3604,6 +3718,7 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co "boolean", "object", "array", + "autoarray", "decode", @@ -3645,6 +3760,7 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co M_BOOLEAN, M_OBJECT, M_ARRAY, + M_AUTOARRAY, M_DECODE, // Debugging M_FREE_CACHE, @@ -3680,6 +3796,7 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co case M_BOOLEAN: return jsonBoolean(cdata, interp, objc-1, objv+1); case M_OBJECT: return jsonObject(cdata, interp, objc-1, objv+1); case M_ARRAY: return jsonArray(cdata, interp, objc-1, objv+1); + case M_AUTOARRAY: return jsonAutoArray(cdata, interp, objc-1, objv+1); case M_DECODE: return jsonDecode(cdata, interp, objc-1, objv+1); case M_ISNULL: return jsonIsNull(cdata, interp, objc-1, objv+1); case M_TEMPLATE: return jsonTemplate(cdata, interp, objc-1, objv+1); @@ -3699,14 +3816,14 @@ static int jsonNRObj(ClientData cdata, Tcl_Interp* interp, int objc, Tcl_Obj *co case M_LEAK_OBJ: Tcl_NewObj(); break; case M_LEAK_INFO: { - unsigned long addr; + Tcl_WideInt addr; Tcl_Obj* obj = NULL; const char* s; int len; CHECK_ARGS(2, "addr"); - TEST_OK(Tcl_GetLongFromObj(interp, objv[2], (long*)&addr)); - obj = (Tcl_Obj*)addr; + TEST_OK(Tcl_GetWideIntFromObj(interp, objv[2], &addr)); + obj = (Tcl_Obj*)(uintptr_t)addr; s = Tcl_GetStringFromObj(obj, &len); fprintf(stderr, "\tLeaked obj: %p[%d] len %d: \"%s\"\n", obj, obj->refCount, len, len < 256 ? s : ""); @@ -4143,6 +4260,7 @@ DLLEXPORT int Rl_json_Init(Tcl_Interp* interp) //{{{ Tcl_CreateObjCommand(interp, ENS "boolean", jsonBoolean, l, NULL); Tcl_CreateObjCommand(interp, ENS "object", jsonObject, l, NULL); Tcl_CreateObjCommand(interp, ENS "array", jsonArray, l, NULL); + Tcl_CreateObjCommand(interp, ENS "autoarray", jsonAutoArray, l, NULL); Tcl_CreateObjCommand(interp, ENS "decode", jsonDecode, l, NULL); Tcl_CreateObjCommand(interp, ENS "isnull", jsonIsNull, l, NULL); Tcl_CreateObjCommand(interp, ENS "template", jsonTemplate, l, NULL); diff --git a/generic/rl_json.decls b/generic/rl_json.decls index b61d977..b4fa8e7 100644 --- a/generic/rl_json.decls +++ b/generic/rl_json.decls @@ -87,7 +87,7 @@ declare 24 generic { int JSON_Normalize(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** normalized) } declare 25 generic { - int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, Tcl_Obj** prettyString) + int JSON_Pretty(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj* indent, int nopadding, int compact, int arrays_inline, Tcl_Obj** prettyString) } declare 26 generic { int JSON_Template(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* dict, Tcl_Obj** res) diff --git a/generic/rl_jsonDecls.h b/generic/rl_jsonDecls.h index a864eb8..3ec69ea 100644 --- a/generic/rl_jsonDecls.h +++ b/generic/rl_jsonDecls.h @@ -87,7 +87,8 @@ EXTERN int JSON_Normalize(Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj**normalized); /* 25 */ EXTERN int JSON_Pretty(Tcl_Interp*interp, Tcl_Obj*obj, - Tcl_Obj*indent, Tcl_Obj**prettyString); + Tcl_Obj*indent, int nopadding, int compact, + int arrays_inline, Tcl_Obj**prettyString); /* 26 */ EXTERN int JSON_Template(Tcl_Interp*interp, Tcl_Obj*template, Tcl_Obj*dict, Tcl_Obj**res); @@ -123,7 +124,7 @@ EXTERN Tcl_Obj* JSON_NewJvalObj(enum json_types type, Tcl_Obj*val); EXTERN Tcl_Obj* JSON_DbNewJvalObj(enum json_types type, Tcl_Obj*val, const char*file, int line); -typedef struct Rl_jsonStubs { +typedef struct TcljsonStubs { int magic; void *hooks; @@ -152,7 +153,7 @@ typedef struct Rl_jsonStubs { int (*jSON_Set) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj* path /* can be NULL */, Tcl_Obj*replacement); /* 22 */ int (*jSON_Unset) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj* path /* can be NULL */); /* 23 */ int (*jSON_Normalize) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj**normalized); /* 24 */ - int (*jSON_Pretty) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*indent, Tcl_Obj**prettyString); /* 25 */ + int (*jSON_Pretty) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj*indent, int nopadding, int compact, int arrays_inline, Tcl_Obj**prettyString); /* 25 */ int (*jSON_Template) (Tcl_Interp*interp, Tcl_Obj*template, Tcl_Obj*dict, Tcl_Obj**res); /* 26 */ int (*jSON_IsNULL) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj* path /* can be NULL */, int*isnull); /* 27 */ int (*jSON_Type) (Tcl_Interp*interp, Tcl_Obj*obj, Tcl_Obj* path /* can be NULL */, enum json_types*type); /* 28 */ diff --git a/generic/rl_jsonInt.h b/generic/rl_jsonInt.h index b191bf3..3be02ee 100644 --- a/generic/rl_jsonInt.h +++ b/generic/rl_jsonInt.h @@ -3,6 +3,10 @@ #define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "rl_json.h" #include "tclstuff.h" #include @@ -18,8 +22,8 @@ #endif #if CBOR #include -#endif #include +#endif #include "tip445.h" #include "names.h" @@ -255,7 +259,7 @@ int apply_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj* actio int build_template_actions(Tcl_Interp* interp, Tcl_Obj* template, Tcl_Obj** actions); int convert_to_tcl(Tcl_Interp* interp, Tcl_Obj* obj, Tcl_Obj** out); int resolve_path(Tcl_Interp* interp, Tcl_Obj* src, Tcl_Obj *const pathv[], int pathc, Tcl_Obj** target, const int exists, const int modifiers, Tcl_Obj* def); -int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, Tcl_Obj* pad, Tcl_DString* ds); +int json_pretty(Tcl_Interp* interp, Tcl_Obj* json, Tcl_Obj* indent, int nopadding, Tcl_Obj* pad, int arrays_inline, Tcl_DString* ds); void foreach_state_free(struct foreach_state* state); #define TEMPLATE_TYPE(s, len, out) \ diff --git a/generic/tip445.h b/generic/tip445.h index a53b808..ecc9ecf 120000 --- a/generic/tip445.h +++ b/generic/tip445.h @@ -1 +1,94 @@ -../teabase/tip445.h \ No newline at end of file +#ifndef _TIP445_H +#define _TIP445_H + +#if TIP445_SHIM +#include +#include +#include + +/* Just enough of TIP445 to build on Tcl 8.6 */ + +#ifndef Tcl_ObjInternalRep +typedef union Tcl_ObjInternalRep { + struct { + void* ptr1; + void* ptr2; + } twoPtrValue; + struct { + void* ptr; + unsigned long value; + } ptrAndLongRep; +} Tcl_ObjInternalRep; +#endif + +#ifndef Tcl_FetchInternalRep +# define Tcl_FetchInternalRep(obj, type) (Tcl_ObjInternalRep*)(((obj)->typePtr == (type)) ? &(obj)->internalRep : NULL) +#endif + +#ifndef Tcl_FreeInternalRep +static inline void Tcl_FreeInternalRep(Tcl_Obj* obj) +{ + if (obj->typePtr) { + if (obj->typePtr && obj->typePtr->freeIntRepProc) + obj->typePtr->freeIntRepProc(obj); + obj->typePtr = NULL; + } +} +#endif + +#ifndef Tcl_StoreInternalRep +static inline void Tcl_StoreInternalRep(Tcl_Obj* objPtr, const Tcl_ObjType* typePtr, const Tcl_ObjInternalRep* irPtr) +{ + Tcl_FreeInternalRep(objPtr); + objPtr->typePtr = typePtr; + memcpy(&objPtr->internalRep, irPtr, sizeof(Tcl_ObjInternalRep)); +} +#endif + +#ifndef Tcl_HasStringRep +# define Tcl_HasStringRep(obj) ((obj)->bytes != NULL) +#endif + +/* +## error: 'Tcl_InitStringRep' defined but not used [-Werror=unused-function] +#ifndef Tcl_InitStringRep +static char* Tcl_InitStringRep(Tcl_Obj* objPtr, const char* bytes, unsigned numBytes) +{ + assert(objPtr->bytes == NULL || bytes == NULL); + + if (numBytes > INT_MAX) { + Tcl_Panic("max size of a Tcl value (%d bytes) exceeded", INT_MAX); + } + + // Allocate + if (objPtr->bytes == NULL) { + // Allocate only as empty - extend later if bytes copied + objPtr->length = 0; + if (numBytes) { + objPtr->bytes = (char*)attemptckalloc(numBytes + 1); + if (objPtr->bytes == NULL) return NULL; + if (bytes) { + // Copy + memcpy(objPtr->bytes, bytes, numBytes); + objPtr->length = (int)numBytes; + } + } else { + //TclInitStringRep(objPtr, NULL, 0); + objPtr->bytes = ""; + objPtr->length = 0; + } + } else { + objPtr->bytes = (char*)ckrealloc(objPtr->bytes, numBytes + 1); + objPtr->length = (int)numBytes; + } + + // Terminate + objPtr->bytes[objPtr->length] = '\0'; + + return objPtr->bytes; +} +#endif*/ + +#endif // TIP445_SHIM + +#endif // _TI445_H diff --git a/teabase b/teabase index b58eac6..b293fee 160000 --- a/teabase +++ b/teabase @@ -1 +1 @@ -Subproject commit b58eac68cc3d07c4f3155a3d390e31fc29fb255b +Subproject commit b293fee95e97cbe6ce1583807e4d2aed8404e2e3 diff --git a/tests/autoarray.test b/tests/autoarray.test new file mode 100644 index 0000000..b85aee0 --- /dev/null +++ b/tests/autoarray.test @@ -0,0 +1,322 @@ +if {"::tcltest" ni [namespace children]} { + package require tcltest + namespace import ::tcltest::* +} + +package require rl_json +namespace import ::rl_json::* + +source [file join [file dirname [info script]] helpers.tcl] + +# Basic functionality tests +test autoarray-1.1 {Empty autoarray} -body { #<<< + json autoarray +} -result {[]} +#>>> +test autoarray-1.2 {Single string} -body { #<<< + json autoarray "hello" +} -result {["hello"]} +#>>> +test autoarray-1.3 {Single number - integer} -body { #<<< + json autoarray 42 +} -result {[42]} +#>>> +test autoarray-1.4 {Single number - float} -body { #<<< + json autoarray 3.14 +} -result {[3.14]} +#>>> +test autoarray-1.5 {Single boolean - true} -body { #<<< + json autoarray true +} -result {[true]} +#>>> +test autoarray-1.6 {Single boolean - false} -body { #<<< + json autoarray false +} -result {[false]} +#>>> + +# Mixed type arrays +test autoarray-2.1 {Mixed types - numbers and strings} -body { #<<< + json autoarray 1 "hello" 2 "world" +} -result {[1,"hello",2,"world"]} +#>>> +test autoarray-2.2 {Mixed types - all types} -body { #<<< + json autoarray 42 "hello" true 3.14 false "world" +} -result {[42,"hello",true,3.14,false,"world"]} +#>>> +test autoarray-2.3 {Mixed types with spaces in strings} -body { #<<< + json autoarray 1 "hello world" 2 "foo bar" +} -result {[1,"hello world",2,"foo bar"]} +#>>> + +# Number format tests +test autoarray-3.1 {Negative integer} -body { #<<< + json autoarray -42 +} -result {[-42]} +#>>> +test autoarray-3.2 {Negative float} -body { #<<< + json autoarray -3.14 +} -result {[-3.14]} +#>>> +test autoarray-3.3 {Zero} -body { #<<< + json autoarray 0 +} -result {[0]} +#>>> +test autoarray-3.4 {Scientific notation - positive exponent} -body { #<<< + json autoarray 1.5e10 +} -result {[15000000000.0]} +#>>> +test autoarray-3.5 {Scientific notation - negative exponent} -body { #<<< + json autoarray 1.5e-10 +} -result {[1.5e-10]} +#>>> +test autoarray-3.6 {Large integer} -body { #<<< + json autoarray 9223372036854775807 +} -result {[9223372036854775807]} +#>>> +test autoarray-3.7 {Very small float} -body { #<<< + json autoarray 0.000001 +} -result {[1e-6]} +#>>> + +# Boolean case sensitivity tests +test autoarray-4.1 {Boolean false case - True (not a boolean)} -body { #<<< + json autoarray True +} -result {["True"]} +#>>> +test autoarray-4.2 {Boolean false case - TRUE (not a boolean)} -body { #<<< + json autoarray TRUE +} -result {["TRUE"]} +#>>> +test autoarray-4.3 {Boolean false case - False (not a boolean)} -body { #<<< + json autoarray False +} -result {["False"]} +#>>> +test autoarray-4.4 {Boolean false case - FALSE (not a boolean)} -body { #<<< + json autoarray FALSE +} -result {["FALSE"]} +#>>> +test autoarray-4.5 {Exact boolean - true} -body { #<<< + json autoarray true +} -result {[true]} +#>>> +test autoarray-4.6 {Exact boolean - false} -body { #<<< + json autoarray false +} -result {[false]} +#>>> + +# Edge cases for numbers in different formats (Tcl interprets these) +test autoarray-5.1 {Octal number with leading zeros} -body { #<<< + json autoarray 007 +} -result {[7]} +#>>> +test autoarray-5.2 {Number with plus sign} -body { #<<< + json autoarray +42 +} -result {[42]} +#>>> +test autoarray-5.3 {Hex number format} -body { #<<< + json autoarray 0x1F +} -result {[31]} +#>>> +test autoarray-5.4 {Octal number format} -body { #<<< + json autoarray 0o17 +} -result {[15]} +#>>> +test autoarray-5.5 {Binary number format} -body { #<<< + json autoarray 0b1010 +} -result {[10]} +#>>> + +# Empty and whitespace strings +test autoarray-6.1 {Empty string} -body { #<<< + json autoarray "" +} -result {[""]} +#>>> +test autoarray-6.2 {String with only spaces} -body { #<<< + json autoarray " " +} -result {[" "]} +#>>> +test autoarray-6.3 {String "true" with spaces} -body { #<<< + json autoarray " true" +} -result {[" true"]} +#>>> +test autoarray-6.4 {String "false" with spaces} -body { #<<< + json autoarray "false " +} -result {["false "]} +#>>> + +# Special string values +test autoarray-7.1 {String "null"} -body { #<<< + json autoarray null +} -result {["null"]} +#>>> +test autoarray-7.2 {String "undefined"} -body { #<<< + json autoarray undefined +} -result {["undefined"]} +#>>> +test autoarray-7.3 {String that looks like object} -body { #<<< + json autoarray {{}} +} -result {["{}"]} +#>>> +test autoarray-7.4 {String that looks like array} -body { #<<< + json autoarray "[]" +} -result {[""]} +#>>> + +# Multiple identical values +test autoarray-8.1 {Multiple true values} -body { #<<< + json autoarray true true true +} -result {[true,true,true]} +#>>> +test autoarray-8.2 {Multiple false values} -body { #<<< + json autoarray false false false +} -result {[false,false,false]} +#>>> +test autoarray-8.3 {Multiple same numbers} -body { #<<< + json autoarray 42 42 42 +} -result {[42,42,42]} +#>>> +test autoarray-8.4 {Multiple same strings} -body { #<<< + json autoarray "test" "test" "test" +} -result {["test","test","test"]} +#>>> + +# Real-world usage examples +test autoarray-9.1 {Array of coordinates} -body { #<<< + json autoarray 10.5 20.3 30.1 40.9 +} -result {[10.5,20.3,30.1,40.9]} +#>>> +test autoarray-9.2 {Array of names} -body { #<<< + json autoarray "Alice" "Bob" "Charlie" +} -result {["Alice","Bob","Charlie"]} +#>>> +test autoarray-9.3 {Array of flags} -body { #<<< + json autoarray true false true true false +} -result {[true,false,true,true,false]} +#>>> +test autoarray-9.4 {Mixed data record} -body { #<<< + json autoarray "John Doe" 30 true "john@example.com" 5.5 +} -result {["John Doe",30,true,"john@example.com",5.5]} +#>>> + +# Comparison with json array command +test autoarray-10.1 {Compare autoarray vs array - strings} -body { #<<< + set auto [json autoarray "hello" "world"] + set manual [json array {string hello} {string world}] + expr {$auto eq $manual} +} -cleanup { + unset -nocomplain auto manual +} -result {1} +#>>> +test autoarray-10.2 {Compare autoarray vs array - numbers} -body { #<<< + set auto [json autoarray 1 2 3] + set manual [json array {number 1} {number 2} {number 3}] + expr {$auto eq $manual} +} -cleanup { + unset -nocomplain auto manual +} -result {1} +#>>> +test autoarray-10.3 {Compare autoarray vs array - booleans} -body { #<<< + set auto [json autoarray true false] + set manual [json array {boolean true} {boolean false}] + expr {$auto eq $manual} +} -cleanup { + unset -nocomplain auto manual +} -result {1} +#>>> +test autoarray-10.4 {Compare autoarray vs array - mixed} -body { #<<< + set auto [json autoarray 42 "hello" true 3.14] + set manual [json array {number 42} {string hello} {boolean true} {number 3.14}] + expr {$auto eq $manual} +} -cleanup { + unset -nocomplain auto manual +} -result {1} +#>>> + +# Type preservation after parsing +test autoarray-11.1 {Number type preserved} -body { #<<< + set arr [json autoarray 42] + json get $arr 0 +} -cleanup { + unset -nocomplain arr +} -result {42} +#>>> +test autoarray-11.2 {String type preserved} -body { #<<< + set arr [json autoarray "hello"] + json get $arr 0 +} -cleanup { + unset -nocomplain arr +} -result {hello} +#>>> +test autoarray-11.3 {Boolean type preserved} -body { #<<< + set arr [json autoarray true] + json get $arr 0 +} -cleanup { + unset -nocomplain arr +} -result {true} +#>>> +test autoarray-11.4 {Type query for number} -body { #<<< + set arr [json autoarray 42] + json type $arr 0 +} -cleanup { + unset -nocomplain arr +} -result {number} +#>>> +test autoarray-11.5 {Type query for string} -body { #<<< + set arr [json autoarray "hello"] + json type $arr 0 +} -cleanup { + unset -nocomplain arr +} -result {string} +#>>> +test autoarray-11.6 {Type query for boolean} -body { #<<< + set arr [json autoarray true] + json type $arr 0 +} -cleanup { + unset -nocomplain arr +} -result {boolean} +#>>> + +# String escaping +test autoarray-12.1 {String with quotes} -body { #<<< + json autoarray {He said "hello"} +} -result {["He said \"hello\""]} +#>>> +test autoarray-12.2 {String with backslashes} -body { #<<< + json autoarray {C:\path\to\file} +} -result {["C:\\path\\to\\file"]} +#>>> +test autoarray-12.3 {String with newline} -body { #<<< + json autoarray "line1\nline2" +} -result {["line1\nline2"]} +#>>> +test autoarray-12.4 {String with tab} -body { #<<< + json autoarray "col1\tcol2" +} -result {["col1\tcol2"]} +#>>> + +# Large arrays +test autoarray-13.1 {Array with 100 numbers} -body { #<<< + set nums [lmap i [lrepeat 100 0] {expr {int(rand() * 1000)}}] + set result [json autoarray {*}$nums] + json length $result +} -cleanup { + unset -nocomplain nums result i +} -result {100} +#>>> +test autoarray-13.2 {Array with 50 strings} -body { #<<< + set strings [lmap i [lrepeat 50 0] {format "string_%d" $i}] + set result [json autoarray {*}$strings] + json length $result +} -cleanup { + unset -nocomplain strings result i +} -result {50} +#>>> + +::tcltest::cleanupTests +return + +# Local Variables: +# mode: tcl +# tab-width: 4 +# End: +# vim: ft=tcl foldmethod=marker foldmarker=<<<,>>> ts=4 shiftwidth=4 diff --git a/tests/helpers.tcl b/tests/helpers.tcl index 13ae0cb..d5ce82e 100644 --- a/tests/helpers.tcl +++ b/tests/helpers.tcl @@ -154,19 +154,14 @@ proc _compare_json {opts j1 j2 {path {}}} { #<<< #>>> proc compare_json args { #<<< - parse_args $args { - -subset {-default none} - j1 {} - j2 {} - } opts - - try { - _compare_json $opts [dict get $opts j1] [dict get $opts j2] - } trap {RL TEST JSON_MISMATCH} {errmsg options} { - return $errmsg - } on ok {} { - return match - } + set opts [list -subset none j1 [lindex $args 0] j2 [lindex $args 1]] + try { + _compare_json $opts [dict get $opts j1] [dict get $opts j2] + } trap {RL TEST JSON_MISMATCH} {errmsg options} { + return $errmsg + } on ok {} { + return match + } } #>>> diff --git a/tests/pretty.test b/tests/pretty.test index 1bd6e51..6c16931 100644 --- a/tests/pretty.test +++ b/tests/pretty.test @@ -4,10 +4,11 @@ if {"::tcltest" ni [namespace children]} { } package require rl_json -package require parse_args -namespace path {::rl_json ::parse_args} +namespace path {::rl_json} -test pretty-1.1 {Basic pretty-print} -body { #<<< +test pretty-jsonPretty-1.1 {} -body {json pretty -indent} -returnCodes error -result {missing argument to "-indent"} -errorCode {TCL ARGUMENT MISSING} +#>>> +test pretty-1.2 {Basic pretty-print} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} } -cleanup { unset -nocomplain o @@ -29,7 +30,7 @@ test pretty-1.1 {Basic pretty-print} -body { #<<< ] }} #>>> -test pretty-1.2 {Basic pretty-print, different indent} -body { #<<< +test pretty-1.3 {Basic pretty-print, different indent} -body { #<<< json pretty -indent " " {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} } -result {{ "foo": null, @@ -49,11 +50,32 @@ test pretty-1.2 {Basic pretty-print, different indent} -body { #<<< ] }} #>>> -test pretty-2.1 {too few args} -body { #<<< +test pretty-1.4 {Basic pretty-print, nopadding} -body { #<<< + json pretty -indent " " -nopadding {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null]}} +} -result {{ + "foo": null, + "empty": {}, + "emptyarr": [], + "hello, world": "bar", + "This is a much longer key": [ + "str", + 123, + 123.4, + true, + false, + null + ] +}} +#>>> +test pretty-jsonPretty-2.1 {} -body {json pretty -- 1} -result 1 +#>>> +test pretty-2.2 {too few args} -body { #<<< json pretty -} -returnCodes error -result {wrong # args: should be "pretty pretty ?-indent indent? json_val ?key ...?"} +} -returnCodes error -result {wrong # args: should be "pretty pretty ?-indent indent? ?-compact? ?-nopadding? ?-arrays inline|multiline? json_val ?key ...?"} #>>> -test pretty-3.1 {path} -body { #<<< +test pretty-jsonPretty-3.1 {} -body {json pretty -indent { }} -returnCodes error -result {wrong # args: should be "pretty ?-indent indent? ?-compact? ?-nopadding? ?-arrays inline|multiline? json_val ?key ...?"} -errorCode {TCL WRONGARGS} +#>>> +test pretty-3.2 {path} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} {This is a much longer key} } -result {[ "str", @@ -67,11 +89,220 @@ test pretty-3.1 {path} -body { #<<< } ]} #>>> -test pretty-3.2 {bad path} -body { #<<< +test pretty-3.3 {bad path} -body { #<<< json pretty {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} bad } -returnCodes error -result {Path element 2: "bad" not found} -errorCode NONE #>>> - +test pretty-jsonPretty-4.1 {} -body {json pretty bad} -returnCodes error -result {Error parsing JSON value: Illegal character at offset 0} -errorCode {RL JSON PARSE {Illegal character} bad 0} +#>>> +test pretty-4.2 {compact output} -body { + json pretty -compact {{"foo":"bar","array":[1,2,3]}} +} -result {{"foo":"bar","array":[1,2,3]}} +#>>> +test pretty-jsonPretty-5.1 {} -body {json pretty -bad} -returnCodes error -result {bad option "-bad": must be *} -errorCode {TCL LOOKUP INDEX option -bad} -match glob +#>>> +test pretty-5.2 {arrays inline mode} -body { + json pretty -arrays inline {{"arr":[1,2,3,4,5]}} +} -result {{ + "arr": [1,2,3,4,5] +}} +#>>> +test pretty-5.3 {arrays multiline mode} -body { + json pretty -arrays multiline {{"arr":[1,2,3]}} +} -result {{ + "arr": [ + 1, + 2, + 3 + ] +}} +#>>> +test pretty-5.4 {arrays default auto mode - small inline} -body { + json pretty {{"small":[1,2,3]}} +} -result {{ + "small": [1,2,3] +}} +#>>> +test pretty-5.5 {arrays default auto mode - large multiline} -body { + json pretty {{"large":[1,2,3,4,5]}} +} -result {{ + "large": [ + 1, + 2, + 3, + 4, + 5 + ] +}} +#>>> +test pretty-6.1 {combine -indent with -arrays inline} -body { + json pretty -indent " " -arrays inline {{"arr":[1,2,3,4,5]}} +} -result {{ + "arr": [1,2,3,4,5] +}} +#>>> +test pretty-6.2 {combine -indent with -arrays multiline} -body { + json pretty -indent " " -arrays multiline {{"arr":[1,2,3]}} +} -result {{ + "arr": [ + 1, + 2, + 3 + ] +}} +#>>> +test pretty-6.3 {tab indent with arrays inline} -body { + json pretty -indent "\t" -arrays inline {{"data":[10,20,30]}} +} -result {{ + "data": [10,20,30] +}} +#>>> +test pretty-6.4 {tab indent with arrays multiline} -body { + json pretty -indent "\t" -arrays multiline {{"data":[10,20]}} +} -result {{ + "data": [ + 10, + 20 + ] +}} +#>>> +test pretty-7.1 {compact overrides indent} -body { + json pretty -compact -indent " " {{"foo":"bar"}} +} -result {{"foo":"bar"}} +#>>> +test pretty-7.2 {compact overrides arrays inline} -body { + json pretty -compact -arrays inline {{"arr":[1,2,3]}} +} -result {{"arr":[1,2,3]}} +#>>> +test pretty-7.3 {compact overrides arrays multiline} -body { + json pretty -compact -arrays multiline {{"arr":[1,2,3,4,5]}} +} -result {{"arr":[1,2,3,4,5]}} +#>>> +test pretty-7.4 {compact with all options} -body { + json pretty -compact -indent "\t" -arrays multiline {{"x":[1,2],"y":{"z":"w"}}} +} -result {{"x":[1,2],"y":{"z":"w"}}} +#>>> +test pretty-8.1 {nested objects with custom indent} -body { + json pretty -indent " " {{"outer":{"inner":{"deep":"value"}}}} +} -result {{ + "outer": { + "inner": { + "deep": "value" + } + } +}} +#>>> +test pretty-8.2 {nested arrays with multiline} -body { + json pretty -arrays multiline {{"matrix":[[1,2],[3,4]]}} +} -result {{ + "matrix": [ + [ + 1, + 2 + ], + [ + 3, + 4 + ] + ] +}} +#>>> +test pretty-8.3 {nested arrays with inline} -body { + json pretty -arrays inline {{"matrix":[[1,2],[3,4]]}} +} -result {{ + "matrix": [[1,2],[3,4]] +}} +#>>> +test pretty-8.4 {mixed nested structures} -body { + json pretty -indent " " {{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}} +} -result {{ + "users": [{ + "name": "Alice", + "age": 30 + },{ + "name": "Bob", + "age": 25 + }] +}} +#>>> +test pretty-9.1 {empty array with default} -body { + json pretty {{"empty":[]}} +} -result {{ + "empty": [] +}} +#>>> +test pretty-9.2 {empty array with arrays inline} -body { + json pretty -arrays inline {{"empty":[]}} +} -result {{ + "empty": [] +}} +#>>> +test pretty-9.3 {empty array with arrays multiline} -body { + json pretty -arrays multiline {{"empty":[]}} +} -result {{ + "empty": [] +}} +#>>> +test pretty-9.4 {empty object} -body { + json pretty {{"empty":{}}} +} -result {{ + "empty": {} +}} +#>>> +test pretty-10.1 {complex structure default formatting} -body { + json pretty {{"api":{"version":"1.0","endpoints":["/user","/data","/login","/logout"]}}} +} -result {{ + "api": { + "version": "1.0", + "endpoints": [ + "/user", + "/data", + "/login", + "/logout" + ] + } +}} +#>>> +test pretty-10.2 {complex structure with indent and inline arrays} -body { + json pretty -indent "\t" -arrays inline {{"config":{"debug":true,"ports":[8080,8081,8082],"name":"server"}}} +} -result {{ + "config": { + "debug": true, + "ports": [8080,8081,8082], + "name": "server" + } +}} +#>>> +test pretty-10.3 {multiple data types} -body { + json pretty {{"string":"text","number":42,"bool":true,"null":null,"array":[1,2]}} +} -result {{ + "string": "text", + "number": 42, + "bool": true, + "null": null, + "array": [1,2] +}} +#>>> +test pretty-11.1 {single space indent} -body { + json pretty -indent " " {{"a":"b"}} +} -result {{ + "a": "b" +}} +#>>> +test pretty-11.2 {large indent} -body { + json pretty -indent " " {{"x":"y"}} +} -result {{ + "x": "y" +}} +#>>> +test pretty-11.3 {three space indent with nested} -body { + json pretty -indent " " {{"outer":{"inner":"val"}}} +} -result {{ + "outer": { + "inner": "val" + } +}} +#>>> test debug-1.1 {Basic debug pretty-print} -body { #<<< json debug {{"foo":null,"empty":{},"emptyarr":[],"hello, world":"bar","This is a much longer key":["str",123,123.4,true,false,null,{"inner": "obj"}]}} } -match regexp -result {^\(0x[0-9a-fA-F]+\[[0-9]+\]+/0x[0-9a-fA-F]+\[[0-9]+\]+ [a-z ]+\){ @@ -91,14 +322,6 @@ test debug-1.1 {Basic debug pretty-print} -body { #<<< } \] }$} -#>>> - -# Coverage golf -test pretty-jsonPretty-1.1 {} -body {json pretty -indent} -returnCodes error -result {missing argument to "-indent"} -errorCode {TCL ARGUMENT MISSING} -test pretty-jsonPretty-2.1 {} -body {json pretty -- 1} -result 1 -test pretty-jsonPretty-3.1 {} -body {json pretty -indent { }} -returnCodes error -result {wrong # args: should be "pretty ?-default defaultValue? json_val ?key ...?"} -errorCode {TCL WRONGARGS} -test pretty-jsonPretty-4.1 {} -body {json pretty bad} -returnCodes error -result {Error parsing JSON value: Illegal character at offset 0} -errorCode {RL JSON PARSE {Illegal character} bad 0} -test pretty-jsonPretty-5.1 {} -body {json pretty -bad} -returnCodes error -result {bad option "-bad": must be *} -errorCode {TCL LOOKUP INDEX option -bad} -match glob json free_cache diff --git a/tests/set.test b/tests/set.test index ce5d737..8da79bb 100644 --- a/tests/set.test +++ b/tests/set.test @@ -4,8 +4,7 @@ if {"::tcltest" ni [namespace children]} { } package require rl_json -package require parse_args -namespace path {::rl_json ::parse_args} +namespace path {::rl_json} source [file join [file dirname [info script]] helpers.tcl] diff --git a/tests/unset.test b/tests/unset.test index 1f0dc34..beb76d9 100644 --- a/tests/unset.test +++ b/tests/unset.test @@ -4,8 +4,7 @@ if {"::tcltest" ni [namespace children]} { } package require rl_json -package require parse_args -namespace path {::rl_json ::parse_args} +namespace path {::rl_json} source [file join [file dirname [info script]] helpers.tcl]